向每个方法添加自动函数调用

时间:2019-06-13 11:08:11

标签: python python-3.x

是否可以为每个函数创建一个“构造函数”或“初始化器”,而不必在类中每个函数的顶部手动编写它?

因此,每次调用一个类中的一个函数时,总是首先调用另一个分配的函数(调用者未知)(在下面的示例中称为pre_check)。

使用super()的示例,但是我不得不在每个函数中手动复制它。

class Helper():
    def pre_check(self):
        print("Helper fcn")


class Parent(Helper):
    def __init__(self):
        print("Initializer")

    def foo(self):
        super().pre_check()  # <---- new code
        # ... existing code here ...

    def bar(self):
        super().pre_check()  # <---- new code
        # ... existing code here ...

    def many_more_functions(self):
        super().pre_check()  # <---- new code
        # ... existing code here ...

m = Parent()

m.foo()
m.bar()

请注意,__init__中的Parent不应运行pre_check

3 个答案:

答案 0 :(得分:2)

您可以为该类使用装饰器,该装饰器将依次装饰该类中定义的所有公共方法:

def addhelper(helpmethod):
    def deco(cls):
        def decomethod(method):
            def inner(self, *args, **kwargs):
                helpmethod(self)
                return method(self, *args, **kwargs)
            # copy signature, doc and names from the original method
            inner.__signature__ = inspect.signature(method)
            inner.__doc__ = method.__doc__
            inner.__name__ = method.__name__
            inner.__qualname__ = method.__qualname__
            return inner
        # search all methods declared in cls with a name not starting with _
        for name, meth in inspect.getmembers(
            cls,lambda x: inspect.isfunction(x)
            and not x.__name__.startswith('_')
            and x.__qualname__.startswith(cls.__name__)):
            # replace each method with its decoration
            setattr(cls, name, decomethod(meth))
        return cls
    return deco

class Helper():
    def pre_check(self):
        print("Helper fcn")

@addhelper(Helper.pre_check)
class Parent(Helper):
    def __init__(self):
        print("Initializer")

    def foo(self):
#        super().pre_check()  # <----
        print('in foo')

    def bar(self):
#        super().pre_check()  # <----
        print('in bar')

    def many_more_functions(self):
#        super().pre_check()  # <----
        print('in many_more_functions')

我们现在可以使用它:

>>> p = Parent()
Initializer
>>> p.foo()
Helper fcn
in foo
>>> p.bar()
Helper fcn
in bar
>>> p.many_more_functions()
Helper fcn
in many_more_functions

答案 1 :(得分:1)

您可以使用元类并为该元类实例中的每个方法定义一个装饰器

代码:

def decorate(f):
    def do_something(self, a):

        if (f(self, a) > 18) :
            return ("Eligible to vote")
        else :
            return ("Not eligible to vote")

    return do_something


class Meta(type):
    def __new__(cls, name, bases, namespace, **kwds):
        namespace = {k: v if k.startswith('__') else decorate(v) for k, v in namespace.items()}
        return type.__new__(cls, name, bases, namespace)


class MetaInstance(metaclass=Meta):

    def foo1(self, val):
        return val + 15

    def foo2(self, val):
        return val + 9

obj1 = MetaInstance()
print(obj1.foo1(5))
print(obj1.foo2(2))

答案 2 :(得分:1)

使用__init_subclass__更改创建的子类。您可以包装子类的方法:

class Helper():
    def __init_subclass__(cls):
        for field, value in cls.__dict__.items():
            # add additional checks as desired, e.g. exclude __special_methods__
            if inspect.isfunction(value) and not getattr(value, 'checked', False):
                setattr(cls, field, cls._check(value))  # wrap method

    @classmethod
    def _check(cls, fcn):
        """Create a wrapper to inspect the arguments passed to methods"""
        @functools.wraps(fcn)
        def checked_fcn(*args, **kwargs):
            print(fcn, "got", args, kwargs)
            return fcn(*args, **kwargs)
        return checked_fcn


class Parent(Helper):
    def __init__(self):
        print("Initializer")

    def foo(self):
        print("Foo")

请注意,这将包装所有方法,包括特殊方法,例如__init__

>>> Parent().foo()
<function Parent.__init__ at 0x1029b2378> got (<__main__.Parent object at 0x102c09080>,) {}
Initializer
<function Parent.foo at 0x1029b2158> got (<__main__.Parent object at 0x102c09080>,) {}
Foo

您可以使用任意规则扩展__init_subclass__的签入,以过滤出函数。例如,field[:2] == field[-2:] == "__"不包括特殊方法。