Chapter 3. Syntax Best Practices – above the Class Level

We will now focus on syntax best practices for classes. It is not intended to cover design patterns here, as they will be discussed in Chapter 14, Useful Design Patterns. This chapter gives an overview of the advanced Python syntax to manipulate and enhance the class code.

Object model evolved greatly during history of Python 2. For a long time we lived in a world where two implementations of the object-oriented programming paradigm coexisted in the same language. These two models were simply referred to as old-style and new-style classes. Python 3 ended this dichotomy and only model known as new-style classes is available to the developers. Anyway, it is still important to know how both of them worked in Python 2 because it will help you in porting old code and writing backwards compatible applications. Knowing how the object model changed will also help you in understanding why it is designed that way right now. This is the reason why the following chapter will have a relatively large number of notes about old Python 2 features despite this book targets the latest Python 3 releases.

The following topics will be discussed in this chapter:

  • Subclassing built-in types
  • Accessing methods from super classes
  • Using properties and slots
  • Metaprogramming

Subclassing built-in types

Subclassing built-in types in Python is pretty straightforward. A built-in type called object is a common ancestor for all built-in types as well as all user-defined classes that have no explicit parent class specified. Thanks to this, every time a class that behaves almost like one of the built-in types needs to be implemented, the best practice is to subtype it.

Now, we will show you the code for a class called distinctdict, which uses this technique. It is a subclass of the usual Python dict type. This new class behaves in most ways like an ordinary Python dict. But instead of allowing multiple keys with the same value, when someone tries to add a new entry with an identical value, it raises a ValueError subclass with a help message:

class DistinctError(ValueError):
    """Raised when duplicate value is added to a distinctdict."""

class distinctdict(dict):
    """Dictionary that does not accept duplicate values."""
    def __setitem__(self, key, value):
        if value in self.values():
            if (
                (key in self and self[key] != value) or
                key not in self
            ):
                raise DistinctError(
                    "This value already exists for different key"
                )

        super().__setitem__(key, value)

The following is an example of using distictdict in interactive session:

>>> my = distinctdict()
>>> my['key'] = 'value'
>>> my['other_key'] = 'value'
Traceback (most recent call last):
  File "<input>", line 1, in <module>
  File "<input>", line 10, in __setitem__
DistinctError: This value already exists for different key
>>> my['other_key'] = 'value2'
>>> my
{'key': 'value', 'other_key': 'value2'}

If you take a look at your existing code, you may find a lot of classes that partially implement the built-in types, and could be faster and cleaner as subtypes. The list type, for instance, manages the sequences and could be used every time a class works internally with a sequence:

class Folder(list):
    def __init__(self, name):
        self.name = name

    def dir(self, nesting=0):
        offset = "  " * nesting
        print('%s%s/' % (offset, self.name))

        for element in self:
            if hasattr(element, 'dir'):
                element.dir(nesting + 1)
            else:
                print("%s  %s" % (offset, element))

Here is an example usage in interactive session:

>>> tree = Folder('project')
>>> tree.append('README.md')
>>> tree.dir()
project/
  README.md
>>> src = Folder('src')
>>> src.append('script.py')
>>> tree.append(src)
>>> tree.dir()
project/
  README.md
  src/
    script.py

Tip

Built-in types cover most use cases

When you are about to create a new class that acts like a sequence or a mapping, think about its features and look over the existing built-in types. The collections module extends basic built-in types with many useful containers. You will end up using one of them most of the time.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset