考虑到响应者和评论者所说的一切,这就是我得到的。
确切的愿望在语法上是不可能的,但有两种方法可以实现所寻求的简单性。
只需使用inspect.signature作为装饰器:
>>> from inspect import signature
>>> @signature
... def sig1(a, b: int, c=0, d: float=0.0): ...
>>> @signature
... def sig2(something_else): ...
>>> @signature
... def sig3(): ...
>>>
>>> sig1
<Signature (a, b: int, c=0, d: float = 0.0)>
>>> sig2
<Signature (something_else)>
>>> sig3
<Signature ()>
但如果你需要更动态的东西,我建议如下:
def sig(obj: Union[Signature, Callable, Mapping, None] = None, return_annotations=_empty, **annotations):
"""Convenience function to make a signature or inject annotations to an existing one.
"""
if obj is None:
return Signature()
if callable(obj):
obj = Signature.from_callable(obj) # get a signature object from a callable
if isinstance(obj, Signature):
obj = obj.parameters # get the parameters attribute from a signature
params = dict(obj) # get a writable copy of parameters
if not annotations:
return Signature(params.values(), return_annotation=return_annotations)
else:
assert set(annotations) <= set(params), \
f"These argument names weren't found in the signature: {set(annotations) - set(params)}"
for name, annotation in annotations.items():
p = params[name]
params[name] = Parameter(name=name, kind=p.kind, default=p.default, annotation=annotation)
return Signature(params.values(), return_annotation=return_annotations)
演示:
>>> s = sig(lambda a, b, c=1, d='bar': ..., b=int, d=str)
>>> s
<Signature (a, b: int, c=1, d: str = 'bar')>
>>> # showing that sig can take a signature input, and overwrite an existing annotation:
>>> sig(s, a=list, b=float) # note the b=float
<Signature (a: list, b: float, c=1, d: str = 'bar')>
>>> sig()
<Signature ()>
>>> sig(lambda a, b=2, c=3: ..., d=int) # trying to annotate an argument that doesn't exist
Traceback (most recent call last):
...
AssertionError: These argument names weren't found in the signature: {'d'}