fix: guide to magic magics code endings

This commit is contained in:
Corbin Crutchley
2022-06-22 13:58:57 -07:00
parent 05a393f139
commit ac9a351c42

View File

@@ -50,7 +50,7 @@ This barely scratches the surface when it comes to the power that magic methods
If youve ever created a class, youre likely familiar with the following method:
`__init__(self, …args)` - `ClassName()
`__init__(self, …args)` - `ClassName()`
Its probably the best-known magic method, Pythons __init__ acts as a class constructor. You can use this to pass initial arguments to a Python class.
@@ -75,7 +75,7 @@ Here, whenever the `Speaker` class is initialized, it will assign `self.message`
In addition to a class initializer, theres also a class deletion handler:
`__del__(self)` - `del instance
`__del__(self)` - `del instance`
This method will run any time you call `del` on a class instance. This is particularly useful whenever you have an I/O operation in the constructor in order to cleanup said I/O operations.
@@ -220,7 +220,7 @@ This is because while weve told Python how to look up the overwritten values,
To do this, we can use the `__dir__` magic method.
`__dir__(self)` - `dir(instance)
`__dir__(self)` - `dir(instance)`
```python
class Test:
@@ -271,7 +271,7 @@ This is because, once we assign `test.string`, it no longer calls `getattr` the
To solve this problem, we need to use the `__setattr__` magic method to “listen” for property assignment.
`__setattr__(self, key, val)` - `instance.property = newVal
`__setattr__(self, key, val)` - `instance.property = newVal`
```python
class Test:
@@ -296,7 +296,7 @@ Just as we can hook into the setting and getting behavior of an attribute, we ca
For example, what if we wanted to create a class that acted like a dictionary. For each key created in this dictionary wed want to automatically create a temporary file. Then, on cleanup (using `del`), lets remove that file with `os.remove`:
`__delattr__(self, key)` - `del instance.property
`__delattr__(self, key)` - `del instance.property`
```python
import os
@@ -341,9 +341,9 @@ We would quickly get an error from Python:
To solve this problem, we need to migrate away from `__setattr__`, which only supports dot notation, to `__setitem__`, which only supports the dictionary-style notation.
`__getitem__(self, key)` - `instance[property]
`__setitem__(self, key, val)` - `instance[property] = newVal
`__delitem__(self, key)` - `del instance[property]
- `__getitem__(self, key)` - `instance[property]`
- `__setitem__(self, key, val)` - `instance[property] = newVal`
- `__delitem__(self, key)` - `del instance[property]`
```python
import os
@@ -399,7 +399,7 @@ Luckily we can!
For example, heres how we can make the `+` symbol run custom logic:
`__add__(self, other)` - `instance + other
- `__add__(self, other)` - `instance + other`
```python
class Test:
@@ -419,8 +419,8 @@ print(firstItem + secondItem)
Theres also other math symbols you can overwrite:
`__sub__(self, other)` - `instance - other
`__mul__(self, other)` - `instance * other
- `__sub__(self, other)` - `instance - other`
- `__mul__(self, other)` - `instance * other`
### Manage comparison symbol behavior
@@ -428,7 +428,7 @@ Addition, subtraction, and multiplication arent the only usages for operator
Lets say we want to check if two strings match, regardless of casing:
`__eq__(self, other)` - `instance == other
- `__eq__(self, other)` - `instance == other`
```python
class Test():
@@ -448,17 +448,16 @@ print(firstItem == secondItem)
You can also have different logic for `==` and `!=` using `__ne__`.
`__ne__(self, other)` - `instance != other
- `__ne__(self, other)` - `instance != other`
However, if you dont provide a `__ne__`, but **do** provide a `__eq__`, Python will simply negate the `__eq__` logic on your behalf when `instance != other` is called.
Theres also a slew of magic methods for customizing other comparison operators:
`__lt__(self, other)` - `instance < other
`__gt__(self, other)` - `instance > other
`__le__(self, other)` - `instance <= other
`__ge__(self, other)` - `instance >= other
- `__lt__(self, other)` - `instance < other`
- `__gt__(self, other)` - `instance > other`
- `__le__(self, other)` - `instance <= other`
- `__ge__(self, other)` - `instance >= other`
### Overwrite a classs type casting logic
@@ -470,7 +469,7 @@ For example, if you call `bool()` on a string, it will cast the truthy value to
What if you could customize the behavior of the `bool()` method? You see where were going with this…
`__bool__(self)` - `bool(instance)
- `__bool__(self)` - `bool(instance)`
```python
from os.path import exists
@@ -492,8 +491,8 @@ print(bool(file))
Theres also other type casts logic you can customize:
`__int__(self)` - `int(instance)
`__str__(self)` - `str(instance)
- `__int__(self)` - `int(instance)`
- `__str__(self)` - `str(instance)`
## How to make your classes iterable
@@ -539,7 +538,7 @@ Or any other kind of iteration on the ListLike. Youll get the following confu
This is because Python doesnt know *how* to iterate through your class, and therefore attempts to access a property in the class. This is where `__iter__` comes into play: It allows you to return an iterable to utilize anytime Python might request iterating through the class, like in [a list comprehension](https://coderpad.io/blog/development/python-list-comprehension-guide/).
`__iter__(self)` - `[x for x in instance]
- `__iter__(self)` - `[x for x in instance]`
```python
class ListLike:
@@ -583,7 +582,7 @@ listLike.append("World")
The `__iter__` magic method isnt the only way to customize traditionally list-like behavior for a class. You can also use the `__contains__` method to add support for simple “is this in the class” checks.
`__contains__(self, item)` - `key in instance`
- `__contains__(self, item)` - `key in instance`
Something to keep in mind is that if `__contains__` isn't defined, Python will use the information provided by `__iter__` to check if the key is present. However, `__contains__` is a more optimized method, since the default `__iter__` checking behavior will iterate through every key until it finds a match.