Next / Previous / Contents / Shipman's homepage

26.2. Life cycle of a new-style class

Most of the features of new-style classes are the same as for old-syle classes. This section will discuss only the differences. We won't cover a few of the more obscure advanced features here; for information on such topics as descriptors and metaclasses, see the “Data model” section of the Python Reference Manual.

The declaration of a new-style class looks the same as for an old-style class, with one constraint: the class must inherit from the universal base class named object, or from one or more other new-style classes.

26.2.1. __new__(): New instance creation

New-style classes have a new special method name, __new__(), that is called on instantiation before the constructor. It handles the creation of a new instance.

  • The .__new__() method is called when an instance is created.

  • Method .__new__() is always a static method (see Section 26.4, “Static methods”), even if you do not specifically make it a static method.

  • A constructor call for some class C has this general form:

    C(*p, **k)
    

    That is, it can have any number of positional arguments and any number of keyword arguments.

    The equivalent call to the .__new__() method will look like this:

        def __new__(cls, *p, **k):
            ...              
    

    The first argument cls must be the class being created.

  • The .__new__() method must call the parent class's .__new__() method to create the instance.

    For example, if your class inherits directly from object, you must call:

            object.__new__(cls)
    

    The value returned by that call is the new instance.

  • In most cases, the .__new__() method will return a new instance of cls, and that class's .__init__() will then be called with that instance as its self argument, and the positional and keyword arguments p and k will be passed to that constructor as well.

    >>> class Test(object):
    ...     def __new__(cls, *p, **k):
    ...         inst = object.__new__(cls)
    ...         return inst
    ...     def __init__(self, *p, **k):
    ...         print "p={0} k={1}".format(p, k)
    ... 
    >>> t=Test('egg', 'kale', sauce='Bearnaise')
    p=('egg', 'kale') k={'sauce': 'Bearnaise'}
    

    However, if the .__new__() method does not return an instance of class cls, the constructor method .__init__() will not be called. This allows the class more control over how new instances are created and initialized. You can return an instance of an entirely different class if you like.

26.2.2. Attribute access control in new-style classes

New-style classes give you more ways to control what happens when an instance's attribute is accessed.

Here is the general procedure for access to attribute a of instance i, where C is the class of i.

  1. If the instance has a __getattribute__() special method (see Section 26.3.15, “__getattribute__(): Intercept all attribute references”), execute that method, which must either return the attribute value or raise AttributeError.

  2. If the instance has a __slots__ attribute (see Section 26.2.4, “Conserving memory with __slots__), return the value of the slot with name a. If a does not match any of the slot names, or if the named slot has never been set to a value, raise AttributeError.

  3. If a is a key in i.__dict__, return the corresponding value.

  4. Search for attribute a in class C. If that fails, search all the parent classes of C all the way back to object.

  5. If all searches in the preceding step failed and the instance has a .__getattr__() special method, call that method. See Section 26.3.14, “__getattr__(): Handle a reference to an unknown attribute”; please note the differences from Section 26.3.15, “__getattribute__(): Intercept all attribute references”.

  6. If all the above steps fail to produce a value, raise AttributeError.

26.2.3. Properties in new-style classes: Fine-grained attribute access control

The outline of attribute access in Section 26.2.2, “Attribute access control in new-style classes” is slightly oversimplified in one respect. Any of the attribute search steps in this procedure may produce a property rather than the actual attribute value.

A property is a special object that is produced by the property() function. For a discussion of the three types of attribute access (get, set, and delete), the protocols for the accessor functions, and examples, see Section 21.15, “property(): Create an access-controlled attribute”.

26.2.4. Conserving memory with __slots__

Normally, you can add new attributes to an instance's namespace with any name you want. The instance's .__dict__ attribute is effectively a dictionary, and you can add any number of names to it.

However, in a new-style class, you may specify a given, limited set of attribute names that are allowed in instances of the class. There are two reasons why you might want to do this:

  • If your program is going to create large numbers of instances of a class, to the point where you may run out of memory, you can save some storage within each instance by sacrificing the ability to add arbitrary attribute names.

  • If you limit the set of permissible attribute names, Python will detect any reference to a name not in the permissible set, and raise an AttributeError exception. This may help you catch certain programming errors.

To limit the set of attribute names in a new-style class, assign to a class variable named __slots__ a tuple containing the allowable names, like this:

    __slots__ = (n1, n2, ...)

Here's a small example. Suppose you want instances of class Point to contain nothing more than two attributes named .x and .y:

>>> class Point(object):
...     __slots__ = ('x', 'y')
...     def __init__(self, abscissa, ordinate):
...         self.x, self.y = abscissa, ordinate
... 
>>> x2=Point(3, 7)
>>> x2.x
3
>>> x2.y
7
>>> x2.temperature = 98.6
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'Point' object has no attribute 'temperature'

When you declare a __slots__ attribute in a new-style class, instances will not have a .__dict__ attribute.