python 函数返回它自己的 运行 时间参数的签名
A python function returning signature of it's own run time arguments
我想要一个功能:
def sig(*args, **kwargs):
"""User-friendly inspect.Signature construction"""
# I'm guessing some introspection of frames or something...
pass
这就像它的文档所说的那样:轻松创建 Signature
个实例。
其行为的一个示例是:
>>> sig(a, b: int, c=1, d: str='bar')
<Signature (a, b: int, c=1, d: str = 'bar')>
>>> sig(something_else)
<Signature (something_else)>
>>> sig()
<Signature ()>
使用 inspect
模块后置框架的能力似乎有些神奇,但到目前为止我只能设法想出丑陋的技巧。
有人有优雅的东西吗?
编辑(约一小时后)
响应者指出,我对简单性的渴望在句法上是不可能的。因此,我将改写这个问题:哪些解决方案可能会达到这种简单性,可能会牺牲一些完整性(例如,没有注释)。
在最新的 python 3.8.2 中,您不能将值传递给带有注释类型的函数。
File "main.py", line 10
print(sig(1, 2: int, c=3, d:str = "Hello"))
^
SyntaxError: invalid syntax
另外,无论你指定什么类型,在运行时都是一样的。但是您可以尝试以这种方式推断类型:
>>> type(123).__name__
'int'
正如@D34DStone 所指出的,您想要实现的目标是不可能的,因为它不是有效的语法。
但是,您可以将 "spec" 作为字符串传递。
这是实现它的一种方法。虽然不是很漂亮......(并且可能不安全)。
def sig(x):
locals = dict(x=x)
exec('''
import inspect
def f(%s): pass
s = inspect.signature(f)
''' % x, {}, locals)
return locals['s']
sig("a, b: int, c=1, d: str='bar'")
=> <Signature (a, b:int, c=1, d:str='bar')>
考虑到回复者和评论者所说的一切,这就是我得到的。
确切的愿望在句法上是不可能的,但有两种方法可以实现所寻求的简单性。
只需使用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'}
我想要一个功能:
def sig(*args, **kwargs):
"""User-friendly inspect.Signature construction"""
# I'm guessing some introspection of frames or something...
pass
这就像它的文档所说的那样:轻松创建 Signature
个实例。
其行为的一个示例是:
>>> sig(a, b: int, c=1, d: str='bar')
<Signature (a, b: int, c=1, d: str = 'bar')>
>>> sig(something_else)
<Signature (something_else)>
>>> sig()
<Signature ()>
使用 inspect
模块后置框架的能力似乎有些神奇,但到目前为止我只能设法想出丑陋的技巧。
有人有优雅的东西吗?
编辑(约一小时后)
响应者指出,我对简单性的渴望在句法上是不可能的。因此,我将改写这个问题:哪些解决方案可能会达到这种简单性,可能会牺牲一些完整性(例如,没有注释)。
在最新的 python 3.8.2 中,您不能将值传递给带有注释类型的函数。
File "main.py", line 10
print(sig(1, 2: int, c=3, d:str = "Hello"))
^
SyntaxError: invalid syntax
另外,无论你指定什么类型,在运行时都是一样的。但是您可以尝试以这种方式推断类型:
>>> type(123).__name__
'int'
正如@D34DStone 所指出的,您想要实现的目标是不可能的,因为它不是有效的语法。 但是,您可以将 "spec" 作为字符串传递。
这是实现它的一种方法。虽然不是很漂亮......(并且可能不安全)。
def sig(x):
locals = dict(x=x)
exec('''
import inspect
def f(%s): pass
s = inspect.signature(f)
''' % x, {}, locals)
return locals['s']
sig("a, b: int, c=1, d: str='bar'")
=> <Signature (a, b:int, c=1, d:str='bar')>
考虑到回复者和评论者所说的一切,这就是我得到的。
确切的愿望在句法上是不可能的,但有两种方法可以实现所寻求的简单性。
只需使用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'}