Understanding how Python classes work

; Date: September 26, 2019

Tags: Python

Python classes allow programmers to define new data types to use in their programs. They act like a blueprint with which a program creates objects. Objects can contain both methods and attributes, giving us a useful higher-level abstraction over the built-in Python data types.

Classes in Python support typical object-oriented programming features like inheritance, deriving a class definition from one or more base classes, and overriding methods and attributes inherited from a base class. But unlike other languages, the implementation fits the dynamic nature of Python programming.

What is a Python class, and how do we use them?

A Python class definition is a series of statements that define the Python class.

class ClassName:
    Statement 1
    Statement 2 ...
    Statement N

The statements are usually function definitions but can be variable assignments. It is helpful to think of a Python class as a blueprint for constructing instances of an object.

To use a class in python code, we must create an instance of the class like so:

X = ClassName()

While creating a new instance the special function __init__ is called. It receives an argument list containing the parameters passed to the constructor.

class Dog(Animal):
    kind = 'canine'

    def __init__(self, name)
        self.name = name

In this Python class example, we have a class Dog that inherits from Animal.

The first parameter of every method defined in a python class is a reference to the object. In Python the convention is to name this variable self, and it has the same purpose as the this variable used in other languages.

Python namespaces and scopes

Before we get too far into Class definitions in Python, we must first discuss Python Namespaces.

A namespace is a mapping from names to objects, which probably sounds hopelessly obtuse. The object is the actual thing, the chunk of data, while the name is the reference to that thing. Consider:

mydog = Dog('fido')

The variable name mydog is recorded as a name whereas the chunk of data that is the instance of the Dog class is the object.

Some examples of Namespaces are:

  • The set of built-in names
  • The global names in a module
  • The local names in a function execution

The same name can be defined in two or more namespaces. This means each namespace is what it sounds like - a space in which to store names.

A scope is a textual region in a Python program where a given namespace is directly accessible. What that means is that in a given scope, an unqualified name is searched for starting in the given namespace.

Python classes and namespaces

When a Python class is defined, a new namespace is created. That namespace is used as the local scope, meaning that variable assignments and function definitions create names in that namespace.

Python class objects

When the Python class definition ends, a class object is created. The previous local scope is reinstated, and the class object is registered in that namespace using the class name.

Class objects and instance variables

When we create an instance of a class Python hammers out a chunk of data using the blueprint in the class object. The data stored for an instance is, of course, not stored in the class object. Instead, we have two objects:

  • The class object stores the attributes and methods common to all instances of a class
  • The instance stores the data unique to that instance

In the example above the kind attribute is stored in the class object, and is shared by all Dog instances. The name attribute is stored in the instance object and is unique to that instance.

Python does not have private attributes - avoiding name conflicts

In many programming languages, the Class implementation supports hidden data or hidden methods. These hidden things are, as expected, cannot be used by code outside the object. This creates some safety since a programmer can use this hidden data or methods with the assurance that no other code can mess with those things.

One issue is whether there are conflicting names when deriving a class from another class.

Since Python does not support any kind of data hiding it is necessary to follow a convention to avoid conflicts.

The convention most Python programmers follow is that a name prefixed with an underscore (e.g. _foo) is to be treated as a non-public part of the API of the class. Users of that class are to expect _foo attributes can change at any time, and should therefore not be used.

The Python language takes this a step further. For any name prefixed by two underscores (e.g. __private), it is textually replaced with the name _classname__private.

Python's this is self

In most languages, a variable named this is always available and references the instance. So (this.name) this.name would reference the name attribute in the instance.

In Python, there is no such predefined object. However, every method defined in a class receives as its first argument a reference to the instance object. By convention Python, programmers call this parameter self.

class Bag:
    def __init__(self):
        self.data = []

    def add(self, x):
        self.data.append(x)

In this case, data is an attribute containing a Python array. The add method adds entries to that array. In both cases, the data array is stored in self making it an instance variable.

About the Author(s)

David Herron : David Herron is a writer and software engineer focusing on the wise use of technology. He is especially interested in clean energy technologies like solar power, wind power, and electric cars. David worked for nearly 30 years in Silicon Valley on software ranging from electronic mail systems, to video streaming, to the Java programming language, and has published several books on Node.js programming and electric vehicles.