这是 Python Class 可变的吗?

Is this Python Class mutable?

这个对象是可变的吗?

class Clock(object):
    def __init__(self, time):
        self.time = time
    def print_time(self):
        print(self.time)

boston_clock = Clock('5:30')
paris_clock = boston_clock
paris_clock.time = '10:30'
boston_clock.print_time()

即使我期望打印“5:30”,它也会打印“10:30”,因为字符串是不可变的。对比代码:

a = "Hello"
b = a
b += " World"
print(a)

它打印 "Hello"。 对象 Clock 表现为一个列表,所以我认为它是可变的,但我不知道为什么。在那种情况下,我怎样才能使时钟 class 不可变?

字符串确实是 immutable,但是,在您的示例中,您更改的不是字符串变量,而是对象的属性。该属性引用一个值,因此通过重新分配它,您只需使其指向另一个值。

Python 是一种非常动态的语言,很难使某些东西真正免受突然突变的影响。但是,有一个约定,在属性名称的开头使用 _,表示不应访问 and/or 由 class 之外的任何代码修改。如果你仍然想要 public time 属性,你可以将其设为 属性:

class Clock:
    def __init__(self, time):
        self._time = time

    @property
    def time(self):
        return self._time

    def print_time(self):
        print(self._time)

UPD: 顺便说一句,在 Python 中,变量与属性没有太大区别。要访问 my_clock.time,解释器需要在对应于 my_time 目的。同时,当您访问局部变量 a 时,在与当前局部范围相对应的另一个符号 table (又名字典)中正在执行类似的查找。试试这个片段:

a = "Hello"
b = a
b += " World"
print(a)

locals()['a'] = "foobar"
print(a)

即使字符串是 immutable,第二个打印输出 foobar.

如果你真的想强制执行你预期的行为,你可以使用 getter/setter 模式:

class Clock(object):
    def __init__(self, time):
        self._time = time
    @property
    def time(self):
        print(self._time)
    @time.setter
    def time(self, value):
        raise AttributeError("Clock time does not support re-assignment")

用法:

>>> c = Clock("5:30")
>>> c.time
5:30
>>> c.time = "10:30"
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 9, in time
AttributeError: Clock time does not support re-assignment

您也可以只 pass 而不是引发异常,但是当他们以其他方式期望更改属性时,这可能会以代码 "failing silently" 的形式出现。

那是说:对于@IvanVelichko 的回答,如果用户想要不厌其烦地这样做,底层 _time 属性仍然可以重新分配给:

>>> c._time = "10:30"
>>> c.time
10:30

更广泛地说,由于上述原因,getter/setter 模式在 Python 中的使用频率远低于 Java。