Составное присваивание переменным класса и экземпляра Python - PullRequest
5 голосов
/ 11 марта 2010

Я пытался понять, как Python обрабатывает переменные класса и экземпляра. В частности, я нашел этот ответ весьма полезным. В основном это говорит о том, что если вы объявите переменную класса, а затем сделаете присваивание [instance].property, вы будете присваивать совершенно другую переменную - одну в другом пространстве имен из переменной класса.

Итак, я подумал - если я хочу, чтобы у каждого экземпляра моего класса был член с некоторым значением по умолчанию (скажем, ноль), я должен сделать это так:

class Foo:
    num = 0

или как это?

class Foo:
    def __init__(self):
        self.num = 0

Исходя из того, что я прочитал ранее, я думаю, что во втором примере будет инициализироваться «правильная» переменная (экземпляр вместо переменной класса). Тем не менее, я считаю, что первый метод тоже работает очень хорошо:

class Foo:
    num = 0

bar = Foo()
bar.num += 1 # good, no error here, meaning that bar has an attribute 'num'
bar.num
>>> 1
Foo.num
>>> 0 # yet the class variable is not modified! so what 'num' did I add to just now?

Итак ... почему это работает? Что я не получаю? FWIW, мое предварительное понимание ООП пришло из C ++, поэтому объяснение по аналогии (или указание, где оно ломается) может быть полезным.

Ответы [ 5 ]

5 голосов
/ 11 марта 2010

Лично я нашел эти документы Шалабха Чатурведи чрезвычайно полезными и информативными по этому вопросу.

bar.num += 1 является сокращением для bar.num = bar.num + 1. Это берет переменную класса Foo.num справа и назначает ее переменной экземпляра bar.num.

2 голосов
/ 11 марта 2010

В следующем коде num является членом класса.

class Foo:
    num = 0

Эквивалент C ++ будет что-то вроде

struct Foo {
  static int num;
};

int Foo::num = 1;

class Foo:
    def __init__(self):
        self.num = 0

self.num является членом экземпляра (self является экземпляром Foo).

В C ++ это было бы что-то вроде

struct Foo {
  int num;
};

Я считаю, что Python позволяет вам иметь члена класса и члена экземпляра с одинаковыми именами (C ++ этого не делает). Поэтому, когда вы делаете bar = Foo(), bar является экземпляром Foo, то есть с bar.num += 1 вы увеличиваете член экземпляра.

0 голосов
/ 03 июня 2012

При поиске именно этого вопроса высоко поднялись и вопрос StackOverflow, и два (довольно старых, но действительных) слайда Гвидо ван Россума ( 1 , 2 ). Слайды Гвидо утверждают, что это поведение связано с порядком поиска в Python для доступа к атрибуту (в приведенном выше примере num). Я подумал, что было бы неплохо соединить их вместе прямо здесь:)

0 голосов
/ 11 марта 2010

Я думаю, вы только что нашли ошибку в Python. bar.num + = 1 должно быть ошибкой, вместо этого он создает атрибут num в панели объектов, отличный от Foo.num.

Это действительно странное поведение.

0 голосов
/ 11 марта 2010
bar.num += 1

создает новую переменную экземпляра 'num' в экземпляре 'bar', потому что она еще не существует (а затем добавляет 1 к этому значению)

пример:

class Foo:
  def __init__(self):
    self.num= 1

bar = Foo()
print bar.num

это печатает 1

print bar.foo

это дает ожидаемую ошибку: Traceback (последний вызов был последним): Файл "", строка 1, в AttributeError: Экземпляр Foo не имеет атрибута 'foo'

bar.foo = 5
print bar.foo

теперь печатается 5

так что происходит в вашем примере: bar.num разрешается как Foo.num, потому что есть только атрибут класса. тогда foo.num фактически создается, потому что вы присваиваете ему значение.

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