Python 如何创建具有另一个签名的新方法
如何从一个类复制方法的签名,并在另一个类中创建具有相同签名的“代理方法” 我正在用python编写一个RPC库。 服务器支持对服务器端类(C)的远程调用。 当客户端连接到服务器时,它应该为具有相同签名的C创建一个代理类。 当程序调用代理实例时,它应该在服务器上使用相同的参数调用函数。您不必复制函数签名。相反,接受任意位置参数和关键字参数并将其传递给:Python 如何创建具有另一个签名的新方法,python,function,python-2.7,proxy,Python,Function,Python 2.7,Proxy,如何从一个类复制方法的签名,并在另一个类中创建具有相同签名的“代理方法” 我正在用python编写一个RPC库。 服务器支持对服务器端类(C)的远程调用。 当客户端连接到服务器时,它应该为具有相同签名的C创建一个代理类。 当程序调用代理实例时,它应该在服务器上使用相同的参数调用函数。您不必复制函数签名。相反,接受任意位置参数和关键字参数并将其传递给: def proxy_function(*args, **kw): return original_function(*args, **kw
def proxy_function(*args, **kw):
return original_function(*args, **kw)
此处,proxy\u函数
签名中的*args
和**kw
语法分别被赋予传递到函数中的参数的元组和字典:
>>> def foo(*args, **kw):
... print args
... print kw
...
>>> foo('spam', 'ham', monty='python')
('spam', 'ham')
{'monty': 'python'}
类似地,original_function()
调用中的*args
和**kw
语法分别采用序列或映射,将其内容作为单独的参数应用于被调用的函数:
>>> def bar(baz, fourtytwo=42):
... print baz
... print fourtytwo
...
>>> args = ('hello world!',)
>>> kwargs = {'fourtytwo': 'the answer'}
>>> bar(*args, **kwargs)
hello world!
the answer
两者结合起来,可以作为代理函数的任意参数传递
另一方面,创建一个完整的正面需要更多的参与:
import inspect
_default = object()
def build_facade(func):
"""Build a facade function, matching the signature of `func`.
Note that defaults are replaced by _default, and _proxy will reconstruct
these to preserve mutable defaults.
"""
name = func.__name__
docstring = func.__doc__
spec = inspect.getargspec(func)
args, defaults = spec[0], spec[3]
boundmethod = getattr(func, '__self__', None)
arglen = len(args)
if defaults is not None:
defaults = zip(args[arglen - len(defaults):], defaults)
arglen -= len(defaults)
def _proxy(*args, **kw):
if boundmethod:
args = args[1:] # assume we are bound too and don't pass on self
# Reconstruct keyword arguments
if defaults is not None:
args, kwparams = args[:arglen], args[arglen:]
for positional, (key, default) in zip(kwparams, defaults):
if positional is _default:
kw[key] = default
else:
kw[key] = positional
return func(*args, **kw)
args = inspect.formatargspec(formatvalue=lambda v: '=_default', *spec)
callargs = inspect.formatargspec(formatvalue=lambda v: '', *spec)
facade = 'def {}{}:\n """{}"""\n return _proxy{}'.format(
name, args, docstring, callargs)
facade_globs = {'_proxy': _proxy, '_default': _default}
exec facade in facade_globs
return facade_globs[name]
这将生成一个具有相同参数名称的全新函数对象,并通过引用原始代理函数默认值而不是将其复制到facade来处理默认值;这确保了即使是可变的默认值也能继续工作
facade生成器也处理绑定方法;在这种情况下,在传递之前从调用中删除self
,以确保目标方法没有获得额外的self
参数(无论如何,这将是错误的类型)
处理未绑定的方法超出了这里的范围;在这种情况下,提供您自己的\u proxy
函数,该函数可以实例化代理类并在不使用self
的情况下传递参数,或者为self
提供新值;您不能原封不动地传入self
演示:
def foobar(条形,baz,默认值=[]):
... 打印栏,baz,默认值
...
>>>建筑立面(foobar)
>>>建筑立面(foobar)(“垃圾”、“鸡蛋”)
垃圾鸡蛋
>>>检查getargspec(建筑立面(foobar))
ArgSpec(args=['bar','baz','default'],varargs=None,keywords=None,defaults=(,))
>>>类Foo(对象):
... 定义栏(自我,垃圾邮件):打印垃圾邮件
...
>>>foo=foo()
>>>类代理(对象):
... bar=建筑立面(foo.bar)
...
>>>FooProxy().bar('hello!')
你好
>>>inspect.getargspec(FooProxy.bar)
ArgSpec(args=['self','spam'],varargs=None,keywords=None,defaults=None)
考虑使用-以下是文档摘录:
boltons.funcutils.wrapps(func,injected=None,**千瓦)
以内置的functools.wrapps()为模型,此函数用于
使装饰器的包装函数反映包装的
职能部门:
名称文档模块签名
内置的functools.wrapps()复制前三个,但不复制
复制签名。此版本的包装可以复制内部包装
函数的签名精确,允许无缝使用和
反省用法与内置版本相同:
>>> from boltons.funcutils import wraps
>>>
>>> def print_return(func):
... @wraps(func)
... def wrapper(*args, **kwargs):
... ret = func(*args, **kwargs)
... print(ret)
... return ret
... return wrapper
...
>>> @print_return
... def example():
... '''docstring'''
... return 'example return value'
>>>
>>> val = example()
example return value
>>> example.__name__
'example'
>>> example.__doc__
'docstring'
此外,Bolton版本的wraps支持修改外部
基于内部签名的签名。通过传递一个列表
参数名称,这些参数将从外部
包装器的签名,允许您的装饰器提供
你没有通过考试
参数:func(function)–属性为
被复制
injected(list)–参数名称的可选列表,不应
出现在新包装的签名中
更新目录(bool)–是否复制的其他非标准属性
func转到包装器。默认为True
inject_to_varkw(bool)–当**kwargs类型为
一网打尽。默认为True
有关函数的更深入包装,请参见FunctionBuilder类型,
在其上构建包裹
谢谢,但我想复制签名。当我键入“help(proxy.foo)”时,我看到了函数的完整描述。@dk7:为什么需要复制签名?你在反思结果吗?为什么要反思结果?@dk7:这里的技术是绝大多数RPC库使用的技术;示例:,。我希望应用程序在使用python console for debug mode运行时更加用户友好。晚会晚了,但供将来参考:Martijin方法有效,我相信这是python方法。为了满足dk7的需要,可以通过设置代理动态添加docstring。_doc_;=orig_func._doc_;。有关更多详细信息,请参阅。
>>> from boltons.funcutils import wraps
>>>
>>> def print_return(func):
... @wraps(func)
... def wrapper(*args, **kwargs):
... ret = func(*args, **kwargs)
... print(ret)
... return ret
... return wrapper
...
>>> @print_return
... def example():
... '''docstring'''
... return 'example return value'
>>>
>>> val = example()
example return value
>>> example.__name__
'example'
>>> example.__doc__
'docstring'