Предварительное объявление функций Python внутри классов

Я впервые разбираюсь в Python и застрял здесь:

class A:
    def __init__(self):
        a = foo("baa")

class B(A):
    b = foo("boo")

def foo(string):
    return string

В этот момент я загружаю указанный выше файл (с именем classes), и происходит следующее:

$ python
Python 2.6.1 (r261:67515, Jun 24 2010, 21:47:49) 
[GCC 4.2.1 (Apple Inc. build 5646)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> from classes import *
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "classes.py", line 5, in <module>
    class B(A):
  File "classes.py", line 6, in B
    b = foo("boo")
NameError: name 'foo' is not defined

Обратите внимание, что ошибка в классе B, где foo вызывается напрямую, а не из __init__. Обратите также внимание на то, что я еще не создал экземпляр класса B.

Первый вопрос:

  • Почему он возвращает ошибку? Я не создаю экземпляр класса.

Идем дальше. «Проблема» решается перемещением определения foo() на несколько строк выше:

def foo(string):
    return string

class A:
    def __init__(self):
        a = foo("baa")

class B(A):
    b = foo("boo")

Теперь я могу сделать

>>> x = B()
>>> x.b
'boo'

но я не могу сделать

>>> y = A()
>>> y.a
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: A instance has no attribute 'a'

Дальнейшие вопросы:

  • Что я не понял про __init__?

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

Кстати, я стремлюсь реализовать DSL, но это в основном предлог для изучения python.


person lorenzog    schedule 06.03.2011    source источник


Ответы (3)


Во-первых, в классе B функция foo() вызывается перед объявлением. A не имеет этой проблемы, потому что foo() вызывается только при создании экземпляра класса - после определения функции foo.

На ваш второй вопрос y.a не подойдет, потому что вы не сказали self.a = foo('stirng'). a = foo('stirng') создает только переменную a в области __ init __.

Идеальная функция __ init __ назначает переменные экземпляру self.

person Dimitry    schedule 06.03.2011
comment
Это строка, набираемая очень быстро. - person Dimitry; 06.03.2011
comment
спасибо за второй ответ. Должно быть, я где-то это пропустил. Это также отвечает на некоторые вопросы о масштабе. Что касается первого вопроса: я думал, что foo() будет вызываться только после создания экземпляра класса B. Я был неправ. Спасибо! - person lorenzog; 06.03.2011

Что вы не поняли о __init__, так это то, что он вызывается только тогда, когда вы создаете экземпляр (т.е. создаете экземпляр) класса A. Поскольку нигде в вашем коде вы фактически не создали экземпляр этого класса, вы никогда не вызываете его инициализатор и, следовательно, никогда не сталкиваетесь с предварительным объявлением.

В общем, предварительные объявления не являются проблемой в Python, потому что ссылка оценивается только тогда, когда вы вызываете необходимую функцию. Проблема здесь в том, что определения классов выполняются во время определения. В частности, вот что происходит, когда интерпретатор Python встречает оператор class:

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

Конечно, вам не нужно знать это, чтобы определить класс; вам просто нужно помнить, что все внутри определения класса будет выполнено, когда класс будет определен впервые! (Это редко является проблемой, потому что переменные уровня класса не так уж распространены, и обычно их не нужно устанавливать динамически во время определения.)

Эта проблема также возникает в определениях функций: когда вы делаете

def foo(x=bar()): pass

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


Попробуйте запустить следующий код и посмотрите, проясняет ли он ситуацию:

class A():
    def __init__(self):
        self.a = foo()

a = A()

def foo():
    pass
person Katriel    schedule 06.03.2011
comment
код не запускается: NameError: global name 'foo' is not defined. Но я понимаю, что вы имеете в виду. - person lorenzog; 07.03.2011
comment
@lorenzog: да, в том-то и дело: происходит то же самое, что и выше, если вы действительно пытаетесь инициализировать класс, с тех пор вы ищете имя, которое еще не было определено. - person Katriel; 07.03.2011

class A:
    def __init__(self):
        a = foo("baa")

class B(A):
    b = foo("boo")

a является атрибутом экземпляра - каждый A имеет свою собственную переменную, а foo("baa") оценивается только при создании объекта A.

b является атрибутом класса - каждый объект B использует одну и ту же копию b, и foo("boo") вычисляется при определении класса.

В общем, вы можете делать ссылки на функции и классы, которые еще не существуют, если они определены до того, как вы оцените ссылку (т.е. до того, как вы на самом деле попытаетесь вызвать функцию ).

person Hugh Bothwell    schedule 06.03.2011