6️⃣

(Optional) Other Special Methods

Other Special Methods

A full list of special methods can be found here at the official python documentation website. There are quite a lot of special methods, so this last section will just go through the special methods that you might eventually use. These will not be on the quiz and are just here for those interested in them.

__contains__

The __contains__ method is called when the in operator is used. It should return True or False.
Structure: def __contains__(self, item) -> bool:
This method should return a bool representing whether or not the item is actually in the class.
Example:
class coordinateGrid: def __init__( self, x_start: int = 0, x_end: int = 10, y_start: int = 0, y_end: int = 10, ): """ Creates a list of coordinates similar to a coordinate grid. Each item in self.coordinates is a list representing one row in a coordinate grid. each item within that row is a point (tuple) of x, y ex: coordinateGrid(0, 1, -1, 1)'s coordinates would be [ [(0, 1), (1, 1)], [(0, 0), (1, 0)], [(0, -1), (1, -1)] ] Arguments: x_start, x_end, y_start, and y_end are all inclusive """ self.coordinates = [ [(x, y) for x in range(x_start, x_end + 1)] for y in range(y_end, y_start - 1, -1) ] def __contains__(self, item: tuple) -> bool: """ Checks to see if the provided tuple (or list) of length 2 (the tuple/list represents a point of x,y) is in self.coordinates. """ return True in [item in row for row in self.coordinates] grid1 = coordinateGrid(-1, 1, -1, 1) grid2 = coordinateGrid(-10, 10, -10, 10) point1 = (10, 10) print(point1 in grid1) # this will be False print(point1 in grid2) # this will be True
View code on GitHub
In this example, we created a class called coordinateGrid whose attribute self.coordinates was a 2D list of points/coordinates. Then, we instantiated coordinateGrid twice. grid1 was instantiated so that both its x and y values range from -1 to 1. grid2 was instantiated so that both its x and y values range from -10 to 10. Lastly, we created a coordinate (tuple) (10, 10) and checked whether it was in both coordinateGrid's. Since we used the in operator, __contains__ was called; it returned False for grid1 and True for grid2.

__r...__

This is the workaround for doing (regular type) + (our defined class). Earlier in this chapter, we demonstrated how to do (our defined class) + (regular type) with the class Vector and __add__, __mul__, and other mathematical methods. However, if we wanted to do int + Vector, we would use the special __r...__ methods
💡
__r...__ methods work just like the regular mathematical methods, but there is an r before the letters. For example, __add__ would be used for Vector + int, but __radd__ would be used for int + Vector. Also, __pow__ would be used for Vector ** int, but __rpow__ would be used for int ** Vector
Structure: def __radd__(self, other):
Depending on what you want to happen, you can return a value (so that print(int + Vector) would print a real number) or you could modify the class instance (so that (int + Vector) would change the Vector's values).

__len__

This allows you to use len(your class).
Structure: def __len__(self) -> int:
This method should return an integer ≥ 0 to represent its length.
Example (building on-top of the coordinateGrid class)
# ... code from coordinateGrid class def __len__(self) -> bool: """ In this case, we're saying that the length of the coordinateGrid is its area. Thus, we do height * width height = len(self.coordinates) and width = len(self.coordinates[0]) (or any row's length) """ return len(self.coordinates) * len(self.coordinates[0]) grid1 = coordinateGrid(-1, 1, -1, 1) grid2 = coordinateGrid(-10, 10, -10, 10) print(len(grid1)) # 3 * 3 = 9 print(len(grid2)) # 21 * 21 = 441
View code on GitHub.

__getitem__, __setitem__

These two special methods allow you to use your class like a dict or list (ie use myclass[item/index])
Structure:
def __getitem__(self, item): (used when retrieving the value stored at myclass[item/index])
def __setitem__(self, item, value): (used when doing myclass[item/index] = ...)
Normally, item is either an integer or a string. Also note that, if the attribute isn't a valid attribute, you should do raise AttributeError to let the user know that it is an invalid attribute.
Example:
class bankAccount: def __init__(self, owner: str, balance: float): self.owner = owner self.balance = balance def __getitem__(self, item: str): if item == "owner": return self.owner elif item == "balance": return self.balance else: # if the attribute isn't a valid attribute, you should # raise an AttributeError raise AttributeError def __setitem__(self, item: str, value): if item == "owner": self.owner = value elif item == "balance": self.balance = value else: # if the attribute isn't a valid attribute, you should # raise an AttributeError raise AttributeError account = bankAccount("John", 100) print(account["owner"]) print(account["balance"]) account["balance"] = 200 print(account["balance"]) account['owner'] = "John Jr." print(account['owner'])
View code on GitHub.
In the above example, we have a class bankAccount with two attributes: owner and balance. Then, we have the __getitem__ and __setitem__ methods that check if item is a valid attribute (either owner or balance) and either return the corresponding value (in __getitem__) or modify the corresponding value (in __setitem__).

__bool__

This allows you to use your class like a boolean. For example, bool(myclass) would give the value that __bool__ returns. Also, if myclass: would be equivalent to if myclass.__bool__(), meaning that the __bool__ method is called there to (same for elif).
Structure: def __bool__(self) -> bool:
Example: (building off of the previous bankAccount code)
# ... code from bankAccount class def __bool__(self): """ If we wanted the bank account to return True if the person is not bankrupt and False if they are bankrupt, we could do: """ return self.balance > 0 print(bool(account)) # this will print True (since the account has $200) if account: print("not bankrupt") # this will run
View code on GitHub.

Previous Section

Next Section

 
⚖️
Copyright © 2021 Code 4 Tomorrow. All rights reserved. The code in this course is licensed under the MIT License. If you would like to use content from any of our courses, you must obtain our explicit written permission and provide credit. Please contact classes@code4tomorrow.org for inquiries.