A matter of style: class.foo = bar vs. class.set_foo(bar)
Chuck Esterbrook
echuck at mindspring.com
Tue Nov 14 10:16:00 EST 2000
Thanks for the example. I was just writing GetterSetter last night and
using a Vector example where the length was maintained as x and y were set.
Interesting that we both chose geometry examples, although I think yours is
more cogent.
In any case, I came to the following realization which turned me off to the
technique. Tell me what you think:
__getattr__ is only invoked if the attribute cannot be found by normal
means. In other words, if obj.foo cannot be found then
obj.__getattr__('foo') is invoked. That means that if obj inherits from A
which inherits B which inherits C, the following operations occur:
search obj for foo
search A for foo
search B for foo
search C for foo
invoke __getattr__('foo')
And the more classes you have the worse it gets. The performance here could
be really, really bad. I know some say "performance doesn't matter in
Python", but I find that in all software it matters to a degree. Just
because your code runs slow doesn't mean you want it to get even worse.
I wish Python had a version of __getattr__ which was always invoked
immediately. Or this would work well if Python had this language feature
built-in. e.g., if setting, getting and deleting foo always invoked
set_foo(), get_foo() and del_foo() if they existed.
And for that matter I wish it had class methods and a unified class hierarchy.
If only I had more time. :-)
-Chuck
--
Webware for Python:
http://webware.sourceforge.net
At 07:44 PM 11/14/00 +1300, Paul Foley wrote:
>On Tue, 14 Nov 2000 04:50:33 GMT, echuck wrote:
>
> > "Alex Martelli" <aleaxit at yahoo.com> wrote:
> >> <echuck at mindspring.com> wrote
> >> [snip]
> >> > The disadvantage of:
> >> > obj.foo = 5
> >> >
> >> > is that in the future if you want to take some action when foo is set,
> >> > like asserting that it's in range or not None, then you're out of luck.
> >>
> >> Naah -- you just have to add a __setattr__ method (or a clause to
> >> your existing __setattr__ method); or, with suitable metaclasses
> >> (such as those in py_cpp), you even get to define a specific
> >> __setattr__foo method to catch the specific setting of obj.foo only.
>
> > Although it's technically feasible to do a __setattr__, what would the
> > results look like for foo, bar, a, b, c?
>
> > if name=="foo":
> > do_this()
> > elif name=="bar":
> > do_that()
> > elif name=="...
>
> > You get the drift. This is really inefficient. Every time you add a new
> > attribute you increase this linear search and pile on more code in
> > __setattr__. I suppose you could do this:
>
> > methodName = "set_"+name
> > method = getattr(self, methodName, None)
> > if method:
> > method(name, value)
> > else:
> > self.__dict__[name] = value
>
> > But it feels silly to make setattr to call set_foo() for you...maybe
> > I'm just not use to it.
>
>I have, in my ~/python directory, a file I wrote as an example for
>someone asking a similar question quite a while back. The contents of
>that file are:
>
> import math
>
> class Point(GetterSetter):
> def __init__(self, x=0, y=0, r=None, phi=None):
> if r and phi:
> self.set_polar(r, phi)
> else:
> self.x, self.y = x, y
> def set_polar(self, r, phi):
> self.x = r*math.cos(phi)
> self.y = r*math.sin(phi)
> def _rd_r(self):
> return math.sqrt(self.x**2 + self.y**2)
> def _rd_phi(self):
> return math.atan2(self.y, self.x)
> def _wr_r(self, r):
> self.set_polar(r, self.phi)
> def _wr_phi(self, phi):
> self.set_polar(self.r, phi)
>
>Unfortunately, I didn't keep the file implementing the "GetterSetter"
>class (I must have just typed it in my reply and pasted it at the
>prompt, based on the lack of "import" statement for it,
>above...reconstructing it would be a good exercise, I suppose), but
>you can see that it did essentially what you've written above: turned
>uses of, and assignments to, point.r and point.phi, for some instance
>point of Point, into calls to _rd_r()/_rd_phi() and _wr_r()/_wr_phi()
>respectively, so that both cartesian and polar coordinates are
>available and kept consistent while providing a nice "point.r = 6"
>interface instead of "point.set_r(6)" or something equally ugly.
>
>[FWIW, the GetterSetter mixin only rewrote calls to the _rd_* and
>_wr_* functions if they actually existed; that's why point.x and
>point.y work without having to write _rd_x() and _wr_x(), etc.]
>
>--
>You don't have to agree with me; you can be wrong if you want.
>
>(setq reply-to
> (concatenate 'string "Paul Foley " "<mycroft" '(#\@) "actrix.gen.nz>"))
More information about the Python-list
mailing list