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

怎么实现给抽象方法类增加__call__方法

  •  
  •   caneman · 2022-05-17 12:09:57 +08:00 · 2035 次点击
    这是一个创建于 922 天前的主题,其中的信息可能已经有所发展或是发生改变。

    需求: 想给抽象方法类实现函数式调用,该怎么写

    分开写,我知道怎么写,但是怎么让一个类同时具有两种特性?

    抽象方法类

    class A(ABC):
    
        def __init__(self, name):
            self.name = name
    
        @abstractmethod
        def run(self):
            pass
    
    
    class B(A):
    
        def __init__(self, name):
            super().__init__(name)
    
        def run(self):
            print(self.name)
    
    

    实现函数式调用

    class MetaB(type):
    
        def __call__(cls, *args, **kwargs):
            obj = super().__call__(*args, **kwargs)
            return obj.run()
    
    class B(metaclass=MetaB):
    
        def run(self):
            pass
    

    怎么让 B 同时拥有两种特性?按我的理解要想让一个类实现函数式调用,就得在它的元类里面重载__call__方法,可是抽象方法类实现的过程中已经定义了元类是 ABCMeta ,这种情况该怎么覆写呢?

    第 1 条附言  ·  2022-05-17 13:10:01 +08:00

    解决了

    from abc import ABC, abstractmethod, ABCMeta
    
    
    class MetaB(ABCMeta):
    
        def __call__(cls, *args, **kwargs):
            obj = super().__call__(*args, **kwargs)
            return obj.run()
    
    
    class A(ABC):
    
        def __init__(self, name):
            self.name = name
    
        @abstractmethod
        def run(self):
            pass
    
    
    class B(A, metaclass=MetaB):
    
        def __init__(self, name):
            super().__init__(name)
    
        def run(self):
            print(self.name)
    
    
    if __name__ == '__main__':
        B(123)
    

    错误提示没好好读。。。 TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass #of the metaclasses of all its bases

    7 条回复    2022-05-17 12:40:36 +08:00
    BingoXuan
        1
    BingoXuan  
       2022-05-17 12:15:35 +08:00
    看了半天也没理解 lz 到底像要啥。你是希望如下还怎么样
    b = B()
    b.run()
    b()
    # b() has same effect like b.run()
    caneman
        2
    caneman  
    OP
       2022-05-17 12:23:38 +08:00
    @BingoXuan 我想实现的是,B 是一个类,但是可以当作函数用,B(),就直接执行了
    fgwmlhdkkkw
        3
    fgwmlhdkkkw  
       2022-05-17 12:27:34 +08:00
    __new__
    caneman
        4
    caneman  
    OP
       2022-05-17 12:30:05 +08:00
    @BingoXuan
    大概这么一个场景,很多类,每个类有细微不同,把里面共同的部分提取出来,放在一个抽象类里面,也定义了一些子类必须实现的方法(代码 1 的部分)

    所有这些子类,并不需要保持一个对象,可以看作一个大号的函数,我想实现,所有的子类,能通过 B() 这样就直接执行(代码 2 的部分)

    有没有一种方法,能让 B 同时实现两种功能,即在 1 的基础上,让其子类能实现,子类()这样作为一个函数直接执行的功能
    BingoXuan
        5
    BingoXuan  
       2022-05-17 12:31:30 +08:00
    @caneman
    3 楼那样,__new__
    调用顺序是 metaclass.__new__->metaclass.__init__->cls.__new__->cls.__init__
    caneman
        6
    caneman  
    OP
       2022-05-17 12:39:58 +08:00
    @BingoXuan 比__new__的时机要晚,在 cls.__init__之后(下面的代码不对,但是大概这么个意思)
    caneman
        7
    caneman  
    OP
       2022-05-17 12:40:36 +08:00
    class A(ABC):

    def __call__(self, *args, **kwargs):
    obj = super().__call__(*args, **kwargs)
    return obj.run()

    def __init__(self, name):
    self.name = name

    @abstractmethod
    def run(self):
    pass


    class B(A):

    def __init__(self, name):
    super().__init__(name)

    def run(self):
    print(self.name)
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2786 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 27ms · UTC 07:51 · PVG 15:51 · LAX 23:51 · JFK 02:51
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.