【问题标题】:Can't dynamically bind __repr__/__str__ to a class created with type [duplicate]无法将 __repr__/__str__ 动态绑定到使用类型 [重复] 创建的类
【发布时间】:2014-02-19 16:54:48
【问题描述】:

我正在为 python 滚动我自己的 Enum 类,我无法让 __str____repr__ 正常工作,我做错了什么?

In [2]: x = Enum(X=1, Y=2)

In [3]: x
Out[3]: common.utils.Enum

In [4]: str(x)
Out[4]: "<class 'common.utils.Enum'>"

In [5]: x.__repr__
Out[5]: <bound method type.reprfun of <class 'common.utils.Enum'>>

In [6]: x.__repr__()
Out[6]: 'Enum(Y=2, X=1)' 

代码本身:

def Enum(*args, **kwargs):
    enums = dict(zip(args, range(len(args))), **kwargs)
    def reprfun(self):
        res = 'Enum(' +\
            ', '.join(map(lambda x: '{}={}'.format(x[0],x[1]), enums.items())) +\
            ')'
        return res

    reverse = dict((value, name) for name, value in enums.items())
    typedict = enums.copy()
    typedict['name'] = reverse
    instance = type('Enum', (), typedict)
    instance.__repr__ = types.MethodType(reprfun, instance)
    instance.__str__ = types.MethodType(reprfun, instance)
    return instance

【问题讨论】:

  • 你应该将__repr__绑定到类,而不是实例。
  • 您使用的是内置枚举 (docs.python.org/3.4/library/enum.html)、它的后端端口还是其他实现? - nm - 我看到你在滚动你自己的。你应该看看python源代码中实际枚举类的实现。

标签: python types


【解决方案1】:

特殊方法必须添加到类中,而不是实例中。任何特殊方法总是在类型上查找。

如果 Python 不能这样工作,则不能使用 repr(ClassObj),因为它会调用 ClassObj.__repr__ 方法,该方法期望 self 作为第一个参数。所以 Python 改为调用 type(obj).__repr__(obj)

来自datamodel documentation

对于新型类,特殊方法的隐式调用只有在对象类型上定义时才能保证正常工作,而不是在对象的实例字典中。

[...]

这种行为背后的基本原理在于许多特殊方法,例如 hash() 和 repr() 由所有对象实现,包括类型对象。如果这些方法的隐式查找使用常规查找过程,则在类型对象本身上调用它们时会失败:

>>>
>>> 1 .__hash__() == hash(1)
True
>>> int.__hash__() == hash(int)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: descriptor '__hash__' of 'int' object needs an argument

以下作品:

typedict = enums.copy()
typedict.update({
    'name': reverse,
    '__repr__': reprfun,
    '__str__': reprfun,
})

instance = type('Enum', (), typedict)
return instance

你确实要小心命名; instance 在此处绑定到 类对象,而不是实例。因此,该名称具有误导性。您可能想使用clsclassobj 或类似名称来代替instance

演示:

>>> x = Enum(X=1, Y=2)
>>> x
<class '__main__.Enum'>
>>> x.__repr__
<unbound method Enum.reprfun>
>>> x()
Enum(Y=2, X=1)
>>> str(x())
'Enum(Y=2, X=1)'
>>> repr(x())
'Enum(Y=2, X=1)'

【讨论】:

  • 我试了你的建议还是不行,repr没有调用我提供的函数。
  • @piotr:我测试过,它确实有效。你确定你正在为你返回的类创建一个instance吗?
  • 所以我所说的实例是类类型。非常感谢,您能否编辑您的答案以减少误导。
  • @piotr:这是您自己将类型命名为instance,这在此处具有误导性。 :-) 我已经在回答中说明了这一点。
猜你喜欢
  • 2013-08-29
  • 1970-01-01
  • 1970-01-01
  • 2014-08-04
  • 2017-04-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多