metaclasses vs. inheritance

518 views
Skip to first unread message

Ian McMeans

unread,
Jun 28, 2002, 2:23:08 AM6/28/02
to
Can someone give an example of a problem that is solved with metaclasses,
which would be difficult or impossible to solve with inheritance?

I (very barely) understand metaclasses, but to me it just seems like
inheritance, where the classes who get the benefit of the meta-class get the
metaclass' features.

PS - what's the antonym of meta?


Jonathan Hogg

unread,
Jun 28, 2002, 5:07:33 AM6/28/02
to
On 28/6/2002 7:23, in article ghTS8.58749$mh.18...@news1.telusplanet.net,
"Ian McMeans" <imcm...@home.com> wrote:

> Can someone give an example of a problem that is solved with metaclasses,
> which would be difficult or impossible to solve with inheritance?
>
> I (very barely) understand metaclasses, but to me it just seems like
> inheritance, where the classes who get the benefit of the meta-class get the
> metaclass' features.

Interesting you should ask this as I've spent the last day reading up on
metaclasses and playing with them to better understand what can be done with
them. I find the best way to understand them is with a little graph:

metaclass metaclass
^ ^
| |
instance instance
| |
class B ---inheritance--> class A
^ ^
| |
instance instance
| |
b a

This shows that metaclasses are "orthogonal" to normal inheritance. By which
I mean when you define a (new-style) class like so:

>>> __metaclass__ = type
>>>
>>> class A:
... pass
...

an instance of class 'A' has the type 'A':

>>> a = A()
>>>
>>> a
<__main__.A object at 0x3f8cf0>
>>> type(a)
<class '__main__.A'>
>>>

But 'A' itself is an instance of the metaclass, in this instance 'type':

>>> A
<class '__main__.A'>
>>> type(A)
<type 'type'>
>>>

Thus the metaclass is used in the construction and definition of classes. A
metaclass is a class itself (with the mind-bending result that the type of
'type' is 'type') and new ones can be defined using the same syntax. The
easiest way to define a new metaclass is to inherit from 'type' as that
defines all of the magic necessary to construct and manage classes.

The main reason one would want to define a new metaclass is to override some
of this magic in order to bend the way classes work. A useful method that
one might want to override in a metaclass is '__init__', which is called to
initialise a new class. An example will show what happens:

>>> class mytype( type ):
... def __init__( cls, name, bases, dict ):
... print '__init__'
... print ' new class:', cls
... print ' name:', name
... print ' base classes:', bases
... print ' dictionary:', dict
... type.__init__( cls, name, bases, dict )
...
>>> __metaclass__ = mytype
>>>
>>> class Foo:
... def foo( self ):
... print 'foo'
...
__init__
new class: <class '__main__.Foo'>
name: Foo
base classes: ()
dictionary: {'__module__': '__main__', 'foo': <function foo at
0x409768>}
>>>

You can see from the above example that the 'mytype.__init__' is called
immediately after we finish defining the class 'Foo'. It is passed as
arguments the information used to initialise a new class, which is: the
class name, a tuple of the base classes (none in this example), and the
class dictionary which contains the method functions and class instance
variables.

Most people won't actually have any particular purpose for metaclasses, but
for altering the way that the language works they are invaluable. The
example I worked on yesterday is a mechanism for declaring methods and
classes final (in the Java sense) - prompted by an example someone else
wrote a while ago on this group. My metaclass intervenes in the creation of
new classes to check that final constraints aren't being broken:

>>> from final import *
>>>
>>> class Foo:
... def foo( self ):
... print 'foo'
... foo = finalmethod( foo )
...
>>> class Bar( Foo ):
... def foo( self ):
... print 'bar'
...
Traceback (most recent call last):
[..]
final.FinalError: finalmethod 'foo' of class 'Foo' may not be overriden
>>>

and also:

>>> class Foo( finalclass ):
... pass
...
>>> class Bar( Foo ):
... pass
...
Traceback (most recent call last):
[...]
final.FinalError: finalclass 'Foo' may not be subclassed
>>>

There's no easy way to do this sort of magic without the use of metaclasses.

Hope this helps a little. I'd better go do some real work ;-)

Jonathan

Aldo Cortesi

unread,
Jun 28, 2002, 6:04:22 AM6/28/02
to
Thus spake Ian McMeans (imcm...@home.com):

> Can someone give an example of a problem that is solved
> with metaclasses, which would be difficult or impossible
> to solve with inheritance?


There are some things that can only be done with metaclasses
- changing the text representation of a class, for instance.


class Extended(type):
def __repr__(self):
attrs = []
for i in dir(self):
attrs.append("%-20s\t\t%s" % (i, type(self.__getattribute__(self, i))))
return "%s:\n\t%s" % (type.__repr__(self), "\n\t".join(attrs))

class Foo(object):
__metaclass__ = Extended
pass
print Foo

Baroque, I know, but it illustrates the point.

> PS - what's the antonym of meta?

Hmmm... the meta- in "metaclass" or "metaphysics" really has
no relation to the original Greek, which means "with", or
"among" (as in "metatarsus" - the bones in the foot which
lie next to the tarsus). So whatever antonym you come up
with can't have anything to do with the original etymology
of the prefix... Erm... How about mundane? Or actual?
Intrinsic? Essential? Or even "per se"....?


Cheers,


Aldo

--
Aldo Cortesi
al...@nullcube.com


Hans Nowak

unread,
Jun 28, 2002, 11:22:16 AM6/28/02
to
Aldo Cortesi wrote:

>>PS - what's the antonym of meta?
>
> Hmmm... the meta- in "metaclass" or "metaphysics" really has
> no relation to the original Greek, which means "with", or
> "among" (as in "metatarsus" - the bones in the foot which
> lie next to the tarsus). So whatever antonym you come up
> with can't have anything to do with the original etymology
> of the prefix... Erm... How about mundane? Or actual?
> Intrinsic? Essential? Or even "per se"....?

In ancient Greek, it also meant "over", like Latin "trans"... e.g. "over the
river". I think the "meta" in "metaclass", "metadata" etc is related to this
meaning.

Cheers,

--
Hans (base64.decodestring('d3VybXlAZWFydGhsaW5rLm5ldA=='))
# decode for email address ;-)
The Pythonic Quarter:: http://www.awaretek.com/nowak/

Mark McEahern

unread,
Jun 28, 2002, 11:20:43 AM6/28/02
to
> Can someone give an example of a problem that is solved with metaclasses,
> which would be difficult or impossible to solve with inheritance?

You can intervene in class creation to do aspect-oriented type stuff. E.g.,
before each method call, do this, after each method call, do that. Etc. I
will post a more detailed example as the start of a separate thread shortly.

// m

-

David Mertz, Ph.D.

unread,
Jun 28, 2002, 2:29:09 PM6/28/02
to
|Can someone give an example of a problem that is solved with metaclasses,
|which would be difficult or impossible to solve with inheritance?
|I (very barely) understand metaclasses, but to me it just seems like
|inheritance, where the classes who get the benefit of the meta-class get the
|metaclass' features.

Well... I am at the "barely" level also. But my Gnosis_Utils package
contains a moderately useful example of a metaclass.

By way of background, gnosis.xml.pickle pickles Python objects to XML.
There are several ways to use it, but one is:

# By inheritence
from gnosis.xml.pickle import XML_Pickler
class MyClass(XML_Pickler):
# create some behavior and attributes for MyClass...
o1 = MyClass()
# o1 knows how to dump itself
xml_str = o1.dumps()

Good enough. But every class that wants to know how to serialize itself
needs to inherit from XML_Pickler. Here's an alternate approach:

"""Perform black magic of unearthly and ungodly sorts

Quick Example 1:

Python 2.2 (#0, Dec 24 2001, 18:42:48) [EMX GCC 2.8.1] on os2emx
Type "help", "copyright", "credits" or "license" for more information.
>>> import gnosis.magic
>>> __metaclass__ = gnosis.magic.MetaPickler
>>> class Boring:
... def __init__(self):
... self.this = 'that'
... self.spam = 'eggs'
... def print_spam(self):
... print self.spam
...
>>> boring = Boring()
>>> boring.print_spam()
eggs
>>> print boring.dumps()
<?xml version="1.0"?>
<!DOCTYPE PyObject SYSTEM "PyObjects.dtd">
<PyObject module="__main__" class="Boring" id="1276364">
<attr name="this" type="string" value="that" />
<attr name="spam" type="string" value="eggs" />
</PyObject>

"""
from gnosis.xml.pickle import dumps
class MetaPickler(type):
def __init__(cls, name, bases, dict):
super(MetaPickler, cls).__init__(name, bases, dict)
setattr(cls, 'dumps', dumps)

--
mertz@ | The specter of free information is haunting the `Net! All the
gnosis | powers of IP- and crypto-tyranny have entered into an unholy
.cx | alliance...ideas have nothing to lose but their chains. Unite
| against "intellectual property" and anti-privacy regimes!
-------------------------------------------------------------------------

David LeBlanc

unread,
Jun 28, 2002, 3:23:47 PM6/28/02
to
<snip>

> PS - what's the antonym of meta?

Mundane I think.

This might also shed some light (from dictionary.com):
meta

<philosophy> /me't*/ or /may't*/ or (Commonwealth) /mee't*/ A prefix meaning
one level of description higher. If X is some concept then meta-X is data
about, or processes operating on, X.

For example, a metasyntax is syntax for specifying syntax, metalanguage is a
language used to discuss language, meta-data is data about data, and
meta-reasoning is reasoning about reasoning.

Dave LeBlanc
Seattle, WA USA

Aldo Cortesi

unread,
Jun 29, 2002, 12:36:54 AM6/29/02
to
Thus spake Hans Nowak (wu...@earthlink.net):

> Aldo Cortesi wrote:
>
> >>PS - what's the antonym of meta?
> >
> >Hmmm... the meta- in "metaclass" or "metaphysics" really
> >has no relation to the original Greek, which means
> >"with", or "among" (as in "metatarsus" - the bones in the
> >foot which lie next to the tarsus). So whatever antonym
> >you come up with can't have anything to do with the
> >original etymology of the prefix... Erm... How about
> >mundane? Or actual? Intrinsic? Essential? Or even "per
> >se"....?
>
> In ancient Greek, it also meant "over", like Latin
> "trans"... e.g. "over the river". I think the "meta" in
> "metaclass", "metadata" etc is related to this meaning.


Hi Hans,


Are you sure about that?


In actual fact, the interesting thing about the meta- prefix
as used in modern English, is that it is entirely based on a
misapprehension of the Ancient Greek - indeed, on the exact
same error you made above... If you have a copy of the OED
handy, look up the etymology of "metaphysics" (which is the
precursor to all of the other common meta- usages in
English). My copy (Second Edition, 1989) says that the term
derives from an Aristotelean phrase "ta meta ta physika"
(excuse the clumsy transliteration), which means "that which
comes after the Physics". It then goes on to note that:


"[This term] came to be misinterpreted as meaning
'the science of things transcending what is physical
or natural'. This misinterpretation is found, though
rarely, in Greek writers, notwithstanding the fact
that "meta" does not admit of any such sense as
'beyond' or 'transcending'. In scholastic Latin
writers the error was general (being helped,
perhaps, by the known equivalence of the prefixes
meta- and trans- in various compounds); and in
English its influence is seen in the custom,
frequent down to the 17th c., of explaining
metaphysical by words like 'supernatural',
'transnatural', etc."

Greg Ewing

unread,
Jul 3, 2002, 1:14:07 AM7/3/02
to mp...@cosc.canterbury.ac.nz
> "Ian McMeans" <imcm...@home.com> wrote:
>>Can someone give an example of a problem that is solved with metaclasses,


I came up with an idea today for making practical use of the
funky ability in 2.2 for a "class" statement to create
something other than a class.

Here's an example of how it's used:

>>> from singleton import singleton

# First let's define an ordinary class:

>>> class C:
... def spam(self):
... print "spam:", self
...

# Now here's a singleton which inherits from it:

>>> class s(singleton, C):
... def grail(self):
... print "grail:", self
...

# At this point, s is *not* a class, it's an
# *instance* of an anonymous class which inherits
# from C.

>>> s
<singleton.s instance at 0x1dbf60>
>>> s.spam()
spam: <singleton.s instance at 0x1dbf60>
>>> s.grail()
grail: <singleton.s instance at 0x1dbf60>

# (There's a slight problem with the naming of the
# singleton's class at the moment -- it should really
# be called "__main__.s", not "singleton.s"! Suggestions
# on how to fix this are welcome.)


#-----------------------------------------------------
#
# singleton.py
#
#-----------------------------------------------------

import types

class SingletonMaker(object):
pass

def __new__(self, name = None, bases = None, dict = None):
if not name: # for bootstrapping
return object.__new__(self)
bases = bases[1:] # discard bases[0] == singleton
if bases:
metaclass = type(bases[0])
else:
metaclass = types.ClassType
return metaclass(name, bases, dict)()

singleton = SingletonMaker()

#-----------------------------------------------------
#
# End of singleton.py
#
#-----------------------------------------------------

--
Greg Ewing, Computer Science Dept,
University of Canterbury,
Christchurch, New Zealand
http://www.cosc.canterbury.ac.nz/~greg

Reply all
Reply to author
Forward
0 new messages