默认参数装饰器 python
Default Argument decorator python
Python 3.6
我正在尝试创建一个自动将参数字符串指定为默认值的装饰器。
如:
def example(one='one', two='two', three='three'):
pass
相当于:
@DefaultArguments
def example(one, two, three):
pass
这是我的尝试(没用..还..)DefaultArguments
:
from inspect import Parameter, Signature, signature
class DefaultArguments(object):
@staticmethod
def default_signature(signature):
def default(param):
if param.kind in (Parameter.POSITIONAL_OR_KEYWORD, Parameter.POSITIONAL_ONLY):
return param.replace(default=param.name)
else:
return param
return Signature([default(param) for param in signature.parameters.values()])
def __init__(self, func):
self.func = func
self.sig = self.default_signature(signature(func))
def __call__(self, *args, **kwargs):
arguments = self.sig.bind(*args, **kwargs)
return self.func(arguments)
静态方法 default_signature
为函数创建了所需的签名,但我很难将新签名分配给函数。我正在尝试使用 Signature。bind
我已阅读 docs 但我遗漏了一些内容。
编辑
结合 Ashwini Chaudhary 的回答:
from inspect import Parameter, Signature, signature
class DefaultArguments(object):
@staticmethod
def default_signature(signature):
def default(param):
if param.kind in (Parameter.POSITIONAL_OR_KEYWORD, Parameter.POSITIONAL_ONLY):
return param.replace(default=param.name)
else:
return param
return Signature([default(param) for param in signature.parameters.values()])
def __init__(self, func):
self.func = func
self.sig = self.default_signature(signature(func))
print(self.sig)
def __call__(self, *args, **kwargs):
ba = self.sig.bind(*args, **kwargs)
ba.apply_defaults()
return self.func(*ba.args, **ba.kwargs)
这似乎有效:
import inspect
def default_args(func):
argspec = inspect.getfullargspec(func)
@functools.wraps(func)
def wrapper(*args, **kwargs):
unpassed_positional_args = argspec.args[len(args):]
kwargs.update((a, a) for a in unpassed_positional_args if a not in kwargs)
return func(*args, **kwargs)
return wrapper
它依赖于您可以在 python 中通过关键字传递位置参数这一事实。例如如果你有一个函数:
def foo(a, b):
...
您完全有权将其称为:
foo(b=1, a=2)
我的解决方案计算出您传递了多少位置参数,并使用它来计算哪些位置参数没有传递。然后我将这些位置参数名称添加到 kwargs
字典中。
这里很酷的是,如果有人需要这个 python2.x,他们只需要将 getfullargspec
更改为 getargspec
,它应该可以正常工作。
速度注意事项:
将我的解决方案与 Ashwini 的出色解释进行比较表明,简单的装饰器比随意使用 Signature 对象快大约 10 倍:
@default_args
def foo(a, b, c):
pass
@DefaultArguments
def bar(a, b, c):
pass
@default_arguments
def qux(a, b, c):
pass
import timeit
print(timeit.timeit('foo()', 'from __main__ import foo')) # 1.72s
print(timeit.timeit('bar()', 'from __main__ import bar')) # 17.4s
print(timeit.timeit('qux()', 'from __main__ import qux')) # 17.6
他的解决方案实际上更新了函数的 __signature__
(这非常好)。原则上,您可以采用 Signature
创建逻辑并将其添加到我的解决方案中以更新 __signature__
但保留 argspec
样式逻辑用于实际计算...
将参数和关键字与签名绑定后,您需要调用 apply_defaults
on the BoundArguments
实例来为缺少的参数设置默认值。
还将使用 BoundArguments
的 args
和 kwargs
属性调用函数调用。
def __call__(self, *args, **kwargs):
ba = self.sig.bind(*args, **kwargs)
ba.apply_defaults()
return self.func(*ba.args, **ba.kwargs)
演示:
>>> @DefaultArguments
... def example(one, two, three):
... print(one, two, three)
...
>>> example()
one two three
>>> example('spam')
spam two three
>>> example(one='spam', three='eggs')
spam two eggs
你的代码的一个功能版本,它也更新了修饰函数的签名:
from functools import wraps
from inspect import Parameter, Signature, signature
def default_arguments(func):
def default(param):
if param.kind in (Parameter.POSITIONAL_OR_KEYWORD, Parameter.POSITIONAL_ONLY):
param = param.replace(default=param.name)
return param
sig = Signature([default(param) for param in signature(func).parameters.values()])
@wraps(func)
def wrapper(*args, **kwargs):
ba = sig.bind(*args, **kwargs)
ba.apply_defaults()
return func(*ba.args, **ba.kwargs)
wrapper.__signature__ = sig
return wrapper
演示:
>>> from inspect import getfullargspec
>>> @default_arguments
... def example(one, two, three):
... print(one, two, three)
...
>>> getfullargspec(example)
FullArgSpec(
args=['one', 'two', 'three'],
varargs=None,
varkw=None,
defaults=('one', 'two', 'three'),
kwonlyargs=[], kwonlydefaults=None, annotations={}
)
Python 3.6
我正在尝试创建一个自动将参数字符串指定为默认值的装饰器。
如:
def example(one='one', two='two', three='three'):
pass
相当于:
@DefaultArguments
def example(one, two, three):
pass
这是我的尝试(没用..还..)DefaultArguments
:
from inspect import Parameter, Signature, signature
class DefaultArguments(object):
@staticmethod
def default_signature(signature):
def default(param):
if param.kind in (Parameter.POSITIONAL_OR_KEYWORD, Parameter.POSITIONAL_ONLY):
return param.replace(default=param.name)
else:
return param
return Signature([default(param) for param in signature.parameters.values()])
def __init__(self, func):
self.func = func
self.sig = self.default_signature(signature(func))
def __call__(self, *args, **kwargs):
arguments = self.sig.bind(*args, **kwargs)
return self.func(arguments)
静态方法 default_signature
为函数创建了所需的签名,但我很难将新签名分配给函数。我正在尝试使用 Signature。bind
我已阅读 docs 但我遗漏了一些内容。
编辑
结合 Ashwini Chaudhary 的回答:
from inspect import Parameter, Signature, signature
class DefaultArguments(object):
@staticmethod
def default_signature(signature):
def default(param):
if param.kind in (Parameter.POSITIONAL_OR_KEYWORD, Parameter.POSITIONAL_ONLY):
return param.replace(default=param.name)
else:
return param
return Signature([default(param) for param in signature.parameters.values()])
def __init__(self, func):
self.func = func
self.sig = self.default_signature(signature(func))
print(self.sig)
def __call__(self, *args, **kwargs):
ba = self.sig.bind(*args, **kwargs)
ba.apply_defaults()
return self.func(*ba.args, **ba.kwargs)
这似乎有效:
import inspect
def default_args(func):
argspec = inspect.getfullargspec(func)
@functools.wraps(func)
def wrapper(*args, **kwargs):
unpassed_positional_args = argspec.args[len(args):]
kwargs.update((a, a) for a in unpassed_positional_args if a not in kwargs)
return func(*args, **kwargs)
return wrapper
它依赖于您可以在 python 中通过关键字传递位置参数这一事实。例如如果你有一个函数:
def foo(a, b):
...
您完全有权将其称为:
foo(b=1, a=2)
我的解决方案计算出您传递了多少位置参数,并使用它来计算哪些位置参数没有传递。然后我将这些位置参数名称添加到 kwargs
字典中。
这里很酷的是,如果有人需要这个 python2.x,他们只需要将 getfullargspec
更改为 getargspec
,它应该可以正常工作。
速度注意事项:
将我的解决方案与 Ashwini 的出色解释进行比较表明,简单的装饰器比随意使用 Signature 对象快大约 10 倍:
@default_args
def foo(a, b, c):
pass
@DefaultArguments
def bar(a, b, c):
pass
@default_arguments
def qux(a, b, c):
pass
import timeit
print(timeit.timeit('foo()', 'from __main__ import foo')) # 1.72s
print(timeit.timeit('bar()', 'from __main__ import bar')) # 17.4s
print(timeit.timeit('qux()', 'from __main__ import qux')) # 17.6
他的解决方案实际上更新了函数的 __signature__
(这非常好)。原则上,您可以采用 Signature
创建逻辑并将其添加到我的解决方案中以更新 __signature__
但保留 argspec
样式逻辑用于实际计算...
将参数和关键字与签名绑定后,您需要调用 apply_defaults
on the BoundArguments
实例来为缺少的参数设置默认值。
还将使用 BoundArguments
的 args
和 kwargs
属性调用函数调用。
def __call__(self, *args, **kwargs):
ba = self.sig.bind(*args, **kwargs)
ba.apply_defaults()
return self.func(*ba.args, **ba.kwargs)
演示:
>>> @DefaultArguments
... def example(one, two, three):
... print(one, two, three)
...
>>> example()
one two three
>>> example('spam')
spam two three
>>> example(one='spam', three='eggs')
spam two eggs
你的代码的一个功能版本,它也更新了修饰函数的签名:
from functools import wraps
from inspect import Parameter, Signature, signature
def default_arguments(func):
def default(param):
if param.kind in (Parameter.POSITIONAL_OR_KEYWORD, Parameter.POSITIONAL_ONLY):
param = param.replace(default=param.name)
return param
sig = Signature([default(param) for param in signature(func).parameters.values()])
@wraps(func)
def wrapper(*args, **kwargs):
ba = sig.bind(*args, **kwargs)
ba.apply_defaults()
return func(*ba.args, **ba.kwargs)
wrapper.__signature__ = sig
return wrapper
演示:
>>> from inspect import getfullargspec
>>> @default_arguments
... def example(one, two, three):
... print(one, two, three)
...
>>> getfullargspec(example)
FullArgSpec(
args=['one', 'two', 'three'],
varargs=None,
varkw=None,
defaults=('one', 'two', 'three'),
kwonlyargs=[], kwonlydefaults=None, annotations={}
)