无法将 __repr__/__str__ 动态绑定到使用 type 创建的类 [英] Can't dynamically bind __repr__/__str__ to a class created with type

查看:22
本文介绍了无法将 __repr__/__str__ 动态绑定到使用 type 创建的类的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

在[2]中:x = Enum(X=1, Y=2)在 [3] 中:x出[3]:common.utils.Enum在 [4] 中:str(x)输出 [4]:"在 [5] 中:x.__repr__Out[5]:在 [6] 中:x.__repr__()输出[6]:'枚举(Y = 2,X = 1)'

代码本身:

def Enum(*args, **kwargs):枚举 = dict(zip(args, range(len(args))), **kwargs)def reprfun(self):res = '枚举(' +\', '.join(map(lambda x: '{}={}'.format(x[0],x[1]), enums.items())) +\')'返回资源reverse = dict((value, name) for name, value in enums.items())typedict = enums.copy()typedict['name'] = 反向instance = type('Enum', (), typedict)instance.__repr__ = types.MethodType(reprfun, instance)instance.__str__ = types.MethodType(reprfun, instance)返回实例

解决方案

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

如果 Python 不能那样工作,你就不能使用 repr(ClassObj),因为它会调用 ClassObj.__repr__ 方法,它期望 self 作为第一个参数.所以 Python 调用 type(obj).__repr__(obj) 代替.

来自 数据模型文档:

<块引用>

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

[...]

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

<预><代码>>>>>>>1 .__hash__() == hash(1)真的>>>int.__hash__() == hash(int)回溯(最近一次调用最后一次):文件<stdin>",第 1 行,在 <module> 中类型错误:int"对象的描述符__hash__"需要一个参数

以下工作:

typedict = enums.copy()typedict.update({'名称':反向,'__repr__':reprfun,'__str__':reprfun,})instance = type('Enum', (), typedict)返回实例

不过,您确实要小心命名;instance 在这里绑定到一个类对象,而不是一个实例.因此,该名称具有误导性.您可能需要使用 clsclassobj 或类似的东西来代替 instance.

演示:

<预><代码>>>>x = 枚举(X=1,Y=2)>>>X<class '__main__.Enum'>>>>x.__repr__<未绑定方法Enum.reprfun>>>>X()枚举(Y=2,X=1)>>>str(x())'枚举(Y = 2,X = 1)'>>>代表(x())'枚举(Y = 2,X = 1)'

I'm rolling my own Enum class for python and I'm having trouble getting __str__ and __repr__ to work correctly, what am I doing wrong?

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)' 

The code itself:

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

解决方案

Special methods must be added to the class, not to the instance. Any special method is always looked up on the type.

If Python didn't work like that, you couldn't use repr(ClassObj) as that'd call the ClassObj.__repr__ method which would expect self as a first argument. So Python calls type(obj).__repr__(obj) instead.

From the datamodel documentation:

For new-style classes, implicit invocations of special methods are only guaranteed to work correctly if defined on an object’s type, not in the object’s instance dictionary.

[...]

The rationale behind this behaviour lies with a number of special methods such as hash() and repr() that are implemented by all objects, including type objects. If the implicit lookup of these methods used the conventional lookup process, they would fail when invoked on the type object itself:

>>>
>>> 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

The following works:

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

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

You do want to be careful with the naming though; instance is bound to a class object here, not an instance. The name is thus misleading. You may want to use cls or classobj or similar instead of instance.

Demo:

>>> 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__/__str__ 动态绑定到使用 type 创建的类的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆