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) (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``````
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.

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