将Python方法动态绑定到实例会正确绑定方法名称,但不会绑定方法

时间:2009-11-27 20:19:16

标签: python types

我正在为一组RESTful服务编写客户端。在给定参数的情况下,REST调用的主体具有相同的XML结构。有几十个电话,我不会实现所有这些电话。因此,我希望它们易于指定且易于使用。 REST方法按功能分组在单独的模块中,需要共享相同的urllib2开启程序以进行身份​​验证和cookie。这是一个如何声明方法的示例:

@rest_method('POST', '/document')
def createDocument(id, title, body):
    # possibly some validation on the arguments
    pass

所有开发人员都必须关心的是验证。 XML(用于POST和PUT)或URL(用于GET和DELETE)以及响应的反序列化的格式是在辅助方法中完成的。装饰的方法收集在客户端对象中,从中执行和处理它们。例如:

c = RESTClient('http://foo.com', username, password)
c.createDocument(1, 'title', 'body')

代码完成。唯一的问题是将装饰方法附加到客户端类。尽管可以在客户端实例中看到所有修饰的方法,但它们都共享相同的定义,即最后一个要绑定的定义。这是一个简短的例子,它复制了我所看到的行为:

import types

class C(object): pass
def one(a): return a
def two(a, b): return a+b
def bracketit(t): return '(%s)' % t

c = C()

for m in (one, two):
    new_method = lambda self, *args, **kwargs:\
            bracketit(m(*args, **kwargs))
    method = types.MethodType(new_method, c, C)
    setattr(C, m.__name__, method)

print c.one 
print c.two
print c.two(1, 2)
print c.one(1)

当我运行它时,我得到以下输出:

<bound method C.<lambda> of <__main__.C object at 0x1003b0d90>>
<bound method C.<lambda> of <__main__.C object at 0x1003b0d90>>
(3)
Traceback (most recent call last):
  File "/tmp/test.py", line 19, in <module>
    print c.one(1)
  File "/tmp/test.py", line 12, in <lambda>
    bracketit(m(*args, **kwargs))
TypeError: two() takes exactly 2 arguments (1 given)

我不确定为什么这两种方法以同样的方式绑定。我无法找到关于instancemethod如何将方法绑定到实例的文档。引擎盖下发生了什么,我将如何修复上面的代码,以便第二个调用打印'(1)'?

2 个答案:

答案 0 :(得分:3)

lambda正在调用m,将其从本地范围中拉出来。在for循环结束后,m设置为two。致电c.onec.two会导致two被调用。

您可以通过查看追溯的最后一行来判断是否正在调用two

TypeError: two() takes exactly 2 arguments (1 given)

A good demonstration of what is going on can be found here.

这应该做你期望的,但是有点混乱:

class C(object): pass
def one(a): return a
def two(a, b): return a+b
def bracketit(t): return '(%s)' % t

c = C()

for m in (one, two):
    def build_method(m):
        return (lambda self, *args, **kwargs:
            bracketit(m(*args, **kwargs)))
    method = build_method(m)
    setattr(C, m.__name__, method)

print c.one 
print c.two
print c.two(1, 2)
print c.one(1)

我还删除了未绑定方法的显式创建,因为它是无关紧要的。

答案 1 :(得分:3)

问题是变量m在循环结束时保留为two,这会影响循环期间的定义。您可以通过使用嵌套函数创建闭包来修复它:

for m in (one, two):
    def make_method(m):
      def new_method(self, *args, **kwargs):
          return bracketit(m(*args, **kwargs))
      return new_method
    method = types.MethodType(make_method(m), c, C)
    setattr(C, m.__name__, method)

在测试代码中运行时产生:

<bound method C.new_method of <__main__.C object at 0x0135EF30>>
<bound method C.new_method of <__main__.C object at 0x0135EF30>>
(3)
(1)