How to Use a List of Derived Classes as an Argument for a Base Class in Python

preview_player
Показать описание
Learn how to overcome type errors in Python by using `Sequence` instead of `List` to pass derived class objects to functions expecting base class lists.
---

Visit these links for original content and any more details, such as alternate solutions, latest updates/developments on topic, comments, revision history etc. For example, the original title of the Question was: Use list of derived class as list of base class in Python

If anything seems off to you, please feel free to write me at vlogize [AT] gmail [DOT] com.
---
Understanding the Type Hinting Problem in Python

In Python, especially when using static type checkers like mypy, you may encounter type errors when dealing with inheritance and lists. For example, you might have a function designed to accept a list of a base class, but you want to pass a list of a derived class instead. This leads to an error: Argument 1 to "do_stuff" has incompatible type "List[DerivedClass]"; expected "List[BaseClass]". In this guide, we’ll break down how to solve this problem effectively and in a manner that is coherent for developers working with typed Python.

The Situation

Consider the following Python code:

[[See Video to Reveal this Text or Code Snippet]]

When we try to pass foo, which is a list of DerivedClass, to do_stuff, Python raises a type error. This typically occurs due to how type lists are managed in Python, specifically the rules of covariance and contravariance.

Understanding Covariance vs. Contravariance

Before diving into the solution, let’s clarify the concepts of covariance and contravariance:

Covariant types allow you to substitute a type with any of its subtypes. For example, if BaseClass is a supertype of DerivedClass, a function that accepts Sequence[BaseClass] can handle a Sequence[DerivedClass].

Invariant types, however, do not permit this substitution. A List[BaseClass] cannot be replaced with a List[DerivedClass] due to potential issues with mutability.

In our case, since List is invariant, Python raises an error when trying to substitute List[DerivedClass] into a function expecting List[BaseClass].

The Solution: Using Sequence Instead of List

A common approach to solve this problem is to change your function parameter from List to Sequence. Here's how to adjust your code:

Step 1: Import the Required Type

You need to import Sequence from the typing module:

[[See Video to Reveal this Text or Code Snippet]]

Step 2: Modify the Function Signature

Change the signature of your function to accept Sequence[BaseClass] instead of List[BaseClass]:

[[See Video to Reveal this Text or Code Snippet]]

Why This Works

Using Sequence instead of List is beneficial because:

Immutability: Sequence denotes a collection that shouldn't be modified. This means do_stuff is not allowed to add or remove elements from data, preventing the type error.

Covariance: Since Sequence is covariant, the function can now accept any sequence containing BaseClass and its descendants, including DerivedClass.

What If Mutation Is Required?

If your function needs to modify the data, you may want to rethink the design. Ask yourself:

Should do_stuff allow adding BaseClass instances to the list?

If it does not, consider changing the parameter to List[DerivedClass].

If it does, ensure foo is declared as List[BaseClass] to handle such cases.

Conclusion

Type hinting in Python can be tricky, especially with inheritance and mutable lists. By understanding the difference between List and Sequence, you can avoid common pitfalls and write more robust code. With the changes suggested above, you'll be able to seamlessly pass derived class instances as arguments to your functions without running into type conflicts.

This approach ensures your functions remain flexible and correctly typed, leading to safer and more maintainable code. Happy coding!
Рекомендации по теме
visit shbcf.ru