如何使用另一个类的方法装饰(monkeypatch...)Python 类?
httplib.HTTPMessage
和 email.message.Message
类[1] 都实现了 RFC822 标头解析的方法。不幸的是,它们有不同的实现[2],并且不提供相同级别的功能。
困扰我的一个例子是:
httplib.HTTPMessage
缺少email.Message
中存在的get_filename
方法,该方法允许您轻松地从 Content-disposition: 附件中检索文件名; filename="fghi.xyz" header;httplib.HTTPMessage
有getparam
、getplist
和parseplist
方法,但据我所知,它们不是也不可能是在content-type
标头解析之外使用;email.Message
有一个通用的get_param
方法来解析带有参数的任何 RFC822 标头,例如content-disposition
或content -type
。
因此,我想要 httplib.HTTPMessage
中 email.message.Message
的 get_filename
或 get_param
方法,但是,当然,我无法修补 httplib.HTTPMessage
因为它在标准库中... :-q
最后,这里是装饰器主题... :-)
我成功创建了一个 Monkeypatch_http_message
函数用我缺少的解析方法来装饰 httplib.HTTPMessage
:
def monkeypatch_http_message(obj):
from email import utils
from email.message import (
_parseparam,
_unquotevalue,
)
cls = obj.__class__
# methods **copied** from email.message.Message source code
def _get_params_preserve(self, failobj, header): ...
def get_params(self, failobj=None, header='content-type',
unquote=True): ...
def get_param(self, param, failobj=None, header='content-type',
unquote=True): ...
def get_filename(self, failobj=None): ...
# monkeypatching httplib.Message
cls._get_params_preserve = _get_params_preserve
cls.get_params = get_params
cls.get_param = get_param
cls.get_filename = get_filename
现在我可以做:
import mechanize
from some.module import monkeypatch_http_message
browser = mechanize.Browser()
# in that form, browser.retrieve returns a temporary filename
# and an httplib.HTTPMessage instance
(tmp_filename, headers) = browser.retrieve(someurl)
# monkeypatch the httplib.HTTPMessage instance
monkeypatch_http_message(headers)
# yeah... my original filename, finally
filename = headers.get_filename()
这里的问题是我从字面上复制了源类中的装饰方法代码,我'我想避免。
所以,我尝试通过引用源方法来装饰:
def monkeypatch_http_message(obj):
from email import utils
from email.message import (
_parseparam,
_unquotevalue,
Message # XXX added
)
cls = obj.__class__
# monkeypatching httplib.Message
cls._get_params_preserve = Message._get_params_preserve
cls.get_params = Message.get_params
cls.get_param = Message.get_param
cls.get_filename = Message.get_filename
但这给了我:
Traceback (most recent call last):
File "client.py", line 224, in <module>
filename = headers.get_filename()
TypeError: unbound method get_filename() must be called with Message instance as first argument (got nothing instead)
我现在正在抓耳挠腮......如何在不直接复制源方法的情况下装饰我的类?
有什么建议吗? :-)
问候,
乔治·马丁
在 Python 2.6 中。我无法在生产中使用 2.7 或 3.x。
httplib.HTTPMessage
继承自mimetools.Message
和rfc822.Message
,而email.Message
有自己的
Both httplib.HTTPMessage
and email.message.Message
classes[1] implements methods for RFC822 headers parsing. Unfortunately, they have different implementations[2] and they do not provide the same level of functionality.
One example that is bugging me is that:
httplib.HTTPMessage
is missing theget_filename
method present inemail.Message
, that allows you to easily retrieve the filename from aContent-disposition: attachment; filename="fghi.xyz"
header;httplib.HTTPMessage
hasgetparam
,getplist
andparseplist
methods but AFAIK, they are not and cannot be used outside of thecontent-type
header parsing;email.Message
has a genericget_param
method to parse any RFC822 header with parameters, such ascontent-disposition
orcontent-type
.
Thus, I want the get_filename
or get_param
methods of email.message.Message
in httplib.HTTPMessage
but, of course, I can't patch httplib.HTTPMessage
as it's in the standard library... :-q
And finally, here comes the decorator subject... :-)
I succesfully created a monkeypatch_http_message
function to decorate an httplib.HTTPMessage
with my missing parsing methods:
def monkeypatch_http_message(obj):
from email import utils
from email.message import (
_parseparam,
_unquotevalue,
)
cls = obj.__class__
# methods **copied** from email.message.Message source code
def _get_params_preserve(self, failobj, header): ...
def get_params(self, failobj=None, header='content-type',
unquote=True): ...
def get_param(self, param, failobj=None, header='content-type',
unquote=True): ...
def get_filename(self, failobj=None): ...
# monkeypatching httplib.Message
cls._get_params_preserve = _get_params_preserve
cls.get_params = get_params
cls.get_param = get_param
cls.get_filename = get_filename
Now I can do:
import mechanize
from some.module import monkeypatch_http_message
browser = mechanize.Browser()
# in that form, browser.retrieve returns a temporary filename
# and an httplib.HTTPMessage instance
(tmp_filename, headers) = browser.retrieve(someurl)
# monkeypatch the httplib.HTTPMessage instance
monkeypatch_http_message(headers)
# yeah... my original filename, finally
filename = headers.get_filename()
The issue here is that I literally copied the decorating methods code from the source class, which I'd like to avoid.
So, I tried decorating by referencing the source methods:
def monkeypatch_http_message(obj):
from email import utils
from email.message import (
_parseparam,
_unquotevalue,
Message # XXX added
)
cls = obj.__class__
# monkeypatching httplib.Message
cls._get_params_preserve = Message._get_params_preserve
cls.get_params = Message.get_params
cls.get_param = Message.get_param
cls.get_filename = Message.get_filename
But that gives me:
Traceback (most recent call last):
File "client.py", line 224, in <module>
filename = headers.get_filename()
TypeError: unbound method get_filename() must be called with Message instance as first argument (got nothing instead)
I'm scratching my head now... how can I decorate my class without literally copying the source methods ?
Any suggestions ? :-)
Regards,
Georges Martin
In Python 2.6. I can't use 2.7 nor 3.x in production.
httplib.HTTPMessage
inherits frommimetools.Message
andrfc822.Message
whileemail.Message
has its own implementation.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
在 Python 3.x 中,未绑定方法消失了,因此在这种情况下您只需获取文件对象,并且您的第二个示例将起作用:
在 Python 2.x 中,您可以通过未绑定方法访问底层函数,也可以通过检索它来访问底层函数直接从类字典中获取(从而绕过将其转换为未绑定方法的正常查找过程):
后一种方法的优点是与 Python 3.x 向前兼容。
In Python 3.x, unbound methods go away so you'll just get the file objects in this case and your second example will work:
In Python 2.x, you can either access the underlying function via the unbound method or by retrieving it directly from the class dictionary (thus bypassing the normal lookup process that turns it into an unbound method):
The latter approach has the benefit of being forward compatible with Python 3.x.
@ncoghlan:我无法在注释中放入缩进代码,所以这里再次:
谢谢! :-)
@ncoghlan: I can't put indented code in comments, so here it is again:
Thanks ! :-)