A question on python performance.
Bruno Desthuilliers
bdesth.quelquechose at free.quelquepart.fr
Sun Sep 23 11:48:40 EDT 2007
Joe Goldthwaite a écrit :
> Hi everyone,
>
> I'm a developer who's been using python for a couple of years. I wrote a
> fairly large application using it but I was learning the language at the
> same time so it most of the code kind of sucks.
>
> I've learned a lot since then and I've been going through my code trying to
> organize it better and make better use of Python's features. I'm still not
> an expert by any definition but I'm slowly getting better.
>
> I've been working on a trend class that takes twelve monthly numbers and
> returns a period to date, quarter to date, year to date and quarterly year
> to date numbers for a specific period. This worked but I ended up with a lot
> of code like this;
>
> def getValue(trend, param, per):
> if param == 'Ptd':
> return trend.Ptd(per)
> elif param == 'Qtd':
> return trend.Qtd(per)
> elif param == 'Ytd':
> return trend.Ytd(per)
> elif param == 'YtdQ':
> return trend.YtdQ(per)
The first obvious simplification is to replace this with:
def getValue(trend, param, per):
meth = getattr(trend, param)
return meth(per)
The main difference is that it will raise (instead of returning None) if
param is not the name of a method of trend.
The second simplification is to either get rid of getValue() (which is
mostly useless).
> The code gets kind of wordy
indeed
> so I started trying to figure out how to call
> them dynamically since the param type is the same as the method the
> retrieves it. I came up with this;
>
> def getValue(trend, param, per):
> return trend.__class__.__dict__[param](trend, per)
Note that this is not strictly equivalent:
class Parent(object):
def __init__(self, name):
self.name = name
def __repr__(self):
return "<%s %s>" % (self.__class__.__name__, self.name)
def dothis(self):
return "parent.dothis %s" % self
class Child(Parent):
def dothis(self):
return "Child.dothis %s" % self
class OtherChild(Parent): pass
def dothat(obj):
return "dothat %s" % obj
p = Parent('p')
c1 = Child('c1')
c2 = Child('c2')
c2.dothis = dothat.__get__(c2, type(c2))
o1 = OtherChild('o1');
o2 = OtherChild('o2');
o2.dothis = dothat.__get__(o2, type(o2))
for obj in p, c1, c2, o1, o2:
print "obj : %s" % obj
print "direct call :"
print obj.dothis()
print "via obj.__class__.__dict__ :"
try:
print obj.__class__.__dict__["dothis"](obj)
except KeyError, e:
print "oops - key error: %s" % e
print
=>
obj : <Parent p>
direct call :
parent.dothis <Parent p>
via obj.__class__.__dict__ :
parent.dothis <Parent p>
obj : <Child c1>
direct call :
Child.dothis <Child c1>
via obj.__class__.__dict__ :
Child.dothis <Child c1>
obj : <Child c2>
direct call :
dothat <Child c2>
via obj.__class__.__dict__ :
Child.dothis <Child c2>
obj : <OtherChild o1>
direct call :
parent.dothis <OtherChild o1>
via obj.__class__.__dict__ :
oops - key error: 'dothis'
obj : <OtherChild o2>
direct call :
dothat <OtherChild o2>
via obj.__class__.__dict__ :
oops - key error: 'dothis'
IOW, direct access to obj.__class__.__dict__ bypasses both inheritence
and per-instance overriding.
> That worked but it seems like the above line would have to do lots more
> object look ups at runtime so I didn't think it would be very efficient. I
> thought maybe I could add a caller method to the trend class and I came up
> with this;
>
> class trend:
> ...
> ...
> ...
> def caller(self, param, *args):
> return self.__class__.__dict__[param](self, *args)
>
> This simplified the getValue function to this;
>
> def getValue(trend, param, per):
> return trend.caller(param, per)
Err... It actually means *more* lookup and function calls - and still
fails to behave correctly wrt/ polymorphic dispatch.
More information about the Python-list
mailing list