Mastering Objectoriented Python
上QQ阅读APP看书,第一时间看更新

The __new__() method and metaclasses

The other use case for the __new__() method as a part of a metaclass is to control how a class definition is built. This is distinct from how __new__() controls building an immutable object, shown previously.

A metaclass builds a class. Once a class object has been built, the class object is used to build instances. The metaclass of all class definitions is type. The type() function is used to create class objects.

Additionally, the type() function can be used as a function to reveal the class of an object.

The following is a silly example of building a new, nearly useless class directly with type() as a constructor:

Useless= type("Useless",(),{})

Once we've created this class, we can create objects of this Useless class. However, they won't do much because they have no methods or attributes.

We can use this newly-minted Useless class to create objects, for what little it's worth. The following is an example:

>>> Useless()
<__main__.Useless object at 0x101001910>
>>> u=_
>>> u.attr= 1    
>>> dir(u)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'attr']

We can add attributes to the objects of this class. It does work, minimally, as an object.

This is almost equivalent to using types.SimpleNamespace or defining a class as follows:

class Useless:
    pass

This brings up the important question: why would we mess with the way classes are defined in the first place?

The answer is that some of the default features of a class aren't perfectly applicable to some edge cases. We'll talk about four situations where we might want to introduce a metaclass:

  • We can use a metaclass to preserve some information about the source text for a class. A class built by the built-in type uses dict to store the various methods and class-level attributes. As dict is inherently unordered, the attributes and methods appear in no particular order. It's extremely unlikely that they would appear in the order originally presented in the source. We'll show this in our first example.
  • Metaclasses are used to create Abstract Base Classes (ABC) that we'll look at from Chapters 4 through 7. An ABC relies on a metaclass __new__() method to confirm that the concrete subclass is complete. We'll introduce this in Chapter 4, The ABCs of Consistent Design.
  • Metaclasses can be used to simplify some aspects of object serialization. We'll look at this in Chapter 9, Serializing and Saving – JSON, YAML, Pickle, CSV, and XML.
  • As a final and rather easy example, we'll look at a self-reference within a class. We'll design classes that reference a master class. This isn't a superclass-subclass relationship. It's a bunch of subclasses that are peer subclasses but have an association with one of its peer group as being the master. To be consistent with its peers, the master needs a reference to itself, something that's impossible without a metaclass. This will be our second example.