Подтвердить что ты не робот

Класс и списки класса Python

Я до сих пор довольно новичок в 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

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

4b9b3361

Ответ 1

Ответ tlayton - часть истории, но она не объясняет все.

Добавьте

print MyClass.mynum

чтобы еще больше смутить:). Он напечатает "0". Зачем? Поскольку строка

self.mynum += 1

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

История mylist отличается.

self.mylist.append("Hey!")

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

print MyClass.mylist

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

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

Ответ 2

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

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

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

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

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

Ответ 3

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

    mylist = []
    mynum = 0

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

    self.mylist.append("Hey!")

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

    self.mynum += 1

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