Bug in New Style Classes

David MacQuigg dmq at gain.com
Fri Jun 18 07:36:36 CEST 2004


On Thu, 17 Jun 2004 11:05:58 GMT, Michael Hudson <mwh at python.net>
wrote:

>michele.simionato at poste.it (Michele Simionato) writes:
>
>> David MacQuigg <dmq at gain.com> wrote in message news:<rmu1d09qiqtosgdq1vavv3736sb62bktri at 4ax.com>...
>> > I have what looks like a bug trying to generate new style classes with
>> > a factory function.
>> > 
>> > class Animal(object): pass
>> > class Mammal(Animal): pass
>> > 
>> > def newAnimal(bases=(Animal,), dict={}):
>> >     class C(object): pass
>> >     C.__bases__ = bases
>> >     dict['_count'] = 0
>> >     C.__dict__ = dict
>> >     return C
>> > 
>> > Canine = newAnimal((Mammal,))
>> > TypeError: __bases__ assignment: 'Mammal' deallocator differs from
>> > 'object'
>> > 
>> > If I remove the 'object' from the class C(object) statement, then I
>> > get a different, equally puzzling error message:
>> > 
>> > TypeError: __bases__ items must be classes
>> > 
>> > The function works only if I remove 'object' from all base classes.
>> > 
>> > -- Dave
>> 
>> This is not a bug. The developers removed the possibility to change
>> the bases of a new-style class. 
>
>Bad news for you: I put it back in for 2.3.
>
>If you read the error message, you'll notice that it's phrased to
>suggest that assignment to __bases__ is *sometimes* possible :-)
>
>David's assignment probably should work -- there's a bug on sf about
>this -- but there are definitely situations where assignment to bases
>*shouldn't* be allowed -- e.g. when the so-called 'solid base' changes
>-- but noone's put in the thinking time to make this precise in code.
>Being over-restrictive seems the better course.

This may be just a documentation problem then.  The error message is
definitely misleading.

>However, newAnimal could be written like this:
>
>def newAnimal(bases=(Animal,), ns=None):
>    if ns is None:
>        ns = {}
>    ns['_count'] = 0
>    return type('C', bases, ns)
>
>which 
>
>a) doesn't use the name of a builtin as a variable
>b) doesn't suffer the 'mutable default arguments' problem
>c) is rather less insane
>d) actually works :-) (probably, haven't tested it)

It works great.  The only thing I would change is the return line,
making that

    globals()[name] = type('C', bases, ns)

so we don't have to type the name twice when creating a new class.
I've also added an __init__ function.  Using the factory is now very
easy:

>>> newAnimal('Dog',(Mammal,))
>>> dog1 = Dog()
Hello from __init__ in Dog
>>> Dog._count
1

The main limitation I see in using a factory function like this,
instead of a metaclass, is that I can't customize the new animal as
easily, because I don't have an indented block like in a class
definition.  I've got to call the newAnimal function, then add a bunch
of attributes one at a time, with fully-qualified names.

Dog.temperature = 102
Dog.pulse = 82
Dog.respiration = 36

If I'm adding methods, it gets even messier, because I've got to
define functions at the module level, then assign them to attributes
of Dog, then maybe delete all the excess names from the module
namespace.

I have one last question. In reviewing all the ways to solve the
problem of creating specialized classes, I see there is a function
new.classobj(name, bases, dict) which appears to do the same thing as
type(name, bases, dict).  What is the purpose of classobj()?  The name
is a little more self-explanatory than 'type', but using it requires a
module import.

- Dave






More information about the Python-list mailing list