В чем разница между типом и типом .__ new__ в python? - PullRequest
13 голосов
/ 09 апреля 2010

Я писал метакласс и случайно сделал это так:

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 ]

7 голосов
/ 04 июня 2016

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

Вот это из документации :

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) могли настраивать создание экземпляров. Это также обычно переопределяется в пользовательских метаклассах для настройки класса создание.

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

7 голосов
/ 09 апреля 2010

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

>>> 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__
>>> 
4 голосов
/ 11 октября 2014

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

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)
3 голосов
/ 01 ноября 2012
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 (в нашем случае 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

3 голосов
/ 09 апреля 2010
return type(name, bases, dict)

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

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

1 голос
/ 09 апреля 2010

Это все очень хорошо описано здесь .

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

...