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

时间:2014-08-23 08:32:04

标签: python function python-2.7 proxy

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

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

2 个答案:

答案 0 :(得分:3)

考虑使用boltons.wraps - 这里是文档的摘录:

  

boltons.funcutils.wraps(func,inject = None,** kw)

     

以内置的functools.wraps()为模型,此函数用于   让你的装饰器的包装函数反映出包装   功能的:

     

名称文档模块签名

     

内置的functools.wraps()复制前三个,但没有   复制签名。这个版本的包装可以复制内部   功能的签名完全正确,允许无缝使用和   内省。用法与内置版本相同:

>>> 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'
     

此外,boltons版本的包裹支持修改外部   基于内部签名的签名。通过传递列表注入   参数名称,这些参数将从外部删除   包装器的签名,允许你的装饰器提供参数   没有传入。

     

参数:func(function) - 属性所属的可调用对象   被复制。

     

inject(list) - 一个不应该的参数名称的可选列表   出现在新包装器的签名中。

     

update_dict(bool) - 是否复制其他非标准属性   func到包装器。默认为True。

     

inject_to_varkw(bool) - 当** kwargs-type时忽略缺少的参数   一切都存在。默认为True。

     

有关函数的更深入包装,请参阅FunctionBuilder类型,   在哪个包装上建立。

答案 1 :(得分:2)

您没有拥有来复制功能签名。相反,接受任意位置和关键字参数并传递它们:

def proxy_function(*args, **kw):
    return original_function(*args, **kw)

此处*args签名中的**kwproxy_function语法分别给出了传递给函数的参数的元组和字典:

>>> def foo(*args, **kw):
...     print args
...     print kw
... 
>>> foo('spam', 'ham', monty='python')
('spam', 'ham')
{'monty': 'python'}

类似地,*args调用中的**kworiginal_function()语法分别采用序列或映射来将其内容作为单独的参数应用于被调用的函数:

>>> 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构建器也处理绑定的方法;在这种情况下,self在传递之前从调用中删除,以确保目标方法没有获得额外的self参数(无论如何都是错误的类型)。

处理未绑定的方法超出了范围;在这种情况下提供您自己的_proxy函数,可以实例化代理类并传递没有self的参数或为self提供新值;你无法传递self未改变的。

演示:

>>> def foobar(bar, baz, default=[]):
...     print bar, baz, default
... 
>>> build_facade(foobar)
<function foobar at 0x10258df50>
>>> build_facade(foobar)('spam', 'eggs')
spam eggs []
>>> inspect.getargspec(build_facade(foobar))
ArgSpec(args=['bar', 'baz', 'default'], varargs=None, keywords=None, defaults=(<object object at 0x102593cd0>,))
>>> class Foo(object):
...     def bar(self, spam): print spam
... 
>>> foo = Foo()
>>> class FooProxy(object):
...     bar = build_facade(foo.bar)
... 
>>> FooProxy().bar('hello!')
hello!
>>> inspect.getargspec(FooProxy.bar)
ArgSpec(args=['self', 'spam'], varargs=None, keywords=None, defaults=None)