什么是装饰器,它将可调用函数应用于某个函数的每个输入参数?

问题描述 投票:0回答:1

我们如何使用装饰器清理函数的每个参数输入?

在下面的示例中,我希望类方法

sani.by_signature
将每个输入参数清理为名为
pow

的函数
import functools

class sani:  
    def getattr(attrname:str):
        lamby = lambda *args, **kwargs: None
        return lamby   

    def by_signature(*arg_cleaners, **kwarg_cleaners):
        return Decorator(*arg_cleaners, **kwarg_cleaners)

    class Decorator:
        def __new__(*arg_cleaners, **kwarg_cleaners):
            obj = super().__new__(cls)
            ### obj = functools.update_wrapper(a, b)
            return obj 

        def __init__(self, *arg_cleaners, **kwarg_cleaners):
            obj = super().__new__()
            obj = functools.
            return obj 
        
        def __call__(*iargs, **ikwargs):

            # variables with an `i` on the left-end of the variable name are inputs 

            for iarg in iargs:   
                args[0]  = sani.to_float(iargs[0])
            args = tuple(iargs)   

            kwargs = dict.fromkeys(ikwargs)  
            for ikey in ikwargs:

                kwargs[ikey] = 
            return pow(*args, **kwargs)

        return new_pow 

下面我们有

pow
函数,它应该被消毒。

class PowClass:
    @classmethod   
    @sani.by_signature(sani.to_float, sani.to_int, debug_on=sani.clean_str_or_none)  
    def pow(cls, base:float, exponent:int, *, debug_on=None)
        """                    

            | BASE  | EXP    | OUTPUT    |
            |-------|--------|-----------|
            | 10    | 0      | 1         |
            | 10    | 1      | 10        |
            | 10    | 2      | 100       |
            | 10    | 3      | 1000      | 
            | 3.5   | 0      | 1         |
            | 3.5   | 4      | 150.0625  |
        """
        if debug_on:
            cls.log("pow(", base, exponent, ")")
        if exponent == 0:
            return 1 
        return base*pow(base, exponent - 1)

    ###### COMMENT: @sani.each_pos_arg(clean_str_or_none)
    def log(*args, sep=" ", end="\n"):
        pass 
        # print(*args, sep=sep, end=end, file=some_file)
        

装饰者应该清理所有的输入。

对于这个问题,

to_float
clean_str_or_none
做什么并不重要,为了使代码可操作,我们可以有以下内容。

import singledispatched from functools 

class sani   
@singledispatched
def to_float(input:object):
    """
    +--------------------------------+-------------+
    |             INPUT              |   OUTPUT    |
    +--------------------------------+-------------+
    | string "4.992"                 | float 4.992 |
    | list ["4", ".", "9", "9", "2"] | float 4.992 |
    | list ["4.9", 9, "2"]           | float 4.992 |
    | float 4.992                    | float 4.992 |
    +--------------------------------+-------------+ 
    """    
    # doc string shown above   



@to_float.register(list)
def to_float_from_iterable(input:object):
    """  
    +--------------------------------+-------------+
    |             INPUT              |   OUTPUT    |
    +--------------------------------+-------------+
    | list ["4.9", 9, "2"]           | float 4.992 |
    +--------------------------------+-------------+ 
    """
    # TO DO LATER: predicate dispatch
    # @to_float.register(lambda obj: hasattr(obj, '__iter__'))
    return float("".join(str(ch) for ch in input).strip())

@to_float.register(float)
def to_float_from_float(input:object):
    """
    +--------------------------------+-------------+
    |             INPUT              |   OUTPUT    |
    +--------------------------------+-------------+
    | float 4.992                    | float 4.992 |
    +--------------------------------+-------------+ 
    """    
    return input   

这是

to_int

的代码
def to_int(input:object):
    if isinstance(input, float):
        input = str(input)
    return float("".join(str(ch) for ch in input).strip())

这是

clean_str_or_none

的代码
import singledispatched from functools 

@singledispatched
def clean_str_or_none(input):
    pass

@clean_str_or_none.register(type(None))     
def clean_str_or_none_from_none(input:type(None)):
    return None 

@clean_str_or_none.register(str)    
def clean_str_or_none_from_str(input:str):
    """
        converts all uppercase letters to lower-case
        deletes all underscore characters, line-feeds, tab characters, and spaces
        
        +----------------------------+------------------------+
        |           INPUT            |         OUTPUT         |
        +----------------------------+------------------------+
        | "makeLinesTransparent"     | "makelinestransparent" |
        | "make_lines_transparent"   | "makelinestransparent" |
        | " make lines transparent " | "makelinestransparent" |
        +----------------------------+------------------------+
    """
    return "".join(filter(lambda ch: ch != "\n\r\t _", input.strip().lower()))   

我不确定如何用

pow
装饰名为
santi.by_signature
的可调用对象,以便
pow
的每个输入都通过以下净化功能之一传递:

  • to_non_positive_int
  • to_float
  • clean_str_or_none
python python-3.x decorator python-decorators sanitization
1个回答
1
投票

装饰器将函数作为参数并返回修改后的函数。您的装饰器在其自己的定义中明确引用了

pow
(您打算装饰的特定功能),这违背了装饰器的目的;它应该改为修改作为参数传递给它的任何函数。您还需要仔细跟踪以下两者之间的区别:

  1. 你调用来创建装饰器的函数
  2. 装饰器函数(将要装饰的函数作为参数)
  3. 转换后的函数(接收调用修饰函数的实际参数)

可以在您添加的 OOP 间接层中执行此操作,但是您的代码中有很多地方混淆了它当前在哪个抽象层运行,并且将功能分布在一堆嵌套类中使得更难解开。我建议首先让你的装饰器的基本版本作为一个简单的嵌套函数工作,如果需要的话,稍后将它扩展成一个更棘手的 OOP 版本。

这是您正在尝试做的工作示例:

def by_signature(*f_args, **f_kwargs):
    def decorator(func):
        def wrapped(*args, **kwargs):
            return func(
                *(f(arg) for f, arg in zip(f_args, args)),
                **{k: f_kwargs[k](arg) for k, arg in kwargs.items()}
            )
        return wrapped
    return decorator

@by_signature(float, int, debug_on=str)
def test(base, exponent, *, debug_on='False'):
    print(f"test({base!r}, {exponent!r}, debug_on={debug_on!r})")

test("1.0", "4", debug_on=True)
# test(1.0, 4, debug_on='True')

使用任意位置和关键字参数

by_signature
f_args
调用
f_kwargs
函数并定义一个
decorator
,它定义一个
wrapped
函数调用它的
func
arg,参数根据
 转换f_args
f_kwargs
。请注意,这个简单的实现不进行任何错误检查,并假设每个
f
都有一个
arg
;更复杂的实现可能会尝试检查
func
的签名并在未提供所有预期转换函数时提出有用的错误消息或提供默认值。

请注意,在对

test
的调用中,位置参数通过
float
int
函数进行转换,kwarg
debug_on
通过
str
函数进行转换,因此当我们在内部打印这些参数时函数,我们看到它们分别呈现为 float、int 和 str。

如果我们删除装饰器,我们会看到:

def test(base, exponent, *, debug_on='False'):
    print(f"test({base!r}, {exponent!r}, debug_on={debug_on!r})")

test("1.0", "4", debug_on=True)
# test('1.0', '4', debug_on=True)
© www.soinside.com 2019 - 2024. All rights reserved.