在 Python 中,如何使包装器同时适用于类和静态方法



我有几个称为parse的方法,它们要么是静态方法,要么是类方法。在这两种情况下,如果输入None,我希望它们返回None。为此,我尝试定义以下包装器maybe

import types
def maybe(function):
    if type(function) == types.FunctionType:
        def wrapped_function(arg, **kwargs):
            return function(arg, **kwargs) if arg is not None else None
        return wrapped_function
    elif type(function) == types.MethodType:
        def wrapped_function(cls, arg, **kwargs):
            return function(cls, arg, **kwargs) if arg is not None else None
        return wrapped_function
    else:
        raise TypeError("The 'maybe' wrapper can wrap either a function or a method.")

下面是一个示例用例,其中包含一些测试:

import pytest
class Resource(object):
    @classmethod
    @maybe
    def parse_class(cls, string):
        pass
    @staticmethod
    @maybe
    def parse_static(string):
        pass

'''Tests'''
def test_maybe_parse_class():
    assert Resource.parse_class(None) == None
def test_maybe_parse_static():
    assert Resource.parse_static(None) == None

if __name__ == "__main__":
    pytest.main([__file__])

问题是第二个测试失败,并显示

TypeError: wrapped_function() takes exactly 1 argument (2 given)

似乎 Python 正在尝试评估或"检查"if type(function) == types.FunctionType块的内容,即使我已经通过 print 语句验证了布尔值是False的。

如何使此包装器在两个测试中工作?

强制在

静态或类注解之后完成maybe注解(将其放在代码的上方(。然后,您可以检查函数/描述符的注释并决定如何处理它。例如。

def maybe(wrapped):
    if isinstance(wrapped, classmethod):
        original_function = wrapped.__func__
        @classmethod
        def wrapper(cls, arg):
            if arg is None:
                return None
            else:
                return original_function(cls, arg)
        return wrapper
    elif isinstance(wrapped, staticmethod):
        original_function = wrapped.__func__
        @staticmethod
        def wrapper(arg):
            if arg is None:
                return None
            else:
                return original_function(arg)
        return wrapper
    raise TypeError("expected classmethod or staticmethod")
class Resource(object):
    @maybe
    @classmethod
    def parse_class(cls, arg):
        return "not none"
    @maybe
    @staticmethod
    def parse_static(arg):
        return "not none"

这是针对 python 3 的。看来你可能有python 2。所以它不能100%保证工作。您可能需要做更多的工作才能将原始函数从类方法 obj 中取出

您的方法实际上有效,除了您犯了一个简单的键入错误,通过键入 arg 而不是 *arg 。 对于classmethod,将传递一个额外的参数,这是cls参数,只是arg不是为了同时接受clsNone,所以发生了错误。 arg*arg确实有不同的含义,只是arg代表给定位置的位置论证,而*arg则取所有未被立场论证捕获的剩余论点。它的运行方式与**kwargs获取所有剩余的关键字参数的方式相同。

所以你应该maybe改成这样:

import types
def maybe(function):
    if type(function) == types.FunctionType:
        def wrapped_function(*arg, **kwargs): # change here
            return function(*arg, **kwargs) if arg is not None else None # change here
        return wrapped_function
    elif type(function) == types.MethodType:
        def wrapped_function(cls, *arg, **kwargs): # change here
            return function(cls, *arg, **kwargs) if arg is not None else None # and change here
        return wrapped_function
    else:
        raise TypeError("The 'maybe' wrapper can wrap either a function or a method.")

最新更新