From: Mel on 2 Apr 2010 13:35 kj wrote: > In <mailman.1437.1270163476.23598.python-list(a)python.org> Steve Holden > <steve(a)holdenweb.com> writes: > >>But the real problem is that the OP is insisting on using purely >>procedural Python when the problem is screaming for an object-oriented >>answer. > > My initial reaction to this comment was something like "What? switch > from procedural to OO just to be able to do some one-time initialization > of function-private data???" Yeah, actually. If the subject had been "Python-style object attributes in C?" somebody might have suggested C static variables. An example I wrote lately volatile static int random_bit () { static unsigned short lfsr = 0xACE1u; // seeded LFSR // taps: 16 14 13 11; characteristic polynomial: x^16 + x^14 + x^13 + x^11 + 1 lfsr = (lfsr >> 1) ^ (-(lfsr & 1u) & 0xB400u); return lfsr & 1; } // random_bit (excuse is: this was written for cheap execution in an 8-bit processor.) This does OK -- but fails the instant I decide that my program needs more than one pseudo-random bit stream. Then I have the choice of writing several different random_bit functions, or extending random_bit to take a pointer to a seeded LFSR provided by the individual caller. Refactoring the Python function to a Python class, as you mention later, solves the static-access problem, but that solution is just as vulnerable to the need-more-than-just-the-one problem as my C function. Mel.
From: Duncan Booth on 2 Apr 2010 13:53 kj <no.email(a)please.post> wrote: > I suppose one could refactor this: > ><procedural> > def spam(x, y, z): > try: > mongo = spam.mongo > except AttributeError: > mongo = spam.mongo = heavy_lifting_at_runtime() > return frobnicate(x, y, z, mongo) > > ham = spam(3, 4, 5) ></procedural> > > into this: > ><OO> > class _Spam(object): > @classmethod > def _(cls, x, y, z): > try: > mongo = cls.mongo > except AttributeError: > mongo = cls.mongo = heavy_lifting_at_runtime() > return frobnicate(x, y, z, mongo) > > ham = _Spam._(1, 2, 3) ></OO> > > > Is this really more natural or more readable? Hmmm. No, but that's because it is needlessly obfuscated. What's with the weird _ method? Why use a class method? Why not just create an instance? class Spam(object): mongo = None def __call__(self, x, y, z): if self.mongo is None: self.mongo = heavy_lifting_at_runtime() return frobnicate(x, y, z, self.mongo) spam = Spam() ham = spam(1, 2, 3) That's natural and readable. There's also another good reason why the class is better than the static variable: you can construct multiple different instances with different calls to 'heavy_lifting_at_runtime'. e.g. You could write a unit test where mongo is initialised to mock_heavy_lifting_at_runtime().
From: Ethan Furman on 2 Apr 2010 14:21 kj wrote: > <OO> > class _Spam(object): > @classmethod > def _(cls, x, y, z): > try: > mongo = cls.mongo > except AttributeError: > mongo = cls.mongo = heavy_lifting_at_runtime() > return frobnicate(x, y, z, mongo) > > ham = _Spam._(1, 2, 3) > </OO> > > > Is this really more natural or more readable? Hmmm. For this type of situation, my preference would be: class spam(object): def __call__(self, x, y, z): try: mongo = self.mongo except AttributeError: mongo = self.mongo = heavy_lifting_at_runtime() return frobnicate(x, y, z, mongo) spam = spam() No extra objects, out-of-place underscores, etc. ~Ethan~
From: Patrick Maupin on 2 Apr 2010 14:39 On Apr 2, 1:21 pm, Ethan Furman <et...(a)stoneleaf.us> wrote: > For this type of situation, my preference would be: > > class spam(object): > def __call__(self, x, y, z): > try: > mongo = self.mongo > except AttributeError: > mongo = self.mongo = heavy_lifting_at_runtime() > return frobnicate(x, y, z, mongo) > spam = spam() > > No extra objects, out-of-place underscores, etc. > > ~Ethan~ Well, I'm not a big fan of unnecessary try/except, so I would at least change it to: class spam(object): def __getattr__(self, name): if name != 'mongo': raise AttributeError self.mongo = heavy_lifting_at_runtime() return self.mongo def __call__(self, x, y, z): return frobnicate(x, y, z, self.mongo) spam = spam() Regards, Pat
From: Steven D'Aprano on 2 Apr 2010 14:40
On Fri, 02 Apr 2010 16:08:42 +0000, kj wrote: > Other responses advocated for global variables. I avoid them in > general, In general this is wise, but remember that because Python globals are not globally global, but local to a single module, they're safer than globals in other languages. Still, it's better to avoid them when possible. > and doubly so in Python, because I find Python's shenanigans > with globals mystifying (this business of becoming silently local if > assigned to); Globals don't become local when assigned to. You can shadow a global with a local of the same name, but the global remains untouched: >>> myglobal = 42 >>> def test(): .... myglobal = 0 # shadow the global with a new local .... >>> test() >>> myglobal 42 I find this behaviour perfectly natural, and desirable: it means I can assign to locals without worrying whether or not I'm about to stomp all over a global and destroy it. The alternative behaviour would be disastrous: >>> def f(x): return x+1 .... >>> def test(): .... f = 'spam' .... >>> test() >>> f(2) # this doesn't happen Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: 'str' object is not callable -- Steven |