为什么带参数的python装饰器需要3个嵌套函数?

bq9c1y66  于 2022-12-15  发布在  Python
关注(0)|答案(1)|浏览(263)

我有一些代码我想改进。我得到一个建议,使用一个复杂的解决方案,使用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
u7up0aaq

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)只是一行程序,比装饰器更显式,但是可能有更多的方法来实现它。

相关问题