Прямое объявление Python функций внутри классов - PullRequest
3 голосов
/ 06 марта 2011

Я впервые обхожусь с 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.

Ответы [ 3 ]

6 голосов
/ 06 марта 2011

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

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

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

1 голос
/ 07 марта 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") вычисляется при определении класса.

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

1 голос
/ 06 марта 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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...