2013-10-24 3 views
3

У меня есть этот код:Переменные не найдено в области видимости класса внешней

class Attributes(object): 
    class __metaclass__(type): 
     def __init__(self, cls_name, cls_bases, cls_dict): 
      # super(Attributes.__metaclass__, self) # NameError: global name 'Attributes' is not defined 
      super(__metaclass__, self).__init__(
       cls_name, cls_bases, cls_dict) 

дает

NameError: global name '__metaclass__' is not defined 

Почему __metaclass__ переменное не найден во внешнем объеме?

+0

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

+0

Я пробовал воспроизвести проблему, и выясняется, что вы не можете иметь классы внутри классов, которые имеют методы __init__, которые вызывают супер. Это не только 3-й уровень наследования, даже если вы пытались называть 'super' внутри' Attributes', то это не сработало бы. Вам нужно будет вызвать 'SimpleModel.Attributes', чтобы получить его. –

ответ

2

попробовать это вместо

super(Attributes.__metaclass__, self).__init__(cls_name, cls_bases, cls_dict) 
+0

Спасибо за обходной путь. Я сделал другое обходное решение, поставив рассматриваемый класс на верхний уровень модуля. – warvariuc

+1

Я упростил пример в вопросе. Если вы попытаетесь использовать 'super (Attributes .__ metaclass__, self)' - вы получите 'NameError: глобальное имя 'Атрибуты' не определены'. Поэтому ваше решение не работает. – warvariuc

+0

Warwaruk вправо. «Атрибуты» еще не существуют, когда создается первый экземпляр метакласса: сначала создается метакласс, затем сразу создается экземпляр (автоматически) до завершения внешнего класса. – Alfe

0

Похоже, ответ таков:

Внешняя сфера для метода не тело класса, но внешняя функция, в которой содержится класс или модуль.

Вот почему в случае

class Test(): 
    attr_1 = 'smth' 
    def a_method(self): 
     attr_1 # not visible on this line 

attr_1 не видно внутри метода Test.a_method

И решение заключается в определении метакласса на глобальном уровне:

class AttributesMeta(type): 
    def __init__(self, cls_name, cls_bases, cls_dict): 
     super(__metaclass__, self).__init__(
      cls_name, cls_bases, cls_dict) 

class Attributes(object): 
    __metaclass__ = AttributesMeta 
1

При создании класса , видно только его имя. Его содержимое еще не существует, пока класс не будет создан. Поэтому части внутри класса не могут получить доступ к каким-либо полям класса во время создания. Таким образом, вы должны будете использовать полные имена для обозначения того, что вы хотите получить доступ к полям класса

Вы в настоящее время создания класса SimpleModel и при этом вы создаете класс Attributes и при этом класса __metaclass__. Поскольку пока вы это делаете, класс SimpleModel еще не существует, метод __init__ еще не является частью чего-либо существующего. Сначала он создается, а затем, позже, станет частью класса __metaclass__. Следовательно, это не может знать идентификатор __metaclass__. И так как __metaclass__ также никогда не становится глобальным, при вызове этот идентификатор не может быть известен.

Вот причина, почему вы не имеете в своем объеме в данный момент не __metaclass__, но когда позже __init__ вызывается, только SimpleModel доступен через глобальную область видимости, так что вы можете основывать свое имя на нем: SimpleModel.Attributes.__metaclass__.

+0

Внутри '__init__' уже создан не только класс, но и его экземпляр создается и передается как' self'.В этом случае 'self' является классом, который является экземпляром его метакласса. – warvariuc

+0

@warwaruk: Вы говорите о времени выполнения, я говорю о времени компиляции (так сказать, если действительно можно разделить их на Python). Когда '__init__' получает _compiled_, классы еще не существуют. Это основная причина, по которой Python не позволяет ссылаться на локальные классы без полной квалификации имен. Вопрос: Должен ли я подробно изложить этот вопрос в своем ответе? (Я добавил (надеюсь) поясняющее предложение во втором абзаце.) Обратите внимание, что я говорю о «создании класса», а не о «создании экземпляра» ... – Alfe

+0

Я получаю указанное исключение во время выполнения, потому что после класса «Атрибуты» полностью определенный, он создается путем вызова атрибутов .__ metaclass__ '' __new__' и '__init__'. Создание класса означает создание экземпляра его метакласса. Я думаю, вы ошибаетесь. – warvariuc

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