Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python metaclass and the object base class

After reading the excellent SO post, I tried crafting a module level metaclass:

def metaclass(future_class_name, future_class_parents, future_class_attrs):
    print "module.__metaclass__"
    future_class_attrs["bar"]="bar"
    return type(future_class_name, future_class_parents, future_class_attrs)

__metaclass__=metaclass


class Foo(object):

    def __init__(self):
        print 'Foo.__init__'

f=Foo()

This doesn't work (i.e. "module.metaclass" doesn't get printed) unless I remove the object base class of Foo. How come?

NOTE: I am using Python 2.6.1.

like image 543
jldupont Avatar asked Oct 31 '11 18:10

jldupont


2 Answers

Inheriting from object automatically brings the type metaclass along with it. This overrides your module level __metaclass__ specification.

If the metaclass is specified at the class level, then object won't override it:

def metaclass(future_class_name, future_class_parents, future_class_attrs):
    print "module.__metaclass__"
    future_class_attrs["bar"]="bar"
    return type(future_class_name, future_class_parents, future_class_attrs)

class Foo(object):
    __metaclass__ = metaclass

    def __init__(self):
        print 'Foo.__init__'

f=Foo()

See http://docs.python.org/reference/datamodel.html?highlight=metaclass#customizing-class-creation

like image 195
Raymond Hettinger Avatar answered Sep 23 '22 22:09

Raymond Hettinger


The specification specifies the order in which Python will look for a metaclass:

The appropriate metaclass is determined by the following precedence rules:

  • If dict['__metaclass__'] exists, it is used.
  • Otherwise, if there is at least one base class, its metaclass is used (this looks for a __class__ attribute first and if not found, uses its type).
  • Otherwise, if a global variable named __metaclass__ exists, it is used.
  • Otherwise, the old-style, classic metaclass (types.ClassType) is used.

You will see from the above that having a base class at all (whatever the base class is, even if it does not ultimately inherit from object) pre-empts the module-level __metaclass__.

like image 34
Marcin Avatar answered Sep 24 '22 22:09

Marcin