Credit: an anonymous contributor, Moshe Zadka
During debugging, you want to identify certain specific instance
objects so that print
statements display more
information when applied to those objects.
The print
statement implicitly calls an
object’s _ _str_ _
special method, so we can rebind
the _ _str_ _
attribute of the object to a
suitable new bound method, which the new
module
lets us build:
import string import new def rich_str(self): classStr = '' for name, value in self._ _class_ _._ _dict_ _.items( ) + self._ _dict_ _.items( ): classStr += string.ljust(name, 15) + ' ' + str(value) + ' ' return classStr def addStr(anInstance): anInstance._ _str_ _ = new.instancemethod(rich_str, anInstance, anInstance._ _class_ _) # Test it class TestClass: classSig = 'My Sig' def _ _init_ _(self, a = 1, b = 2, c = 3): self.a = a self.b = b self.c = c test = TestClass( ) addStr(test) print test
This recipe demonstrates the runtime addition of a _ _str_ _
special method to a class instance. Python calls
obj._ _str_ _
when you ask for
str(obj)
or when you print
obj
. Changing the _ _str_ _
special method of obj
lets you display more
information for the specific instance object in question when the
instance is printed during debugging.
The recipe as shown is very simple and demonstrates the use of the
special attributes _ _dict_ _
and
_ _class_ _
. A serious defect of this approach is
that it creates a reference cycle in the object.
Reference cycles are no longer killers in Python 2.0 and later,
particularly because we’re focusing on
debugging-oriented rather than production code. Still, avoiding
reference cycles, when feasible, makes your code faster and more
responsive, because it avoids overloading the garbage-collection task
with useless work. The following function will add any function to an
instance in a cycle-free way by creating a specially modified class
object and changing the instance’s class to it:
def add_method(object, method, name=None): if name is None: name = method.func_name class newclass(object._ _class_ _): pass setattr(newclass, name, method) object._ _class_ _ = newclass
We could also use the new
module to generate the
new class object, but there is no particular reason to do so, as the
class
statement nested inside the
add_method
function suits our purposes just as
well.
With this auxiliary function, the addStr
function
of the recipe can, for example, be more effectively (and
productively) coded as:
def addStr(anInstance): add_method(anInstance, rich_str, '_ _str_ _')
The second approach also works for new-style classes in Python 2.2.
The _ _class_ _
attribute of such an instance
object is assignable only within certain constraints, but because
newclass
extends the object’s
existing class, those constraints are met (unless some strange
metaclass is in use). In Python 2.2, operations on instances of
new-style classes don’t use special methods bound in
the instance, but only special methods bound in the class (in all
other cases, per-instance binding still override per-class bindings).
Recipe 15.7; Recipe 5.14 and Recipe 15.10 for other
approaches to modifying the methods of an instance; documentation on
the new
standard library module in the
Library Reference.