Object-oriented programming
Python is sometimes described as an object-oriented programming language. Thiscan be somewhat misleading and needs to be clarified.
In Python, everything is an object, and can be handled as such. This is what ismeant when we say, for example, that functions are first-class objects.Functions, classes, strings, and even types are objects in Python: like anyobject, they have a type, they can be passed as function arguments, and theymay have methods and properties. In this understanding, Python is anobject-oriented language.
However, unlike Java, Python does not impose object-oriented programming as themain programming paradigm. It is perfectly viable for a Python project to notbe object-oriented, i.e. to use no or very few class definitions, classinheritance, or any other mechanisms that are specific to object-orientedprogramming.
Moreover, as seen in the modules section, the way Python handles modules andnamespaces gives the developer a natural way to ensure theencapsulation and separation of abstraction layers, both being the most commonreasons to use object-orientation. Therefore, Python programmers have morelatitude to not use object-orientation, when it is not required by the businessmodel.
There are some reasons to avoid unnecessary object-orientation. Definingcustom classes is useful when we want to glue together some state and somefunctionality. The problem, as pointed out by the discussions about functionalprogramming, comes from the “state” part of the equation.
In some architectures, typically web applications, multiple instances of Pythonprocesses are spawned to respond to external requests that can happen at thesame time. In this case, holding some state in instantiated objects, whichmeans keeping some static information about the world, is prone to concurrencyproblems or race conditions. Sometimes, between the initialization of the stateof an object (usually done with the init()
method) and the actual useof the object state through one of its methods, the world may have changed, andthe retained state may be outdated. For example, a request may load an item inmemory and mark it as read by a user. If another request requires the deletionof this item at the same time, it may happen that the deletion actually occursafter the first process loaded the item, and then we have to mark as read adeleted object.
This and other issues led to the idea that using stateless functions is abetter programming paradigm.
Another way to say the same thing is to suggest using functions and procedureswith as few implicit contexts and side-effects as possible. A function’simplicit context is made up of any of the global variables or items in thepersistence layer that are accessed from within the function. Side-effects arethe changes that a function makes to its implicit context. If a function savesor deletes data in a global variable or in the persistence layer, it is said tohave a side-effect.
Carefully isolating functions with context and side-effects from functions withlogic (called pure functions) allow the following benefits:
- Pure functions are deterministic: given a fixed input,the output will always be the same.
- Pure functions are much easier to change or replace if they need tobe refactored or optimized.
- Pure functions are easier to test with unit tests: There is lessneed for complex context setup and data cleaning afterwards.
- Pure functions are easier to manipulate, decorate, and pass around.
In summary, pure functions are more efficient building blocks than classesand objects for some architectures because they have no context or side-effects.
Obviously, object-orientation is useful and even necessary in many cases, forexample when developing graphical desktop applications or games, where thethings that are manipulated (windows, buttons, avatars, vehicles) have arelatively long life of their own in the computer’s memory.