Python OOP
- Summary
-
Discussion
- What are some essential terms in Python OOP?
- How do I define classes and instantiate objects in Python?
- What's the lifecycle of a Python object?
- What's the naming convention for Python class definitions?
- What are nested functions and nested classes in Python?
- What are magic methods in Python OOP?
- Could you compare a Python object's static methods and class methods?
- How do I define private or protected class attributes in Python?
- What's Method Resolution Order (MRO) in Python?
- What is cooperative multiple inheritance in Python?
- Milestones
- References
- Further Reading
- Article Stats
- Cite As
Python is an Object-Oriented Programming (OOP) language. Many OOP concepts are available in Python including classes, objects, (multiple) inheritance, polymorphism, and encapsulation. Functions are also objects in Python. Data hiding in terms of access modifiers is not strict: class and instance attributes are public and private membership is not strictly enforced.
Apart from instance methods, class methods and static methods are possible. Abstract classes are possible and they're equivalent to interfaces in other languages. Implementations of these typically employ decorators. Nested functions and nested classes are supported. What's called operator overloading in OOP is implemented using magic methods.
Since its beginning, Python support for OOP has continued to evolve. The aim has been to balance a simple intuitive syntax with features and performance desired by developers.
Discussion
-
What are some essential terms in Python OOP? Here are a few essentials terms in Python OOP:
- Class: Bundles data and functionality together. Acts as a template or factory for creating new instances.
- Instance: Created from a class. We say an instance's type is the class from which it's created. Eg.
point
is an instance ofPoint
given thatpoint = Point()
. Many instances of a class can be created. - Attribute: Relevant to both classes and instances. Accessed using the dot notation
obj.name
. Attributes are either data attributes (eg.point.x
) or methods (eg.point.move_left(5)
). - Method: Belongs to an object. It's called to execute the code it contains. Note that
point.move_left
returns a method object whereasPoint.move_left
returns a function object.
Instances are also called objects. Strictly speaking, we distinguish two types of objects:
- Class Object: Nothing more than a wrapper around the namespace created by the class definition. There are two things we can do with a class object: access its attributes or instantiate a new instance.
- Instance Object: This is an instance created from a class. The only thing we can do with an instance object is to access its attributes.
-
How do I define classes and instantiate objects in Python? A class of name 'Foo' can be defined with
class Foo: pass
, whereclass
is a Python keyword. An instance or object of this class can be created withfoo = Foo()
, wherefoo
is an instance of theFoo
class.Every Python class inherits from
object
. It's the base for all classes. It doesn't accept any arguments. It's also featureless, that is, we can't assign attributes to instances ofobject
.A class
Rectangle
that inherits from another classShape
can be defined asdef Rectangle(Shape): pass
. Multi-level inheritance is also supported, such as,def Square(Rectangle): pass
. Multiple inheritance, which is the ability of a class to inherit from multiple class, is also supported. For example, we can design aNovel
class that inherits from two classesBook
andFiction
:def Novel(Book, Fiction)
. The order of the base classes is significant.Unlike
foo
in the earlier example we can create classes that are initialized with arguments. For example,corner = Point(1.3, -2.4)
creates aPoint
object namedcorner
that's initialized with (x=1.3, y=-2.4) coordinates. -
What's the lifecycle of a Python object? When instantiated from its class definition, a Python object is created (memory is allocated) and then initialized. The object is stored in memory and accessible by the program. When the object is no longer required, it's destroyed and its memory is reclaimed by the system.
The following class methods are relevant to a Python object's lifecycle:
__new__
: Called automatically to instantiate an object. A developer rarely needs to customize the default implementation of this method.__init__
: Called automatically after__new__
for initializing the object. Can accept arguments, such as,def __init__(self, x, y)
of aPoint
class to initialize it's attributes.__del__
: Called automatically when the object is destroyed. A programmer can explicitly destroy the object by calling thedel()
built-in function or leave it to Python's garbage collector to do it. Either way,__del__
is called. If the interpreter exits, there's no guarantee that__del__
is called for existing instances.
-
What's the naming convention for Python class definitions? Python PEP 8 describes the naming convention and styling to be followed by developers. The following are relevant to class definitions:
- Surround top-level function and class definitions with two blank lines.
- Method definitions inside a class are surrounded by a single blank line.
- Write docstrings for all public modules, functions, classes, and methods.
single_trailing_underscore_
argument name: avoids conflict with Python keywords. Eg.register_student(self, class_, section)
.__double_leading_underscore
attribute name: kind of private and avoids conflicts with subclasses. Invokes Python's name mangling rules.__double_leading_and_trailing_underscore__
name: convention is reserved for magic methods or other special uses. Don't name your attributes this way.- Class names should normally use the CapWords convention.
- Class naming applies to Exceptions since they're also classes. Use the suffix "Error" in exception names.
- Always use
self
for the first argument to instance methods. - Always use
cls
for the first argument to class methods. - For variable annotations, use single space after colon and no space before colon. Eg.
coords: Tuple[int, int]
.
-
What are nested functions and nested classes in Python? We typically define functions at module level. But it's possible to define a function within another function. For example,
def g(): pass
can be defined within the body of another functionf()
. Functiong
is therefore a nested function. It can't be invoked from outside its container function. It can be invoked only byf
. Nested functions are therefore useful to implement encapsulation. For example, functionget_projection
is defined within functionget_area
but how the projection is calculated is particular to the area calculation. Therefore, we don't want others to use the projection.It's possible for a nested function to be returned from its container function. In this case, the returned function can be called from outside. In fact, partial functions and closures are implemented this way.
Like nested functions, for better encapsulation but at the expense of code reuse, a class can be defined within another class. The former is therefore a nested class. The outer class may instantiate objects of its inner classes. An inner class can be instantiated without instantiating its outer class.
-
What are magic methods in Python OOP? Magic methods, also called dunder methods, are special methods that are typically not explicitly called in application code. The Python interpreter calls them depending on the operation currently requested by the program.
For example,
__new__
and__init__
are magic methods called respectively for creating and initializing an instance from its class. When we compare two objects (a == b
), magic method__eq__
is called on one object with the other object passed in as an argument. When we calldel()
method on an attribute, magic method__delattr__
is called. To add two objects (a + b
), magic method__add__
is called.We may say that magic methods dictate what happens under the hood. They're useful when defining custom classes and we wish to override default behaviour. Custom classes can emulate the behaviour of built-in types. For example, we may define a custom string class derived from
str
built-in class. By overriding__eq__
, we can enable case-insensitive comparison of strings.All magic methods are named with leading and trailing double underscores. Other methods should not be named this way.
-
Could you compare a Python object's static methods and class methods? When an instance is created, its methods are called instance methods. An instance method takes in
self
as the first argument. This points to that particular instance. Via this argument, the method can access all other attributes of the instance.There are some methods that don't need the instance. They operate purely on the input arguments and output a result. They don't read or change state of the instance. These are called static methods and are defined via the
@staticmethod
decorator. In one use case, static methods could be helper functions that assist instance methods in their computations. They could also be called from outside the class.While each instance has its own state, we might need all instances share some attributes. For example, multiple instances of a
Logger
class log to the same file. Filename could be a shared state. Shared attributes are defined as class attributes rather than instance attributes. Methods that operate only on class attributes are called class methods and are defined via the@classmethod
operator. A class method takes incls
as the first argument. -
How do I define private or protected class attributes in Python? Class and instance attributes are public. However, by naming convention, some developers treat attributes that start with a single underscore as protected. That is, only subclasses can access base class attributes. Others use single underscore to imply non-public access. The language itself offers no protection against public access.
Another convention is to name attributes starting with double underscores. Such attributes are private to the class. In reality, Python does name mangling. It renames the attribute so that it becomes inaccessible from outside the class under its original name. However, within the class, the original name is valid. As an example, attribute
__update
in classMapping
will become_Mapping__update
.Even with name mangling, a persistent developer can still access the attribute from outside the class by using the mangled name. In conclusion, Python's support for private attributes is not strict. Name mangling does solve the problem of conflicting names due to subclass attributes.
-
What's Method Resolution Order (MRO) in Python? Consider
Novel
class that inherits from two classesBook
andFiction
:def Novel(Book, Fiction)
. If bothBook
andFiction
implement the methodget_title()
butNovel
doesn't override this method, MRO determines which one gets called. Python adopts C3 MRO algorithm. Essentially, it says that method calls are resolved depth first but where a common ancestor is involved, it is called as late as possible.In the above
Novel
example,Book.get_title()
is called. But let's consider a more complex example shown in the figure. Method call on aRomantic19Novel
instance resolves toRomanticNovel
and thenNovel
(depth first rule). However,Novel
is also a base class ofNineteenthCenturyNovel
. Hence,Novel
is deferred until later. Thus, the MRO is (Romantic19Novel
,RomanticNovel
,NineteenthCenturyNovel
,Novel
,Book
,object
).Given any class, we can introspect the MRO by calling the
mro()
method, or accessing the__mro__
attribute. Eg.Romantic19Novel.mro()
. Note that these can't be called on instances.While C3 MRO algorithm works in most cases, sometimes it's unable to resolve. In such a case, Python interpreter will raise an exception and fail to even define the class.
-
What is cooperative multiple inheritance in Python? It's possible to implement our methods in such a way that MRO becomes easier. Even if the order of base classes were changed in the case of multiple inheritance, we would still achieve consistent runtime behaviour. Cooperative multiple inheritance also makes it easier to inject new classes into the inheritance hierarchy.
Implementing methods cooperatively means fulfilling the following three conditions:
- Every implementation of the method need to call
super()
. - The method being called by
super()
needs to exist. - The caller and called base class methods have matching argument signatures. In particular, they accept
**kwargs
keyword arguments dictionary as the last argument. A method will strip any keyword argument it requires and pass on the rest to its immediate base class via**kwargs
.
- Every implementation of the method need to call
Milestones
1994
2001
Python 2.2 is released. Via PEP 252 and 253, this release unifies built-in types and user-defined classes. We can now subclass built-in types. We can define static methods and class methods. We also have properties to automatically call methods when accessing or setting instance attributes. The introspection API is now unified across types and classes. Despite being a big change, this is done with backwards compatibility. Python 2.1 and earlier are now known as "classic Python" and classes without built-in types in their bases are called "classic classes".
2003
Python 2.3 is released. This version changes the Method Resolution Order (MRO). Specifically, it adopts C3 MRO that was invented back in 1996 for the Dylan programming language. Even with C3, there are some complex inheritance hierarchies where its impossible to resolve the order and Python raises an exception.
2004
Python 2.4 is released. Via PEP 318, this release includes decorators for functions and methods. Wrappers staticmethod()
and classmethod()
available since Python 2.2 are now available as decorators @staticmethod
and @classmethod
. Class decorators are introduced in Python 3.0 (Dec 2008) via PEP 3129.
2007
PEP 3119 is created to introduce Abstract Base Class (ABC) into the language. ABCs are conceptually close to what other languages call interfaces. In fact, back in 2001, PEP 245 considered introducing interfaces but the proposal was rejected. In December 2008, ABCs become part of Python 3.0 release and also backported to Python 2.6.
2008
Python 3.0 is released. Via PEP 3135, this release allows super()
to be called without arguments to invoke methods. For example, the syntax super(Foo, self).foo(1, 2)
is simplified to super().foo(1, 2)
. The word super
itself is not reserved and is interpreted specially only within the context of a method definition. This release also gets rid of unbound methods. Accessing a method as class attribute now results in a plain function object.
2012
Python 3.3 is released. Via PEP 3155, this release adds qualified name attribute __qualname__
to classes and functions. This enhances introspection for nested classes and functions. The qualified name also helps in Python3 since there's no im_class
attribute (unbound methods in Python2).
References
- Bagheri, Reza. 2020. "Closures and Decorators in Python." Towards Data Science, on Medium, February 5. Accessed 2021-11-26.
- Barrett, Kim, Bob Cassels, Paul Haahr, David A. Moon, Keith Playford, and P. Tucker Withington. 1996. "A Monotonic Superclass Linearization for Dylan." OOPSLA '96: Proceedings of the 11th ACM SIGPLAN conference on Object-oriented programming, systems, languages, and applications, pp. 69-82, October. doi: 10.1145/236337.236343. Accessed 2021-11-23.
- Brett, Matthew. 2021. "Functions are objects." Accessed 2021-11-23.
- Carattino, Aquiles. 2018. "A Primer on Classes in Python." Python for the Lab, May 22. Accessed 2021-11-21.
- Danjou, Julien. 2013. "The definitive guide on how to use static, class or abstract methods in Python." Blog, August 1. Accessed 2021-11-23.
- Francis, Howard. 2021. "Public and Private." Lesson, Object Oriented Programming, Real Python. Accessed 2021-11-21.
- GeeksforGeeks. 2016. "Python OOPs Concepts." GeeksforGeeks, May 16. Updated 2021-08-24. Accessed 2021-11-21.
- GeeksforGeeks. 2018. "Operator Overloading in Python." GeeksforGeeks, December 10. Updated 2021-05-26. Accessed 2021-11-21.
- Hettinger, Raymond. 2011. "Python’s super() considered super!" Blog, Deep Thoughts, May 26. Accessed 2021-11-24.
- Kaul, Sarath. 2020. "Public, Private, and Protected — Access Modifiers in Python." Better Programming, on Medium, January 8. Accessed 2021-11-21.
- Klein, Bernd. 2021. "Object Oriented Programming." Python Course, November 2. Accessed 2021-11-21.
- Kuchling, A.M. 2002. "What’s New in Python 2.2." Release 2.2.2, October 14. Updated 2021-11-23. Accessed 2021-11-23.
- Malik, Farhad. 2020. "Advanced Python: What Are Magic Methods?" FinTecExplained, on Medium, May 16. Accessed 2021-11-21.
- Nair, Adarsh. 2021. "Python OOP Primer." Notebook, Jovian, February 17. Accessed 2021-11-21.
- Pelletier, Michel. 2001. "PEP 245 -- Python Interface Syntax." Python.org, January 11. Updated 2019-07-03. Accessed 2021-11-21.
- Pitrou, Antoine. 2011. "PEP 3155 -- Qualified name for classes and functions." Python.org, October 29. Updated 2021-02-09. Accessed 2021-11-21.
- Python Docs. 2009. "Built-in Functions." Python Documentation, v3.0.1. Accessed 2021-11-24.
- Python Docs. 2012. "What’s New in Python 3.3." Release 3.3, September 29. Updated 2021-11-23. Accessed 2021-11-23.
- Python Docs. 2021a. "Classes." Section 9, The Python Tutorial, v3.10.0, November 20. Accessed 2021-11-21.
- Python Docs. 2021b. "Data Model." Section 3, The Python Language Reference, v3.10.0, November 20. Accessed 2021-11-21.
- Shaik, Hafeezul Kareem. 2020. "Inner Classes in Python." DataCamp, January 3. Accessed 2021-11-23.
- Simionato, Michele. 2003. "The Python 2.3 Method Resolution Order." Releases, Python, July 29. Accessed 2021-11-21.
- Smith, Kevin D., Jim J. Jewett, Skip Montanaro, and Anthony Baxter. 2003. "PEP 318 -- Decorators for Functions and Methods." Python.org, June 5. Updated 2020-06-12. Accessed 2021-11-23.
- Spealman, Calvin, Tim Delaney, and Lie Ryan. 2007. "PEP 3135 -- New Super." Python.org, April 28. Updated 2021-09-17. Accessed 2021-11-21.
- Tayal, Rachit. 2019. "Python Magic Methods Explained." Python Features, on Medium, May 29. Accessed 2021-11-21.
- Thelin, Ryan. 2021. "Python Version History: How Python has changed over the years." Blog, Educative, January 13. Accessed 2021-11-21.
- UCF. 2017. "Multiple Inheritance and Multilevel Inheritance." In: Multiple Inheritance and Multilevel Inheritance, Web Courses, University of Central Florida, December 4. Accessed 2021-11-27.
- Winter, Collin. 2007. "PEP 3129 -- Class Decorators." Python.org, May 1. Updated 2021-02-09. Accessed 2021-11-23.
- van Rossum, Guido. 2001. "PEP 252 -- Making Types Look More Like Classes." Python.org, April 19. Updated 2019-09-17. Accessed 2021-11-21.
- van Rossum, Guido. 2003. "Unifying types and classes in Python 2.2." Releases, Python, May 30. Accessed 2021-11-21.
- van Rossum, Guido. 2009a. "A Brief Timeline of Python." Blog, The History of Python, January 20. Accessed 2021-11-23.
- van Rossum, Guido. 2009b. "What’s New In Python 3.0." Release 3.0.1, February 14. Accessed 2021-11-23.
- van Rossum, Guido, and Talin. 2007. "PEP 3119 -- Introducing Abstract Base Classes." Python.org, April 18. Updated 2021-11-09. Accessed 2021-11-23.
- van Rossum, Guido, Barry Warsaw, and Nick Coghlan. 2001. "PEP 8 -- Style Guide for Python Code." Python.org, July 5. Updated 2021-09-02. Accessed 2019-02-05.
Further Reading
- University of Cape Town. 2014. "Classes." In: Object-Oriented Programming in Python, March 26. Accessed 2021-11-24.
- Malik, Farhad. 2020. "Advanced Python: What Are Magic Methods?" FinTecExplained, on Medium, May 16. Accessed 2021-11-21.
- Python Docs. 2021a. "Classes." Section 9, The Python Tutorial, v3.10.0, November 20. Accessed 2021-11-21.
- Python Docs. 2021b. "Data Model." Section 3, The Python Language Reference, v3.10.0, November 20. Accessed 2021-11-21.
- Danjou, Julien. 2013. "The definitive guide on how to use static, class or abstract methods in Python." Blog, August 1. Accessed 2021-11-23.
Article Stats
Cite As
See Also
- Python Descriptor
- Magic Methods in Python
- Introspection in Python
- Abstraction in Python
- Object-Oriented Programming Concepts
- Multiple Inheritance