1

当使用 __new__ 自定义元类的创建时,我们可以将属性传递给 type().__new__ 方法,该方法将在对象返回之前对其进行设置,例如

class Foo(type):    
    def __new__(cls, name, bases, attrs):
        attrs['customAttr'] = 'someVal' 
        return type.__new__(cls, name, bases, attrs)

以便:

>> Foo.__dict__
{'customeAttr': 'someVal', ...}

但是我不知道如何对普通(非元)类做同样的事情,这在使用 __setattr__ 时会导致问题:

class Bar(object):
    def __new__(cls, someVal):
        obj = object().__new__(cls) # cant pass custom attrs
        obj.customAttr = someVal # obj is already a Bar and invokes __setattr__
        return obj

    def __setattr__(*args): raise Exception('read-only class')

所以不幸的是:

>>> Bar(42)
...
Exception: read-only class

在 Bar 的 __new__ 中,我从 object() 中获取了一个完全成熟的类实例,并且任何属性访问都通过正常的查找规则,在这种情况下调用 __setattr__。元类 Foo 避免了这种情况,因为 type() 将在低级创建期间返回实例之前设置属性,而 object() 不会。

有没有办法将属性传递给 object() 或者是另一种我可以用作从 __new__ 返回的实例的另一种类型,它允许在属性成为完整的类实例之前设置属性?我对在实例创建后设置 __class__ 之类的解决方案不感兴趣。

4

1 回答 1

2

__setattr__您必须通过调用super或 root来明确绕过您自己的课程object __setattr__。所以你会改变:

obj.customAttr = someVal

至:

object.__setattr__(obj, 'customAttr', someVal)

一种不太通用的方法(不适用于__slots__基于类)是直接分配给__dict__usingdict操作:

obj.__dict__['customAttr'] = someVal  # Equivalently: vars(obj)['customAttr'] = someVal

第一种方法是new -ed现在__slots__uuid.UUID使用的方法;在它成为__slots__-ed 之前,它使用了第二种方法。在这两种情况下,这都是必需的,因为它们使用相同的__setattr__技巧使类型尽可能不可变(无需麻烦子类化tuplea latyping.NamedTuple)。

于 2019-01-25T04:11:22.800 回答