Newbie Class Questions
Joe Mason
joe at notcharles.ca
Sun Mar 28 11:26:45 EST 2004
In article <c46hup$d9d$05$1 at news.t-online.com>, Peter Otten wrote:
> Joe Mason wrote:
>
>>>> I think it would be cool for my "AutoText" class (see below, hereby
>>>> GPL'd) to be able to tell what subclasses inherit from it and
>>>> instantiate objects for all of those subclasses to implement a "AutoAll"
>>>> function.. Any ideas?
>>
>> Actually, somebody pointed out in the other thread that Python has a
>> __subclasses__ function, so AutoAll can just do "for i in
>> AutoText.__subclasses__:".
>
> My first reaction was disbelief :-) Now we can change
>
> # add as base whenever you create a new subclass of AutoText
> class AutoAll(AutoUrl, AutoEmphasis):
> pass
>
> to
>
> AutoAll = type("AutoAll", tuple(AutoText.__subclasses__()), {})
Woah. That's a weird one. You're on the right track, but what you're
doing here is creating a class with absolutely nothing in it - not even
the default attributes that I'll show below. What you really want to do
is make class normally, but only override the base classes.
Fortunately, that's even easier:
class AutoAll(tuple(AutoText.__subclasses__())):
pass
... except that doesn't work, and nobody's been able to explain to me
why not.
So, I'll turn to metaclasses for the clean solution. But first:
> Do you know what the last dictionary parameter of type() is for? A cursory
> glance over the 2.3 library did not bring up a usecase of type() with three
> parameters.
It's the __dict__ of the class that's about to be created:
>>> class Verbose(type):
... def __new__(*args):
... print "new: ", args
... return type.__new__(*args)
... def __init__(*args):
... print "init: ", args
... type.__init__(*args)
...
>>> class Empty(object): __metaclass__ = Verbose
...
new: (<class '__main__.Verbose'>, 'Empty', (<type 'object'>,), {'__module__': '__main__', '__metaclass__': <class '__main__.Verbose'>})
init: (<class '__main__.Empty'>, 'Empty', (<type 'object'>,), {'__module__': '__main__', '__metaclass__': <class '__main__.Verbose'>})
>>> class HasAnAttr(Empty): x = 1
...
new: (<class '__main__.Verbose'>, 'HasAnAttr', (<class '__main__.Empty'>,), {'x': 1, '__module__': '__main__'})
init: (<class '__main__.HasAnAttr'>, 'HasAnAttr', (<class '__main__.Empty'>,), {'x': 1, '__module__': '__main__'})
(Hmm, I guess the __metaclass__ means that first one is not Empty after
all...)
The metaclass I just made hooks in to the class creation, in this case
just to print some extra stuff and then call the normal one. Here's one
that replaces the bases cleanly:
class AutoTextSubclasses(type):
def __new__(metaclass, name, bases, attrs):
return type.__new__(metaclass, name,
tuple(AutoText.__subclasses__()), attrs)
class AutoAll: __metaclass__ = AutoTextSubclasses
That'll throw away the normal "bases" list, and do the same thing your
type class did - make "bases" out of __subclasses__ - except it passes
through the attrs unchanged.
(You can replace both uses of "type" in AutoTextSubclasses with Verbose
to see what's happening.)
Joe
More information about the Python-list
mailing list