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