One Python 2.1 idea

Tim Peters tim.one at home.com
Thu Dec 28 21:31:49 CET 2000


[Tim]
> About x.foo = x.foo, I never saw anyone suggest that to save
> time, before this thread.  Where it did come up several times
> over the years was people trying to customize how a method works
> for a particular instance, rather than make a proper subclass.
> Since Python made instance.__class__ writable, I don't see any
> excuse for *that* form of trickery anymore.

[Alex Martelli]
> Might I ask for a clarification of this latter thought...?
>
> Say I have a class Ogre with a few methods:
>
> class Ogre(Bugaboo):
>     def fee(self):
>         print 'fee'
>     def fie(self):
>         print 'fie'
>     def foo(self):
>         print 'foo'
>     def fum(self):
>         print 'fum'
>
> and several instances thereof, one of which is referenced by
> variable x.
>
> Now, I find out that, for that one instance only, I want to
> override the normal Ogre.foo method with another function
> which I also happen to have around:
>
> def fun(self):
>     print 'have fun!'
>
>
> What are the 'good Pythonic ways' to do this, as opposed to
> 'dirty tricks'?
> ... [assorted trickery with the "new" module] ...

class UniqueOgreFoo(Ogre):
    foo = fun
x.__class__ = UniqueOgreFoo

x.foo() # now prints 'have fun!'

> ...
>     x.__class__ = new.classobj('NewOgre', (Ogre,), {'foo':fun})
>
> Are you saying the second approach is preferable?

I'm saying (1) it's clearer to use a subclass than to do the "x.foo = x.foo"
trick; (2) the subclass method does not create cycles; and, (3) using the
"new" module to spell a subclass is unnecessarily obscure.

> Or is there some even-simpler one that I'm missing?  I do realize
> a cycle of references is generated by the new.instancemethod, as
> discussed on this thread -- is that the only issue,

Very few Python programmers understand the "new" module, so I'd say the
obscurity of the code is a bigger issue in practice.  Not that mucking
around with __class__ is recommended to begin with!  That's obscure too,
just not as obscure as new.*.  Best would have been to make x an instance of
UniqueOgreFoo from the start.

> and does GC solve it (if no __del__'s are present),

I hesitate to answer that (and not just because I did before <wink>),
because the set of types gc will "chase" is very likely to change from
release to release, and people should be able to guess good answers to this
kind of question for their particular case of interest via just trying it.
For example,

>>> class C:
        def f(self): pass

>>> addresses = {}
>>> for i in range(1000):
        c = C()
        c.f = c.f
        addresses[id(c)] = 1

>>> len(addresses)
617
>>>

If gc had not cleaned up some of the C instances, every id(c) would have
been unique.  OTOH, that 617 unique addresses were seen strongly suggests
that pure refcounting wasn't enough to clean them up.  Two variations:

>>> addresses = {}
>>> for i in range(1000):
        c = C()
        addresses[id(c)] = 1

>>> len(addresses)
3
>>>

So in that case (no "c.f = c.f") C instances are getting reused very
quickly, so pure refcounting probably sufficed.  While in this case (after
adding a __del__) they're not getting recycled at all:

>>> C.__del__ = lambda self: 0
>>> addresses = {}
>>> for i in range(1000):
	c = C()
	c.f = c.f
	addresses[id(c)] = 1

>>> len(addresses)
1000
>>>

Those are all probabilistic clues, and platform-dependent, but good enough.
Running gc.collect() explicitly and looking at the length of gc.garbage
before and after can nail it if you need more assurance.

give-a-man-a-fish-and-you've-fed-him-for-a-day-but-teach-a-man-to-
    fish-and-he'll-come-by-every-day-asking-for-bait-lures-spare-
    lines-and-your-best-pole<wink>-ly y'rs  - tim





More information about the Python-list mailing list