What do you call a class not intended to be instantiated

Aaron "Castironpi" Brady castironpi at gmail.com
Sat Sep 27 19:33:43 EDT 2008


On Sep 27, 6:16 pm, Terry Reedy <tjre... at udel.edu> wrote:
> Aaron "Castironpi" Brady wrote:
> >>>> class A(type):
> > ...     def __call__( self, *ar ):
> > ...             print 'call', self, ar
> > ...
> >>>> class B(object):
> > ...     __metaclass__= A
> > ...
> >>>> B(3)
> > call <class '__main__.B'> (3,)
>
> > Overriding the __call__ method of 'type' has the effect of giving you
> > a static __call__ method on a class-- a method which doesn't need an
> > instance to call.  Your behavior may be counterintuitive though, to
> > someone who wants to instantiate 'B', in this case, and proceed like a
> > normal object.  That is, they want to call a generic class and use it,
> > and also expect instances of B to behave as B.  You can't have both,
> > so either return B from B.__new__, or, to instantiate B, take the long
> > way and call B.__new__ directly.
>
> >>>> B.__new__(B)
> > <__main__.B object at 0x009FDB70>
>
> > Has anyone stepped through the C code to find out when the decision is
> > made to call which function, B.__new__ or A.__call__, when B is
> > called?  
>
> For Python coded objects, ob(*args) in code translates to internal
> execution of type(ob).__call__(ob, *args) (without further
> translation!).  The interpreter compiles a statement at a time, without
> looking back to do type inferencing, and so does not know what type is
> being called or if it is even callable.
>
> For B, B(*args) == type(B).__call__(B, *args) == A.__call__(B, *args).
> So there is no decision.
>
> For C coded objects, I believe ob(*args) in Python code translate to a C
> call of the C equivalent of type(ob).tp_call (or something like that).
>  From observation, type.tp_call acts something like this:
>
> def __call__(cls, *args):
>    if cls == type:
>      if len(*args):
>        return arg[0].__class__
>      elif len(*args) == 3:
>        return type.__new__(type, *args) # or maybe not pass type?
>      else:
>        raise TypeError('type() takes 1 or 3 arguments')
>    else:
>      return cls.__new__(cls, *args)
>
> So, for a normal class C (an instance of type), type.__call__ calls
> C.__new__.
>
> Terry Jan Reedy

Oh, I see.  Then it's the class statement that calls type.__new__.

class A: ...
-> A= type( 'A', ... )
-> A= type.__call__( type, 'A', ... )
-> A= type.__new__( type, 'A', ... )

Plus an iteration over the contents of 'namespace', to search for
properties that themselves have a __get__ method.  And returns an
unboundmethod instance "of" it, for a value of "of" that's hard to
concentrate on.  I jest.

Perhaps what Steven is looking for is a subclass of 'type' that does
not give this default behavior of 'unboundmethoding' everything it
can.  That is, defaulting to 'staticmethod' or 'classmethod', and
perhaps offering a 'boundmethod' decorator for the exceptions.

For the case of '__call__', which he does want to control, that could
merely call B.__call__, instead of B.__new__.  Untested:

>>> class A(type):
...     def __call__( self, *ar ):
...             return self.__call__( *ar )

or

...             return self.__call__( self, *ar )

Which would come through to 'B' as:

>>> class B(object):
...     __metaclass__= A
...     def __call__( cls, *ar ).

This is because self == B in the example.  This makes me scowl.  Very
odd.



More information about the Python-list mailing list