Erik said:
Thanks for your reply, Nick. My first thought was "Ahhh, now I see. That's
slick!", but after playing with this a bit...
... def __getattr__(self, attr):
... def intercepted(*args):
... print "%s%s" % (attr, args)
... return intercepted
...
__repr__()
Traceback (most recent call last):
File "<stdin>", line 1, in ?
TypeError: __repr__ returned non-string (type NoneType)
my thought is "Oooooh... that is some nasty voodoo there!" Especially
if one wants to also preserve the basic functionality of __getattr__ so that
it still works to just get an attribute where no arguments were given.
I was thinking it would be clean to maintain an interface where you
could call things like f.set_Spam('ham') and implement that as self.Spam =
'ham' without actually having to define all the set_XXX methods for all the
different things I would want to set on my object (as opposed to just making
an attribute assignment), but I am starting to think that is probably an
idea I should just simply abandon.
Well, you could tweak __getattr__ as follows:
.... def __getattr__(self, attr):
.... if attr.startswith('__'):
.... raise AttributeError
.... def intercepted(*args):
.... print "%s%s" % (attr, args)
.... return intercepted
But abandoning the whole idea is probably a good idea. How is defining
a magic set_XXX method cleaner than just setting the attribute? Python
is not C++/Java/C#. Accessors and mutators for simple attributes are
overkill. Keep it simple, you'll thank yourself for it later when
maintaining your code.
I guess I don't quite follow the error above though. Can you explain
exactly what happens with just the evaluation of f?
Sure. (Note, this is greatly simplified, but still somewhat complex.)
The Python interpreter does the following when you type in an
expression:
(1) evaluate the expression, store the result in temporary object
(2) attempt to access the object's __repr__ method
(3) if step 2 didn't raise an AttributeError, call the method, output
the result, and we're done
(4) if __getattr__ is defined for the object, call it with "__repr__"
as the argument
(5) if step 4 didn't raise an AttributeError, call the method, output
the result, and we're done
(6) repeat steps 2 through 5 for __str__
(7) as a last resort, output the default "<class __main__.Foo at
0xDEADBEEF>" string
In your case, the intepreter hit step 4. f.__getattr__("__repr__")
returned the "intercepted" function, which was then called. However,
the "interpreted" function returned None. The interpreter was
expecting a string from __repr__, so it raised a TypeError.
Clear as mud, right? Cutting out the __getattr__ trickery, here's a
simplified scenario (gets to step 3 from above):
... def __repr__(self):
... return None
... Traceback (most recent call last):
File "<stdin>", line 1, in ?
TypeError: __repr__ returned non-string (type NoneType)
Hope that helps! One other small thing... please avoid top posting.
--Ben