staticmethod and __call__
Roeland Rengelink
r.b.rigilink at chello.nl
Fri Dec 7 04:20:10 EST 2001
Hi,
The reason this doesn't work is that ItemGenerator() never looks for a
static attribute __call__ in its own definition. __call__ is used to
define the meaning of instance(). Or, put differently, obj(*args,
**kwargs) is equivalent to obj.__class__.__call__(obj, *args, **kwargs).
Now, the __class__ of a class is its metaclass. Hence, ItemGenerator()
looks for the __call__ method in its metaclass.
So, what's the metaclass of ItemGenerator? Well, unfortunately for
classic (pre-2.2) classes that's not really obvious. Unless you've used
the Don Beaudry hook (you don't wanna know) to roll your own metaclass,
you can't point to an object and say that's the metaclass of
ItemGenerator. For new style classes (derived from object or one of the
other build-ins), the answer is given by the call type(ItemGenerator),
which turns out to be 'type'. That is 'type' is the metaclass of all
build in types (including itself). Since the type of a new style
instance is its class, this means (a.o.) that in Python 2.2
obj(*args, **kwargs) is simply equivalent to
type(obj).__call__(obj, *args, **kwargs)
Python 2.2 also introduces staticmethods. This has led some of us to try
to do the same thing you do here. Namely use staticmethod to try to
override the metaclass method. However, the general rule is that
staticmethod can't be used to force an override for methods defined in
the metaclass. I (now) think this is a good thing. Why? Well consider
exactly this example. The meaning of obj() is now very straightforward.
obj() => type(obj).__call__(obj)
If we allowed staticmethod to override, it would become something like
if (isinstance(type(obj), type) and
hasattr(obj, '__call__') and
isinstance(obj.__call__, staticmethod)):
return obj.__call__()
else:
return type(obj).__call__(obj)
So, what use are staticmethods?
I think Guido resisted introducing static methods for a long time with
an argument that goes something like:
"Classes are for generating instances and defining their behaviour. If
you just want a namespace, use modules (or dictionaries)."
I.e. somethink like
--- items.py
import random
rgen = random.Random()
class Item:
__init__(self, val):
self.val = val
def f(self):
print 'An item with value', self.val
items = [Item('a'), Item('b'), Item('c')]
def ItemGenerator():
return rgen.choice(items)
---
--- use_it.py ---
import items
myitems = [items.ItemGenerator() for i in range(5)]
for i in myitems:
i.f()
---
I have the feeling the argument against static methods still applies in
most cases, probably augmented with:
"... and if you really, really want do _that_, use metaclasses"
Hope this helps,
Roeland
Bruce Eckel wrote:
>
> I seem to have trouble getting __call__ to behave as a static
> method:
>
> class Item:
> def f(): print 'An Item'
>
> Item.a = 'a'
> Item.b = 'b'
> Item.c = 'c'
>
> class ItemGenerator:
> import random
> rgen = random.Random()
> items = [j for j in vars(Item).values() if isinstance(j, Item)]
> def __call__():
> return ItemGenerator.rgen.choice(ItemGenerator.items)
> __call__ = staticmethod(__call__)
>
> items = [ItemGenerator() for i in range(5)]
> for i in items:
> i.f()
>
--
r.b.rigilink at chello.nl
"Half of what I say is nonsense. Unfortunately I don't know which half"
More information about the Python-list
mailing list