python3 的元类问题 心地善良的给些指点吧

2019-07-12 21:03:00 +08:00
 waibunleung
# -*- coding:utf-8 -*-

class MyMeta(type):
    def __new__(cls, name, bases, attr):
        # attr['add'] = lambda self, x, y : x+y
        # attr['age'] = 0
        attr['addr'] = 'gz'
        return type.__new__(cls, name, bases, attr)

    def __init__(cls, name, bases, attr):
        super(MyMeta, cls).__init__(name, bases, attr)
        attr['age'] = 0
        cls.gender = 'male'
        print(cls)
        print(name)
        print(bases)
        print(attr)

        

class MyClass(object, metaclass=MyMeta):

    def __init__(self):
        # self.age = 1
        self.name = 'hh'


m = MyClass()

print(m.name)
print(m.gender)
print(m.addr)
print(m.age)

# output:

<class '__main__.MyClass'>
MyClass
(<class 'object'>,)
{'__module__': '__main__', '__qualname__': 'MyClass', '__init__': <function MyClass.__init__ at 0x1100cb268>, 'addr': 'gz', 'age': <function MyMeta.__init__.<locals>.<lambda> at 0x1100cb2f0>}
hh
male
gz
Traceback (most recent call last):
  File "/Users/luengwaiban/Desktop/meta.py", line 32, in <module>
    print(m.age)
AttributeError: 'MyClass' object has no attribute 'age'

上面的代码里,MyMeta 是元类,MyClass 是使用元类实例化的类。 我在元类中的__new__()方法中的 addr 参数里添加 age 元素后,在 MyClass 实例化后是可以正常访问到 age 的。 但是现在屏蔽掉__new__()方法中的往 addr 参数里添加 age 元素的语句,将它放到__init__()方法中的 addr 参数里,却发现在 MyClass 实例化后是访问不到 age,但是将 age 绑在__init__()的 cls 上却可以访问(类似于 gender 的绑定)。

这样子我就不是很理解了,为什么在元类中的__init__()方法里,将属性添加到 attr 后,MyClass 实例化完成后却访问不到对应的属性?但是将同样的操作放到元类中的__new__()方法中却可以?

2343 次点击
所在节点    Python
22 条回复
zpoint
2019-07-18 17:09:49 +08:00
@telnetning 这位老哥已经解答了, 我再补充下


执行到如下这行的时候

class MyClass(object, metaclass=MyMeta):

会调用 type(MyMeta).__call__ 去创建这个类, 这个 __call__ 函数在 C 里面的流程可以

简单的理解为 1. 调用 MyMeta.__new__ 生成一个类, 叫做 MyClass, __new__ 是上面你自己定义的, 其中你调用了 type.__new__(cls, name, bases, attr), 这一步会把 attr 中的值都复制到 MyMeta 对应的属性中, attr 只是个字典而已

2. 判断一下 issubclass(type(MyClass), MyMeta) 是否为 True, 是的话再调用一下 type(MyClass).__init__(MyClass, name, bases, attr), 这里你没有写任何代码处理 attr 和自身属性的关联, 同样的, attr 还是同一个字典

到这里, 类已经创建完了, 接下来创建实例, 过程类似
区别就是 __new__ 你写了一行代码 type.__new__(cls, name, bases, attr) 创建了一个类, 创建的过程中会把 attr 中的值都复制到新创建的类中对应的属性上

而 __init__ 你没有做对应的操作


还有, metaclass 的 __new__ 的第一个参数应该是 mcs, 为你定义的 metaclass 本身
而 metaclass 的 __init__ 的第一个参数应该是 cls, 为 metaclass 的 __new__ 函数创建并返回的新的类, 并不是 metaclass 本身 你定义的时候重名了
zpoint
2019-07-18 17:12:15 +08:00
更正一下错别字

调用 MyMeta.__new__ 生成一个类, 这里生成的类名称叫做 MyClass, __new__ 是上面你自己定义的, 其中你调用了 type.__new__(cls, name, bases, attr), 这一步会把 attr 中的值都复制到 MyClass 对应的属性中, attr 只是个字典

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/582511

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX