我是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 = 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__()
会自动调用父类的,因此总共调用了父类两次,一次在创建之前(确定),一次 在之后 (这 *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.
。