Вопрос по python, oop, metaclass, python-datamodel – Что такое метаклассы в Python?

5017

Что такое метаклассы и для чего мы их используем?

Ваш Ответ

15   ответов
65

Python 3 update

На данный момент есть два ключевых метода в метаклассе:

__prepare__, and __new__

__prepare__ позволяет предоставить пользовательское сопоставление (например,OrderedDict) для использования в качестве пространства имен при создании класса. Вы должны вернуть экземпляр любого пространства имен, которое вы выберете. Если вы не реализуете__prepare__ нормальныйdict используется.

__new__ отвечает за фактическое создание / модификацию финального класса.

Метаклассу «голые кости», «ничего не делать» хотелось бы:

class Meta(type):

    def __prepare__(metaclass, cls, bases):
        return dict()

    def __new__(metacls, cls, bases, clsdict):
        return super().__new__(metacls, cls, bases, clsdict)

Простой пример:

Скажем, вы хотите, чтобы на ваших атрибутах выполнялся простой проверочный код - как будто он всегда должен бытьint илиstr, Без метакласса ваш класс будет выглядеть примерно так:

class Person:
    weight = ValidateType('weight', int)
    age = ValidateType('age', int)
    name = ValidateType('name', str)

Как видите, вы должны повторить имя атрибута дважды. Это делает возможным опечатки наряду с раздражающими ошибками.

Простой метакласс может решить эту проблему:

class Person(metaclass=Validator):
    weight = ValidateType(int)
    age = ValidateType(int)
    name = ValidateType(str)

Вот как будет выглядеть метакласс (без использования__prepare__ так как это не нужно)

class Validator(type):
    def __new__(metacls, cls, bases, clsdict):
        # search clsdict looking for ValidateType descriptors
        for name, attr in clsdict.items():
            if isinstance(attr, ValidateType):
                attr.name = name
                attr.attr = '_' + name
        # create final class and return it
        return super().__new__(metacls, cls, bases, clsdict)

Примерный прогон:

p = Person()
p.weight = 9
print(p.weight)
p.weight = '9'

производит:

9
Traceback (most recent call last):
  File "simple_meta.py", line 36, in <module>
    p.weight = '9'
  File "simple_meta.py", line 24, in __set__
    (self.name, self.type, value))
TypeError: weight must be of type(s) <class 'int'> (got '9')

Note: Этот пример достаточно прост, его также можно было бы выполнить с помощью декоратора классов, но, по-видимому, реальный метакласс сделал бы гораздо больше.

«ValidateType» класс для справки:

class ValidateType:
    def __init__(self, type):
        self.name = None  # will be set by metaclass
        self.attr = None  # will be set by metaclass
        self.type = type
    def __get__(self, inst, cls):
        if inst is None:
            return self
        else:
            return inst.__dict__[self.attr]
    def __set__(self, inst, value):
        if not isinstance(value, self.type):
            raise TypeError('%s must be of type(s) %s (got %r)' %
                    (self.name, self.type, value))
        else:
            inst.__dict__[self.attr] = value
В примере метакласса я получаюNameError: name 'ValidateType' is not defined, Любые предложения, как лучше всего это исправить? Я использую Python 2
47

который сообщает, как (какой-то) другой класс должен быть создан.

Это тот случай, когда я увидел метакласс как решение моей проблемы: У меня была действительно сложная проблема, которую, возможно, можно было решить по-другому, но я решил решить ее с помощью метакласса. Из-за сложности, это один из немногих написанных мной модулей, где комментарии в модуле превосходят объем написанного кода. Вот...

#!/usr/bin/env python

# Copyright (C) 2013-2014 Craig Phillips.  All rights reserved.

# This requires some explaining.  The point of this metaclass excercise is to
# create a static abstract class that is in one way or another, dormant until
# queried.  I experimented with creating a singlton on import, but that did
# not quite behave how I wanted it to.  See now here, we are creating a class
# called GsyncOptions, that on import, will do nothing except state that its
# class creator is GsyncOptionsType.  This means, docopt doesn't parse any
# of the help document, nor does it start processing command line options.
# So importing this module becomes really efficient.  The complicated bit
# comes from requiring the GsyncOptions class to be static.  By that, I mean
# any property on it, may or may not exist, since they are not statically
# defined; so I can't simply just define the class with a whole bunch of
# properties that are @property @staticmethods.
#
# So here's how it works:
#
# Executing 'from libgsync.options import GsyncOptions' does nothing more
# than load up this module, define the Type and the Class and import them
# into the callers namespace.  Simple.
#
# Invoking 'GsyncOptions.debug' for the first time, or any other property
# causes the __metaclass__ __getattr__ method to be called, since the class
# is not instantiated as a class instance yet.  The __getattr__ method on
# the type then initialises the class (GsyncOptions) via the __initialiseClass
# method.  This is the first and only time the class will actually have its
# dictionary statically populated.  The docopt module is invoked to parse the
# usage document and generate command line options from it.  These are then
# paired with their defaults and what's in sys.argv.  After all that, we
# setup some dynamic properties that could not be defined by their name in
# the usage, before everything is then transplanted onto the actual class
# object (or static class GsyncOptions).
#
# Another piece of magic, is to allow command line options to be set in
# in their native form and be translated into argparse style properties.
#
# Finally, the GsyncListOptions class is actually where the options are
# stored.  This only acts as a mechanism for storing options as lists, to
# allow aggregation of duplicate options or options that can be specified
# multiple times.  The __getattr__ call hides this by default, returning the
# last item in a property's list.  However, if the entire list is required,
# calling the 'list()' method on the GsyncOptions class, returns a reference
# to the GsyncListOptions class, which contains all of the same properties
# but as lists and without the duplication of having them as both lists and
# static singlton values.
#
# So this actually means that GsyncOptions is actually a static proxy class...
#
# ...And all this is neatly hidden within a closure for safe keeping.
def GetGsyncOptionsType():
    class GsyncListOptions(object):
        __initialised = False

    class GsyncOptionsType(type):
        def __initialiseClass(cls):
            if GsyncListOptions._GsyncListOptions__initialised: return

            from docopt import docopt
            from libgsync.options import doc
            from libgsync import __version__

            options = docopt(
                doc.__doc__ % __version__,
                version = __version__,
                options_first = True
            )

            paths = options.pop('<path>', None)
            setattr(cls, "destination_path", paths.pop() if paths else None)
            setattr(cls, "source_paths", paths)
            setattr(cls, "options", options)

            for k, v in options.iteritems():
                setattr(cls, k, v)

            GsyncListOptions._GsyncListOptions__initialised = True

        def list(cls):
            return GsyncListOptions

        def __getattr__(cls, name):
            cls.__initialiseClass()
            return getattr(GsyncListOptions, name)[-1]

        def __setattr__(cls, name, value):
            # Substitut option names: --an-option-name for an_option_name
            import re
            name = re.sub(r'^__', "", re.sub(r'-', "_", name))
            listvalue = []

            # Ensure value is converted to a list type for GsyncListOptions
            if isinstance(value, list):
                if value:
                    listvalue = [] + value
                else:
                    listvalue = [ None ]
            else:
                listvalue = [ value ]

            type.__setattr__(GsyncListOptions, name, listvalue)

    # Cleanup this module to prevent tinkering.
    import sys
    module = sys.modules[__name__]
    del module.__dict__['GetGsyncOptionsType']

    return GsyncOptionsType

# Our singlton abstract proxy class.
class GsyncOptions(object):
    __metaclass__ = GetGsyncOptionsType()
56
Role of a metaclass' __call__() method when creating a class instance

в, вы в конечном итоге натолкнетесь на код, который выглядит следующим образом:

# define a class
class SomeClass(object):
    # ...
    # some definition here ...
    # ...

# create an instance of it
instance = SomeClass()

# then call the object as if it's a function
result = instance('foo', 'bar')

Последнее возможно при реализации__call__() магический метод на уроке.

class SomeClass(object):
    # ...
    # some definition here ...
    # ...

    def __call__(self, foo, bar):
        return bar + foo

__call__() Метод вызывается, когда экземпляр класса используется как вызываемый. Но, как мы видели из предыдущих ответов, сам класс является экземпляром метакласса, поэтому, когда мы используем этот класс в качестве вызываемого (то есть когда мы создаем его экземпляр), мы фактически называем его метаклассом.__call__() метод. На данный момент большинство программистов на Python немного смущены, потому что им сказали, что при создании экземпляра, подобного этомуinstance = SomeClass() Вы называете его__init__() метод. Те, кто копал немного глубже, знают это раньше__init__() есть & APOS; s__new__(), Ну, сегодня еще один слой истины раскрывается, прежде чем__new__() есть метакласс__call__().

Давайте изучим цепочку вызовов метода, в частности, с точки зрения создания экземпляра класса.

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

class Meta_1(type):
    def __call__(cls):
        print "Meta_1.__call__() before creating an instance of ", cls
        instance = super(Meta_1, cls).__call__()
        print "Meta_1.__call__() about to return instance."
        return instance

Это класс, который использует этот метакласс

class Class_1(object):

    __metaclass__ = Meta_1

    def __new__(cls):
        print "Class_1.__new__() before creating an instance."
        instance = super(Class_1, cls).__new__(cls)
        print "Class_1.__new__() about to return instance."
        return instance

    def __init__(self):
        print "entering Class_1.__init__() for instance initialization."
        super(Class_1,self).__init__()
        print "exiting Class_1.__init__()."

А теперь давайте создадим экземплярClass_1

instance = Class_1()
# Meta_1.__call__() before creating an instance of <class '__main__.Class_1'>.
# Class_1.__new__() before creating an instance.
# Class_1.__new__() about to return instance.
# entering Class_1.__init__() for instance initialization.
# exiting Class_1.__init__().
# Meta_1.__call__() about to return instance.

Обратите внимание, что приведенный выше код на самом деле не делает ничего, кроме регистрации задач. Каждый метод делегирует фактическую работу его родительской реализации, таким образом сохраняя поведение по умолчанию. посколькуtype являетсяMeta_1родительский класс (type являясь родительским метаклассом по умолчанию) и учитывая последовательность упорядочения вышеприведенных выходных данных, теперь у нас есть подсказка относительно того, какой будет псевдо-реализацияtype.__call__():

class type:
    def __call__(cls, *args, **kwarg):

        # ... maybe a few things done to cls here

        # then we call __new__() on the class to create an instance
        instance = cls.__new__(cls, *args, **kwargs)

        # ... maybe a few things done to the instance here

        # then we initialize the instance with its __init__() method
        instance.__init__(*args, **kwargs)

        # ... maybe a few more things done to instance here

        # then we return it
        return instance

Мы можем видеть, что метакласс & apos;__call__() метод - это тот, который вызывается первым. Затем он делегирует создание экземпляра классу__new__() способ и инициализация для экземпляров__init__(), Это также тот, который в конечном итоге возвращает экземпляр.

Из вышесказанного следует, что метакласс & apos;__call__() также предоставляется возможность решить, стоит ли звонитьClass_1.__new__() или жеClass_1.__init__() в конечном итоге будет сделано. В течение его выполнения он мог фактически возвращать объект, который не был затронут ни одним из этих методов. Возьмем для примера такой подход к шаблону синглтона:

class Meta_2(type):
    singletons = {}

    def __call__(cls, *args, **kwargs):
        if cls in Meta_2.singletons:
            # we return the only instance and skip a call to __new__()
            # and __init__()
            print ("{} singleton returning from Meta_2.__call__(), "
                   "skipping creation of new instance.".format(cls))
            return Meta_2.singletons[cls]

        # else if the singleton isn't present we proceed as usual
        print "Meta_2.__call__() before creating an instance."
        instance = super(Meta_2, cls).__call__(*args, **kwargs)
        Meta_2.singletons[cls] = instance
        print "Meta_2.__call__() returning new instance."
        return instance

class Class_2(object):

    __metaclass__ = Meta_2

    def __new__(cls, *args, **kwargs):
        print "Class_2.__new__() before creating instance."
        instance = super(Class_2, cls).__new__(cls)
        print "Class_2.__new__() returning instance."
        return instance

    def __init__(self, *args, **kwargs):
        print "entering Class_2.__init__() for initialization."
        super(Class_2, self).__init__()
        print "exiting Class_2.__init__()."

Давайте посмотрим, что происходит при неоднократных попытках создать объект типаClass_2

a = Class_2()
# Meta_2.__call__() before creating an instance.
# Class_2.__new__() before creating instance.
# Class_2.__new__() returning instance.
# entering Class_2.__init__() for initialization.
# exiting Class_2.__init__().
# Meta_2.__call__() returning new instance.

b = Class_2()
# <class '__main__.Class_2'> singleton returning from Meta_2.__call__(), skipping creation of new instance.

c = Class_2()
# <class '__main__.Class_2'> singleton returning from Meta_2.__call__(), skipping creation of new instance.

a is b is c # True
87
What are metaclasses? What do you use them for?

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

псевдокод:

>>> Class(...)
instance

Выше должно выглядеть знакомо. Ну гдеClass родом из? Это экземпляр метакласса (также псевдокода):

>>> Metaclass(...)
Class

В реальном коде мы можем передать метакласс по умолчанию,type, все, что нам нужно для создания экземпляра класса, и мы получаем класс:

>>> type('Foo', (object,), {}) # requires a name, bases, and a namespace
<class '__main__.Foo'>
Putting it differently

A class is to an instance as a metaclass is to a class.

When we instantiate an object, we get an instance:

>>> object()                          # instantiation of class
<object object at 0x7f9069b4e0b0>     # instance

Likewise, when we define a class explicitly with the default metaclass, type, we instantiate it:

>>> type('Object', (object,), {})     # instantiation of metaclass
<class '__main__.Object'>             # instance

Put another way, a class is an instance of a metaclass:

>>> isinstance(object, type)
True

Put a third way, a metaclass is a class's class.

>>> type(object) == type
True
>>> object.__class__
<class 'type'>

Когда вы пишете определение класса, и Python выполняет его, он использует метакласс для создания экземпляра объекта класса (который, в свою очередь, будет использоваться для создания экземпляров этого класса).

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

Для чего они могут быть использованы? Отдокументы:

The potential uses for metaclasses are boundless. Some ideas that have been explored include logging, interface checking, automatic delegation, automatic property creation, proxies, frameworks, and automatic resource locking/synchronization.

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

You use a metaclass every time you create a class:

Когда вы пишете определение класса, например, как это,

class Foo(object): 
    'demo'

Вы создаете объект класса.

>>> Foo
<class '__main__.Foo'>
>>> isinstance(Foo, type), isinstance(Foo, object)
(True, True)

Это так же, как функциональный вызовtype с соответствующими аргументами и присвоением результата переменной с таким именем:

name = 'Foo'
bases = (object,)
namespace = {'__doc__': 'demo'}
Foo = type(name, bases, namespace)

Обратите внимание, что некоторые вещи автоматически добавляются в__dict__то есть пространство имен:

>>> Foo.__dict__
dict_proxy({'__dict__': <attribute '__dict__' of 'Foo' objects>, 
'__module__': '__main__', '__weakref__': <attribute '__weakref__' 
of 'Foo' objects>, '__doc__': 'demo'})

metaclass объекта, который мы создали, в обоих случаяхtype.

(Примечание о содержании класса__dict__: __module__ есть, потому что классы должны знать, где они определены, и__dict__ а также__weakref__ есть, потому что мы не определяем__slots__ - если мыопределять__slots__ мы сэкономим немного места в экземплярах, поскольку мы можем запретить__dict__ а также__weakref__ исключив их. Например:

>>> Baz = type('Bar', (object,), {'__doc__': 'demo', '__slots__': ()})
>>> Baz.__dict__
mappingproxy({'__doc__': 'demo', '__slots__': (), '__module__': '__main__'})

... но я отвлекся.)

We can extend type just like any other class definition:

Здесь по умолчанию__repr__ классов:

>>> Foo
<class '__main__.Foo'>

Одна из самых ценных вещей, которые мы можем сделать по умолчанию при написании объекта Python, - это предоставить ему хороший__repr__, Когда мы звонимhelp(repr) мы узнаем, что это хороший тест для__repr__ что также требует проверки на равенство -obj == eval(repr(obj)), Следующая простая реализация__repr__ а также__eq__ для экземпляров класса нашего класса типов предоставляет нам демонстрацию, которая может улучшить по умолчанию__repr__ классов:

class Type(type):
    def __repr__(cls):
        """
        >>> Baz
        Type('Baz', (Foo, Bar,), {'__module__': '__main__', '__doc__': None})
        >>> eval(repr(Baz))
        Type('Baz', (Foo, Bar,), {'__module__': '__main__', '__doc__': None})
        """
        metaname = type(cls).__name__
        name = cls.__name__
        parents = ', '.join(b.__name__ for b in cls.__bases__)
        if parents:
            parents += ','
        namespace = ', '.join(': '.join(
          (repr(k), repr(v) if not isinstance(v, type) else v.__name__))
               for k, v in cls.__dict__.items())
        return '{0}(\'{1}\', ({2}), {{{3}}})'.format(metaname, name, parents, namespace)
    def __eq__(cls, other):
        """
        >>> Baz == eval(repr(Baz))
        True            
        """
        return (cls.__name__, cls.__bases__, cls.__dict__) == (
                other.__name__, other.__bases__, other.__dict__)

Так что теперь, когда мы создаем объект с этим метаклассом,__repr__ echoed в командной строке обеспечивает гораздо менее уродливый вид, чем по умолчанию:

>>> class Bar(object): pass
>>> Baz = Type('Baz', (Foo, Bar,), {'__module__': '__main__', '__doc__': None})
>>> Baz
Type('Baz', (Foo, Bar,), {'__module__': '__main__', '__doc__': None})

С хорошим__repr__ определенный для экземпляра класса, у нас есть более сильная способность отлаживать наш код. Тем не менее, гораздо больше проверять сeval(repr(Class)) маловероятно (так как функции было бы невозможно оценить по умолчанию__repr__&'S).

An expected usage: __prepare__ a namespace

Если, например, мы хотим знать, в каком порядке создаются методы класса, мы можем предоставить упорядоченный dict в качестве пространства имен класса. Мы бы сделали это с__prepare__ которыйвозвращает dict пространства имен для класса, если он реализован в Python 3:

from collections import OrderedDict

class OrderedType(Type):
    @classmethod
    def __prepare__(metacls, name, bases, **kwargs):
        return OrderedDict()
    def __new__(cls, name, bases, namespace, **kwargs):
        result = Type.__new__(cls, name, bases, dict(namespace))
        result.members = tuple(namespace)
        return result

И использование:

class OrderedMethodsObject(object, metaclass=OrderedType):
    def method1(self): pass
    def method2(self): pass
    def method3(self): pass
    def method4(self): pass

И теперь у нас есть запись порядка, в котором были созданы эти методы (и другие атрибуты класса):

>>> OrderedMethodsObject.members
('__module__', '__qualname__', 'method1', 'method2', 'method3', 'method4')

Обратите внимание, этот пример был адаптирован издокументация - новыйenum в стандартной библиотеке Является ли это.

Итак, мы создали экземпляр метакласса, создав класс. Мы также можем обращаться с метаклассом так же, как и с любым другим классом. У него есть порядок разрешения метода:

>>> inspect.getmro(OrderedType)
(<class '__main__.OrderedType'>, <class '__main__.Type'>, <class 'type'>, <class 'object'>)

И это примерно правильноrepr (который мы больше не сможем оценить, если не сможем найти способ представить наши функции.):

>>> OrderedMethodsObject
OrderedType('OrderedMethodsObject', (object,), {'method1': <function OrderedMethodsObject.method1 at 0x0000000002DB01E0>, 'members': ('__module__', '__qualname__', 'method1', 'method2', 'method3', 'method4'), 'method3': <function OrderedMet
hodsObject.method3 at 0x0000000002DB02F0>, 'method2': <function OrderedMethodsObject.method2 at 0x0000000002DB0268>, '__module__': '__main__', '__weakref__': <attribute '__weakref__' of 'OrderedMethodsObject' objects>, '__doc__': None, '__d
ict__': <attribute '__dict__' of 'OrderedMethodsObject' objects>, 'method4': <function OrderedMethodsObject.method4 at 0x0000000002DB0378>})
339

Note, this answer is for Python 2.x as it was written in 2008, metaclasses are slightly different in 3.x, see the comments.

Метаклассы - это секретный соус, который делает «класс» Работа. Метакласс по умолчанию для нового объекта стиля называется «тип».

class type(object)
  |  type(object) -> the object's type
  |  type(name, bases, dict) -> a new type

Метаклассы занимают 3 аргумента. & APOS;name','bases& APOS; и & apos;dict& APOS;

Вот где начинается секрет. Ищите, откуда взято имя, основания и диктат в этом примере определения класса.

class ThisIsTheName(Bases, Are, Here):
    All_the_code_here
    def doesIs(create, a):
        dict

Давайте определим метакласс, который продемонстрирует, как & apos;class:& APOS; называет это

def test_metaclass(name, bases, dict):
    print 'The Class Name is', name
    print 'The Class Bases are', bases
    print 'The dict has', len(dict), 'elems, the keys are', dict.keys()

    return "yellow"

class TestName(object, None, int, 1):
    __metaclass__ = test_metaclass
    foo = 1
    def baz(self, arr):
        pass

print 'TestName = ', repr(TestName)

# output => 
The Class Name is TestName
The Class Bases are (<type 'object'>, None, <type 'int'>, 1)
The dict has 4 elems, the keys are ['baz', '__module__', 'foo', '__metaclass__']
TestName =  'yellow'

А теперь, пример, который на самом деле что-то значит, он автоматически внесет переменные в список & quot; атрибуты & quot; установить на класс и установить Нет.

def init_attributes(name, bases, dict):
    if 'attributes' in dict:
        for attr in dict['attributes']:
            dict[attr] = None

    return type(name, bases, dict)

class Initialised(object):
    __metaclass__ = init_attributes
    attributes = ['foo', 'bar', 'baz']

print 'foo =>', Initialised.foo
# output=>
foo => None

Обратите внимание, что магическое поведение "инициализировано" выигрывает от наличия метаклассаinit_attributes не передается на подкласс Initalised.

Вот еще более конкретный пример, показывающий, как вы можете создавать подклассы «типа». создать метакласс, который выполняет действие при создании класса. Это довольно сложно:

class MetaSingleton(type):
    instance = None
    def __call__(cls, *args, **kw):
        if cls.instance is None:
            cls.instance = super(MetaSingleton, cls).__call__(*args, **kw)
        return cls.instance

 class Foo(object):
     __metaclass__ = MetaSingleton

 a = Foo()
 b = Foo()
 assert a is b
102

что введение ONLamp в программирование метаклассов хорошо написано и дает действительно хорошее введение в тему, хотя ему уже несколько лет.

http://www.onlamp.com/pub/a/python/2003/04/17/metaclasses.html (архивируется вhttps://web.archive.org/web/20080206005253/http://www.onlamp.com/pub/a/python/2003/04/17/metaclasses.html)

Вкратце: класс - это план создания экземпляра, метакласс - это план создания класса. Легко видеть, что в классах Python должны быть объекты первого класса, чтобы включить это поведение.

Я сам никогда не писал, но я думаю, что одно из лучших применений метаклассов можно увидеть вDjango Framework, Классы моделей используют метаклассовый подход, чтобы включить декларативный стиль написания новых моделей или классов форм. Пока метакласс создает класс, все члены получают возможность настраивать сам класс.

Creating a new model The metaclass enabling this

Остается сказать следующее: если вы не знаете, что такое метаклассы, вероятность того, что выwill not need them это 99%.

20

например, - их метакласса.

Метакласс по умолчанию, который применяется, когда вы определяете классы как:

class foo:
    ...

метакласс используется для применения некоторого правила ко всему набору классов. Например, предположим, что вы создаете ORM для доступа к базе данных, и вы хотите, чтобы записи из каждой таблицы относились к классу, сопоставленному с этой таблицей (основанным на полях, бизнес-правилах и т. например, логика пула соединений, которая является общей для всех классов записей из всех таблиц. Другое использование - логика для поддержки внешних ключей, которая включает в себя несколько классов записей.

когда вы определяете метакласс, вы подклассируете тип и можете переопределить следующие магические методы для вставки вашей логики.

class somemeta(type):
    __new__(mcs, name, bases, clsdict):
      """
  mcs: is the base metaclass, in this case type.
  name: name of the new class, as provided by the user.
  bases: tuple of base classes 
  clsdict: a dictionary containing all methods and attributes defined on class

  you must return a class object by invoking the __new__ constructor on the base metaclass. 
 ie: 
    return type.__call__(mcs, name, bases, clsdict).

  in the following case:

  class foo(baseclass):
        __metaclass__ = somemeta

  an_attr = 12

  def bar(self):
      ...

  @classmethod
  def foo(cls):
      ...

      arguments would be : ( somemeta, "foo", (baseclass, baseofbase,..., object), {"an_attr":12, "bar": <function>, "foo": <bound class method>}

      you can modify any of these values before passing on to type
      """
      return type.__call__(mcs, name, bases, clsdict)


    def __init__(self, name, bases, clsdict):
      """ 
      called after type has been created. unlike in standard classes, __init__ method cannot modify the instance (cls) - and should be used for class validaton.
      """
      pass


    def __prepare__():
        """
        returns a dict or something that can be used as a namespace.
        the type will then attach methods and attributes from class definition to it.

        call order :

        somemeta.__new__ ->  type.__new__ -> type.__init__ -> somemeta.__init__ 
        """
        return dict()

    def mymethod(cls):
        """ works like a classmethod, but for class objects. Also, my method will not be visible to instances of cls.
        """
        pass

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

6130
Classes as objects

вам нужно освоить классы на Python. И у Python очень своеобразное представление о том, что такое классы, заимствованные из языка Smalltalk.

В большинстве языков классы - это просто фрагменты кода, которые описывают, как создать объект. Это также верно в Python:

>>> class ObjectCreator(object):
...       pass
...

>>> my_object = ObjectCreator()
>>> print(my_object)
<__main__.ObjectCreator object at 0x8974f2c>

Но классы больше, чем в Python. Классы тоже объекты.

Да, объекты.

Как только вы используете ключевое словоclass, Python выполняет его и создает объект. Инструкция

>>> class ObjectCreator(object):
...       pass
...

создает в памяти объект с именем «ObjectCreator».

This object (the class) is itself capable of creating objects (the instances), and this is why it's a class.

Но все же это объект, и поэтому:

you can assign it to a variable you can copy it you can add attributes to it you can pass it as a function parameter

например.:

>>> print(ObjectCreator) # you can print a class because it's an object
<class '__main__.ObjectCreator'>
>>> def echo(o):
...       print(o)
...
>>> echo(ObjectCreator) # you can pass a class as a parameter
<class '__main__.ObjectCreator'>
>>> print(hasattr(ObjectCreator, 'new_attribute'))
False
>>> ObjectCreator.new_attribute = 'foo' # you can add attributes to a class
>>> print(hasattr(ObjectCreator, 'new_attribute'))
True
>>> print(ObjectCreator.new_attribute)
foo
>>> ObjectCreatorMirror = ObjectCreator # you can assign a class to a variable
>>> print(ObjectCreatorMirror.new_attribute)
foo
>>> print(ObjectCreatorMirror())
<__main__.ObjectCreator object at 0x8997b4c>
Creating classes dynamically

Так как классы являются объектами, вы можете создавать их на лету, как и любой объект.

Во-первых, вы можете создать класс в функции, используяclass:

>>> def choose_class(name):
...     if name == 'foo':
...         class Foo(object):
...             pass
...         return Foo # return the class, not an instance
...     else:
...         class Bar(object):
...             pass
...         return Bar
...
>>> MyClass = choose_class('foo')
>>> print(MyClass) # the function returns a class, not an instance
<class '__main__.Foo'>
>>> print(MyClass()) # you can create an object from this class
<__main__.Foo object at 0x89c6d4c>

Но это не так динамично, так как вам все равно придется писать весь класс самостоятельно.

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

Когда вы используетеclass Ключевое слово, Python создает этот объект автоматически. Но, как с большинством вещей в Python, это дает вам возможность сделать это вручную.

Запомнить функциюtype? Старая добрая функция, которая позволяет узнать, что Тип объекта:

>>> print(type(1))
<type 'int'>
>>> print(type("1"))
<type 'str'>
>>> print(type(ObjectCreator))
<type 'type'>
>>> print(type(ObjectCreator()))
<class '__main__.ObjectCreator'>

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

(Я знаю, что глупо, что одна и та же функция может иметь два совершенно разных использования в зависимости от параметров, которые вы ей передаете. Это проблема из-за обратной совместимость в Python)

type работает так:

type(name of the class,
     tuple of the parent class (for inheritance, can be empty),
     dictionary containing attributes names and values)

например.:

>>> class MyShinyClass(object):
...       pass

можно создать вручную таким образом:

>>> MyShinyClass = type('MyShinyClass', (), {}) # returns a class object
>>> print(MyShinyClass)
<class '__main__.MyShinyClass'>
>>> print(MyShinyClass()) # create an instance with the class
<__main__.MyShinyClass object at 0x8997cec>

Вы заметите, что мы используем «MyShinyClass» как имя класса и как переменная для хранения ссылки на класс. Они могут быть разными, но нет никаких причин, чтобы усложнять вещи.

type принимает словарь для определения атрибутов класса. Так:

>>> class Foo(object):
...       bar = True

Можно перевести на:

>>> Foo = type('Foo', (), {'bar':True})

И используется как обычный класс:

>>> print(Foo)
<class '__main__.Foo'>
>>> print(Foo.bar)
True
>>> f = Foo()
>>> print(f)
<__main__.Foo object at 0x8a9b84c>
>>> print(f.bar)
True

И, конечно, вы можете наследовать от него, так что:

>>>   class FooChild(Foo):
...         pass

было бы:

>>> FooChild = type('FooChild', (Foo,), {})
>>> print(FooChild)
<class '__main__.FooChild'>
>>> print(FooChild.bar) # bar is inherited from Foo
True

В конце концов вы захотите добавить методы в свой класс. Просто определите функцию с правильной подписью и назначить его в качестве атрибута.

>>> def echo_bar(self):
...       print(self.bar)
...
>>> FooChild = type('FooChild', (Foo,), {'echo_bar': echo_bar})
>>> hasattr(Foo, 'echo_bar')
False
>>> hasattr(FooChild, 'echo_bar')
True
>>> my_foo = FooChild()
>>> my_foo.echo_bar()
True

И вы можете добавить еще больше методов после динамического создания класса, точно так же, как добавление методов в нормально созданный объект класса.

>>> def echo_bar_more(self):
...       print('yet another method')
...
>>> FooChild.echo_bar_more = echo_bar_more
>>> hasattr(FooChild, 'echo_bar_more')
True

Вы видите, куда мы идем: в Python классы являются объектами, и вы можете динамически создавать классы на лету.

Это то, что делает Python, когда вы используете ключевое словоclassи делает это с помощью метакласса.

What are metaclasses (finally)

Метаклассы - это «вещи». это создает классы.

Вы определяете классы для создания объектов, верно?

Но мы узнали, что классы Python являются объектами.

Ну, метаклассы - это то, что создает эти объекты. Это классы & apos; классы, Вы можете изобразить их таким образом:

MyClass = MetaClass()
my_object = MyClass()

Вы видели этоtype позволяет вам сделать что-то вроде этого:

MyClass = type('MyClass', (), {})

Это потому, что функцияtype на самом деле метакласс.type это метакласс Python использует для создания всех классов за кулисами.

Теперь вы удивляетесь, почему, черт возьми, это написано в нижнем регистре, а неType?

Ну, я думаю, это вопрос соответствияstrкласс, который создает струнные объекты иint класс, который создает целочисленные объекты.type является просто класс, который создает объекты класса.

Вы видите это, проверив__class__ приписывать.

Все, и я имею в виду все, является объектом в Python. Это включает в себя целые, строки, функции и классы. Все они объекты. И все они имеют был создан из класса:

>>> age = 35
>>> age.__class__
<type 'int'>
>>> name = 'bob'
>>> name.__class__
<type 'str'>
>>> def foo(): pass
>>> foo.__class__
<type 'function'>
>>> class Bar(object): pass
>>> b = Bar()
>>> b.__class__
<class '__main__.Bar'>

Теперь, что является__class__ любой__class__ ?

>>> age.__class__.__class__
<type 'type'>
>>> name.__class__.__class__
<type 'type'>
>>> foo.__class__.__class__
<type 'type'>
>>> b.__class__.__class__
<type 'type'>

Итак, метакласс - это просто материал, который создает объекты класса.

Вы можете назвать это «фабрикой классов». если хочешь.

type это встроенный метакласс Python, но, конечно, вы можете создать свой собственный метакласс.

The __metaclass__ attribute

В Python 2 вы можете добавить__metaclass__ Атрибут при написании класса (см. следующий раздел для синтаксиса Python 3):

class Foo(object):
    __metaclass__ = something...
    [...]

Если вы это сделаете, Python будет использовать метакласс для создания классаFoo.

Осторожно, это сложно.

Ты пишешьclass Foo(object) во-первых, но объект классаFoo не создан в памяти еще нет.

Python будет искать__metaclass__ в определении класса. Если он найдет это, он будет использовать его для создания класса объектаFoo, Если это не так, он будет использовать type создать класс.

Прочитайте это несколько раз.

Когда вы делаете:

class Foo(Bar):
    pass

Python делает следующее:

Есть ли__metaclass__ приписыватьFoo?

Если да, создайте в памяти объект класса (я сказал объект класса, оставайтесь со мной здесь) с именемFoo используя то, что в__metaclass__.

Если Python не может найти__metaclass__, он будет искать__metaclass__ на уровне MODULE и попытайтесь сделать то же самое (но только для классов, которые ничего не наследуют, в основном классы старого стиля).

Затем, если он не может найти__metaclass__ вообще, он будет использоватьBars (первый родительский) собственный метакласс (который может быть по умолчаниюtype) создать объект класса.

Будьте осторожны, что__metaclass__ атрибут не будет наследоваться, метакласс родительского (Bar.__class__) будет. ЕслиBar использовал__metaclass__ атрибут, который создалBar сtype() (и неtype.__new__()), подклассы не будут наследовать это поведение.

Теперь большой вопрос, что вы можете положить в__metaclass__ ?

Ответ: что-то, что может создать класс.

А что может создать класс?typeили все, что подклассы или использует его.

Metaclasses in Python 3

Синтаксис для установки метакласса был изменен в Python 3:

class Foo(object, metaclass=something):
    ...

то есть__metaclass__ атрибут больше не используется, в пользу ключевого аргумента в списке базовых классов.

Однако поведение метаклассов остаетсяво многом то же самое.

Одна вещь, добавленная к метаклассам в Python 3, заключается в том, что вы также можете передавать атрибуты как аргументы-ключевые слова в метакласс, например, так:

class Foo(object, metaclass=something, kwarg1=value1, kwarg2=value2):
    ...

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

Custom metaclasses

Основной целью метакласса является автоматическое изменение класса, когда он будет создан.

Вы обычно делаете это для API, где вы хотите создать классы, соответствующие текущий контекст.

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

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

К счастью,__metaclass__ на самом деле может быть любым вызываемым, это не обязательно должно быть формальный класс (я знаю, что-то с «классом» в его имени не должно быть класс, пойди разберись ... но это полезно).

Итак, начнем с простого примера с использования функции.

# the metaclass will automatically get passed the same argument
# that you usually pass to `type`
def upper_attr(future_class_name, future_class_parents, future_class_attr):
    """
      Return a class object, with the list of its attribute turned
      into uppercase.
    """

    # pick up any attribute that doesn't start with '__' and uppercase it
    uppercase_attr = {}
    for name, val in future_class_attr.items():
        if not name.startswith('__'):
            uppercase_attr[name.upper()] = val
        else:
            uppercase_attr[name] = val

    # let `type` do the class creation
    return type(future_class_name, future_class_parents, uppercase_attr)

__metaclass__ = upper_attr # this will affect all classes in the module

class Foo(): # global __metaclass__ won't work with "object" though
    # but we can define __metaclass__ here instead to affect only this class
    # and this will work with "object" children
    bar = 'bip'

print(hasattr(Foo, 'bar'))
# Out: False
print(hasattr(Foo, 'BAR'))
# Out: True

f = Foo()
print(f.BAR)
# Out: 'bip'

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

# remember that `type` is actually a class like `str` and `int`
# so you can inherit from it
class UpperAttrMetaclass(type):
    # __new__ is the method called before __init__
    # it's the method that creates the object and returns it
    # while __init__ just initializes the object passed as parameter
    # you rarely use __new__, except when you want to control how the object
    # is created.
    # here the created object is the class, and we want to customize it
    # so we override __new__
    # you can do some stuff in __init__ too if you wish
    # some advanced use involves overriding __call__ as well, but we won't
    # see this
    def __new__(upperattr_metaclass, future_class_name,
                future_class_parents, future_class_attr):

        uppercase_attr = {}
        for name, val in future_class_attr.items():
            if not name.startswith('__'):
                uppercase_attr[name.upper()] = val
            else:
                uppercase_attr[name] = val

        return type(future_class_name, future_class_parents, uppercase_attr)

Но это не совсем ООП. Мы называемtype напрямую, и мы не переопределяем или позвонить родителю__new__, Давайте сделаем это:

class UpperAttrMetaclass(type):

    def __new__(upperattr_metaclass, future_class_name,
                future_class_parents, future_class_attr):

        uppercase_attr = {}
        for name, val in future_class_attr.items():
            if not name.startswith('__'):
                uppercase_attr[name.upper()] = val
            else:
                uppercase_attr[name] = val

        # reuse the type.__new__ method
        # this is basic OOP, nothing magic in there
        return type.__new__(upperattr_metaclass, future_class_name,
                            future_class_parents, uppercase_attr)

Возможно, вы заметили дополнительный аргументupperattr_metaclass, Есть ничего особенного в этом нет__new__ всегда получает класс, в котором он определен, в качестве первого параметра. Так же, как у васself для обычных методов, которые получают экземпляр в качестве первого параметра, или определяющий класс для методов класса.

Конечно, имена, которые я здесь использовал, длинные для ясности, но вроде заselfвсе аргументы имеют условные имена. Итак, настоящее производство метакласс будет выглядеть так:

class UpperAttrMetaclass(type):

    def __new__(cls, clsname, bases, dct):

        uppercase_attr = {}
        for name, val in dct.items():
            if not name.startswith('__'):
                uppercase_attr[name.upper()] = val
            else:
                uppercase_attr[name] = val

        return type.__new__(cls, clsname, bases, uppercase_attr)

Мы можем сделать его еще чище, используяsuper, что облегчит наследование (потому что да, вы можете иметь метаклассы, наследуя от метаклассов, наследуя от типа):

cla,ss UpperAttrMetaclass(type):

    def __new__(cls, clsname, bases, dct):

        uppercase_attr = {}
        for name, val in dct.items():
            if not name.startswith('__'):
                uppercase_attr[name.upper()] = val
            else:
                uppercase_attr[name] = val

        return super(UpperAttrMetaclass, cls).__new__(cls, clsname, bases, uppercase_attr)

Да, и в Python 3, если вы делаете этот вызов с аргументами ключевых слов, например:

class Foo(object, metaclass=Thing, kwarg1=value1):
    ...

Это переводит это в метакласс, чтобы использовать это:

class Thing(type):
    def __new__(class, clsname, bases, dct, kwargs1=default):
        ...

Вот и все. В метаклассах больше ничего нет.

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

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

intercept a class creation modify the class return the modified class Why would you use metaclasses classes instead of functions?

поскольку__metaclass__ может принять любой вызываемый, почему вы используете класс так как это явно сложнее?

Для этого есть несколько причин:

The intention is clear. When you read UpperAttrMetaclass(type), you know what's going to follow You can use OOP. Metaclass can inherit from metaclass, override parent methods. Metaclasses can even use metaclasses. Subclasses of a class will be instances of its metaclass if you specified a metaclass-class, but not with a metaclass-function. You can structure your code better. You never use metaclasses for something as trivial as the above example. It's usually for something complicated. Having the ability to make several methods and group them in one class is very useful to make the code easier to read. You can hook on __new__, __init__ and __call__. Which will allow you to do different stuff. Even if usually you can do it all in __new__, some people are just more comfortable using __init__. These are called metaclasses, damn it! It must mean something! Why would you use metaclasses?

Теперь большой вопрос. Зачем вам использовать какую-то неясную функцию, склонную к ошибкам?

Ну, обычно вы не делаете:

Metaclasses are deeper magic that 99% of users should never worry about. If you wonder whether you need them, you don't (the people who actually need them know with certainty that they need them, and don't need an explanation about why).

Python Guru Tim Peters

Основным вариантом использования метакласса является создание API. Типичным примером этого является Django ORM.

Это позволяет вам определить что-то вроде этого:

class Person(models.Model):
    name = models.CharField(max_length=30)
    age = models.IntegerField()

Но если вы сделаете это:

guy = Person(name='bob', age='35')
print(guy.age)

Это не вернетIntegerField объект. Это вернетintи даже может взять его прямо из базы данных.

Это возможно, потому чтоmodels.Model определяет__metaclass__ а также он использует магию, которая превратитPerson вы только что определили с простыми утверждениями в сложный крюк к полю базы данных.

Django делает что-то сложное простым, предоставляя простой API и используя метаклассы, воссоздавая код из этого API для выполнения реальной работы за кулисами.

The last word

Во-первых, вы знаете, что классы - это объекты, которые могут создавать экземпляры.

Ну, на самом деле, классы сами по себе являются примерами. Метаклассов.

>>> class Foo(object): pass
>>> id(Foo)
142630324

В Python все является объектом, и все они являются экземплярами классов. или экземпляры метаклассов.

За исключениемtype.

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

Во-вторых, метаклассы сложны. Вы не можете использовать их для очень простые изменения класса. Вы можете изменить классы, используя два разных метода:

monkey patching class decorators

В 99% случаев вам нужно изменить класс, лучше использовать их.

Но в 98% случаев вам вообще не нужно менять класс.

Похоже, что в Джангоmodels.Model он не использует__metaclass__ скорееclass Model(metaclass=ModelBase): ссылаться наModelBaseError: User Rate Limit Exceededgithub.com/django/django/blob/master/django/db/models/…
Error: User Rate Limit Exceeded
& lt; & lt; Будьте осторожны, чтобы__metaclass__Error: User Rate Limit ExceededBar.__class__) будет. ЕслиBarError: User Rate Limit Exceeded__metaclass__Error: User Rate Limit ExceededBarError: User Rate Limit Exceededtype()Error: User Rate Limit Exceededtype.__new__()Error: User Rate Limit Exceeded
Error: User Rate Limit ExceededPython 3.6 Data modelError: User Rate Limit ExceededPython 2.7 Data model
Error: User Rate Limit Exceededpython2Error: User Rate Limit Exceededpythono3?
31
The tl;dr version

type(obj) Функция получает тип объекта.

The type() of a class is its metaclass.

Чтобы использовать метакласс:

class Foo(object):
    __metaclass__ = MyMetaClass
35

type на самом делеmetaclass - класс, который создает другие классы. Наиболееmetaclass подклассыtype,metaclass получаетnew класс в качестве первого аргумента и обеспечивает доступ к объекту класса с деталями, как указано ниже:

>>> class MetaClass(type):
...     def __init__(cls, name, bases, attrs):
...         print ('class name: %s' %name )
...         print ('Defining class %s' %cls)
...         print('Bases %s: ' %bases)
...         print('Attributes')
...         for (name, value) in attrs.items():
...             print ('%s :%r' %(name, value))
... 

>>> class NewClass(object, metaclass=MetaClass):
...    get_choch='dairy'
... 
class name: NewClass
Bases <class 'object'>: 
Defining class <class 'NewClass'>
get_choch :'dairy'
__module__ :'builtins'
__qualname__ :'NewClass'

Note:

Обратите внимание, что класс не был создан в любое время; простой акт создания класса запускает выполнениеmetaclass.

7

metaclass определяет поведение для класса. Таким образом, вы можете явно установить свой метакласс. Всякий раз, когда Python получает ключевое словоclass затем начинается поискmetaclass, Если он не найден & # x2013; тип метакласса по умолчанию используется для создания объекта класса. С использованием__metaclass__ атрибут, вы можете установитьmetaclass вашего класса:

class MyClass:
   __metaclass__ = type
   # write here other method
   # write here one more method

print(MyClass.__metaclass__)

Он будет производить вывод, подобный этому:

class 'type'

И, конечно же, вы можете создать свой собственныйmetaclass определить поведение любого класса, созданного с использованием вашего класса.

Для этого по умолчаниюmetaclass Тип класса должен быть унаследован, так как это основнойmetaclass:

class MyMetaClass(type):
   __metaclass__ = type
   # you can write here any behaviour you want

class MyTestClass:
   __metaclass__ = MyMetaClass

Obj = MyTestClass()
print(Obj.__metaclass__)
print(MyMetaClass.__metaclass__)

Выход будет:

class '__main__.MyMetaClass'
class 'type'
136

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

class MyMeta(type):

    counter = 0

    def __init__(cls, name, bases, dic):
        type.__init__(cls, name, bases, dic)
        cls._order = MyMeta.counter
        MyMeta.counter += 1

class MyType(object):              # Python 2
    __metaclass__ = MyMeta

class MyType(metaclass=MyMeta):    # Python 3
    pass

Все, что является подклассомMyType затем получает атрибут класса_order это записывает порядок, в котором были определены классы.

2388

как ведет себя экземпляр класса (то есть объект), а метакласс определяет поведение класса. Класс является экземпляром метакласса.

В то время как в Python вы можете использовать произвольные вызовы для метаклассов (например,Jerub показывает), лучший подход состоит в том, чтобы сделать его самим классом.type это обычный метакласс в Python.type сам по себе класс, и это его собственный тип. Вы не сможете воссоздать что-то вродеtype чисто в Python, но Python немного обманывает. Чтобы создать свой собственный метакласс в Python, вы действительно хотите создать подклассtype.

Метакласс чаще всего используется как фабрика классов. Когда вы создаете объект, вызывая класс, Python создает новый класс (когда он выполняет оператор «class»), вызывая метакласс. В сочетании с нормальным__init__ а также__new__ методы, метаклассы, следовательно, позволяют вам делать «дополнительные вещи»; при создании класса, например, регистрация нового класса в каком-либо реестре или замена класса чем-то другим полностью.

Когдаclass оператор выполняется, Python сначала выполняет телоclass оператор как обычный блок кода. Результирующее пространство имен (dict) содержит атрибуты будущего класса. Метакласс определяется путем просмотра базовых классов будущего класса (метаклассы наследуются),__metaclass__ атрибут будущего класса (если есть) или__metaclass__ глобальная переменная. Затем метакласс вызывается с именем, основами и атрибутами класса, чтобы создать его экземпляр.

Тем не менее, метаклассы фактически определяютtype класса, а не просто фабрика для него, так что вы можете сделать с ними гораздо больше. Например, вы можете определить нормальные методы в метаклассе. Эти метакласс-методы похожи на методы класса в том смысле, что их можно вызывать в классе без экземпляра, но они также не похожи на методы класса в том смысле, что их нельзя вызывать в экземпляре класса.type.__subclasses__() пример метода наtype метаклассом. Вы также можете определить нормальную «магию» методы, как__add__, __iter__ а также__getattr__, чтобы реализовать или изменить поведение класса.

Вот агрегированный пример битов и кусочков:

def make_hook(f):
    """Decorator to turn 'foo' method into '__foo__'"""
    f.is_hook = 1
    return f

class MyType(type):
    def __new__(mcls, name, bases, attrs):

        if name.startswith('None'):
            return None

        # Go over attributes and see if they should be renamed.
        newattrs = {}
        for attrname, attrvalue in attrs.iteritems():
            if getattr(attrvalue, 'is_hook', 0):
                newattrs['__%s__' % attrname] = attrvalue
            else:
                newattrs[attrname] = attrvalue

        return super(MyType, mcls).__new__(mcls, name, bases, newattrs)

    def __init__(self, name, bases, attrs):
        super(MyType, self).__init__(name, bases, attrs)

        # classregistry.register(self, self.interfaces)
        print "Would register class %s now." % self

    def __add__(self, other):
        class AutoClass(self, other):
            pass
        return AutoClass
        # Alternatively, to autogenerate the classname as well as the class:
        # return type(self.__name__ + other.__name__, (self, other), {})

    def unregister(self):
        # classregistry.unregister(self)
        print "Would unregister class %s now." % self

class MyObject:
    __metaclass__ = MyType


class NoneSample(MyObject):
    pass

# Will print "NoneType None"
print type(NoneSample), repr(NoneSample)

class Example(MyObject):
    def __init__(self, value):
        self.value = value
    @make_hook
    def add(self, other):
        return self.__class__(self.value + other.value)

# Will unregister the class
Example.unregister()

inst = Example(10)
# Will fail with an AttributeError
#inst.unregister()

print inst + inst
class Sibling(MyObject):
    pass

ExampleSibling = Example + Sibling
# ExampleSibling is now a subclass of both Example and Sibling (with no
# content of its own) although it will believe it's called 'AutoClass'
print ExampleSibling
print ExampleSibling.__mro__
Очевидно, он имел в виду, что вы не можете воссоздать тип без использования самого типа в качестве метакласса. Что достаточно справедливо сказать.
class A(type):pass<NEWLINE>class B(type,metaclass=A):pass<NEWLINE>b.__class__ = b
Разве unregister () не должен вызываться экземпляром класса Example?
15

например, мы можем создать класс Hi с функцией type (), и нам не нужно использовать этот способ с классом Hi (object):

def func(self, name='mike'):
    print('Hi, %s.' % name)

Hi = type('Hi', (object,), dict(hi=func))
h = Hi()
h.hi()
Hi, mike.

type(Hi)
type

type(h)
__main__.Hi

Помимо использования type () для динамического создания классов, вы можете управлять поведением создания класса и использовать метакласс.

Согласно объектной модели Python, класс является объектом, поэтому класс должен быть экземпляром другого определенного класса. По умолчанию класс Python является экземпляром класса type. Таким образом, тип является метаклассом большинства встроенных классов и метаклассом пользовательских классов.

class ListMetaclass(type):
    def __new__(cls, name, bases, attrs):
        attrs['add'] = lambda self, value: self.append(value)
        return type.__new__(cls, name, bases, attrs)

class CustomList(list, metaclass=ListMetaclass):
    pass

lst = CustomList()
lst.add('custom_list_1')
lst.add('custom_list_2')

lst
['custom_list_1', 'custom_list_2']

Магия вступит в силу, когда мы передадим аргументы ключевого слова в метаклассе, он указывает интерпретатору Python для создания CustomList через ListMetaclass.new (), на этом этапе мы можем, например, изменить определение класса, добавить новый метод и затем вернуть исправленное определение.

140

тв и методов в экземпляр.

Например, если вы посмотрите наДжанго моделиИх определение выглядит немного запутанным. Похоже, вы определяете только свойства класса:

class Person(models.Model):
    first_name = models.CharField(max_length=30)
    last_name = models.CharField(max_length=30)

Однако во время выполнения объекты Person заполняются всевозможными полезными методами. Увидетьисточник для какой-то удивительной метаклассерии.

Это не использование мета-классов, добавляющих новые свойства и методы кclass а не экземпляр? Насколько я понял, метакласс изменяет сам класс, и в результате измененный класс может по-разному создавать экземпляры. Может быть немного вводит в заблуждение людей, которые пытаются понять природу мета-класса. Наличие полезных методов на экземплярах может быть достигнуто путем обычной инерции. Ссылка на код Django в качестве примера, тем не менее, хороша.

Похожие вопросы