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