Bug in New Style Classes

Michael Hudson mwh at python.net
Fri Jun 18 06:18:06 EDT 2004


David MacQuigg <dmq at gain.com> writes:

> 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.

Well, possibly.  It's generally assumed that you know what you're
doing if you want to muck with things on this level.

Clarifying patches welcome :-)

> >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)

Ugh!

> 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,
  ^^^^^^^^^^^^^^^^^^^^^^
What a what?  I _really_ don't think you mean metaclass here.

> 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

Well, there are ways around that, too, eg:

def newAnimal(bases=(Animal,), **kw):
    kw['_count'] = 0
    return type('C', bases, kw)

Dog = newAnimal('Dog', (Mammal,), temperature=102, 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.

Well, not necessarily.  Use the third parameter to the call to type().

> 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).  

new.classobj() is a holdover from the days of old-style classes,
though I see it creates new-style classes if passed new-style bases...

> What is the purpose of classobj()?  The name is a little more
> self-explanatory than 'type', but using it requires a module import.

Historical cruft, more-or-less.

Cheers,
mwh

-- 
  ARTHUR:  Why should a rock hum?
    FORD:  Maybe it feels good about being a rock.
                    -- The Hitch-Hikers Guide to the Galaxy, Episode 8



More information about the Python-list mailing list