Welcome, guest | Sign In | My Account | Store | Cart

Since a class object is created after the body is executed, it can't be available to the class body. Even the name is unavailable, at least by default. However, you can use the __prepare__() method in a metaclass to stick it in there. This recipe is a simple demonstration of how.

Python, 9 lines
1
2
3
4
5
6
7
8
9
class Meta(type):
    @classmethod
    def __prepare__(meta, name, bases, **kwargs):
        return {"__name__": name}

class X(metaclass=Meta):
    print(locals())

#{'__name__': 'X', '__module__': 'X', '__locals__': {...}}

When a class definition is evaluated, the following happens (roughly):

  1. the base classes and metaclass are evaluated[1],
  2. <metaclass>.__prepare__(name, bases, **kwargs) is called to generate a mutable mapping (call it <namespace>),
  3. if a docstring is in the class definition, <namespace>["__doc__"] is set to it,
  4. the class body is exec'ed separately, using <namespace> as the frame locals,
  5. <metaclass>(name, bases, <namespace>) is called[2],
    1. <metaclass>.__new__(<metaclass>, name, bases, <namespace>) is called and returns a new[3] object (and the contents of <namespace> are copied into <class>.__dict__),
    2. if the new object is an instance of <metaclass>, <metaclass>.__init__(cls, name, bases, <namespace>) is called.
    3. the new object is returned,
  6. the object from (5) is bound in the namespace where the class was defined.

This demonstrates that when the class body is exec'ed the class object does not exist yet (and so cannot be referenced there). The class's name, which _is_ around, is also not available. This recipe shows how to make it available.

[1] the default metaclass is type.

[2] this is the same way you instantiate any class (i.e. <class>.__call__(...) is called). The difference is that in this case <class> is a metaclass (subclass of type) and the instance is a class object.

[3] it actually isn't necessarily a new object. It could be any object. However, by default (and normally) it will be a new instance of <metaclass>.

4 comments

Stephen Chappell 12 years, 8 months ago  # | flag

If you just want a __name__ attribute to be available on class instances, you can also inherit from the following class:

class _NamedInstance:

    "_NamedInstance(*args, **kwargs) -> _NamedInstance"

    __slots__ = ()

    def __init__(self, *args, **kwargs):
        "Raises an error since this is an abstract class."
        raise NotImplementedError('This is an abstract class!')

    @property
    def __name__(self):
        "Provides a way for callable instances to be identified."
        return self.__class__.__name__
Eric Snow (author) 12 years, 8 months ago  # | flag

That's a helpful class, but unrelated to the recipe. I'll update the discussion section to clarify.

tokenmacguy 12 years, 8 months ago  # | flag

using metaclass in that way applies only to python 3. In python 2, the metaclass only gets access to the body after the body has been evaluated; And requiring a specific metaclass for this is probably inconvenient, since you might really want to use another metaclass instead.

The alternative is to fetch the class name yourself:

>>> import traceback
>>> def get_input(class_name):
...     return class_name.encode('rot13')
... 
>>> class foo(object):
...      _name = traceback.extract_stack()[-1][2]
...     input = get_input(_name)
... 
>>> foo.input
'sbb'
Eric Snow (author) 12 years, 8 months ago  # | flag

Very true. In Python 2 have less options. I have used something similar, though with the inspect module: inspect.currentframe().f_code.co_name.

For this recipe my motivation was to avoid frame-iness, because that route is (technically) CPython specific; the language reference says nothing about executing a class body via a code object. However, I gladly admit that is a weak objection since most other implementations work pretty hard to be compatible with CPython when possible. Also, this recipe relies on Python 3, which is still mostly just CPython anyway. :)

As to metaclasses, you're right that it would be less convenient to use this one exclusively. If you want to use another metaclass, you write one that subclasses the one you want to use. Then you throw the __prepare__ on that subclass.

Created by Eric Snow on Sun, 31 Jul 2011 (MIT)
Python recipes (4591)
Eric Snow's recipes (39)

Required Modules

  • (none specified)

Other Information and Tasks