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
saximi
V2EX  ›  Python

请教关于类实例化时调用__call__方法的问题,为何有时会调用,有时却不会?

  •  
  •   saximi · 2017-07-19 20:38:36 +08:00 · 1833 次点击
    这是一个创建于 2472 天前的主题,其中的信息可能已经有所发展或是发生改变。

    class MyType(type):

     def __init__(self, what, bases=None, dict=None):   
         print('call myType.__init__()')   #语句 1  
         super().__init__(what, bases, dict)   
    
     def __new__(cls, name, bases, attrs):   
         print("call MyType.__new__()")      #语句 2  
         return type.__new__(cls, name, bases, attrs)   
    
     def __call__(self, *args, **kwargs):  
         print("MyType.__call__")  
    

    class Foo(object, metaclass=MyType):

     def __init__(self, name=None):           
         self.name = name   
         print("Foo self.name=", self.name)   
     def __new__(cls, *args, **kwargs):   
         print("Foo.__new__ cls=", cls)   
         return(object.__new__(cls, *args, **kwargs))   
     def __call__(self, cls):   
         print("Foo.__call__ cls=", cls)   
    

    if name == 'main':
    print("---------test---------") #语句 3
    obj=Foo() #语句 4

    上面代码在 PYTHON3.6 中输出如下:
    call MyType.new() #语句 2 的输出 call myType.init() #语句 1 的输出
    ---------test---------
    MyType.call #语句 4 的输出

    请问,为何语句 4 “ obj=Foo()” 会导致__call__方法的执行? 我看了这篇博文 python.jobbole.com/83747/ 博文的“ 5.call”小节写了这段话:“注:构造方法的执行是由创建对象触发的,即:对象=类名();而对于__call__方法的执行是由对象后加括号触发的,即:对象()或者类()()”

    看了这段话后再结合上面这个程序,我就觉得有问题,obj=Foo()这个语句应该是博文中的 对象=类名() 这个形式,所以不应该调用 Foo.call()方法才对啊? 关于这个观点,我还有一个例子可以说明,比如下面这段代码和对应的输出,请注意语句 1 就是一个把对象实例化的语句,这个语句并没有调用类的__call__()方法,所以两个例子出现了矛盾,同样是类的实例化,上面的程序调用了类的 call 方法,下面的却没有:

    class Deco:

    def __init__(self,func): 
        self.func=func 
        print("__init__执行完毕。func=",self.func) 
    
    def __call__(self,*arg,**arg2): 
        print("开始执行__call__。") 
        self.func('abc') 
        print(self,arg,arg2) 
    

    class MyCls():

    @Deco 
    def myFunc(self): 
        print('this is my work arg is %s'%self) 
    

    mycls=MyCls()
    deco=Deco(mycls.myFunc)

    代码输出如下: __init__执行完毕。func= <function MyCls.myFunc at 0x01C49AE0>

    __init__执行完毕。func= <main.Deco object at 0x01C4B190>

    8 条回复    2017-07-20 00:19:27 +08:00
    lolizeppelin
        1
    lolizeppelin  
       2017-07-19 22:23:31 +08:00
    初学不要去关心 new 之类的黑魔法 先避开

    装饰器也尽量避开不要折腾

    装饰器只是个套娃语法糖,
    具体工作的时候涉及到闭包还有描述器之类的黑魔法

    等你比较熟了再回来弄这个
    lrxiao
        2
    lrxiao  
       2017-07-19 23:11:08 +08:00
    你自己写的 metaclass 的__call__啊
    yufpga
        3
    yufpga  
       2017-07-19 23:11:16 +08:00
    注意到 Foo 中的 metaclass 了么,这里面涉及到元类,这个东西一时半会讲不清楚(限于自身水平原因),你可以去掉 metaclass 部分,打印一下 obj.__class__, 自己慢慢体会下
    saximi
        4
    saximi  
    OP
       2017-07-19 23:15:39 +08:00
    @lrxiao 为何写了 metaclass 的__call__就会导致实例化时的调用呢,我也看过一些介绍 metaclass 的博文,但是没有针对这个问题作出解释
    saximi
        5
    saximi  
    OP
       2017-07-19 23:23:44 +08:00
    @yufpga 去掉 metaclass 的话,确实就不会发生调用 Foo 类的__call__方法了,但是就是不明白为何加上 metaclass 就会如此呢
    raptium
        6
    raptium  
       2017-07-19 23:49:19 +08:00 via iPad
    https://stackoverflow.com/questions/6966772/using-the-call-method-of-a-metaclass-instead-of-new

    感觉就是在做蠢事,metaclass 上定义了 __call__,导致根本没法通过()实例化了。
    lrxiao
        7
    lrxiao  
       2017-07-20 00:17:02 +08:00
    @saximi 因为 class 是 metaclass 的实例 相当于你这个 Foo 已经是 MyType(....) 出来的 就有 MyType.__call__
    但是装饰器之间没有实例关系 不调用__call__
    lrxiao
        8
    lrxiao  
       2017-07-20 00:19:27 +08:00
    @raptium 实际上很常用啊
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   992 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 27ms · UTC 20:19 · PVG 04:19 · LAX 13:19 · JFK 16:19
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.