我有一些代码我想改进。我得到一个建议,使用一个复杂的解决方案,使用functools
,我不能理解。
代码解释:我正在尝试创建字符串转换器。我想在这里做的是在执行转换函数之前运行一些固定的代码。然而,执行取决于变量参数,如国家代码和字符串的验证长度。
这是我实现的灵感来自:https://www.scaler.com/topics/python/python-decorators/
我不明白为什么我们需要3层嵌套的函数来实现需要参数country_code和valid_length的装饰器。
import functools
from collections.abc import Callable
class Number:
def prevalidate(country_code: str, valid_lengths: list[int]): # type: ignore
def decorator(func: Callable):
@functools.wraps(func)
def wrapper(num: str, validate=False):
if num.startswith(country_code):
num = num[2:]
if validate and len(num) not in valid_lengths:
raise ValueError(f"{num} is not valid {country_code} number")
return func(num, validate)
return wrapper
return decorator
@staticmethod
@prevalidate(country_code="DZ", valid_lengths=[13])
def convert_dz(num: str, validate=False) -> str:
return num[4:6] + num[-4:]
... # other similar methods
num = Number.convert_dz("W/2011/012346") # => 1012346
1条答案
按热度按时间u7up0aaq1#
让我先解释一下每一个层次。
1.最外层是装饰器工厂,它基于一些输入值生成装饰器。
1.第二层是装饰器,它是一个以函数为参数的函数,并返回一个 Package 原始函数的新函数。
1.内部级别是 Package 器,它是将替换原始函数的函数。
现在,你想知道为什么第一层和第二层没有合并。它们确实可以合并,但是这三层是由
@
符号给出的快捷方式驱动的。函数func
上的@deco
等价于用func = deco(func)
覆盖函数名。而@deco_factory(args)
等价于deco = deco_factory(args); func=deco(func)
,所以@
符号只会将函数作为单个参数传递,尽管如此,您仍然可以手动修饰函数,但您可能会让其他已经习惯于三层设计的Python开发人员感到困惑。编辑:
我还没有评论你的代码示例,只是解释了标题问题。注意,每次你用相同的参数调用装饰器工厂,你就创建了一个新的装饰器。如果你只重用装饰器的一个示例会更好。此外,如果装饰器工厂的输入值改变了你的类
Number
的行为方式,最好将这些值添加到类构造函数中,我指的是__init__
方法,并使用Number
的示例。现在,实现可能不需要装饰器,因为在每个函数的开头添加self.prevalidate(num)
只是一行程序,比装饰器更显式,但是可能有更多的方法来实现它。