Polymorphism (poly- multiple, morph- shape), is the ability to write code that works over multiple data types. It’s very easy to write polymorphic functions in Python–for instance, what type does the following function return?
def add(x, y): return x + y
Depending on the type of its arguments, it could return an integer, a float, or a string. This property can be confusing–if we only plan on calling this function with a specific type, we might want to add annotations:
def add(x: str, y: str) -> str: return x + y
Polymorphic code can also be useful, though, as we’ll see below.
Polymorphism at the library
Let’s revisit an example we saw in CSCI 0111: tracking items at a library. The library has both books and movies, and we can make classes for both of them:
class Book: def __init__(self, title: str, author: str): self.title = title self.author = author class Movie: def __init__(self, title: str, director: str, actors: list): self.title = title self.director = director self.actors = actors
We can represent a library as a list of these items:
library = [Book("The Nickel Boys", "Colson Whitehead"), Movie("Parasite", "Bong Joon-ho", ["Song Kang-ho", "Lee Sun-kyun"])]
An operation we might want on libraries is text search over library items. An advanced search might allow users to search over authors or directors, or search for particular item types (books or movies). In Pyret we implemented this by making books and movies constructors of the same datatype. We could do a similar thing in Python, but there’s a more elegant solution.
We’ll add a method to each class:
class Book: def __init__(self, title: str, author: str): self.title = title self.author = author def matches(self, query: str) -> bool: return query in self.title or query in self.author class Movie: def __init__(self, title: str, director: str, actors: list): self.title = title self.director = director self.actors = actors def matches(self, query: str) -> bool: return query in self.title or query in self.director or any([query in actor for actor in self.actors])
Then we can implement our search function:
def search(library: list, query: str) -> list: return [item for item in library if item.matches(query)]
Let’s work though what happens when we call
search(library, "Colson"). See the
lecture capture for details.
We talked about the second project. See the lecture capture for details.
Polymorphism example: Python’s special methods
Python uses polymorphism ubiquitously. For example, recall that dataclass instances printed out in a nice way at the Python console. How can we get similar behavior with our own classes?
We can implement the
class Book: # other methods... def __repr__(self): return self.title + " by " self.author
Then if we view a book at the console:
>>> library The Nickel Boys by Colson Whitehead
When we view an object at the console, Python calls the
for “representation”) method internally to decide what to display. Note that
this only affects the way Python displays the object–it doesn’t change
anything about the object’s internal representation.