向子类方法中的函数添加钩子

Dav*_*vid 4 python inheritance

给出以下简化代码:

from abc import ABC, abstractmethod

class Parent(ABC):
    def __init__(self,*args,**kwargs):
        self.parent_name = 'SuperClass'
    
    # global hook to run before each subclass run()
    def global_pre_run_hook(self):
        pass
    
    @abstractmethod
    def run(self, *args, **kwargs):
        raise NotImplementedError()

        
        
class Child(Parent):
    def __init__(self,*args,**kwargs):
        super().__init__(*args,**kwargs)
        self.name = 'ChildClass'
    
    def run(self):
        print(f'my parent name is {self.parent_name}')
        print(f'my name is {self.name}')
        
        return 22
        
obj = Child()
result = obj.run()
Run Code Online (Sandbox Code Playgroud)

有没有办法添加功能,以便run()直接调用子类方法时,首先触发父类的钩子函数?假设有一个父类和许多子类,我是否需要在每个子类的call global hook()每个定义的开头手动添加一个?有没有一种Pythonic的方法来完成这个?run()Parent()

Hyp*_*ane 5

绿斗篷人的答案有效,但不能被腌制!为了解决这个问题,我们需要将钩子创建移到__new__. functools.wraps另外,在钩子创建器中使用也是一个好主意。

import pickle
from abc import ABC, abstractmethod
from functools import wraps

def create_hook(func, hook):
    @wraps(func)
    def wrapper(*args, **kwargs):
        hook(*args, **kwargs)
        return func(*args, **kwargs)
    return wrapper

class Parent(ABC):
    def __new__(cls, *args, **kwargs):
        cls.run = create_hook(cls.run, cls.global_pre_run_hook)
        return super().__new__(cls, *args, **kwargs)

    # global hook to run before each subclass run()
    def global_pre_run_hook(self, *args, **kwargs):
        print("Hooked")

    @abstractmethod
    def run(self, *args, **kwargs):
        raise NotImplementedError()

class Child(Parent):
    def run(self):
        print(f"my parents are {self.__class__.__mro__}")
        print(f"my name is {self.__class__.__name__}")
        return 22

obj = Child()
result = obj.run()
pickle.dumps(obj)
Run Code Online (Sandbox Code Playgroud)