1

Supposing the following code:

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

which prints out:

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

How do I get the desired output with the desired syntax?

As far as I can see there are the following approaches:

  1. derive my own class from KeyPoint and implement str and repr there: unwanted as a) some functions in cv2 expect a list of cv.KeyPoint objects and conversion would slow down the code b) the additional class layer makes the code more complex

  2. monkey patch KeyPoint: would be nice but does not work (see code below). In addition I don't know if the functions in cv2 expecting lists of KeyPoint would accept such a modified class.

  3. monkey patch a0: would be less nice but does not work neither (see code below).

  4. register a global formatting function for objects of type cv2.KeyPoint that gets called whenever such an object has to be converted to string. (Approach similar to copyreg for pickle serialization). I didn't find any hint that such a registry actually exists.

My monkey patch intents:

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

output:

patching class
failed
'builtin_function_or_method' object attribute '__str__' is read-only
patching object
failed
'cv2.KeyPoint' object attribute '__str__' is read-only
durron597
  • 31,968
  • 17
  • 99
  • 158
wedert
  • 11
  • 2
  • You should try to search "monkey patch property" (however, I'm not sure it is possible at all), because "read-only" exception most likely means that this attribute (```__str__``` method in your case) is property. – pavel_form Dec 05 '14 at 18:50
  • http://stackoverflow.com/questions/972/adding-a-method-to-an-existing-object doesn't answer your question but may provide some pointers – kdopen Dec 05 '14 at 19:29

1 Answers1

0

This worked in the REPL.

In a module (test.py) I had the following class:

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

With no str defined I see the following in the REPL

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

Then I did the following:

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

Note that patching the class automatically updated all extant instances.

This enables the 'print a0' syntax you wanted.

However, if you are hesitant to replace the magic __str__ method because of potential conflicts with existing code, you could use this approach to add a new method with a specific name.

>>> setattr(test.A,"formatted", f)
>>> print a.formatted()
'1,2'
kdopen
  • 8,032
  • 7
  • 44
  • 52
  • Of course, this assumes that cv2 is written in Python. If it's a C extension, none of this may work – kdopen Dec 05 '14 at 19:54
  • I fear cv2 is a C extension (compiled C code wrapped for python). I tried your suggestion but it throws "'cv2.KeyPoint' object attribute '__str__' is read-only" – wedert Dec 05 '14 at 20:47
  • How about the option of adding a new method? That will be less intrusive and (because it never existed before) shouldn't have any impact on existing code. – kdopen Dec 05 '14 at 21:44
  • Or actually as any of { `print a0`, `print str(a0)`, or `print repr(a0) `} are acceptable (i.e. desired syntax) just have a "global" function `cv2AsString()` which is basically your `custom_str()` method and go with `print cv2AsString(a0)`. It's not automagic, but the intent is clear – kdopen Dec 05 '14 at 21:50
  • wrt. cv2AsString(): would not work if printing list of KeyPoints (and this is what actually has to be handled most of the time) wrt. "adding new method": sounds good, but how to do it so that it gets called by print? All the ways I tried failed. – wedert Dec 05 '14 at 22:37
  • Then subclassing and overriding \_\_str\_\_ is probably your only option. Python uses duck-typing so everything that can handle a KeyPoint should be able to handle your subclass – kdopen Dec 06 '14 at 14:30