diff --git a/Tutorium/tut14/README.md b/Tutorium/tut14/README.md index e69de29..145b112 100644 --- a/Tutorium/tut14/README.md +++ b/Tutorium/tut14/README.md @@ -0,0 +1,153 @@ +--- +marp: true +paginate: true +# class: invert +theme: rose-pine +footer: Tutorium 14 - 26.01.2024 - Nils Pukropp - https://s.narl.io/s/tutorium-14 +header: +math: mathjax +--- + +# Tutorium 14 - 02.02.2024 + +Decorator, Testing + +--- + +# Decorator + +- **Design-Pattern**, oft auch **Wrapper** genannt +- Verpackt ein Objekt um **zusätzliche Funktionalität** zu bieten + - Funktionen sind auch Objekte + - eine Klasse ist ein Objekt +- Oft einfach **syntax sugar** + +--- +## Beispiel - execute_two_times + +```python +def execute_two_times(fn: Callable[..., Any]) -> Callable[..., Any]: + def wrapper(*args, **kwargs) + fn(*args, **kwargs) + fn(*args, **kwargs) + return wrapper + return wrapper + +@execute_two_times() +def print_two_times(msg: str): + print(msg) + +print_two_times("hello") # hello + # hello +``` + +--- + +## Beispiel - execute_by + +```python +def execute_by(n: int): + def wrapper(fn): + def wrapped_fn(*args, **kwargs): + for _ in range(0, n): + fn(*args, **kwargs) + return wrapped_fn + return wrapped_fn + return wrapper + +@execute_by(10) +def print_ten_times(msg: str): + print(msg) + +print_ten_times("hello") # hello + # hello + # ... (10 mal) +``` + +--- + +## Beispiel - CommandExecutor + +```python +class CommandExecutor[R]: + + def __init__(self): + self.__commands: dict[str, Callable[..., R]] = {} +``` + +--- + +## Beispiel - run + +```python + def run(self, name: str, *args, **kwargs) -> list[R]: + results : list[R] = [] + for command_name, command in self.__commands.items(): + if command_name == name: + results += [command(*args, **kwargs)] + return results +``` + +--- + +## Beispiel - register + +```python + def register(self, cmd: Callable[..., R]) -> Callable[..., R]: + self.__commands[cmd.__name__] = cmd + return cmd +``` + +--- + +## Beispiel - CommandExecutor + +```python +class CommandExecutor[R]: + def __init__(self): + self.__commands: dict[str, Callable[..., R]] = {} + + def run(self, name: str, *args, **kwargs) -> list[R]: + results : list[R] = [] + for command_name, command in self.__commands.items(): + if command_name == name: + results += [command(*args, **kwargs)] + return results + + def register(self, cmd: Callable[..., R]) -> Callable[..., R]: + self.__commands[cmd.__name__] = cmd + return cmd +``` + +--- + +## Beispiel - How to use + +```python +app = CommandExecutor[str]() + +@app.register +def hello_world() -> str: + return 'hello_world' + +@app.register +def divide(a: int, b: int) -> str: + if b == 0: + return "tried to divide by zero" + return str(a / b) + +print(app.run('hello_world')) +print(app.run('divide', 5, 0)) +print(app.run('divide', 10, 2)) +``` +--- + +## Decorator in der Klausur + +- Waren noch nie Bestandteil der Klausur +- Mut zur Lücke +- Kann euch natürlich nichts versprechen + +--- + +# Testing mit `pytest` \ No newline at end of file diff --git a/Tutorium/tut14/src/commands.py b/Tutorium/tut14/src/commands.py new file mode 100644 index 0000000..7c90ecd --- /dev/null +++ b/Tutorium/tut14/src/commands.py @@ -0,0 +1,36 @@ +from typing import Callable, Iterator + + +class CommandExecutor[R]: + def __init__(self): + self.__commands: dict[str, Callable[..., R]] = {} + + def run(self, name: str, *args, **kwargs) -> list[R]: + results : list[R] = [] + for command_name, command in self.__commands.items(): + if command_name == name: + results += [command(*args, **kwargs)] + return results + + def register(self, cmd: Callable[..., R]) -> Callable[..., R]: + self.__commands[cmd.__name__] = cmd + return cmd + + +if __name__ == '__main__': + app = CommandExecutor[str]() + + @app.register + def hello_world() -> str: + return 'hello_world' + + @app.register + def divide(a: int, b: int) -> str: + if b == 0: + return "tried to divide by zero" + return str(a / b) + + print(app.run('hello_world')) + print(app.run('divide', 5, 0)) + print(app.run('divide', 10, 2)) + \ No newline at end of file diff --git a/Tutorium/tut14/src/decorator.py b/Tutorium/tut14/src/decorator.py new file mode 100644 index 0000000..c3a7265 --- /dev/null +++ b/Tutorium/tut14/src/decorator.py @@ -0,0 +1,67 @@ +from typing import Any, Callable + + +def count_calls[T](func: Callable[..., T]) -> Callable[..., T]: + count_calls.calls = 0 + def wrapper(*args, **kwargs) -> T: + init_calls = count_calls.calls + count_calls.calls += 1 + result = func(*args, **kwargs) + wrapper.calls = count_calls.calls - init_calls + return result + return wrapper + +def f(x: int, y: int) -> int: + if x % 2 == 0: + return x // 2 + else: + return x + 2 * y - 1 + +def count_iterations(a: int, b: int) -> int: + f_a(a, a, b) + return f_a.calls - 1 + +@count_calls +def f_a(init: int, a: int, b: int) -> None: + if a < b: + return + return f_a(init, f(a, init), b) + +def execute_by(n: int): + def wrapper(fn): + def wrapped_fn(*args, **kwargs): + for _ in range(0, n): + fn(*args, **kwargs) + return wrapped_fn + return wrapped_fn + return wrapper + +@execute_by(10) +def hello_world(): + print('hello world!') + +@execute_by(10) +def print_ten_times(msg: str): + print(msg) + + +def execute_two_times(fn) -> Callable[..., Any]: + def wrapper(*args, **kwargs): + for _ in range(0, 2): + fn(*args, **kwargs) + return wrapper + return wrapper + +@execute_two_times +def test(msg: str): + print(msg) + +if __name__ == '__main__': + assert (i := count_iterations(7, 6)) == 3, i + assert (i := count_iterations(3, 2)) == 4, i + assert (i := count_iterations(13, 9)) == 18, i + assert (i := count_iterations(13, 10)) == 8, i + assert (i := count_iterations(3, 4)) == 0, i + print_ten_times("hello world") + test("hello") + \ No newline at end of file