GetAttr вызывает AttributeErorr в метаклассе - PullRequest
0 голосов
/ 01 июля 2018

Я играю с python, чтобы понять, как он работает, но есть что-то странное. Я определяю метод __new__ в своем MetaClass и ожидаю, что четвертый аргумент (который является атрибутом атрибута будущего класса) будет содержать методы, определенные в моем теле будущего класса.

У меня есть этот код:

#!/usr/bin/python3


class MyMetaClass(type):
    def __new__(metacls, name, bases, attrs):
        instance = super(MyMetaClass, metacls) #Create instance of super class, which metaclass is type
        print(type(type))
        print(type(type(instance)))
        print(instance.__new__)
        print(type.__new__)
        print(attrs)
        print(getattr(attrs, 'foobar'))
        return super(MyMetaClass, metacls).__new__(metacls, name, bases, attrs)

class TestClass(metaclass=MyMetaClass):
    def __get__(self, obj, type=None):
        return self
    def foobar(self):
        print(f'My name is {self.name}') 
    def __init__(self, args):
        print(args)
        self.firstname = args['firstname'] if 'firstname' in args else ''
        self.lastname = args['lastname'] if 'lastname' in args else ''
        self.name = f'{self.firstname} {self.lastname}'



data = {'firstname': 'Florian'}
c = TestClass(data)
c.foobar()

Вывод:

<class 'type'>
<class 'type'>
<built-in method __new__ of type object at 0x10d759e40>
<built-in method __new__ of type object at 0x10d759e40>
{'__module__': '__main__', '__qualname__': 'TestClass', '__get__': <function TestClass.__get__ at 0x10e192b70>, 'foobar': <function TestClass.foobar at 0x10e192bf8>, '__init__': <function TestClass.__init__ at 0x10e192c80>}
Traceback (most recent call last):
  File "<stdin>", line 25, in <module>
  File "<string>", line 13, in <module>
  File "<string>", line 10, in __new__
AttributeError: 'dict' object has no attribute 'foobar'

Итак print(attrs) в MyMetaClass.__new__ результат, как и ожидалось:

{'__module__': '__main__', '__qualname__': 'TestClass', '__get__': <function TestClass.__get__ at 0x7f042b9e6158>, 'foobar': <function TestClass.foobar at 0x7f042b9e61e0>, '__init__': <function TestClass.__init__ at 0x7f042b9e6268>}

Как видите, он содержит клавишу «foobar».

Однако, следующая строка поднимает AttributeError : 'dict' object has no attribute 'foobar'. Почему?

Я пытался hasattr(attrs, 'foobar'), который также возвращает false.

1 Ответ

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

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

Просто используйте доступ к словарной подписке:

print(attrs['foobar'])

или вызовите super().__new__(), сохраните возвращаемое значение и найдите атрибут этого нового объекта класса:

class MyMetaClass(type):
    def __new__(metacls, name, bases, attrs):
        new_class = super(MyMetaClass, metacls).__new__(metacls, name, bases, attrs)
        print(new_class.foobar)
        return new_class

Примечание: super(MyMetaClass, metacls) не не создает экземпляр! Он возвращает прокси-объект super(), атрибуты которого будут найдены в Порядке разрешения методов метакласса (MRO). super(MyMetaClass, metacls).__new__ - это такой поиск, который находит следующий атрибут __new__ в MRO метакласса. В этом случае это type.__new__, и вызов этого метода приводит к фактическому объекту класса.

...