【问题标题】:How to patch compiled 3rd party class attribute in python如何在python中修补编译的第3方类属性
【发布时间】:2014-12-05 17:12:37
【问题描述】:

假设如下代码:

import cv2 #3rd party python module (C extension!!)
a0 = cv2.KeyPoint(x=100,y=200,_size=20, _angle=1, _response=2, _octave=3, _class_id=4)

print "undesired output, desired syntax"
print a0
print str(a0)
print repr(a0)

print "desired output, undesired syntax"
print a0.pt, a0.size, a0.angle, a0.response, a0.octave, a0.class_id

打印出来的:

undesired output, desired syntax
<KeyPoint 0x7fc2575a9a20>
<KeyPoint 0x7fc2575a9a20>
<KeyPoint 0x7fc2575a9a20>
desired output, undesired syntax
(100.0, 200.0) 20.0 1.0 2.0 3 4

如何使用所需的语法获得所需的输出?

据我所知有以下几种方法:

  1. 从 KeyPoint 派生我自己的类并在那里实现 strrepr:不需要为 a) cv2 中的某些函数需要一个 cv.KeyPoint 对象列表,并且转换会减慢代码速度 b) 额外的类层使代码更复杂

  2. 猴子补丁关键点: 会很好但不起作用(见下面的代码)。另外我不知道 cv2 中期望 KeyPoint 列表的函数是否会接受这样的修改类。

  3. 猴子补丁 a0: 会不太好,但也不起作用(见下面的代码)。

  4. 为类型为 cv2.KeyPoint 的对象注册一个全局格式化函数,只要此类对象必须转换为字符串,就会调用该函数。 (类似于用于泡菜序列化的 copyreg 的方法)。我没有发现任何暗示确实存在这样的注册表。

我的猴子补丁意图:

def custom_str(self):
    return " ".join(self.pt, self.size, self.angle, self.response, self.octave, self.class_id)

try:
    print "patching class"      
    cv2.KeyPoint.__str__ = custom_str
    print str(a0)
    a1 = cv2.KeyPoint(x=100,y=200,_size=20, _angle=1, _response=2, _octave=3, _class_id=4)
    print str(a1)
except Exception, e:
    print "failed"
    print e     

try:
    print "patching object"     
    a0.__str__ = custom_str
    print str(a0)
except Exception, e:
    print "failed"
    print e

输出:

patching class
failed
'builtin_function_or_method' object attribute '__str__' is read-only
patching object
failed
'cv2.KeyPoint' object attribute '__str__' is read-only

【问题讨论】:

  • 您应该尝试搜索“猴子补丁属性”(但是,我不确定它是否可能),因为“只读”异常很可能意味着该属性(__str__方法在你的情况下)是财产。
  • stackoverflow.com/questions/972/… 没有回答您的问题,但可能会提供一些指示

标签: python module external monkeypatching


【解决方案1】:

这在 REPL 中有效。

在一个模块(test.py)中,我有以下类:

class A(object):
    def __init__(self):
        self.a = 1
        self.b = 2

没有定义 str 我在 REPL 中看到以下内容

>>> import test
>>> a=test.A()
>>> a
<test.A at 0x7fa4c327e450>
>>> str(a)
'<test.A object at 0x7fa4c327e450>'

然后我做了以下事情:

>>> def f(self) : return ",".join([str(self.a),str(self.b)])
>>> setattr(test.A, "__str__", f)
>>> str(a)
'1,2'

请注意,修补 会自动更新所有现存的实例。

这会启用您想要的“打印 a0”语法。

但是,如果您因为与现有代码的潜在冲突而不愿替换魔术 __str__ 方法,则可以使用此方法添加具有特定名称的新方法。

>>> setattr(test.A,"formatted", f)
>>> print a.formatted()
'1,2'

【讨论】:

  • 当然,这里假设 cv2 是用 Python 编写的。如果它是 C 扩展,则这些都不起作用
  • 我担心 cv2 是一个 C 扩展(为 python 包装的编译 C 代码)。我试过你的建议,但它抛出“'cv2.KeyPoint'对象属性'str'是只读的”
  • 添加新方法的选项怎么样?这将减少干扰并且(因为它以前从未存在过)不应该对现有代码产生任何影响。
  • 或者实际上是 {print a0print str(a0)print repr(a0) } 中的任何一个都是可以接受的(即所需的语法)只有一个“全局”函数 cv2AsString() 基本上是你的 @987654329 @ 方法并使用 print cv2AsString(a0)。这不是自动的,但意图很明确
  • 写。 cv2AsString():如果打印关键点列表(这是大多数时间实际上必须处理的)wrt,则不会工作。 “添加新方法”:听起来不错,但如何做到这一点才能被 print 调用?我尝试的所有方法都失败了。
猜你喜欢
  • 1970-01-01
  • 2014-03-05
  • 2010-11-01
  • 1970-01-01
  • 2020-09-01
  • 2015-11-22
  • 2021-09-03
  • 1970-01-01
  • 2013-08-12
相关资源
最近更新 更多