此问题与this,this,this和this有关,但并非重复。这些链接在这里没有回答我的问题。 This though,几乎回答了我的问题,但没有,因为答案中的代码不能在Python 3.6中运行,而且在任何情况下,问题都不是我在这里问的具体问题。 (见下面我自己的答案。
从the Python documentation page,我找到以下文字。
__new__()
主要用于允许不可变类型的子类(如int,str或tuple)自定义实例创建。也是 通常在自定义元类中重写以自定义类 创建
但为什么?为什么我们不能覆盖__init__()
而不必覆盖__new__()
?显然,frozenset
例如甚至没有实现__init__()
; 为什么?我从here了解到,在极少数情况下,__new__()
和__init__()
需要做不同的事情,但据我所知,这只是在酸洗和去除油漆时。什么是不可变类型需要使用__new__()
而不是__init__()
?
答案 0 :(得分:6)
我是OP的问题,我将回答我自己的问题,因为我认为我在键入它的过程中找到了答案。在其他人确认它是正确的之前,我不打算将其标记为正确。
This question here特别相关,但问题与这个问题不一样,虽然答案非常有启发性(尽管这些评论变成了关于C和Python以及“pythonic”的启发但却深奥的论点) ,这里应该更清楚地阐述,以专门解决这个问题。我希望这将有助于未来的读者。本答案中的代码已在Python 3.6.1中得到验证。
关于不可变对象的问题是,显然你不想在创建成员后设置它们。在Python中执行此操作的方法是将__setattr__()
特殊方法覆盖为raise
错误(AttributeError
),以便人们无法执行my_immutable_object.x = 3
之类的操作。以下面的自定义不可变类为例。
class Immutable(object):
def __init__(self, a, b):
self.a = a
self.b = b
def __setattr__(self, key, value):
raise AttributeError("LOL nope.")
让我们尝试使用它。
im = Immutable(2, 3)
print(im.a, im.b, sep=", ")
输出:
AttributeError: LOL nope.
“但是什么!?”,我听到你问道,“创建之后我没有设置任何属性!”啊,但是的,你做了,在__init__()
。由于在创建对象后__init__()
被称为,因此行self.a = a
和self.b = b
正在设置属性a
和b
< em>在创建im
之后。您真正想要的是在创建不可变对象之前设置属性a
和b
。一个显而易见的方法是首先创建可变类型(允许允许在__init__()
中设置的属性),然后创建不可变键入它的子类,并确保实现不可变子类的__new__()
方法首先构造一个可变版本,然后使其成为不可变的,如以下
class Mutable(object):
def __init__(self, a, b):
self.a = a
self.b = b
class ActuallyImmutable(Mutable):
def __new__(cls, a, b):
thing = Mutable(a, b)
thing.__class__ = cls
return thing
def __setattr__(self, key, value):
raise AttributeError("LOL nope srsly.")
现在让我们试试吧。
im = ActuallyImmutable(2, 3)
print(im.a, im.b, sep=", ")
输出:
AttributeError: LOL nope srsly.
“WTF !?这次是什么时候召唤__setattr__()
?”问题是,ActuallyImmutable
是Mutable
的子类,如果没有明确实现其__init__()
,则父类__init__()
会在后自动调用创建ActuallyImmutable
对象,因此父母的__init__()
总共被调用两次,一次在创建im
之前(可以),一次之后(这是不行)。所以让我们再试一次,这次重写AcutallyImmutable.__init__()
。
class Mutable(object):
def __init__(self, a, b):
print("Mutable.__init__() called.")
self.a = a
self.b = b
class ActuallyImmutable(Mutable):
def __new__(cls, a, b):
thing = Mutable(a, b)
thing.__class__ = cls
return thing
# noinspection PyMissingConstructor
def __init__(self, *args, **kwargs):
# Do nothing, to prevent it from calling parent's __init__().
pass
def __setattr__(self, key, value):
raise AttributeError("LOL nope srsly.")
现在应该可以了。
im = ActuallyImmutable(2, 3)
print(im.a, im.b, sep=", ")
输出:
2, 3
好,它有效。哦,不要担心# noinspection PyMissingConstructor
,这只是一个PyCharm黑客阻止PyCharm抱怨我没有打电话给父母的__init__()
,这显然是我们打算在这里。最后只是检查im
是否真的不可变,请确认im.a = 42
会给您AttributeError: LOL nope srsly.
。