我正在尝试创建一个Python文件,其中包含所有我需要在程序其余部分中使用的装饰器。这些装饰器存储在一个类中,我称之为Decorators
。然后,我尝试添加装饰器,以检查装饰函数的参数是否与传递给装饰器本身的参数类型匹配(我从站点https://www.python.org/dev/peps/pep-0318/#examples的示例4中获取了这种装饰器,但我对其进行了更改更好地适合我的编码风格)。语法如下:
class Decorators(object):
""" Decorators class: contain all the decorators """
@classmethod
def argument_consistency(cls, *function_arguments_type):
""" check the consistency of argument and their types of the decorated function """
def check_arguments(function):
""" check if the number of passed arguments is different from the number of accepted arguments """
# check if the number of passed arguments is different from the number of accepted arguments
if not len(function_arguments_type) == function.__code__.co_argcount:
raise Exception("the number of passed argument is different from the number of the accepted arguments")
def inner_function(*args, **kwds):
""" check if the type of the passed arguments match with the requested ones """
# iterate through the list of couples (argument, argument's type) and check for their match
for (arguments, argument_types) in zip(args, function_arguments_type):
# remember that: {arguments} is the n-th argument passed to the function, while
# the {argument_types} is the n-th argument types. {args} is the entire list of arguments
# passed to the function; {function_arguments_type} is the entire list of types. So zip
# returns an iterator of tuples of element of {args} and {function_arguments_type} paired
# together, for example zip((1, 2, 3, 4), (a, b, c, d)) = ((1, a), (2, b), (3, c), (4, d))
# check if the n-th argument type match with the one requested
if not type(arguments) == argument_types:
raise Exception(f"The argument {arguments} doesn't match the type, "
f"which must be {argument_types}")
# returning the passed function using the passed arguments
return function(*args, **kwds)
# changing the name of the inner_function to the {function}'s name
inner_function.__name__ = function.__name__
# return the inner function
return inner_function
# return the check_argument function
return check_arguments
为了测试以前的装饰器,我创建了带有函数A
的简单类a
:
class A():
def __init__(self):
pass
@Decorators.argument_consistency(str, str)
def a(self, str1, str2):
print(f"{str1} AND {str2}")
a = A()
a.a("ciao", "ciao2")
很显然,当我装饰函数a时,出现了一个错误(由argument_consistency
装饰器本身引起)。这是因为列表参数类型的长度与传递的参数列表的长度不同。出现错误是因为我没有输入self
参数。理解了此错误之后,我尝试将self
传递给装饰器,但收到一个错误:NameError: name 'self' is not defined
(即使我通过type(self)
也会发生这种情况);然后我尝试传递类A
本身,但仍然遇到相同的错误。因此,我尝试通过在装饰器的for
循环和if not type(arguments) == argument_types
之间添加一行来解决此问题:
if not (args.index(arguments) == 0 and argument_types is None):
# check if the n-th argument type match with the one requested
if not type(arguments) == argument_types:
# the rest of the code
pass
这些行检查传递给函数装饰器的第一个参数是否为None
,这意味着该函数的第一个参数为self
,因此该函数不会继续检查{ {1}}等于None
参数的类型(显然不是)。这种方式非常麻烦,与优雅的方式相反。因此,我想知道是否有一种方法可以避免此问题,并将self
类型直接传递给装饰器。
答案 0 :(得分:1)
您可以为对象/类方法的自变量创建一个存根类
class selftype:
pass
并将其传递给装饰器
@Decorators.argument_consistency(selftype, str, str)
def a(self, str1, str2):
print(f"{str1} AND {str2}")
然后在inner_function
中检查装饰器中的第一种类型是否是您的存根类型:
def inner_function(*args, **kwds):
for (argument, argument_type, i) in zip(args, function_arguments_type, range(0, len(args))):
if argument_type == selftype and i == 0:
pass
# check if the n-th argument type match with the one requested
elif not type(argument) == argument_type:
raise Exception(f"The argument {argument} doesn't match the type, "
f"which must be {argument_type}")
# returning the passed function using the passed arguments
return function(*args, **kwds)
不太优雅,但这对对象/类方法和函数都有效
class A():
def __init__(self):
pass
@Decorators.argument_consistency(selftype, str, str)
def a(self, str1, str2):
print(f"{str1} AND {str2}")
a = A()
a.a("ciao", "ciao2")
@Decorators.argument_consistency(str)
def b(str1):
print(f"{str1}")
b("a")
此外,如果您想与@classmethod
或@staticmethod
一起使用装饰器,请确保先应用装饰器,否则将无法访问function.__code__
属性
我非常喜欢@ go2nirvana在评论中提出的解决方案,但不幸的是,它对我不起作用。 inspect.ismethod(function)
在装饰器函数调用中返回False
,这就是为什么。