Я пытался понять обработку 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++, таким образом, объяснение по аналогии (или указывающий, где это ломается) могло бы быть полезным.
Лично я нашел эти документы Шалаба Чатурведи чрезвычайно полезными и информативными по этому вопросу.
bar.num + = 1
- это сокращение для bar.num = bar.num + 1
. Это выбирает переменную класса Foo.num
с правой стороны и присваивает ее переменной экземпляра bar.num
.
В следующем коде 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
, вы увеличиваете член экземпляра.
bar.num += 1
создает новую переменную экземпляра 'num' в экземпляре 'bar', поскольку она еще не существует (и затем добавляет 1 к этому значению)
пример:
class Foo:
def __init__(self):
self.num= 1
bar = Foo()
print bar.num
это печатает 1
print bar.foo
это дает ожидаемую ошибку: Traceback (последний вызов последним): File "", строка 1, в AttributeError: Foo instance не имеет атрибута 'foo'
bar.foo = 5
print bar.foo
, теперь это печатает 5
, поэтому, что происходит в вашем примере: bar.num разрешается как Foo.num, потому что есть только атрибут класса. тогда фактически создается foo.num, потому что вы присваиваете ему значение.
Я думаю, вы только что нашли там ошибку в Python. bar.num + = 1 должен быть ошибкой, вместо этого он создает атрибут num на панели объектов, отличный от Foo.num.
Это действительно странное поведение.