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__method is called when the
inoperator is used. It should return True or False.
def __contains__(self, item) -> bool:
This method should return a bool representing whether or not the item is actually in the class.
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
inoperator, __contains__ was called; it returned False for grid1 and True for grid2.
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
__mul__, and other mathematical methods. However, if we wanted to do int + Vector, we would use the special
__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
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).
This allows you to use
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) (or any row's length) """ return len(self.coordinates) * len(self.coordinates) grid1 = coordinateGrid(-1, 1, -1, 1) grid2 = coordinateGrid(-10, 10, -10, 10) print(len(grid1)) # 3 * 3 = 9 print(len(grid2)) # 21 * 21 = 441
These two special methods allow you to use your class like a dict or list (ie use
def __getitem__(self, item):(used when retrieving the value stored at
def __setitem__(self, item, value):(used when doing
myclass[item/index] = ...)
itemis either an integer or a string. Also note that, if the attribute isn't a valid attribute, you should do
raise AttributeErrorto let the user know that it is an invalid attribute.
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'])
In the above example, we have a class bankAccount with two attributes: owner and balance. Then, we have the
__setitem__methods that check if
itemis a valid attribute (either owner or balance) and either return the corresponding value (in
__getitem__) or modify the corresponding value (in
This allows you to use your class like a boolean. For example,
bool(myclass)would give the value that
if myclass:would be equivalent to
if myclass.__bool__(), meaning that the
__bool__method is called there to (same for elif).
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