python - 将对象传递给装饰器
问题描述
我正在尝试创建一个 Python 文件,其中包含我需要在程序的其余部分中使用的所有装饰器。这些装饰器存储在一个类中,我称之为Decorators
. 然后我尝试添加一个装饰器来检查装饰函数的参数是否与传递给装饰器本身的参数类型匹配(我从站点https://www.python.org/的示例 4 中获取了这种装饰器dev/peps/pep-0318/#examples,但我对其进行了一些更改以更好地适应我的编码风格)。语法是这样的:
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
,因此函数不会继续检查是否None
等于self
参数的类型(显然不是) . 这种方式非常繁琐,与优雅相反。因此我想知道是否有办法避免这个修复并直接将self
类型传递给装饰器。
解决方案
您可以为对象/类方法的 self 参数创建一个存根类
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
or配对使用@staticmethod
,请确保先应用您的装饰器,否则将无法访问function.__code__
属性。
我非常喜欢@go2nirvana 在评论中提出的解决方案,但不幸的是,它对我不起作用。inspect.ismethod(function)
返回False
内部装饰器函数调用,idk 为什么。
推荐阅读
- php - 使用 Ajax 调用设置会话时出现不可预测的行为
- performance - 提高 PowerShell 函数从 CSV 中删除重复项的性能
- anylogic - 在 AnyLogic 中计算司机工资
- php - nodejs应用程序的PHP子进程不输出任何内容
- coldfusion - 如何将分块上传文件名从“blob”更改为另一个值?
- c# - 如何从返回类型为 int 的方法返回错误消息/null?
- java - 为什么程序在第二行和第三行打印不正确?
- r - 部署 Shiny 应用程序时包加载导致错误
- docker - 来自守护程序的错误响应:驱动器尚未共享
- java - javac 返回“错误:找不到模块:ini4j”