Erik Johnson wrote: > Thanks for your reply, Nick. My first thought was "Ahhh, now I see. That's > slick!", but after playing with this a bit... > > >>> class Foo: > ... def __getattr__(self, attr): > ... def intercepted(*args): > ... print "%s%s" % (attr, args) > ... return intercepted > ... > >>> f = Foo() > >>> f > __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.
You're right, you should probably abandon it because python is not java; there's no reason to bloat your API with getters and setters instead of the natural attribute access and assignment. Now if you've spent a lot of time in the java camp and can't live without verbose getters and setters, it's not hard to emulate them: class Foo(object): def __getattr__(self, attr): if not (attr.startswith('get_') or attr.startswith('set_')): raise AttributeError name = attr[4:] if attr[0] == 'g': return lambda: getattr(self,name) else: return lambda value: setattr(self,name,value) f = Foo() f.set_bar(1) print f.get_bar() print f.bar > I guess I don't quite follow the error above though. Can you explain > exactly what happens with just the evaluation of f? Several thing happen: 1. When you give an expression in the shell, its value is computed and then passed to the repr() function. The result of repr(f) is what's printed. 2. repr(f) attempts to call f.__repr__() 3. __repr__ is not defined in class Foo or any of its (zero) superclasses. 4. As a result, f.__getattr__('__repr__') is called instead and returns the intercept local function. 5. intercept() is called with zero arguments (remember, intercept is the result of f.__repr__) 6. intecept prints something but it doesn't return explicitly; thus it returns None. 7. There's a rule that __repr__ (like __str__ and __unicode__) has to return a string-type (I'm not sure where is this rule enforced, by the classobj maybe ?). Since you returned None, an exception is raised. Note that there is not an exception if - you replace print with return in __getattr__, or - Foo is a new-style class, i.e. extends object like this: class Foo(object): # rest remain the same The reason is that in step (3) above, __repr__ would be looked up in Foo's superclass, object, and object.__repr__ would be called instead of Foo.__getattr__. Try to use new-style classes in new code unless you have a good reason not to. > Thanks, > -ej HTH, George > "Nick Smallbone" <[EMAIL PROTECTED]> wrote in message > news:[EMAIL PROTECTED] > > Erik Johnson wrote: > > > Maybe I just don't know the right special function, but what I am > wanting to > > > do is write something akin to a __getattr__ function so that when you > try to > > > call an object method that doesn't exist, it get's intercepted *along > with > > > it's argument*, in the same manner as __getattr__ intercepts attributes > > > references for attributes that don't exist. > > > > > > > > > This doesn't quite work: > > > > > > >>> class Foo: > > > ... def __getattr__(self, att_name, *args): > > > ... print "%s%s" % (att_name, str(tuple(*args))) > > > ... > > > >>> f = Foo() > > > > The problem is that the call to f.bar happens in two stages: the > > interpreter calls getattr(f, "foo") to get a function, and then it > > calls that function. When __getattr__ is called, you can't tell what > > the parameters of the function call will be, or even if it'll be called > > at all - someone could have run "print f.bar". > > > > Instead, you can make __getattr__ return a function. Then *that* > > function will be called as f.bar, and you can print out its arguments > > there: > > > > class Foo: > > def __getattr__(self, attr): > > def intercepted(*args): > > print "%s%s" % (attr, args) > > return intercepted > > -- http://mail.python.org/mailman/listinfo/python-list