将 Python 方法动态绑定到实例会正确绑定方法名称,但不会绑定方法 [英] Dynamically binding Python methods to an instance correctly binds the method names, but not the method

查看:19
本文介绍了将 Python 方法动态绑定到实例会正确绑定方法名称,但不会绑定方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在为一组 RESTful 服务编写客户端.给定参数,REST 调用的主体具有相同的 XML 结构.有几十个调用,我不会全部实现.因此,我想让它们易于指定和使用.REST 方法按功能分组在单独的模块中,并且需要共享相同的 urllib2 opener 以进行身份​​验证和 cookie.下面是一个如何声明方法的示例:

I'm writing a client for a group of RESTful services. The body of the REST calls have the same XML structure, given parameters. There are several dozen calls, and I will not be implementing all of them. As such, I want to make them easy to specify and easy to use. The REST methods are grouped by functionality in separate modules and will need to share the same urllib2 opener for authentication and cookies. Here's an example of how a method is declared:

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

开发人员需要关心的只是验证.XML(用于 POST 和 PUT)或 URL(用于 GET 和 DELETE)的格式以及响应的反序列化在辅助方法中完成.修饰的方法被收集在一个客户端对象中,它们将从中执行和处理.例如:

All the developer has to care about is validation. The format of the XML (for POST and PUT) or the URL (for GET and DELETE) and the deserialization of the response is done in helper methods. The decorated methods are collected in a client object from which they will be executed and processed. For example:

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

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

The code is done. The only issue is in attaching the decorated methods to the client class. Although all the decorated methods can be seen in the client instance, they all share the same definition, namely the last one to be bound. Here's a brief example which duplicates the behaviour I'm seeing:

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)

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

When I run this, I get the following output:

<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)"?

I'm not sure why the two methods are bound in the same way. I haven't been able to find much documentation on how instancemethod binds methods to instances. What is going on underneath the hood, and how would I fix the above code so that the second call prints '(1)'?

推荐答案

lambda 正在调用 m,将其从本地作用域中拉出.在 for 循环结束后,m 被设置为 two.调用 c.onec.two 将导致 two 被调用.

The lambda is calling m, pulling it from the local scope. After the end of the for loop, m is set to two. Calling c.one or c.two will result in two being called.

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

You can tell that two is being called by looking at the last line of your traceback:

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

可以很好地演示正在发生的事情在这里找到.

这应该符合您的预期,但有点乱:

This should do what you expect, but is a kinda messy:

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)

我还删除了未绑定方法的显式创建,因为它是不必要的.

I also removed the explicit creation of an unbound method, as it is unnessesary.

这篇关于将 Python 方法动态绑定到实例会正确绑定方法名称,但不会绑定方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆