V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
caneman
V2EX  ›  Python

Python 元类问题求解

  •  
  •   caneman · 2019-06-12 14:56:32 +08:00 · 1959 次点击
    这是一个创建于 2020 天前的主题,其中的信息可能已经有所发展或是发生改变。
    class Base(type):
        print('This is Base.')
    
        def __new__(cls, name, bases, attrs):
            print(cls)
            return type(name, bases, attrs)
            #return type.__new__(cls, name, bases, attrs)
    
    class Parent(object, metaclass=Base):
        print('This is Parent.')
    
    
    class Child(Parent):
        print('This is Child.')
    

    输出结果如下:

    • This is Base.
    • This is Parent.
    • <class '__main__.Base'>
    • This is Child.

    这个结果我还可以理解,由上往下执行,走到 Parent 的时候,由元类__new__方法中的 type 方法生成一个新类,返回给 Parent,下面的 Child 继承了 Parent 这个类。

    为什么当把return type(name, bases, attrs)换成return type.__new__(cls, name, bases, attrs)的时候会导致下面的结果?

    • This is Base.
    • This is Parent.
    • <class '__main__.Base'>
    • This is Child.
    • <class '__main__.Base'>

    我知道复写type.__new__方法,产生的结果是 Base 这个类的一个实例,但同时这个实例也是一个类对象, 但是我不太理解的是为什么这里 Child 会再次执行 Base 里面的__new__方法,为什么上面(type())的不执行?

    查阅了很多资料,国内的论坛好像很少有讲type.__new__type()的区别的,还是我姿势不对? StackOverflow 和 Python3.7 文档我也看了相关的内容,能理解type.__new__type()的区别了,但是不太明白涉及到这种多重继承的时候type.__new__type()为什么会有差异。

    还是我理解错了?希望有人能帮我解答一下,谢谢!!


    总结一下就是:

    • 采用 type()的写法,会导致 Child 仅仅继承 Parent 类的属性和方法。
    • 采用type.__new__()的写法,会使 Child 类是由 Base 生成的,会覆盖掉 Parent 类中由 Base 生成的部分。

    我想问一下为什么会导致这种差异。

    10 条回复    2019-06-12 16:26:14 +08:00
    mayorbryant
        1
    mayorbryant  
       2019-06-12 15:07:32 +08:00
    super 了解一下
    wwqgtxx
        2
    wwqgtxx  
       2019-06-12 15:10:58 +08:00 via iPhone
    因为 type()是构建了一个新的类,而 type.__new__是创建了一个 type object
    简单的来说,当你直接用 type()的时候,你调用 Base()返回的根本就不是你自己定义的 Base,而且动态定义的一个也叫 Base 的 class
    metaclass 的关键并不是要重载__new__,而是要重载__call__才能改变子类的创造过程,你重载了__new__只是改变了自己的构造过程罢了
    wwqgtxx
        3
    wwqgtxx  
       2019-06-12 15:19:54 +08:00 via iPhone
    仔细看了一下,撤回 2#的回复
    wwqgtxx
        4
    wwqgtxx  
       2019-06-12 15:23:14 +08:00 via iPhone   ❤️ 1
    直接 type()之后返回的 Parent 就不再含有 Base 为自己的 metaclass 信息了(.__metaclass__),而你调用了 type.__new__会附加上,所以 Child 的 metaclass 同样是 Base
    wwqgtxx
        5
    wwqgtxx  
       2019-06-12 15:32:24 +08:00 via iPhone   ❤️ 1
    caneman
        6
    caneman  
    OP
       2019-06-12 15:34:21 +08:00
    @wwqgtxx 明白了,那么`type.__new__`给 Parent 附加上 metaclass 信息具体是在哪个部分实现的?如果我想实现`type.__new__`和 type 具有相同的结果(只针对 metaclass 信息有无这一点),我应该重载哪个部分呢?
    wwqgtxx
        7
    wwqgtxx  
       2019-06-12 15:40:40 +08:00 via iPhone   ❤️ 1
    @caneman 实现就是在 typeobject.c 的 2442 行到 2452 行
    wwqgtxx
        8
    wwqgtxx  
       2019-06-12 15:46:11 +08:00 via iPhone
    正常还是应该重载__new__,然后调用 type.__new__
    之前二楼回答有误,type()和 type.__new__返回的均是一个新的类
    wwqgtxx
        9
    wwqgtxx  
       2019-06-12 15:54:26 +08:00 via iPhone   ❤️ 1
    准确说 type()之后创建了一个 type 的实例,而你调用了 type.__new__创建的是一个 Base 的实例(这个实例本身的父类当然还是 type )
    caneman
        10
    caneman  
    OP
       2019-06-12 16:26:14 +08:00
    @wwqgtxx 明白了,谢谢!
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   968 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 29ms · UTC 22:54 · PVG 06:54 · LAX 14:54 · JFK 17:54
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.