为什么Python不可变类型(如int,str或tuple)需要使用__new __()而不是__init __()?

为什么Python不可变类型(如int,str或tuple)需要使用__new __()而不是__init __()?,第1张

为什么Python不可变类型(如int,str或tuple)需要使用__new __()而不是__init __()?

我是OP的问题,我将回答我自己的问题,因为我认为我在键入它的过程中发现了答案。在其他人确认它正确之前,我不会将其标记为正确。

这里的这个问题特别相关,但是这个问题与这个问题并不相同,尽管答案很有启发性(尽管注释变成了关于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
创建的
im
。你真正想要的是设置属性
a
b
之前 创建的不可变对象。一种明显的方法是首先创建一个
可变 类型( 允许 在其中设置其属性
__init__()
),然后将 不可变 类型作为其 子类 ,并确保实现
__new__()

不可变子类的方法首先构造一个可变版本,然后使其变为不可变,如下所示。

class Mutable(object):    def __init__(self, a, b):        self.a = a        self.b = bclass 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 = bclass 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

很好,很有效。哦,不用担心

# noinspectionPyMissingConstructor
,这只是一个PyCharm骇客,它可以阻止PyCharm抱怨我没有打电话给父母的
__init__()
,这显然是我们打算在这里做的。最后,只是要检查它是否
im
确实是不变的,请验证是否
im.a= 42
可以给您
AttributeError: LOL nope srsly.



欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5667572.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-16
下一篇 2022-12-16

发表评论

登录后才能评论

评论列表(0条)

保存