为什么Python不可变类型(如int、str或tuple)需要使用“\uuuu new\uuuuuuuuu()”,而不仅仅是“\uuuuu init\uuuuuuuuuuuuuuuuu()”?
此问题与、和有关,但不是重复的。这些链接不能回答我的问题,几乎回答了我的问题,但没有,因为答案中的代码没有在Python3.6中运行,而且在任何情况下,这里的问题都没有具体说明我在问什么。(见下面我自己的答案 从中,我找到以下文本为什么Python不可变类型(如int、str或tuple)需要使用“\uuuu new\uuuuuuuuu()”,而不仅仅是“\uuuuu init\uuuuuuuuuuuuuuuuu()”?,python,python-3.x,constructor,subclass,Python,Python 3.x,Constructor,Subclass,此问题与、和有关,但不是重复的。这些链接不能回答我的问题,几乎回答了我的问题,但没有,因为答案中的代码没有在Python3.6中运行,而且在任何情况下,这里的问题都没有具体说明我在问什么。(见下面我自己的答案 从中,我找到以下文本 \uuuuuuuuuuuuuuuuuuuuuuuuuuuuu()主要用于允许不可变类型的子类(如int、str或tuple)自定义实例创建 通常在自定义元类中重写以自定义类 创造 但是为什么?为什么我们不能直接重写\uuuuuu init\uuuuuuu(),而不是重
\uuuuuuuuuuuuuuuuuuuuuuuuuuuuu()
主要用于允许不可变类型的子类(如int、str或tuple)自定义实例创建
通常在自定义元类中重写以自定义类
创造
但是为什么?为什么我们不能直接重写
\uuuuuu init\uuuuuuu()
,而不是重写\uuuu new\uuuuuu()
?显然,比如说frozenset
,甚至没有实现\uuuuuuuu init()
,?我从中了解到,在一些罕见的情况下,\uuuuuuuuuuuuuuuu new()
和\uuuuuuuuu init
被要求做不同的事情,但据我所知,这只是在酸洗和解酸洗的过程中。特别是不可变类型需要使用\uuuu new\uuu()
而不是\uu init\uuuuuuu()的原因是什么
?我是问题OP,我将回答我自己的问题,因为我想我是在键入答案的中途找到答案的。在其他人确认答案正确之前,我不会将其标记为正确
特别相关,但问题与这个问题不同,尽管答案很有启发性(尽管这些评论变成了关于C、Python和“pythonic”的启发性但深奥的论点),应该在这里更清楚地说明这个问题。我希望这将有助于未来的读者。这个答案中的代码已经在Python 3.6.1中得到验证
关于不可变对象,很明显,您不希望在创建它之后设置它的成员。在Python中这样做的方法是重写\uuuu setattr\uuuu()
特殊方法,以引发错误(AttributeError
),这样人们就不能做像my\u immutable\u 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.
AttributeError: LOL nope srsly.
2, 3
“但是什么!?”,我听到你问,“在它被创建之后,我没有设置它的任何属性!”啊,但是是的,你在\uuuu init\uu()
中设置了
在创建对象后被调用,行self.a=a
和self.b=b
在创建im
之后设置属性a
和b
。您真正想要的是在创建不可变对象之前设置属性a
和b
。这是一个显而易见的方法也就是说,首先创建一个可变类型(允许您在\uuuu init\uuuuuuuuuuuu()
中设置其属性),然后使不可变类型成为其子类,并确保实现不可变子类的\uuuuuu new\uuuuuuuuuuu()
方法,以首先构造一个可变版本,然后使其不可变,如下所示
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=", ")
im = ActuallyImmutable(2, 3)
print(im.a, im.b, sep=", ")
输出:
AttributeError: LOL nope.
AttributeError: LOL nope srsly.
2, 3
“WTF!?这次什么时候调用了\uuu setattr\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu()
”问题是,实际上是\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
在创建ActuallyImmutable
对象后自动调用,因此总的来说,父对象的\uuuu init\uuu()
会被调用两次,一次是在创建im
之前(这是正常的),一次是之后(这是不正常的)。因此,让我们再试一次,这次重写AcutallyImmutable.\uu init
现在应该可以了
输出:
AttributeError: LOL nope.
AttributeError: LOL nope srsly.
2, 3
很好,它成功了。哦,不要担心#noinspection PyMissingConstructor
,这只是PyCharm黑客阻止PyCharm抱怨我没有调用父级的uu init()
,这显然是我们在这里的意图。最后,为了检查im
是否真的是不可变的,请验证im.a=42
是否会给您提供AttributeError:LOL nope srsly。
注意,它不是真的不可变的,只是“很难”变异。您可以始终覆盖“不可变的纯Python类”的属性使用object.\uuuuSetAttr\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu对象.\uuuuuuuuuuSetAttr\uuuuuuuuuuuuuuuuuuuuim'a',10)
或者在您的情况下,甚至使用可变。\uu。