Почему объекты, созданные в течение __init__, не видят своего создателя? - PullRequest
2 голосов
/ 28 января 2012

Ну, я новичок в SO, OOP и Python, поэтому, пожалуйста, будьте осторожны;)

Я искал темы и объяснения, связанные с этой проблемой, в другом месте и не нашел ни одного.Буду признателен за любую помощь.

Пример кода:

class Zeus(object):
    def __init__(self):
        self.name='zeus'
        self.maze=Maze()
        self.maze.get_zeus_name_1()
        self.maze.get_zeus_name_2(self)
        self.get_name_1()
        self.get_name_2(self)

    def get_name_1(self):
        try:
            print zeus.name
        except:
            print "impossible!?"

    def get_name_2(self,scope):
        print scope.name

class Maze(object):
    def get_zeus_name_1(self):
        try:
            print zeus.name
        except:
            print "can't be done!?"

    def get_zeus_name_2(self,scope):
        print scope.name

zeus=Zeus()
print 'now external calls:'
zeus.maze.get_zeus_name_1()
zeus.maze.get_zeus_name_2(zeus)
zeus.get_name_1()
zeus.get_name_2(zeus)

Вывод:

can't be done!?
zeus
impossible!?
zeus
now external calls:
zeus
zeus
zeus
zeus

Во время создания zeus, если метод __init__создает экземпляр другого класса, maze, этот новый экземпляр не может получить доступ к своему объекту-создателю, zeus (если ему не передан self).(Кроме того, если метод __init__ вызывает метод в своем собственном классе get_name_1, этот метод также не может получить доступ к его атрибутам объектов (если только ему не передано self).)

Однако, ПОСЛЕоба объекта созданы, второй объект, maze, теперь может распознавать и получать доступ к объекту-создателю zeus.

. Такое поведение вызвало у меня некоторую путаницу и трудности, так как я работал над некоторым кодом, гдевсе было инициализировано и запущено из последовательности __init__ - теперь я полагаю, что этого лучше избегать ..

Мои вопросы:

  1. Почему это так?
  2. Можно ли избежать проблем, которые могут возникнуть в результате этого, лучше всего исключить создание экземпляров из вызовов __init__?
  3. Существуют ли другие конструктивные последствия?
  4. Передача self в новый экземплярКажется, что это может создать проблемы из-за самореференции, следует ли этого также избегать?
  5. Другие интересные последствия / Я что-то упустил?

ЧемKS за вашу помощь.

Ответы [ 3 ]

5 голосов
/ 28 января 2012

get_zeus_name_1() пытается получить доступ к глобальной переменной zeus, которая еще не была инициализирована в момент вызова get_zeus_name_1().

get_zeus_name_2() принимает аргумент (scope) и получает доступ к тому, что работает. Он не пытается получить доступ к глобальной переменной zeus.

Та же история с get_name_1() и get_name_2().

Я думаю, что ключевой момент - понять, как python выполняет эту строку:

zeus=Zeus()

Эта строка говорит Python: выполнить метод Zeus() (который является другим именем для метода __init__(self) в классе Zeus), а затем назначить объект, возвращаемый этим методом, глобальной переменной zeus .

Python сначала выполняет метод Zeus(), а затем, после , метод init завершил выполнение и возвратил объект-экземпляр класса Zeus, python назначает этот объект глобальной переменной zeus. Таким образом, глобальная переменная zeus не была определена до тех пор, пока не завершится метод init, поэтому get_name_1 () и get_zeus_name_1 () не могут получить к ней доступ.

get_name_2 () и get_zeus_name_2 () обращаются к тому же экземпляру объекта класса Zeus, к которому пытаются получить get_name_1 () и get_zeus_name_1 (), но они получают к нему доступ через параметр, который передается им, а не через глобальную переменную чтобы они не столкнулись с проблемой.

Вот более простой пример, демонстрирующий точно такую ​​же проблему:

>>> class Foo:
...     def __init__(self):
...         self.name = 'foobar'
...         print self.name
...         print foo.name
... 
>>> foo = Foo()
foobar
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 5, in __init__
NameError: global name 'foo' is not defined
>>> 

Строка print self.name работает просто отлично (эквивалентно get_name_2 () и get_zeus_name_2 ()), но строка print foo.name падает (эквивалентно get_name_2 () и get_zeus_name_2 ()).

4 голосов
/ 28 января 2012

Я полагаю, это из-за природы Python - он выполняет исходный код "строка за строкой".В этой строке: zeus=Zeus() вы создаете объект и вызывается метод __init__.Когда он выполняется, глобальной переменной zeus пока нет.Я предполагаю, что вы поймали NameError: имя 'zeus' не определено (кстати, ловить каждое исключение - плохая практика except: ...; вместо этого вы должны ловить только те исключения, которые ожидаете; except NameError: ...).

Вам нужно запомнитьчто в Python все происходит во время выполнения.Здесь

x = 1 # x is just created, y 2 isn't created yet, you can't access it
y = 2 # y is just created, you can use it

Это также может вызвать циклический импорт, когда модуль A импортирует модуль B, а модуль B импортирует что-то из модуля A, но модуль A еще не создан - ошибка.

В вашем случае было бы лучше передать ссылку на оригинальный объект Zeus на объект Maze, как в __init__, и обращаться к нему из Maze только после полного создания объекта Zeus.

1 голос
/ 28 января 2012

Давайте посмотрим, смогу ли я привести пример, чтобы немного помочь.

Если мы хотим работать с кучкой греческих богов, нам нужно начать с очень простого GreekGod класса:

class GreekGod(object):

    def __init__(self, name):
        self.name = name

zeus = GreekGod('Zeus')
athena = GreekGod('Athena')
print zeus.name

Если теперь мы хотим сделать больше, например, дать каждому греческому богу свой эпитет , нам нужно добавить атрибут эпитета в наш класс:

class GreekGod(object):

    def __init__(self, name, epithet):
        self.name = name
        self.epithet = epithet

    def get_catch_frase(self):
        return self.name + ' ' + self.epithet

zeus = GreekGod('Zeus', 'Father of the gods')
print zeus.get_catch_frase()  # -> Zeus Father of the gods

Как вы можете видеть, метод get_catch_frase возвращает строку, не печатает эту строку, такое поведение ожидается, когда вы вызываете метод "get_something", потому что можно ожидать, что что-то будет возвращено, и ничего больше.

Я также надеюсь, что вы видите роль self, которая представляет экземпляр объекта внутри класса.

...