我们如何使用装饰器清理函数的每个参数输入?
在下面的示例中,我希望类方法
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
装饰器将函数作为参数并返回修改后的函数。您的装饰器在其自己的定义中明确引用了
pow
(您打算装饰的特定功能),这违背了装饰器的目的;它应该改为修改作为参数传递给它的任何函数。您还需要仔细跟踪以下两者之间的区别:
可以在您添加的 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)