Python在__new__期间创建对象时设置属性

Python set attributes during object creation in __new__

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

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

以便:

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

但是,我不知道如何对普通(非元)类执行相同的操作,这会在使用uu setattr_uuuuuu时导致问题:

1
2
3
4
5
6
7
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')

不幸的是:

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

在uuu new_uuuuuo的bar中,我从object()中得到一个完全成熟的类实例,任何属性访问都会通过正常的查找规则,在本例中调用uu setattr_uuuu。元类foo避免了这一点,因为type()将在低级创建期间返回实例之前设置属性,而object()则不会。

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


您必须通过调用super或根object__setattr__,明确地绕过自己类的__setattr__。所以你会改变:

1
obj.customAttr = someVal

到:

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

一种不太一般的方法(不适用于基于__slots__的类)是使用dict操作直接分配给__dict__

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

第一种方法是新的__slots__ed uuid.UUID现在使用的方法;在它成为__slots__ed之前,它使用了第二种方法。在这两种情况下都需要这样做,因为他们使用相同的__setattr__技巧使类型尽可能不可变(而不必麻烦将tuple子类化,即la typing.NamedTuple)。