6.11. OOP Pattern Matching¶
6.11.1. Problem¶
>>> language = 'English'
>>>
>>> if language == 'Polish':
... result = 'Cześć'
... elif language == 'English':
... result = 'Hello'
... elif language == 'German':
... result = 'Guten Tag'
... elif language == 'Spanish':
... result = 'Buenos Días'
... elif language == 'Chinese':
... result = '你好'
... elif language == 'French':
... result = 'Bonjour'
... else:
... result = 'Unknown language'
>>>
>>> print(result)
Hello
6.11.2. Switch¶
In other languages you may find switch
statement:
(note that this is not a valid Python code)
>>> language = 'English'
>>>
>>> switch(language):
... case 'Polish': result = 'Cześć'
... case 'English': result = 'Hello'
... case 'German': result = 'Guten Tag'
... case 'Spanish': result = 'Buenos Días'
... case 'Chinese': result = '你好'
... case 'French': result = 'Bonjour'
... default: result = 'Unknown language'
>>>
>>> print(result)
Hello
6.11.3. Pattern Matching¶
Since Python 3.10: PEP 636 -- Structural Pattern Matching: Tutorial
>>> language = 'English'
>>>
>>> match language:
... case 'Polish': result = 'Cześć'
... case 'English': result = 'Hello'
... case 'German': result = 'Guten Tag'
... case 'Spanish': result = 'Buenos Días'
... case 'Chinese': result = '你好'
... case 'French': result = 'Bonjour'
... case _: result = 'Unknown language'
>>>
>>> print(result)
Hello
6.11.4. Behavior¶
Statement |
Meaning |
---|---|
|
assign x = subject |
|
test subject == 'x' |
|
test subject == x.y |
|
test isinstance(subject, x) |
|
test isinstance(subject, Mapping) and subject.get('x') == 'y' |
|
test isinstance(subject, Sequence) and len(subject) == 1 and subject[0] == 'x' |
6.11.5. Matching Alternatives¶
>>> status = 418
>>>
>>>
>>> match status:
... case 400:
... result = 'Bad request'
... case 401 | 403 | 405:
... result = 'Not allowed'
... case 404:
... result = 'Not found'
... case 418:
... result = "I'm a teapot"
... case _:
... result = 'Unexpected status'
6.11.6. Matching on Sequence¶
>>> request = 'GET /index.html HTTP/2.0'
>>>
>>>
>>> match request.split():
... case ['GET', uri, version]:
... server.get(uri)
... case ['POST', uri, version]:
... server.post(uri)
... case ['PUT', uri, version]:
... server.put(uri)
... case ['DELETE', uri, version]:
... server.delete(uri)
6.11.7. Matching on Sequence with Assignment¶
>>> class Hero:
... def action(self):
... return ['move', 'left', 20]
>>>
>>>
>>> match hero.action():
... case ['move', ('up'|'down'|'left'|'right') as direction, value]:
... hero.move(direction, value)
... case ['make_damage', value]:
... hero.make_damage(value)
... case ['take_damage', value]:
... hero.take_damage(value)
6.11.8. Matching on Enum¶
>>> from enum import Enum
>>>
>>>
>>> class Key(Enum):
... ESC = 27
... ARROW_LEFT = 37
... ARROW_UP = 38
... ARROW_RIGHT = 39
... ARROW_DOWN = 40
>>>
>>>
>>> match keyboard.on_key_press():
... case Key.ESC:
... game.quit()
... case Key.ARROW_LEFT:
... hero.move_left()
... case Key.ARROW_UP:
... hero.move_up()
... case Key.ARROW_RIGHT:
... hero.move_right()
... case Key.ARROW_DOWN:
... hero.move_down()
... case _:
... raise ValueError(f'Unrecognized key')
6.11.9. References¶
- 1
Raymond Hettinger. Retrieved: 2021-03-07. URL: https://twitter.com/raymondh/status/1361780586570948609?s=20
6.11.10. Assignments¶
"""
* Assignment: Idioms Match Range
* Complexity: medium
* Lines of code: 25 lines
* Time: 13 min
English:
1. Write own implementation of a built-in function `range()`
2. Note, that function does not take any keyword arguments
3. How to implement passing only stop argument
`myrange(start=0, stop=???, step=1)`?
4. Run doctests - all must succeed
Polish:
1. Zaimplementuj własne rozwiązanie wbudowanej funkcji `range()`
2. Zauważ, że funkcja nie przyjmuje żanych argumentów nazwanych (keyword)
3. Jak zaimplementować możliwość podawania tylko końca
`myrange(start=0, stop=???, step=1)`?
4. Uruchom doctesty - wszystkie muszą się powieść
Hint:
* https://github.com/python/cpython/blob/main/Objects/rangeobject.c#LC75
* `raise TypeError('error message')`
* use `*args` and `**kwargs`
* `if len(args) == ...`
* `match len(args)`
Tests:
>>> import sys; sys.tracebacklimit = 0
>>> from inspect import isfunction
>>> assert isfunction(myrange)
>>> myrange(0, 10, 2)
[0, 2, 4, 6, 8]
>>> myrange(0, 5)
[0, 1, 2, 3, 4]
>>> myrange(5)
[0, 1, 2, 3, 4]
>>> myrange()
Traceback (most recent call last):
TypeError: myrange expected at least 1 argument, got 0
>>> myrange(1,2,3,4)
Traceback (most recent call last):
TypeError: myrange expected at most 3 arguments, got 4
>>> myrange(stop=2)
Traceback (most recent call last):
TypeError: myrange() takes no keyword arguments
>>> myrange(start=1, stop=2)
Traceback (most recent call last):
TypeError: myrange() takes no keyword arguments
>>> myrange(start=1, stop=2, step=2)
Traceback (most recent call last):
TypeError: myrange() takes no keyword arguments
"""
# myrange(start=0, stop=???, step=1)
# note, function does not take keyword arguments
# type: Callable[[int,int,int], list[int]]
def myrange():
current = start
result = []
while current < stop:
result.append(current)
current += step
return result