2012-09-16 3 views
2

Мне нужна помощь, чтобы понять, как работает инициализация python. У меня есть класс (Bar) с другим классом (Foo) как поле/переменная. Когда я пытаюсь инициализировать эту переменную непосредственно в Bar (не в классе __init__), все экземпляры Bar указывают на то же Foo. Но если у меня есть метод __init__, как в Bar2, каждый экземпляр Bar2 будет иметь уникальный экземпляр Foo. Что здесь происходит?Инициализация поля вне __init__

class Foo(): 
    number = 0 

class Bar(): 
    foo = Foo() 

class Bar2(): 
    foo = None 

    def __init__(self): 
     self.foo = Foo() 

first = Bar() 
second = Bar() 

print "Bar" 
print first 
print second 
print first.foo 
print second.foo 

first = Bar2() 
second = Bar2() 

print "\nBar2" 
print first 
print second 
print first.foo 
print second.foo 

выход будет, например, быть:

Bar 
<\__main__.Bar instance at 0x025B2AF8> 
<\__main__.Bar instance at 0x025B2B20> 
<\__main__.Foo instance at 0x004A3AA8> 
<\__main__.Foo instance at 0x004A3AA8> 

Bar2 
<\__main__.Bar2 instance at 0x025B2B48> 
<\__main__.Bar2 instance at 0x025B2AF8> 
<\__main__.Foo instance at 0x025B2B70> 
<\__main__.Foo instance at 0x025B2B98> 

Использование Bar оба экземпляра будет ссылаться на тот же экземпляр Foo. Зачем?

Edit: Исправлена ​​ошибка с печатью first.foo дважды Бар. Результирующее поведение по-прежнему наблюдается на выходе.

+0

Не пишите 'class Foo():' в Python 2.x. Это создаст старый класс стиля. Ты почти наверняка этого не хочешь. Вместо этого вы хотите [новый стиль] (http://docs.python.org/reference/datamodel.html#newstyle). Для этого напишите 'class Foo (object):'. – pillmuncher

+0

Я не знал об этом, спасибо, что я тоже. На самом деле я должен был знать, так как это произошло до моей первой встречи с Python. –

ответ

2

Python - динамический язык. В статических языках, таких как Java, компилятор читает код, находит определения классов, вычисляет, являются ли они правильными и генерирует некоторый код соответственно. В python определение класса (или определение функции) является просто инструкцией как любая другая, например, назначение переменной. Синтаксис немного отличается.

При определении класса интерпретатор запускает определение класса, то есть он запускает весь код после строки класса. Если он находит определения функций, он также запускает их, то есть определяет функции и связывает их с именем функции. Поскольку определения классов и функций являются утверждениями как любое другое задание, вы также можете использовать их во многих местах. Например, как следующее:

def foo(): 
    class A: pass 
    a = A() 
    a.msg = "Hello" 
    return a 

Поскольку Питон утка напечатал (если она крякает как утка и выглядит как один, это один), пользователи функции Foo не должны даже знать, что класс они просто должны знать, что foo возвращает объект с сообщением msg.Вы бы использовать его как это:

a = foo() 
print a.msg 

Таким образом, в вашем примере, когда определение бара выполнено, операторы классов выполняются в том числе создания объекта Foo. Когда выполняется определение Bar2, операторы классов запускают среди них определение функции с именем init. Python использует это как имя вызываемой функции, когда создается объект (после вызова другой функции __new__, но это не так).

Итак, определение класса (код внутри класса, где Bar создает объект Foo) запускается только один раз при вводе класса. __init__ вызывается снова и снова каждый раз, когда создается новый объект, поэтому в Bar2 создание Foo выполняется снова и снова.

«foo = None», насколько я могу судить, лишний, это действительно не нужно. В python вы можете добавлять переменные экземпляра из любого места, даже извне класса, и определенно изнутри __init__.

+0

Вы правы, что __foo = None__ не нужно. Я оставлю это в примере, так как я положил его туда в первую очередь. Я не совсем понимаю, что вы объясните, почему я вижу это поведение. –

+0

Я добавил разъяснения. –

+0

Легче понять, спасибо. –

2

Bar.foo - переменная класса. Он инициализируется один раз, когда класс создается.

(Обратите внимание, что ваш код ALS печати first.foo дважды, поэтому не удивительно, что выход не одно и то же.)

+0

Исправлена ​​ошибка в примере, все еще такое же поведение. –

+0

@ HåkonK.Olafsen: Да, потому что это переменная класса. Он хранится в классе, а не в отдельных экземплярах. Комментарий об ошибке был только побочным примечанием, поэтому я помещал его в круглые скобки. –

+0

Это было легче понять. Благодарю. –

0
first = Bar() 
second = Bar() 

print "Bar" 
print first 
print second 
print first.foo 
print first.foo 

Здесь вы печатаете first.foo дважды, поэтому тот же объект Foo печатается.

first = Bar2() 
second = Bar2() 

print "\nBar2" 
print first 
print second 
print first.foo 
print second.foo 

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

class Bar2(): 
foo = None 

def __init__(self): 
    self.foo = Foo() 

class Bar(): 
def __init__(self): 
    self.foo = Foo() 

В BAR2, все объекты BAR2, будут иметь объект Foo, указывающий на тот же объект, построенный на строительстве последнего построенного объекта BAR2.

В баре все объекты foo будут уникальными для каждого объекта бара, если не указано иное.

+0

В моем примере была небольшая ошибка с печатью first.foo, но мой реальный код этого не сделал, вот как я нашел это поведение. Я думаю, вы могли бы переключиться на __Bar__ и __Bar2__ в своем ответе. Когда я запускаю мой обновленный пример (правильная печать), вывод все тот же. Это __Bar__, который создает статическую версию Foo? –

0

Классы также являются объектами, и они имеют свой собственный набор переменных. Не только это, но когда Python не может найти переменную в объекте, он смотрит внутри класса, чтобы увидеть, может ли он найти его там, и именно это он использует. Так как класс разделяется между всеми объектами этого класса, то и переменные внутри этого класса.

Смежные вопросы