2016-01-01 4 views
0

Python (3 и 2) не позволяет ссылаться на класс внутри своего тела (за исключением методов):Почему Python не позволяет ссылаться на класс внутри его определения?

class A: 
    static_attribute = A() 

Это поднимает NameError во второй строке, потому что 'A' is not defined, а в этом

class A: 
    def method(self): 
     return A('argument') 

работает нормально. На других языках, например Java, первое не представляет проблемы, и это выгодно во многих ситуациях, например, в реализации синглетонов.

Почему это невозможно в Python? Каковы причины этого решения?

EDIT: я редактировал my other question так он просит только пути «обойти» это ограничение, в то время как эти вопросы просит его мотивации/технические детали.

+0

Это происходит потому, что первый случай использования, который вы показываете, происходит до вызова конструктора. –

+2

Почему? Поскольку имя не привязано до тех пор, пока не будет выполнено определение класса. –

ответ

1

Оператор класса выполняется так же, как и любое другое утверждение. Ваш первый пример (примерно) эквивалентно

a = A() 
A = type('A',(), {'static_attribute': a}) 

Первая линия, очевидно, поднимает NameError, потому что A еще не связан ни к чему.

В вашем втором примере A не не ссылается, пока method фактически называется, к тому времени A действительно относится к классу.

4

Python является динамически типизированным языком и выполняет операции при импорте модуля. Не существует скомпилированного определения объекта класса, объект создается путем выполнения инструкции class.

Python по существу выполняет тело класса как функцию, беря результирующее локальное пространство имен для формирования тела. Таким образом, следующий код:

class Foo(object): 
    bar = baz 

переводится примерно на:

def _Foo_body(): 
    bar = baz 
    return locals() 
Foo = type('Foo', (object,), _Foo_body()) 

В результате, имя для класса не назначен, пока class оператор не завершил выполнение. Вы не можете использовать имя внутри оператора класса до тех пор, пока этот оператор не будет завершен, таким же образом, что вы не сможете использовать функцию до тех пор, пока инструкция def не завершит ее определение.

Это означает, что вы можете динамически создавать классы на лету:

def class_with_base(base_class): 
    class Foo(base_class): 
     pass 
    return Foo 

Вы можете хранить эти классы в списке:

classes = [class_with_base(base) for base in list_of_bases] 

Теперь у вас есть список классов с не глобального имена, ссылающиеся на них в любом месте. Без глобального имени я не могу полагаться на такое имя, существующее в методе; return Foo не будет работать, так как нет Foo глобально для этого.

Далее, Python поддерживает концепцию, называемую метаклассом , который производит классы так же, как класс производит экземпляры. Функция type() выше является метаклассом по умолчанию, но вы можете предоставить свой собственный для класса. Метакласс свободен, чтобы создавать все, что ему нравится, даже вещи, которые являются бит-классами! Поскольку такой Python не может, перед тем, знать, какой объект выдает оператор class, и не может делать предположений о том, что в конечном итоге будет связывать имя, используемое. См. What is a metaclass in Python?

Все это не то, что вы можете сделать на статически типизированном языке, таком как Java.

0

По существу, класс не существует, пока его полное определение не будет скомпилировано полностью. Это похоже на конечные блоки, которые явно написаны на других языках, а Python использует неявные концевые блоки, которые определяются отступом.

+0

Конечно, вопрос о том, почему это работает, - это функции, которые являются объектами сами. Например, 'def fun(): a = fun()'. – timgeb

+0

@timgeb: потому что функция не выполняется * немедленно *. Оператор 'class' * является *, прежде чем имя будет связано. –

+0

Оператор 'class' заставляет его тело выполнять; инструкция 'def' не делает. – chepner

0

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

Декоратор @classmethod аннотирует метод, который будет передан тип класса, вместо обычного экземпляра класса (self). Это похоже на статический метод Java (также есть декоратор @staticmethod, который немного отличается).

Для одноточечного, вы можете получить доступ к экземпляру класса для хранения экземпляра объекта (Атрибуты, определяемые на уровне класса являются поля определены как статические в классе Java):

class A(object): 
    instance = None 

    @classmethod 
    def get_singleton(cls): 
     if cls.instance is None: 
      print "Creating new instance" 
      cls.instance = cls() 

     return cls.instance 


>>> a1 = A.get_singleton() 
Creating new instance 

>>> a2 = A.get_singleton() 

>>> print a1 is a2 
True 

Вы можете также использовать класс методы для создания «статических» методов в стиле Java:

class Name(object): 

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

    @classmethod 
    def make_as_victoria(cls): 
     return cls("Victoria") 

    @classmethod 
    def make_as_stephen(cls): 
     return cls("Stephen") 


>>> victoria = Name.make_as_victoria() 
>>> stephen = Name.make_as_stephen() 

>>> print victoria.name 
Victoria 
>>> print stephen.name 
Stephen 
-1

Ответ «только потому что».

Он не имеет ничего общего с системой типов Python или динамическим. Это связано с порядком, в котором инициализируется новый введенный тип.

Несколько месяцев назад я разработал систему объекта для языка TxR, в котором это работает:

 
1> (defstruct foo nil (:static bar (new foo))) 
# 
2> (new foo) 
#S(foo) 
3> *2.bar 
#S(foo) 

Здесь bar статический слот («переменная класса») в foo. Он инициализируется выражением, которое создает foo.

Почему это работает можно понять из function-based API для конкретизации нового типа, где инициализация статического класса выполняется с помощью функции, которая передается в. defstruct макросъемки составляет вызов make-struct-type, в котором выражение концы (new foo) вверх в теле анонимной функции, которая передается для аргумента static-initfun . Эта функция вызывается после того, как тип уже зарегистрирован под символом foo.

Мы могли бы легко исправить C implementation of make_struct_type так, чтобы это ломалось.Последние несколько строк этой функции являются:

 
    sethash(struct_type_hash, name, stype); 

    if (super) { 
     mpush(stype, mkloc(su->dvtypes, super)); 
     memcpy(st->stslot, su->stslot, sizeof (val) * su->nstslots); 
    } 

    call_stinitfun_chain(st, stype); 

    return stype; 
    } 

call_stinifun_chain делает инициализацию, которая заканчивает оценку (new foo) и хранить его в bar статическом слоте, а sethash вызова, что регистрирует тип под своим именем.

Если мы просто отменим порядок, в котором эти функции вызывают, язык и система типов будут по-прежнему совпадать, и почти все будет работать по-прежнему. Тем не менее, спецификатор слота (:static bar (new foo)) не сработает.

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

Я не могу думать о какой-либо причине, чтобы foo не был известен в то время, когда этот тип структуры инициализируется, не говоря уже о серьезной причине. Для статической конструкции для создания экземпляра является законным. Например, мы могли бы использовать его для создания «singleton».

Это похоже на ошибку в Python.

+0

* Это похоже на ошибку в Python *. Нет, Python делает явный выбор для разделения * объектов * и * имен *. Имена ссылаются на объекты, например теги, привязанные к воздушному шару. Классы не имеют * * привязаны к имени, вы можете безопасно хранить их в списке и удалять глобальное имя. Или вы можете создать несколько имен, относящихся к одному классу (это происходит все время при импорте). Таким образом, вы помещаете операцию привязки где-то в середине процесса создания объекта. –

+0

Также см. [* Факты и мифы об именах и знаках Python *] (http://nedbatchelder.com/text/names.html). –

+0

@MartijnPieters Даже на языке, в котором имена типа/класса являются обычными привязками лексических переменных, область охвата может позволить конструкциям видеть привязку переменной. – Kaz