在运行时覆盖第三方类的_调用_方法,同时维护其他方法

2024-10-01 15:28:26 发布

您现在位置:Python中文网/ 问答频道 /正文

考虑下面的类层次:

       A
       |
 _____________
 |     |     |
Foo   Bar   Baz

A定义了一个__call__方法,该方法FooBarBaz实现。这些类还定义了我感兴趣的其他各种方法。这三个类都来自第三方库,所以我不想更改它们的实现。然而,在某些情况下,在运行时,我想改变FooBarBaz{}方法的实现。现在,我通过定义自己的类来实现这一点,这些类只覆盖__call__方法:

Foo   Bar   Baz
 |     |     |
Foo1  Bar1  Baz1

然后,根据情况,我将所需的类实例化为一个公共变量。我对这个解决方案不满意,因为它有以下缺点:

  • 我对__call__所做的添加不一定是由类唯一确定的Bar1和{}可能共享{}的实现(但是{}和{}在其他方面有所不同)。所以,据我所知,我需要重复代码

  • 在运行时,实例化所需的类需要区分大小写(其中,mFooBarBaz级别的类数,nFoo1Bar1Baz1级别的类数)。这将随着mn的增加而迅速增加。理想情况下,我希望案例区分的数量为m+n

我已经看过{a1}、{a2}和{a3}。然而,这些解决方案并不完全适合我的情况,因为(1)我正在重写第三方类的dunder方法(2),并且(3)仍然希望能够访问其他类方法FooBarBaz

是否有一个优雅的理由来实现我所追求的

编辑:在运行时更改__call__应该只影响实例对象,而不是一次影响类或该类的所有对象

编辑2(由@enzo请求):

# -*- coding: utf-8 -*-
from abc import ABCMeta, abstractmethod


class A(metaclass=ABCMeta):
    @abstractmethod
    def __call__(self, x, y):
        """Docstring A"""
    
    @abstractmethod
    def method1(self, a, b):
        """Docstring method1"""

class Foo(A):
    def __call__(self, x, y):
        return x + y # indeed, no dependence on `self`
    
    def method1(self, a, b):
        # but this might depend on `self`
        pass

class Bar(A):
    def __call__(self, x, y):
        return x * y # indeed, no dependence on `self`
    
    def method1(self, a, b):
        # but this might depend on `self`
        pass

    def method2(self, c, d):
        # but this might depend on `self`
        pass

class Baz(A):
    def __call__(self, x, y):
        return x ** y # indeed, no dependence on `self`
    
    def method1(self, a, b):
        # but this might depend on `self`
        pass

    def method2(self, c, d):
        # but this might depend on `self`
        pass

Tags: 方法selffooondefbar情况pass
2条回答

我不知道这是否是您的意思,但根据您的示例代码,您可以这样做:

# Create a decorator to keep the reference to the original __call__'s
class CustomA(A):
    def __init__(self, obj):
        self.obj = obj
        
    def __call__(self, x, y):
        return self.obj(x, y)
        
    def method1(self, a, b):
        return self.obj.method1(a, b)
        
# Create a decorator that calls the original __call__
class MultiplyA(CustomA):
    def __init__(self, obj):
        super().__init__(obj)
        
    def __call__(self, x, y):
        result = super().__call__(x, y)
        return result * 10
        
# Create a decorator that ignores the original __call__
class DivideA(CustomA):
    def __init__(self, obj):
        super().__init__(obj)
        
    def __call__(self, x, y):
        # You can still access the original object' attributes here
        super().method1(x, y)
        return x / y

用法:

foo = Foo()
print(foo(1, 2))
# Outputs 3

foo = MultiplyA(foo)
print(foo(1, 2))
# Outputs 30

bar = Bar()
print(bar(2, 3))
# Outputs 6

bar = DivideA(bar)
print(bar(10, 5))
# Outputs 2.0

您可以向其中插入自定义调用:

class A():
    def __call__(self, *args, **kwargs):
        print("In A.__call__")

    def showCall(self):
        print(self.__call__)

class foo(A):
    pass

class bar(A):
    pass

class baz(A):
    pass

def my_custom_func():
    print("In my custom func")

f = foo()
f()

b = bar()
b()

f.showCall()
b.showCall()

print("##setting call##")
f.__call__ = my_custom_func
print("##running f calls##")
f.showCall()
f()

print("##running b calls##")
b.showCall()
b()

虽然这确实解决了您的注入问题,但在您仍然需要原始__call__方法的情况下,其他事情可能会起作用。在这种情况下,您需要在设置它们之前保存它们

相关问题 更多 >

    热门问题