Область и списки классов Python - PullRequest
10 голосов
/ 01 июля 2010

Я все еще довольно новичок в Python, и мой опыт работы с OO происходит с Java. Итак, у меня есть некоторый код, написанный на Python, который действует очень необычно для меня, учитывая следующий код:

class MyClass():
        mylist = []
        mynum = 0

        def __init__(self):
                # populate list with some value.
                self.mylist.append("Hey!")
                # increment mynum.

                self.mynum += 1

a = MyClass()

print a.mylist
print a.mynum

b = MyClass()

print b.mylist
print b.mynum

Запуск этого приводит к следующему выводу:

['Hey!']
1
['Hey!', 'Hey!']
1

Ясно, что я ожидаю, что переменные класса приведут к одинаковым точным данным и одинаковым точным выводам ... Похоже, я нигде не могу найти нигде, что отличает список от, скажем, строки или числа, почему Список ссылается на тот же список из первого экземпляра в последующих? Очевидно, я, вероятно, неправильно понимаю какую-то механику области действия или механику создания списка ..

Ответы [ 3 ]

8 голосов
/ 01 июля 2010

Ответ Тлейтона является частью истории, но не все объясняет.

Добавить

print MyClass.mynum

, чтобы стать еще более запутанным :). Будет напечатано «0». Зачем? Потому что линия

self.mynum += 1

создает переменную экземпляра и впоследствии увеличивает ее. Он не увеличивает переменную class .

История с Милистом другая.

self.mylist.append("Hey!")

не будет создавать список. Он ожидает, что переменная с функцией 'append' существует. Поскольку экземпляр не имеет такой переменной, он в конечном итоге ссылается на переменную из класса, который существует , поскольку вы инициализировали его. Как и в Java, экземпляр может «неявно» ссылаться на переменную класса. Предупреждение типа «Классовые поля должны ссылаться на класс, а не на экземпляр» (или что-то в этом роде; с тех пор, как я увидел его в Java), это будет в порядке. Добавить строку

print MyClass.mylist

чтобы проверить этот ответ:).

Вкратце: вы инициализируете переменные класса и обновляете переменные экземпляра. Экземпляры могут ссылаться на переменные класса, но некоторые операторы «update» автоматически создадут переменные экземпляра для вас.

5 голосов
/ 01 июля 2010

Я считаю, что разница в том, что += - это назначение (точно так же, как = и +), в то время как append меняет объект на месте.

    mylist = []
    mynum = 0

Это назначает некоторые переменные класса, один раз, во время определения класса.

    self.mylist.append("Hey!")

Это изменяет значение MyClass.mylist, добавляя строку.

    self.mynum += 1

Это то же самое, что и self.mynum = self.mynum + 1, то есть он присваивает self.mynum (элемент экземпляра). Чтение из self.mynum переходит к члену класса, поскольку в это время нет члена экземпляра с таким именем.

5 голосов
/ 01 июля 2010

То, что вы делаете здесь, это не просто создание переменной класса. В Python переменные, определенные в теле класса, приводят как к переменной класса («MyClass.mylist»), так и к переменной экземпляра («a.mylist»). Это отдельные переменные, а не просто разные имена для одной переменной.

Однако, когда переменная инициализируется таким образом, начальное значение оценивается только один раз и передается переменным каждого экземпляра. Это означает, что в вашем коде переменная mylist каждого экземпляра MyClass ссылается на один объект списка.

Разница между списком и числом в этом случае заключается в том, что, как и в Java, примитивные значения, такие как числа, копируются при передаче из одной переменной в другую. Это приводит к поведению, которое вы видите; даже если инициализация переменной оценивается только один раз, 0 копируется, когда она передается в переменную каждого экземпляра. Как объект, однако, список не имеет такой вещи, поэтому все ваши вызовы append () поступают из одного и того же списка. Попробуйте вместо этого:

class MyClass():
    def __init__(self):
        self.mylist = ["Hey"]
        self.mynum = 1

Это приведет к тому, что значение будет оцениваться отдельно при каждом создании экземпляра. Очень сильно в отличие от Java, вам не нужны объявления тела класса для сопровождения этого фрагмента; присваивания в __init __ () служат всем необходимым объявлением.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...