Почему мой метакласс не ведет себя так, как ожидалось

Хорошо, поэтому я начинаю возиться с voodoo и более темной стороной Python с метаклассами, я хотел бы получить простой пример работы, прежде чем я его модифицировал дальше, почему он не печатает инструкцию печати при выполнении этого кода,

class Versioned(type):
    def __new__(cls, name, bases, dct):
        return type.__new__(cls, name, bases, dct)

    def __setattr__(cls, key, value):
        print "Setting attr %s to %s" % (key, value)
        return object.__setattr__(cls, key, value)

class Test(object):
    __metaclass__ = Versioned

    def __init__(self):
        self.a = 1
        self.b = 2

a = Test()
a.b = 31
print dir(a)
1
nl ja de

3 ответы

Дело в том, что метакласс не является классом как таковой - это вызываемый, который возвращает класс - класс , Он служит той же цели, что и type (__ name__, __bases__, __dict __) .

>>> type('myclass', (), {})

Когда вы определяете __ metaclass __ , вы просто переопределение фабрики по умолчанию для этого класса или модуля . Например, это метакласс:

def setattr_logging_class(name, bases, dict_):
    """I am a metaclass"""
    def __setattr__(self, k, v):
        print "{} set attribute {} to {}".format(self, k, v)
        super(self.__class__, self).__setattr__(k, v)
    dict_['__setattr__'] = __setattr__
    cls = type(name, bases, dict_)
    return cls

class MyClass(object):
    __metaclass__ = setattr_logging_class
    def __init__(self):
        self.a = 1

obj = MyClass()
obj.b = 2

print obj.__dict__

Most importantly, a metaclass does not participate in the method resolution of the created class (unless you change bases). This is why your Versioned.__setattr__ is not visible to your Test instances. All Versioned did was return a new class (of type Versioned rather than type type) with the same name, bases and dict_ the Python runtime parsed out of your class Test(object): block.

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

class MyClass(object):
    def __new__(cls_self, *args, **kwargs):
        print "I am {} called with {} and {}".format(cls_self, args, kwargs)
        return None

myobj = MyClass(1,2,3,a=4,b=5,c=6)
print myobj # == None

Поскольку классы вызываемы, вы можете использовать класс как метакласс. Фактически, type - это класс. Хотя вы можете просто использовать экземпляр с помощью метода __ call __ ! . Эти два примера похожи, и нет ничего, что вы, возможно, захотите сделать, что не может быть сделано в любом случае. (Фактически, использование объекта обычно более простое.)

class MetaClass(type):
    def __new__(cls, name, bases, dict_):
        print "I am {} called with {}, {}, {}".format(cls, name, bases, dict_)
        return type.__new__(cls, name, bases, dict_)

class MetaObject(object):
    def __call__(self, name, bases, dict_):
        print "I am {} called with {}, {}, {}".format(self, name, bases, dict_)
        return type(name, bases, dict_)


class MyClass(object):
    __metaclass__ = MetaClass


class MyClass2(object):
    __metaclass__ = MetaObject()

Note that Python has many other metaprogramming methods besides metaclasses. In particular, Python >= 2.6 added support for class decorators, which covers most of the use cases for metaclasses with a much simpler interface. (Rather than create the class yourself, you get an already-created class object you modify.)

Вы можете просмотреть обзор нескольких методов метапрограммирования в этот ответ stackoverflow, который я недавно написал . Первоначальный вопрос: «Как создать встроенный контейнер типа Python threadafe?»

2
добавлено
С такой фамилией, как «Авила», как вы могли пойти в школу с Dominicans ? Настоящий открытый ум, который вы должны иметь ...
добавлено автор FastAl, источник
Никогда не обращайте внимания на имя ...
добавлено автор FastAl, источник

Я никогда не использовал метаклассы, но я думаю, что вы смешиваете наследование с метаклассами. Метаклассы существуют, чтобы создавать экземпляры, но они не добавляют ничего неявно вашему экземпляру. Нет причин, чтобы метод __ setattr __ был импортирован в Test . Чтобы добавить его явно, вам нужно изменить Test dict ( dct ). Что-то вроде dct ["__ setattr__"] = cls .__ setattr __ . Однако это не работает, и я не понимаю, почему. Единственный способ заставить меня работать:

class Versioned(type):
    def __new__(cls, name, bases, dct):
        print("BLAH")
        def __setattr__(cls, key, value):
            print("Setting attr %s to %s" % (key, value))
            return object.__setattr__(cls, key, value)
        dct["__setattr__"] = __setattr__
        return type.__new__(cls, name, bases, dct)

Надеюсь, это может помочь :-)

Наконец, ссылка может помочь вам понять метаклассы в python.

1
добавлено

Вы определяете __ setattr __ для самого класса, а не для его экземпляров. Это приведет к выполнению инструкции печати:

Test.key = value
1
добавлено
Python
Python
7 654 участник(ов)

Уютный чат для профессионалов, занимающихся поиском питоньих мудростей. Как не получить бан: https://t.me/ru_python/577926

Python beginners
Python beginners
4 449 участник(ов)

Вопросы про Python для чайников. Cпам и троллинг неприемлем. Не злоупотребляйте стикерами. Частозадаваемые вопросы: https://github.com/ru-python-beginners/faq/blob/master/README.md Статистика тут: https://grstats.me/chat/x4qym2k5uvfkr3al6at7

pro.python
pro.python
1 090 участник(ов)

Сообщество разработчиков под Python Создатель: @rodgelius

Rude Python
Rude Python
971 участник(ов)

Python без „девочек”, здесь матерятся и унижают Django. Not gay friendly. Правила: t.me/rudepython/114107 @rudepython | t.me/rudepython

rupython
rupython
509 участник(ов)

Группа создана с целью оперативного получения ответов на возникающие вопросы по разработке на яп python, смежные темы, а также человеческого общения. Приветствую!

Python-programming
Python-programming
266 участник(ов)

Чат группы вконтакте https://vk.com/python_community