Similar presentations:
decorators
1.
Замыкания иДекораторы
Докладчик: Евграфов Михаил
2.
Замыкания2
3.
Область видимости функцийvar_global = 42
def print_vars(
var_local: int
) -> None:
print(
f"{var_local = }",
f"{var_global = }",
sep="\n",
)
глобальные
переменные
локальные
переменные
3
4.
Чтение глобальных переменныхdef print_vars(var_local: int) -> None:
print(f"{var_local = }")
print(f"{var_global = }")
var_global = 42
print_vars(var_local=5)
# var_local = 5
# var_global = 42
4
5.
Изменение глобальных переменныхdef modify_global_list() -> None:
lst.append(42)
lst = list(range(3))
print(f"lst: {lst}")
# lst: [0, 1, 2]
modify_global_list()
print(f"lst: {lst}")
# lst: [0, 1, 2, 42]
5
6.
Перепривязкаdef print_object_info(obj: object) -> None:
print(f"{obj = }", f"obj ID: {id(obj)}", sep="\n")
def try_to_rebound_global_list() -> None:
lst = [0 - 1j, 2.72, 3.14]
print_object_info(lst)
lst = list(range(3))
print_object_info(lst)
try_to_rebound_global_list()
print_object_info(lst)
6
7.
Результаты перепривязки# obj = [0, 1, 2]
# obj ID: 2827461041664
# obj = [-1j, 2.72, 3.14]
# obj ID: 2827461397440
# obj = [0, 1, 2]
# obj ID: 2827461041664
7
8.
Когда переменная глобальнаяdef print_vars(var_local: int) -> None:
print(f"{var_local = }")
print(f"{var_global = }")
var_global = 42
print_vars(var_local=1337)
# var_local = 1337
# var_global = 42
8
9.
Когда переменная локальнаяdef print_vars(var_local: int) -> None:
var_global = 3.14
print(f"{var_local = }")
print(f"{var_global = }")
var_global = 42
print_vars(var_local=1337)
# var_local = 1337
# var_global = 3.14
9
10.
И так, и так не бываетdef print_vars(var_local: int) -> None:
print(f"{var_local = }")
print(f"{var_global = }")
var_global = 3.14
print(f"{var_global = }")
var_global = 42
print_vars(var_local=1337)
...
UnboundLocalError: ...
10
11.
globaldef print_vars(var_local: int) -> None:
global var_global
print(f"{var_local = }")
print(f"{var_global = }")
var_global = 3.14
var_global = 42
print_vars(var_local=1337)
print(f"{var_global = }")
# var_local = 1337
# var_global = 42
# var_global = 3.14
11
12.
Вложенные функцииdef get_printer() -> Callable[[int], None]:
var_nonloc = 42
def print_vars(var_local: int) -> None:
print(f"{var_local = }")
print(f"{var_nonloc = }")
нелокальные
переменные
локальные
переменные
return print_vars
12
13.
Когда переменная нелокальнаяdef get_printer() -> Callable[[int], None]:
var_nonloc = 42
def print_vars(var_local: int) -> None:
print(f"{var_local = }")
print(f"{var_nonloc = }")
return print_vars
print_vars = get_printer()
print_vars(var_local=5)
# var_local = 5
# var_nonloc = 42
13
14.
Когда переменная локальнаяdef get_printer() -> Callable[[int], None]:
var_nonloc = 42
def print_vars(var_local: int) -> None:
var_nonloc = 3.14
print(f"{var_local = }")
print(f"{var_nonloc = }")
return print_vars
print_vars = get_printer()
print_vars(var_local=5)
# var_local = 5
# var_nonloc = 3.14
14
15.
И так, и так не бываетdef get_printer() -> Callable[[int], None]:
var_nonloc = 42
def print_vars(var_local: int) -> None:
print(f"{var_local = }")
print(f"{var_nonloc = }")
var_nonloc = 3.14
return print_vars
print_vars = get_printer()
print_vars(var_local=5)
...
UnboundLocalError: ...
15
16.
nonlocaldef get_printer() -> Callable[[int], None]:
var_nonloc = 42
def print_vars(var_local: int) -> None:
nonlocal var_nonloc
print(f"{var_local = }")
print(f"{var_nonloc = }")
var_nonloc = 3.14
return print_vars
16
17.
nonlocal: результатprint_vars = get_printer()
print_vars(var_local=5)
# var_local = 5
# var_nonloc = 42
print_vars(var_local=5)
# var_local = 5
# var_nonloc = 3.14
17
18.
Когда использовать globaldef get_counter_curr() -> int:
global counter
counter += 1
return counter
counter = 0
for _ in range(3):
print(f"counter: {get_counter_curr()}")
# counter: 1
# counter: 2
# counter: 3
18
19.
Когда global не работаетdef get_counter_curr() -> int:
global counter
counter += 1
return counter
counter = 0
get_counter1 = get_counter_curr
get_counter2 = get_counter_curr
print(get_counter1(), get_counter2(), sep="\n")
# 1
# 2
19
20.
Замыканияфабричная функция
def get_counter_func() -> Callable[[], int]:
counter = 0
def get_counter_curr() -> int:
nonlocal counter
counter += 1
return counter
контекст
замыкания
замыкание
return get_counter_curr
20
21.
Замыкания: результатget_counter1 = get_counter_func()
get_counter2 = get_counter_func()
for _ in range(3):
get_counter1()
for _ in range(5):
get_counter2()
print(get_counter1(), get_counter2(), sep="\n")
# 4
# 6
21
22.
Где хранится контекст замыканияprint(f"locals: {get_counter1.__code__.co_varnames}")
# locals: ()
print(f"free vars: {get_counter1.__code__.co_freevars}")
# free vars: ('counter',)
print(f"context: {get_counter1.__closure__}")
# context: (<cell at 0x00000292518E9BA0: ...)
for i, cell in enumerate(get_counter1.__closure__):
print(f"cell_{i} content: {cell.cell_contents}")
# cell_0 content: 4
22
23.
Декораторы23
24.
Описание проблемыimport time
def do_something() -> None:
print("start doing
something")
time.sleep(1)
print("stop doing something")
хотим измерять
время работы
do_something()
# start doing something
# stop doing something
24
25.
Возможное решениеimport time
def do_something() -> None:
print("start doing something")
time.sleep(1)
print("stop doing something")
time_start = time.time()
do_something()
print(f"time taken: {time.time() - time_start:.2f}")
# start doing something
# stop doing something
# time taken: 1.00
25
26.
Минусы: дублирование логикиimport time
time_start = time.time()
do_something()
print(f"time taken: {time.time() - time_start:.2f}")
time_start = time.time()
do_another_thing()
print(f"time taken: {time.time() - time_start:.2f}")
26
27.
Декораторimport time
from typing import Callable, ParamSpec, TypeVar
P = ParamSpec("P")
R = TypeVar("R")
def timeit(func: Callable[P, R]) -> Callable[P, R]:
def wrapper(*args: P.args, **kwargs: P.kwargs) -> R:
time_start = time.time()
result = func(*args, **kwargs)
print(f"time taken: {time.time() - time_start:.2f}")
return result
return wrapper
27
28.
Декоратор подробнеедекоратор
декорируемая функция
def timeit(func: Callable[P, R]) -> Callable[P, R]:
def wrapper(*args: ..., **kwargs: ...) -> R:
time_start = time.time()
result = func(*args, **kwargs)
print(time.time() - time_start)
return result
декорирующая
функция
return wrapper
декорирующая функция дополняет поведение
декорируемой функции
28
29.
Декоратор как замыканиеконтекст замыкания
def timeit(func: Callable[P, R]) -> Callable[P, R]:
def wrapper(*args: ..., **kwargs: ...) -> R:
time_start = time.time()
result = func(*args, **kwargs)
print(time.time() - time_start)
return result
замыкание
return wrapper
декорирующая функция замыкается на декорируемую
функцию
29
30.
Использование декоратора@timeit
def do_something() -> None:
print("start doing something")
time.sleep(1)
print("stop doing something")
do_something()
# start doing something
# stop doing something
# time taken: 1.00
30
31.
@Callable@timeit
def do_something() -> None:
print("start doing something")
time.sleep(1)
print("stop doing something")
def do_something() -> None:
print("start doing something")
time.sleep(1)
print("stop doing something")
do_something = timeit(do_something)
31
32.
@Callable@timeit
def do_something() -> None:
print("start doing something")
time.sleep(1)
print("stop doing something")
def do_something() -> None:
print("start doing something")
time.sleep(1)
print("stop doing something")
do_something = timeit(do_something)
32
33.
Подмена@timeit
def do_something() -> None:
print("start doing something")
time.sleep(1)
print("stop doing something")
print(do_something.__name__)
# wrapper
33
34.
wrapsfrom functools import wraps
def timeit(func: Callable[P, R]) -> Callable[P, R]:
@wraps(func)
def wrapper(*args: P.args, **kwargs: P.kwargs) -> R:
time_start = time.time()
result = func(*args, **kwargs)
print(f"time taken: {time.time() - time_start:.2f}")
return result
return wrapper
34
35.
wraps: результат@timeit
def do_something() -> None:
print("start doing something")
time.sleep(1)
print("stop doing something")
print(do_something.__name__)
# do_something
35
36.
Момент выполнения декораторовdef decorate(func: Callable[P, R]) -> Callable[P, R]:
print("decorate was applied")
return func
@decorate
def do_something() -> None:
print("do something")
# decorate was applied
@decorate
def do_another_thing() -> None:
print("do another thing")
# decorate was applied
36
37.
Композиция декораторовdef inner(func: Callable[P, R]) -> Callable[P, R]:
def wrapper(*args: P.args, **kwargs: P.kwargs) -> R:
print("call inner")
return func(*args, **kwargs)
return wrapper
def outer(func: Callable[P, R]) -> Callable[P, R]:
def wrapper(*args: P.args, **kwargs: P.kwargs) -> R:
print("call outer")
return func(*args, **kwargs)
return wrapper
37
38.
Композиция декораторов@outer
@inner
def do_something() -> None:
print("do something")
do_something()
# call outer
# call inner
# do something
38
39.
Логика композиции@outer
@inner
def do_something() -> None:
print("do something")
def do_something() -> None:
print("do something")
do_something = outer(inner(do_something))
39
40.
Логика композиции@outer
@inner
def do_something() -> None:
print("do something")
def do_something() -> None:
print("do something")
do_something = outer(inner(do_something))
40
41.
Параметризация декораторовdef register(
name: str,
registry: dict[str, Callable[P, R]],
) -> Callable[[Callable[P, R]], Callable[P, R]]:
def _register(func: Callable[P, R]):
registry[name] = func
return func
return _register
41
42.
Использованиеregistry = dict[str, Callable[P, R]]()
@register("do_something", registry)
def do_something() -> None:
print("do something")
print(registry)
# {'do_something': <function do_something ...>}
42
43.
Логика использованияregistry = dict[str, Callable[P, R]]()
@register("do_something", registry)
def do_something() -> None:
print("do something")
def do_something() -> None:
print("do something")
register_decorator = register("do_something", registry)
do_something = register_decorator(do_something)
43
44.
Логика использованияregistry = dict[str, Callable[P, R]]()
@register("do_something", registry)
def do_something() -> None:
print("do something")
def do_something() -> None:
print("do something")
register_decorator = register("do_something", registry)
do_something = register_decorator(do_something)
44
45.
Семинар45