How to mix-in __getattr__ after the fact?

D

dhyams

Python 2.7.2

I'm having trouble in a situation where I need to mix-in the
functionality of __getattr__ after the object has already been
created. Here is a small sample script of the situation:

=============snip

import types

class Cow(object):
pass
# this __getattr__ works as advertised.
#def __getattr__(self,a):
# print "CAUGHT INCLASS: Attempting to get attribute ",a


def attrgetter(self,a):
print "CAUGHT: Attempting to get attribute ",a

bessie = Cow()

bessie.__getattr__ = types.MethodType(attrgetter,bessie,Cow)

# here, I should see my printout "attempting to get attribute"
# but I get an AttributeException
print bessie.milk
======================snip

If I call __getattr__ directly, as in bessie.__getattr__('foo'), it
works as it should obviously; so the method is bound and ready to be
called. But Python does not seem to want to call __getattr__
appropriately if I mix it in after the object is already created. Is
there a workaround, or am I doing something wrongly?

Thanks,
 
E

Ethan Furman

dhyams said:
Python 2.7.2

I'm having trouble in a situation where I need to mix-in the
functionality of __getattr__ after the object has already been
created. Here is a small sample script of the situation:

=============snip

import types

class Cow(object):
pass
# this __getattr__ works as advertised.
#def __getattr__(self,a):
# print "CAUGHT INCLASS: Attempting to get attribute ",a


def attrgetter(self,a):
print "CAUGHT: Attempting to get attribute ",a

bessie = Cow()

bessie.__getattr__ = types.MethodType(attrgetter,bessie,Cow)

# here, I should see my printout "attempting to get attribute"
# but I get an AttributeException
print bessie.milk
======================snip

If I call __getattr__ directly, as in bessie.__getattr__('foo'), it
works as it should obviously; so the method is bound and ready to be
called. But Python does not seem to want to call __getattr__
appropriately if I mix it in after the object is already created. Is
there a workaround, or am I doing something wrongly?

Thanks,

Python only looks up __xxx__ methods in new-style classes on the class
itself, not on the instances.

So this works:

8<----------------------------------------------------------------
class Cow(object):
pass

def attrgetter(self, a):
print "CAUGHT: Attempting to get attribute", a

bessie = Cow()

Cow.__getattr__ = attrgetter

print bessie.milk
8<----------------------------------------------------------------

~Ethan~
 
L

Lie Ryan

Python only looks up __xxx__ methods in new-style classes on the class
itself, not on the instances.

So this works:

8<----------------------------------------------------------------
class Cow(object):
pass

def attrgetter(self, a):
print "CAUGHT: Attempting to get attribute", a

bessie = Cow()

Cow.__getattr__ = attrgetter

print bessie.milk
8<----------------------------------------------------------------

a minor modification might be useful:

bessie = Cow()
bessie.__class__.__getattr__ = attrgetter
 
D

dhyams

Thanks for all of the responses; everyone was exactly correct, and
obeying the binding rules for special methods did work in the example
above. Unfortunately, I only have read-only access to the class
itself (it was a VTK class wrapped with SWIG), so I had to find
another way to accomplish what I was after.
 
D

DevPlayer

Thanks for all of the responses; everyone was exactly correct, and
obeying the binding rules for special methods did work in the example
above.  Unfortunately, I only have read-only access to the class
itself (it was a VTK class wrapped with SWIG), so I had to find
another way to accomplish what I was after.
Please share what you found as the other way.
 
L

Lie Ryan

Thanks for all of the responses; everyone was exactly correct, and
obeying the binding rules for special methods did work in the example
above. Unfortunately, I only have read-only access to the class
itself (it was a VTK class wrapped with SWIG), so I had to find
another way to accomplish what I was after.

As a big huge hack, you can always write a wrapper class:

class Wrapper(object):
def __init__(self, *args, **kwargs):
self.__object = MySWIGClass(*args, **kwargs)
def __getattr__(self, attr):
try:
return getattr(self.__object, attr)
except AttributeError:
...
 
S

Steven D'Aprano

As a big huge hack, you can always write a wrapper class:

class Wrapper(object):
def __init__(self, *args, **kwargs):
self.__object = MySWIGClass(*args, **kwargs)
def __getattr__(self, attr):
try:
return getattr(self.__object, attr)
except AttributeError:
...


That's not a hack, that's a well-respected design pattern called
Delegation.

http://rosettacode.org/wiki/Delegate
http://en.wikipedia.org/wiki/Delegation_pattern


In this case, you've implemented about half of automatic delegation:

http://code.activestate.com/recipes/52295

which used to be much more important in Python prior to the type/class
unification in version 2.2.


To also delegate special dunder methods using new-style classes, see this:

http://code.activestate.com/recipes/252151
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,744
Messages
2,569,484
Members
44,905
Latest member
Kristy_Poole

Latest Threads

Top