2010-04-09 5 views
11

я писал метакласса и случайно сделал это так:В чем разница между типом и типом .__ new__ в python?

class MetaCls(type): 
    def __new__(cls, name, bases, dict): 
     return type(name, bases, dict) 

... вместо того, чтобы, как это:

class MetaCls(type): 
    def __new__(cls, name, bases, dict): 
     return type.__new__(cls, name, bases, dict) 

Что именно представляет собой разницу между этими двумя метаклассами? И более конкретно, что заставило первый работать некорректно (некоторые классы не были вызваны метаклассом)?

ответ

6

В первом примере вы создаете новый класс:

>>> class MetaA(type): 
...  def __new__(cls, name, bases, dct): 
...   print 'MetaA.__new__' 
...   return type(name, bases, dct) 
...  def __init__(cls, name, bases, dct): 
...   print 'MetaA.__init__' 
... 
>>> class A(object): 
...  __metaclass__ = MetaA 
... 
MetaA.__new__ 
>>> 

в то время как во втором случае вы вызываете родителя __new__:

>>> class MetaA(type): 
...  def __new__(cls, name, bases, dct): 
...   print 'MetaA.__new__' 
...   return type.__new__(cls, name, bases, dct) 
...  def __init__(cls, name, bases, dct): 
...   print 'MetaA.__init__' 
... 
>>> class A(object): 
...  __metaclass__ = MetaA 
... 
MetaA.__new__ 
MetaA.__init__ 
>>> 
1

Все это довольно хорошо описано here.

Если вы не вернете правильный тип объекта, нет смысла определять настраиваемый метакласс.

+1

Но это вопрос, на который я обращаюсь: почему «type» не возвращает правильный тип объекта? Я знаю, что мне нужно использовать метод '__new__' подкласса, но как это отличается от использования' type'? –

+1

Как тип должен магически знать, какого объекта вы хотите его создать? –

+0

хорошо, потому что я говорю. Я передал имя, базы и диктофон. Какую еще часть информации мне нужно дать? –

3
return type(name, bases, dict) 

Что вы получите обратно от это новый type, а не MetaCls экземпляр вообще. Следовательно, ваши методы, определенные в MetaCls (включая __init__), никогда не могут быть вызваны.

type.__new__ будет называться как часть создания этого нового типа, да, но значение cls происходит в этой функции будет type и не MetaCls.

3
class MyMeta(type): 
    def __new__(meta, cls, bases, attributes): 
     print 'MyMeta.__new__' 
     return type.__new__(meta, cls, bases, attributes) 
    def __init__(clsobj, cls, bases, attributes): 
     print 'MyMeta.__init__' 

class MyClass(object): 
    __metaclass__ = MyMeta 
    foo = 'bar' 

Другой способ достижения тот же результат:

cls = "MyClass" 
bases =() 
attributes = {'foo': 'bar'} 
MyClass = MyMeta(cls, bases, attributes) 

MyMeta является вызываемым, поэтому Python будет использовать специальный метод __call__.

Python будет искать __call__ в MyMeta «s тип (который type в нашем случае)

«Для новых классов, неявных вызовов специальных методов только гарантированно работать правильно, если они определены по типу объекта, а не в словаре экземпляра объекта»

MyClass = MyMeta(...) интерпретируется как:

my_meta_type = type(MyMeta) 
MyClass = my_meta_type.__call__(MyMeta, cls, bases, attributes) 

Внутри type.__call__() я представляю себе что-то вроде этого:

MyClass = MyMeta.__new__(MyMeta, cls, bases, attributes) 
meta_class = MyClass.__metaclass__ 
meta_class.__init__(MyClass, cls, bases, attributes) 
return MyClass 

MyMeta.__new__() будет решить, как MyClass построен:

type.__new__(meta, cls, bases, attributes) установит правильный метакласса (который MyMeta) для MyClass

type(cls, bases, attributes) установит метакласс по умолчанию (который является типом) для MyClass

4

Пожалуйста, обратитесь к аннотации ниже, надейтесь, что это поможет.

class MetaCls(type): 
    def __new__(cls, name, bases, dict): 
     # return a new type named "name",this type has nothing 
     # to do with MetaCls,and MetaCl.__init__ won't be invoked 
     return type(name, bases, dict) 

class MetaCls(type): 
    def __new__(cls, name, bases, dict): 
     # return a new type named "name",the returned type 
     # is an instance of cls,and cls here is "MetaCls", so 
     # the next step can invoke MetaCls.__init__ 
     return type.__new__(cls, name, bases, dict) 
8

Первое, что вам нужно выяснить, это то, как работает object.__new__().

Здесь из documentation:

object.\__new__(cls[, ...])

Вызывается, чтобы создать новый экземпляр класса cls. __new__() - это статический метод (специально обрезанный, поэтому вам не нужно объявлять его как таковой), который принимает класс, для которого экземпляр был запрошен как его первый аргумент. Остальные аргументы - это те, которые передаются объекту-конструктору (вызов класса). Возвращаемое значение __new__() должно быть экземпляром нового объекта (обычно это экземпляр cls).

Типичные реализации создать новый экземпляр класса, вызывая __new__() метод суперкласса, используя super(currentclass, cls).__new__(cls[, ...]) с соответствующими аргументами и затем изменить вновь созданный экземпляр по мере необходимости, перед его возвращением.

Если __new__() возвращает экземпляр cls, затем __init__() метода нового экземпляра будет вызываться как __init__(self[, ...]), где self является новым экземпляром, а остальные аргументы такие же, как были переданы __new__().

Если __new__() не возвращает экземпляр cls, затем __init__() метод нового экземпляра не будет вызван.

__new__() предназначен, главным образом, чтобы подклассы неизменных типов (как int, str или tuple), чтобы настроить создание экземпляра. Также обычно переопределяется в пользовательских метаклассах, чтобы настроить класс .

Так в мг. По answer, бывший не вызывает функции __init__ а второй вызывает функцию __init__ после вызова __new__.

+0

Как 'type .__ new__' связан с' object .__ new__'? Я не получил эту часть. – Nishant

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