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. -- http://mail.python.org/mailman/listinfo/python-list