__new__, __init__ и метаклассы (и суперклассы) - PullRequest
0 голосов
/ 01 июня 2018

Я провел целый день, пытаясь понять тонкости модели классов Python, возиться с декораторами, метаклассами и суперклассами.

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

Я создал новый модуль макета для запуска тестовв, здесь:

#! /usr/bin/env python3

import sys as system
import os  as operating_system

from functools import partial
from time      import perf_counter as counter

class Meta(type):

    @classmethod
    def __prepare__(instance, name, supers, *list, **map):
        print('{} in meta prepare'.format(name))
        return {}

    def __new__(instance, name, supers, attributes, *list, **map):
        print('{} in meta new'.format(name))
        return instance

    def __init__(self, name, supers, attributes, *list, **map):
            print('{} in meta init'.format(self))

    def __call__(self, *list, **map):
        print('{} in meta call'.format(self))
        return type.__call__(self)
        print('after call')

class Super(object):

    def __new__(instance, *list, **map):
        print('{} in Super new'.format(instance))
        return instance

    def __init__(self, *list, **map):
        print('{} in Super init'.format(self))

    def __call__(self, *list, **map):
        print('{} in Super call'.format(self))
        return object.__call__(self)

class Other(object):

    def __new__(instance, *list, **map):
        print('{} in Other new'.format(instance))
        return instance

    def __init__(self, *list, **map):
        print('{} in Other init'.format(self))

    def __call__(self, *list, **map):
        print('{} in Other call'.format(self))
        return object.__call__(self)

class MetaSuper(object, metaclass = Meta):

    def __new__(instance, *list, **map):
        print('{} in MetaSuper new'.format(instance))
        return instance

    def __init__(self, *list, **map):
        print('{} in MetaSuper init'.format(self))

    def __call__(self, *list, **map):
        print('{} in MetaSuper call'.format(self))
        return object.__call__(self)

class DoubleSuper(Super, MetaSuper):

    def __new__(instance, *list, **map):
        print('{} in DoubleSuper new'.format(instance))
        return instance

    def __init__(self, *list, **map):
        print('{} in DoubleSuper init'.format(self))
        Super.__init__(self, *list, **map)
        MetaSuper.__init__(self, *list, **map)

    def __call__(self, *list, **map):
        print('{} in DoubleSuper call'.format(self))
        return object.__call__(self)

class SuperThenMeta(Super, metaclass = Meta):

    def __new__(instance, *list, **map):
        print('{} in SuperThenMeta new'.format(instance))
        return instance

    def __init__(self, *list, **map):
        print('{} in SuperThenMeta init'.format(self))
        Super.__init__(self, *list, **map)

    def __call__(self, *list, **map):
        print('{} in SuperThenMeta call'.format(self))
        return object.__call__(self)

class Triple(Super, Other, metaclass = Meta):

    def __new__(instance, *list, **map):
        print('{} in Triple new'.format(instance))
        return instance

    def __init__(self, *list, **map):
        print('{} in Triple init'.format(self))
        Super.__init__(self, *list, **map)
        Other.__init__(self, *list, **map)

    def __call__(self, *list, **map):
        print('{} in Triple call'.format(self))
        return object.__call__(self)

class Simple(Super):

    def __new__(instance, *list, **map):
        print('{} in Simple new'.format(instance))
        return instance.__init__(instance, *list, **map)

    def __init__(self, *list, **map):
        print('{} in Simple init'.format(self))
        Super.__init__(self, *list, **map)
        Other.__init__(self, *list, **map)

    def __call__(self, *list, **map):
        print('{} in Simple call'.format(self))
        return object.__call__(self)    

def main():
    #thing = SuperThenMeta()
    #other = DoubleSuper()
    last  = Super()
    simp  = Simple()
    trip  = Triple()

if __name__ == '__main__':
    main()

TL; DR, я экспериментировал с несколькими различными настройками между этими рабочими частями.

Если я запустил это, это будет вывод:

MetaSuper in meta prepare
MetaSuper in meta new
SuperThenMeta in meta prepare
SuperThenMeta in meta new
Triple in meta prepare
Triple in meta new
<class '__main__.Super'> in Super new
<class '__main__.Simple'> in Simple new
<class '__main__.Simple'> in Simple init
<class '__main__.Simple'> in Super init
<class '__main__.Simple'> in Other init
Traceback (most recent call last):
File "./metaprogramming.py", line 134, in <module>
  main()
File "./metaprogramming.py", line 131, in main
  trip = Triple()
TypeError: __new__() missing 3 required positional arguments: 'name', 'supers', and 'attributes'

Отсюда у меня есть несколько вопросов:

  • Должен ли я вызывать экземпляр. init (экземпляр, * список, ** карта)в конце новых функций?Я так не думаю, но добавление этого в «простой» пример, похоже, сработало, в то время как «Super» никогда не достигал init .У меня сложилось впечатление, что при вызове object. call в моих собственных методах вызова это будет выполнено реализацией по умолчанию, но __call__s не выполняется в течение всей программы.

  • Почему вызов Triple () вызывает сначала метаклассы new ?Если это нормально, значит ли это, что это типично для любого класса с метаклассом?Это поведение похоже на суперклассы?

  • Я ожидал, что call будет где-то в этом списке.Разве он не вызывается во время процедуры создания объектов (например, [prepare], new, init)?

Я знаю, что это много информации, поэтому спасибо, что прочитали это далеко;Любое руководство будет оценено.

1 Ответ

0 голосов
/ 01 июня 2018

Метакласс '__new__

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

В случае метакласса ожидается, что __new__ вернет экземплярвашего метакласса, это класс .Его подпись выглядит так:

class Meta(type):
    def __new__(metacls, name, bases, namespace, **kwargs):
        ...
  • metacls - это сам метакласс.

  • name - это строка, представляющая имяэкземпляра класса.

  • bases - кортеж классов, от которых унаследован класс.

  • namespace - это класспространство имен класса, это объект, возвращаемый __prepare__, теперь заполненный атрибутами класса.

  • **kwargs - любые аргументы ключевого слова, передаваемые классу при создании экземпляра

Чтобы создать экземпляр класса, вам нужно вызвать type.__new__, который является метаклассом по умолчанию.Вы обычно делаете это, вызывая super().__new__.

class Meta(type):
    def __new__(metacls, name, bases, namespace, **kwargs):
        print('You can do stuff here')

        cls = super().__new__(metacls, name, bases, namespace, **kwargs)

        # You must return the generated class
        return cls

Metaclass '__init__

Метод __init__ ведет себя не иначе, как для любого другого класса.Он получает созданный экземпляр, здесь класс, в качестве аргумента, если __new__ вернул экземпляр ожидаемого типа.В вашем примере __new__ не возвращает объект типа Meta.Он возвращает Meta самого типа type.

Следующий метод __init__ никогда не вызывается в экземпляре.

class Meta(type):
    def __new__(metacls, name, bases, namespace, **kwargs):
        return None # or anything such that type(obj) is not Meta

    def __init__(self, name, bases, namespace, **kwargs):
        # This will never be called because the return type of `__new__` is wrong
        pass

Следующее вызывается при создании экземпляра, потому что Meta.__new__ правильно возвращает объект типа Meta.

class Meta(type):
        def __new__(metacls, name, bases, namespace, **kwargs):
            return super().__new__(metacls, name, bases, namespace, **kwargs)

        def __init__(self, name, bases, namespace, **kwargs):
            print('__init__ was called')

Metaclass '__call__

Опять же, поведение __call__ ничем не отличается от поведения любого другого класса.Он вызывается, когда вы пытаетесь вызвать экземпляр метакласса, тогда как __new__ и __init__ вызываются, когда вы вызываете метакласс для создания экземпляра (класса).

Конечно, ожидается, что вызов класса вернет экземпляр, поэтому не забудьте вызвать super().__call__ и вернуть его результат, иначе вы бы замкнули замыкание при создании экземпляра.

class Meta(type):
    def __call__(self, *args, **kwargs):
        print(f'An instance was called with {args}')
        return super().__call__(self, *args, **kwargs)

# This declaration if what calls __new__ and __init__ of the metaclass
class Klass(metaclass=Meta):
    pass

# This calls the __call__ method of the metaclass
instance = Klass()
...