En Wed, 29 Apr 2009 15:27:48 -0300, mrstevegross <mrstevegr...@gmail.com>
escribió:
I was exploring techniques for implementing method_missing in Python.
I've seen a few posts out there on the subject... One tricky aspect is
if it's possible to not just intercept a method_missing call, but
actually dynamically add a new function to an existing class. I
realized you can modify the Class.__dict__ variable to actually add a
new function. Here's how to do it:
class Foo:
def __init__(self):
self.foo = 3
def method_missing(self, attr, *args):
print attr, "is called with %d args:" % len(args),
print args
def __getattr__(self, attr):
print "Adding a new attribute called:", attr
def callable(*args):
self.method_missing(attr, *args[1:])
Foo.__dict__[attr] = callable
return lambda *args :callable(self, *args)
You're adding a function (a closure, actually) to some *class* that refers
to a specific *instance* of it. This doesn't work as expected; let's add
some verbosity to show the issue:
class Foo:
def __init__(self, foo):
self.foo = foo
def method_missing(self, attr, *args):
print 'Foo(%r)' % self.foo, attr, "is called with %d args:" %
len(args),
print args
# __getattr__ stays the same
f = Foo('f')
f.go_home(1,2,3)
g = Foo('g')
g.go_home('boo')
# output:
Adding a new attribute called: go_home
Foo('f') go_home is called with 3 args: (1, 2, 3)
Foo('f') go_home is called with 1 args: ('boo',)
One would expect Foo('g') on the last line. Worse:
py> print f
Adding a new attribute called: __str__
Foo('f') __str__ is called with 0 args: ()
Traceback (most recent call last):
File "<pyshell#21>", line 1, in <module>
print f
TypeError: __str__ returned non-string (type NoneType)
py> if f: print "Ok!"
Adding a new attribute called: __nonzero__
Foo('f') __nonzero__ is called with 0 args: ()
Traceback (most recent call last):
File "<pyshell#28>", line 1, in <module>
if f: print "Ok!"
TypeError: __nonzero__ should return an int
Almost anything you want to do with a Foo instance will fail, because
Python relies on the existence or not of some __magic__ methods to
implement lots of things.
The code below tries to overcome this problems. Still, in Python, unlike
Ruby, there is no way to distinguish between a "method" and an "attribute"
-- both are looked up exactly the same way. __getattr__ already is,
roughly, the equivalent of method_missing (well, attribute_missing, I'd
say).
class Foo(object):
def __init__(self, foo):
self.foo = foo
def method_missing(self, attr, *args, **kw):
print 'Foo(%r)' % self.foo, attr, "is called with %d args:" %
len(args),
print args
def __getattr__(self, attr):
if attr[:2]==attr[-2:]=='__':
raise AttributeError(attr)
print "Adding a new attribute called:", attr
def missing(self, *args, **kw):
return self.method_missing(attr, *args, **kw)
setattr(type(self), attr, missing)
return missing.__get__(self, type(self))
f = Foo('f')
f.go_home(1,2,3)
f.go_home(2,3,4)
g = Foo('g')
g.go_home('boo')
f.go_home('f again')
print f # works fine now
print f.foox # oops! typos have unexpected side effects :(
print len(f) # should raise TypeError
--
Gabriel Genellina
--
http://mail.python.org/mailman/listinfo/python-list