Python OOP

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 of Point given that point = 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 whereas Point.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?
    Anatomy of a class and its usage in Python. Source: Devopedia 2021.
    Anatomy of a class and its usage in Python. Source: Devopedia 2021.

    A class of name 'Foo' can be defined with class Foo: pass, where class is a Python keyword. An instance or object of this class can be created with foo = Foo(), where foo is an instance of the Foo 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 of object.

    A class Rectangle that inherits from another class Shape can be defined as def 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 a Novel class that inherits from two classes Book and Fiction: 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 a Point object named corner 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 a Point class to initialize it's attributes.
    • __del__: Called automatically when the object is destroyed. A programmer can explicitly destroy the object by calling the del() 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?
    Examples of nested function and nested class. Source: Devopedia 2021.
    Examples of nested function and nested class. Source: Devopedia 2021.

    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 function f(). Function g is therefore a nested function. It can't be invoked from outside its container function. It can be invoked only by f. Nested functions are therefore useful to implement encapsulation. For example, function get_projection is defined within function get_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?
    Illustrating the use of a magic method. Source: Devopedia 2021.
    Illustrating the use of a magic method. Source: Devopedia 2021.

    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 call del() 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?
    Illustrating static method and class method. Source: Devopedia 2021.
    Illustrating static method and class method. Source: Devopedia 2021.

    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 in cls as the first argument.

  • How do I define private or protected class attributes in Python?
    Use leading double underscores for private attributes. Source: Python Docs 2021a, sec. 9.6.
    Use leading double underscores for private attributes. Source: Python Docs 2021a, sec. 9.6.

    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 class Mapping 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?
    Illustrating MRO for the case of multiple inheritance. Source: Devopedia 2021.
    Illustrating MRO for the case of multiple inheritance. Source: Devopedia 2021.

    Consider Novel class that inherits from two classes Book and Fiction: def Novel(Book, Fiction). If both Book and Fiction implement the method get_title() but Novel 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 a Romantic19Novel instance resolves to RomanticNovel and then Novel (depth first rule). However, Novel is also a base class of NineteenthCenturyNovel. 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?
    Illustrating cooperative multiple inheritance. Source: Hettinger 2011.
    Illustrating cooperative multiple inheritance. Source: Hettinger 2011.

    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.

Milestones

Jan
1994

Python 1.0 is released. This version includes support for classes and inheritance, which were also present in earlier version 0.9.

Dec
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".

Jul
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.

Nov
2004
Illustrating decorators for functions and classes. Source: Adapted from Python Docs.
Illustrating decorators for functions and classes. Source: Adapted from Python Docs.

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.

Apr
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.

Dec
2008
Unlike Python2, Python3 doesn't have unbound methods. Source: Devopedia 2021.
Unlike Python2, Python3 doesn't have unbound methods. Source: Devopedia 2021.

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.

Sep
2012
Illustrating the usefulness of qualified name attribute. Source: Adapted from Python Docs 2012.
Illustrating the usefulness of qualified name attribute. Source: Adapted from Python Docs 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

  1. Bagheri, Reza. 2020. "Closures and Decorators in Python." Towards Data Science, on Medium, February 5. Accessed 2021-11-26.
  2. 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.
  3. Brett, Matthew. 2021. "Functions are objects." Accessed 2021-11-23.
  4. Carattino, Aquiles. 2018. "A Primer on Classes in Python." Python for the Lab, May 22. Accessed 2021-11-21.
  5. Danjou, Julien. 2013. "The definitive guide on how to use static, class or abstract methods in Python." Blog, August 1. Accessed 2021-11-23.
  6. Francis, Howard. 2021. "Public and Private." Lesson, Object Oriented Programming, Real Python. Accessed 2021-11-21.
  7. GeeksforGeeks. 2016. "Python OOPs Concepts." GeeksforGeeks, May 16. Updated 2021-08-24. Accessed 2021-11-21.
  8. GeeksforGeeks. 2018. "Operator Overloading in Python." GeeksforGeeks, December 10. Updated 2021-05-26. Accessed 2021-11-21.
  9. Hettinger, Raymond. 2011. "Python’s super() considered super!" Blog, Deep Thoughts, May 26. Accessed 2021-11-24.
  10. Kaul, Sarath. 2020. "Public, Private, and Protected — Access Modifiers in Python." Better Programming, on Medium, January 8. Accessed 2021-11-21.
  11. Klein, Bernd. 2021. "Object Oriented Programming." Python Course, November 2. Accessed 2021-11-21.
  12. 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.
  13. Malik, Farhad. 2020. "Advanced Python: What Are Magic Methods?" FinTecExplained, on Medium, May 16. Accessed 2021-11-21.
  14. Nair, Adarsh. 2021. "Python OOP Primer." Notebook, Jovian, February 17. Accessed 2021-11-21.
  15. Pelletier, Michel. 2001. "PEP 245 -- Python Interface Syntax." Python.org, January 11. Updated 2019-07-03. Accessed 2021-11-21.
  16. Pitrou, Antoine. 2011. "PEP 3155 -- Qualified name for classes and functions." Python.org, October 29. Updated 2021-02-09. Accessed 2021-11-21.
  17. Python Docs. 2009. "Built-in Functions." Python Documentation, v3.0.1. Accessed 2021-11-24.
  18. Python Docs. 2012. "What’s New in Python 3.3." Release 3.3, September 29. Updated 2021-11-23. Accessed 2021-11-23.
  19. Python Docs. 2021a. "Classes." Section 9, The Python Tutorial, v3.10.0, November 20. Accessed 2021-11-21.
  20. Python Docs. 2021b. "Data Model." Section 3, The Python Language Reference, v3.10.0, November 20. Accessed 2021-11-21.
  21. Shaik, Hafeezul Kareem. 2020. "Inner Classes in Python." DataCamp, January 3. Accessed 2021-11-23.
  22. Simionato, Michele. 2003. "The Python 2.3 Method Resolution Order." Releases, Python, July 29. Accessed 2021-11-21.
  23. 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.
  24. Spealman, Calvin, Tim Delaney, and Lie Ryan. 2007. "PEP 3135 -- New Super." Python.org, April 28. Updated 2021-09-17. Accessed 2021-11-21.
  25. Tayal, Rachit. 2019. "Python Magic Methods Explained." Python Features, on Medium, May 29. Accessed 2021-11-21.
  26. Thelin, Ryan. 2021. "Python Version History: How Python has changed over the years." Blog, Educative, January 13. Accessed 2021-11-21.
  27. 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.
  28. van Rossum, Guido. 2001. "PEP 252 -- Making Types Look More Like Classes." Python.org, April 19. Updated 2019-09-17. Accessed 2021-11-21.
  29. van Rossum, Guido. 2003. "Unifying types and classes in Python 2.2." Releases, Python, May 30. Accessed 2021-11-21.
  30. van Rossum, Guido. 2009a. "A Brief Timeline of Python." Blog, The History of Python, January 20. Accessed 2021-11-23.
  31. van Rossum, Guido. 2009b. "What’s New In Python 3.0." Release 3.0.1, February 14. Accessed 2021-11-23.
  32. van Rossum, Guido, and Talin. 2007. "PEP 3119 -- Introducing Abstract Base Classes." Python.org, April 18. Updated 2021-11-09. Accessed 2021-11-23.
  33. 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.
  34. Winter, Collin. 2007. "PEP 3129 -- Class Decorators." Python.org, May 1. Updated 2021-02-09. Accessed 2021-11-23.

Further Reading

  1. University of Cape Town. 2014. "Classes." In: Object-Oriented Programming in Python, March 26. Accessed 2021-11-24.
  2. Malik, Farhad. 2020. "Advanced Python: What Are Magic Methods?" FinTecExplained, on Medium, May 16. Accessed 2021-11-21.
  3. Python Docs. 2021a. "Classes." Section 9, The Python Tutorial, v3.10.0, November 20. Accessed 2021-11-21.
  4. Python Docs. 2021b. "Data Model." Section 3, The Python Language Reference, v3.10.0, November 20. Accessed 2021-11-21.
  5. 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

Author-wise Stats for Article Edits

Author
No. of Edits
No. of Chats
DevCoins
5
0
1604
2355
Words
2
Likes
1601
Hits

Cite As

Devopedia. 2021. "Python OOP." Version 5, November 28. Accessed 2022-06-16. https://devopedia.org/python-oop
Contributed by
1 author


Last updated on
2021-11-28 12:10:55

Improve this article

Article Warnings

  • In References, replace these sub-standard sources: geeksforgeeks.org