Python 如何创建具有另一个签名的新方法

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

如何从一个类复制方法的签名,并在另一个类中创建具有相同签名的“代理方法”

我正在用python编写一个RPC库。 服务器支持对服务器端类(C)的远程调用。 当客户端连接到服务器时,它应该为具有相同签名的C创建一个代理类。 当程序调用代理实例时,它应该在服务器上使用相同的参数调用函数。

您不必复制函数签名。相反,接受任意位置参数和关键字参数并将其传递给:

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'