Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/python-3.x/16.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
为什么Python不可变类型(如int、str或tuple)需要使用“\uuuu new\uuuuuuuuu()”,而不仅仅是“\uuuuu init\uuuuuuuuuuuuuuuuu()”?_Python_Python 3.x_Constructor_Subclass - Fatal编程技术网

为什么Python不可变类型(如int、str或tuple)需要使用“\uuuu new\uuuuuuuuu()”,而不仅仅是“\uuuuu init\uuuuuuuuuuuuuuuuu()”?

为什么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(),而不是重

此问题与、和有关,但不是重复的。这些链接不能回答我的问题,几乎回答了我的问题,但没有,因为答案中的代码没有在Python3.6中运行,而且在任何情况下,这里的问题都没有具体说明我在问什么。(见下面我自己的答案

从中,我找到以下文本

\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。