GetAttr在元类中引发AttributeErorr

时间:2018-07-01 14:34:08

标签: python python-3.x metaclass

我正在玩python以了解它是如何工作的,但是有些奇怪。我在我的MetaClass中定义了一个__new__方法,并且我希望第四个参数(这是将来类的属性的决定)包含在我将来类主体中定义的方法。

我有以下代码:

#!/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 :(得分:1)

字典不会将键公开为属性。字典将成为要创建的类的名称空间,但还不是类的一部分。类(及其元类)具有__getattribute__ method,可将属性访问转换为字典查找,但是在字典对象本身上不可用。

只需使用字典订阅访问权限:

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__就是这样的查找,在元类的MRO中找到下一个__new__属性。在这种情况下为type.__new__,调用该方法将生成实际的类对象。