Является ли этот класс Python изменчивым? - PullRequest
2 голосов
/ 26 мая 2020

Является ли этот объект изменяемым?

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()

Он печатает «10:30», даже если я ожидал напечатать «5:30», потому что строки неизменяемы. По сравнению с кодом:

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

он печатает «Привет». Объект Clock ведет себя как список, поэтому я думаю, что он изменяемый, но я не знаю почему. И как в таком случае сделать класс Clock неизменяемым?

Ответы [ 2 ]

1 голос
/ 26 мая 2020

Если вы действительно хотите реализовать что-то похожее на ваше ожидаемое поведение, вы можете использовать шаблон получения / установки:

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 вместо того, чтобы вызывать исключение, но это потенциально могло бы сработать для некоторых, поскольку код «молча терпит неудачу», когда они в противном случае ожидали, что атрибут будет изменен.

Тем не менее: на ответ @ IvanVelichko, базовый атрибут _time все еще может быть переназначен, если пользователь хочет go изо всех сил делать это:

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

В более широком смысле, шаблон получателя / установщика используется гораздо реже в Python, чем что-то вроде Java по указанной выше причине.

1 голос
/ 26 мая 2020

Строки действительно неизменяемы, однако в вашем примере вы меняете не строковую переменную, а атрибут объекта. Атрибуты ссылаются на значение, поэтому, переназначив его, вы просто указываете на другое значение.

Python - очень динамичный c язык, и сложно сделать что-то действительно защищенное от внезапная мутация. Однако существует соглашение об использовании _ в начале имени атрибута, что означает, что он не должен быть доступен и / или изменен каким-либо кодом за пределами класса. И если вы все же хотите иметь атрибут publi c 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, интерпретатору необходимо выполнить внутренний поиск атрибута с именем time в таблице символов (просто причудливое имя для словаря), соответствующего объекту my_time. В то же время, когда вы обращаетесь к локальной переменной a, аналогичный поиск выполняется в другой таблице символов (он же словарь), соответствующей текущей локальной области видимости. Попробуйте этот фрагмент:

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

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

Несмотря на то, что строки неизменяемы, вторая печать выводит foobar.

...