python-3.x 什么是一个装饰器,它将应用一个可调用的每一个输入参数到一些函数?

6jjcrrmo  于 2023-04-22  发布在  Python
关注(0)|答案(1)|浏览(102)

我们如何使用装饰器清理函数的每个参数输入?
在下面的示例中,我希望类方法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_floatclean_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()))

我不知道如何用santi.by_signature修饰名为pow的可调用对象,以便pow的每个输入都通过一个清理函数传递,例如以下函数之一:

  • to_non_positive_int
  • to_float
  • clean_str_or_none
6yjfywim

6yjfywim1#

装饰器接受一个函数作为其参数,并返回一个修改后的函数。你的装饰器在它自己的定义中显式引用pow(你打算装饰的特定函数),这违背了装饰器的目的;相反,它应该修改作为参数传递给它的任何函数。您还需要仔细跟踪以下两者之间的差异:
1.创建装饰器所调用的函数

  1. decorator函数(它将被装饰的函数作为其参数)
    1.转换后的函数(它接收调用修饰函数的实际参数)
    在你添加的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')

使用任意位置和关键字参数f_argsf_kwargs调用by_signature函数,并定义一个decorator,它定义了一个wrapped函数,该函数使用根据f_argsf_kwargs转换的参数调用其func arg。t做任何错误检查,并假设它将有一个f为每个arg;更复杂的实现可能尝试检查func的签名,并在没有提供所有期望的转换函数时产生有用的错误消息或提供默认值。
请注意,在对test的调用中,位置参数通过floatint函数进行转换,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)

相关问题