为什么 Python 不可变类型(如 int、str 或元组)需要使用 `__new__()` 而不仅仅是 `__init__()`?
Why do Python immutable types (like int, str, or tuple) need to use `__new__()` instead of just `__init__()`?
这个问题与 this, this, this, and . Those links don't answer my question here. This though 有关,但不是重复=] 3.6 无论如何,问题都不是关于我在这里问的。 (请参阅下面我自己的回答。
从the Python documentation page,我找到了以下文字。
__new__()
is intended mainly to allow subclasses of immutable types (like int, str, or tuple) to customize instance creation. It is also
commonly overridden in custom metaclasses in order to customize class
creation.
但是为什么?为什么我们不能只覆盖 __init__()
而不是必须覆盖 __new__()
?显然,例如 frozenset
,甚至没有实现 __init__()
; 为什么会这样?我从 here 了解到,在极少数情况下,__new__()
和 __init__()
需要做不同的事情,但据我所知,这只是在 pickling 和 unpickling 期间。需要使用 __new__()
而不是 __init__()
的 不可变类型,特别是 是什么?
我是问题 OP,我要回答我自己的问题,因为我想我通过输入找到了答案 half-way。在其他人确认它是正确的之前,我不会将其标记为正确的。
This question here 特别相关,但问题与这个问题不同,虽然答案很有启发性(尽管评论变成了关于 C 和 [=100= 的启发性但深奥的争论] 和 "pythonic"), 应该在这里更清楚地列出来专门解决这个问题。我希望这对未来的读者有所帮助。此答案中的代码已在 Python 3.6.1.
中得到验证
关于不可变 object 的事情是,您显然不想在创建后设置其成员。你在Python中这样做的方法是覆盖__setattr__()
特殊方法到raise
一个错误(AttributeError
),这样人们就不能做像[=22这样的事情=].以下面的自定义不可变 class 为例。
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.
"But what!?",我听到你问,"I didn't set any of its attributes after it's been created!" 啊,但是 是的,你做到了 ,在 __init__()
。由于在 创建 object 之后 调用了 __init__()
,因此行 self.a = a
和 self.b = b
正在设置属性 a
b
在 创建 im
之后。您真正想要的是在创建不可变 object 之前 a
和 b
设置属性。一个明显的方法是首先创建一个 mutable 类型(你 允许 在 __init__()
中设置它的属性,并且然后使 immutable 键入它的 subclass ,并确保实现不可变的 __new__()
方法child class 先构造一个可变的版本,然后再让它不可变,像下面这样。
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!? When did __setattr__()
get called this time?" 事实是,ActuallyImmutable
是 Mutable
的子 class,并且没有明确实现其 __init__()
,parent class 的 __init__()
在 创建 ActuallyImmutable
object 之后自动调用 ,所以总共 parent 的 __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 抱怨我没有打电话给 parent 的 __init__()
,这显然是我们在这里的意图。最后只是为了检查 im
真的是不可变的,验证 im.a = 42
会给你 AttributeError: LOL nope srsly.
.
这个问题与 this, this, this, and
从the Python documentation page,我找到了以下文字。
__new__()
is intended mainly to allow subclasses of immutable types (like int, str, or tuple) to customize instance creation. It is also commonly overridden in custom metaclasses in order to customize class creation.
但是为什么?为什么我们不能只覆盖 __init__()
而不是必须覆盖 __new__()
?显然,例如 frozenset
,甚至没有实现 __init__()
; 为什么会这样?我从 here 了解到,在极少数情况下,__new__()
和 __init__()
需要做不同的事情,但据我所知,这只是在 pickling 和 unpickling 期间。需要使用 __new__()
而不是 __init__()
的 不可变类型,特别是 是什么?
我是问题 OP,我要回答我自己的问题,因为我想我通过输入找到了答案 half-way。在其他人确认它是正确的之前,我不会将其标记为正确的。
This question here 特别相关,但问题与这个问题不同,虽然答案很有启发性(尽管评论变成了关于 C 和 [=100= 的启发性但深奥的争论] 和 "pythonic"), 应该在这里更清楚地列出来专门解决这个问题。我希望这对未来的读者有所帮助。此答案中的代码已在 Python 3.6.1.
中得到验证关于不可变 object 的事情是,您显然不想在创建后设置其成员。你在Python中这样做的方法是覆盖__setattr__()
特殊方法到raise
一个错误(AttributeError
),这样人们就不能做像[=22这样的事情=].以下面的自定义不可变 class 为例。
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.
"But what!?",我听到你问,"I didn't set any of its attributes after it's been created!" 啊,但是 是的,你做到了 ,在 __init__()
。由于在 创建 object 之后 调用了 __init__()
,因此行 self.a = a
和 self.b = b
正在设置属性 a
b
在 创建 im
之后。您真正想要的是在创建不可变 object 之前 a
和 b
设置属性。一个明显的方法是首先创建一个 mutable 类型(你 允许 在 __init__()
中设置它的属性,并且然后使 immutable 键入它的 subclass ,并确保实现不可变的 __new__()
方法child class 先构造一个可变的版本,然后再让它不可变,像下面这样。
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!? When did __setattr__()
get called this time?" 事实是,ActuallyImmutable
是 Mutable
的子 class,并且没有明确实现其 __init__()
,parent class 的 __init__()
在 创建 ActuallyImmutable
object 之后自动调用 ,所以总共 parent 的 __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 抱怨我没有打电话给 parent 的 __init__()
,这显然是我们在这里的意图。最后只是为了检查 im
真的是不可变的,验证 im.a = 42
会给你 AttributeError: LOL nope srsly.
.