Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update decorators materials for resurfacing #499

Merged
merged 2 commits into from
Feb 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 3 additions & 9 deletions primer-on-python-decorators/README.md
Original file line number Diff line number Diff line change
@@ -1,17 +1,11 @@
# Primer on Python Decorators

Code examples from the [Primer on Python Decorators](https://realpython.com/primer-on-python-decorators/) tutorial on [Real Python](https://realpython.com/).
Here you can find code examples from the [Primer on Python Decorators](https://realpython.com/primer-on-python-decorators/) tutorial on [Real Python](https://realpython.com/).

## Decorators

As noted in the article, most decorators are stored in the file [`decorators.py`](https://github.com/realpython/materials/blob/master/primer-on-python-decorators/decorators.py). The exceptions are those decorators that depend on third party packages ([Flask](http://flask.pocoo.org/) and [Pint](https://pint.readthedocs.io/)), which are available in [`decorators_flask.py`](https://github.com/realpython/materials/blob/master/primer-on-python-decorators/decorators_flask.py) and [`decorators_unit.py`](https://github.com/realpython/materials/blob/master/primer-on-python-decorators/decorators_unit.py), respectively.
You can find most decorators in the file `decorators.py`. The exceptions are those decorators that depend on the third-party packages ([Flask](http://flask.pocoo.org/) and [Pint](https://pint.readthedocs.io/)). These decorators are available in `secret_app.py`, `validate_input.py`, and `units.py`.

## Examples

Most of the code examples from the article are available in the [`examples.py`](https://github.com/realpython/materials/blob/master/primer-on-python-decorators/examples.py) file.

## Cheat Sheet

We’ve put together a short & sweet Python decorators cheat sheet for you that summarizes the techniques explained in this tutorial:

[Get the decorators cheat sheet »](https://realpython.com/optins/view/decorators-cheatsheet/)
You can find many code examples from the tutorial in separate Python files.
13 changes: 13 additions & 0 deletions primer-on-python-decorators/calculate_e.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import math

from decorators import debug

math.factorial = debug(math.factorial)


def approximate_e(terms=18):
return sum(1 / math.factorial(n) for n in range(terms))


if __name__ == "__main__":
print(approximate_e(5))
35 changes: 35 additions & 0 deletions primer-on-python-decorators/circle.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
class Circle:
def __init__(self, radius):
self.radius = radius

@property
def radius(self):
"""Get value of radius"""
return self._radius

@radius.setter
def radius(self, value):
"""Set radius, raise error if negative"""
if value >= 0:
self._radius = value
else:
raise ValueError("radius must be non-negative")

@property
def area(self):
"""Calculate area inside circle"""
return self.pi() * self.radius**2

def cylinder_volume(self, height):
"""Calculate volume of cylinder with circle as base"""
return self.area * height

@classmethod
def unit_circle(cls):
"""Factory method creating a circle with radius 1"""
return cls(1)

@staticmethod
def pi():
"""Value of π, could use math.pi instead though"""
return 3.1415926535
22 changes: 22 additions & 0 deletions primer-on-python-decorators/class_decorators.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
from decorators import debug, timer


class TimeWaster:
@debug
def __init__(self, max_num):
self.max_num = max_num

@timer
def waste_time(self, num_times):
for _ in range(num_times):
sum([number**2 for number in range(self.max_num)])


@timer
class TimeWaster2:
def __init__(self, max_num):
self.max_num = max_num

def waste_time(self, num_times):
for _ in range(num_times):
sum([i**2 for i in range(self.max_num)])
49 changes: 11 additions & 38 deletions primer-on-python-decorators/decorators.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,10 @@
"""Examples of decorators

See https://realpython.com/primer-on-python-decorators/

The decorators with dependencies outside of the standard library (Flask
and Pint) are available in separate files.
"""

import functools
import time


PLUGINS = dict() # Dictionary used by @register to store plugins
PLUGINS = dict()


def do_twice(func):
"""Run the decorated function twice"""

@functools.wraps(func)
def wrapper_do_twice(*args, **kwargs):
func(*args, **kwargs)
Expand All @@ -33,7 +22,7 @@ def wrapper_timer(*args, **kwargs):
value = func(*args, **kwargs)
end_time = time.perf_counter()
run_time = end_time - start_time
print(f"Finished {func.__name__!r} in {run_time:.4f} secs")
print(f"Finished {func.__name__}() in {run_time:.4f} secs")
return value

return wrapper_timer
Expand All @@ -45,25 +34,25 @@ def debug(func):
@functools.wraps(func)
def wrapper_debug(*args, **kwargs):
args_repr = [repr(a) for a in args]
kwargs_repr = [f"{k}={v!r}" for k, v in kwargs.items()]
kwargs_repr = [f"{k}={repr(v)}" for k, v in kwargs.items()]
signature = ", ".join(args_repr + kwargs_repr)
print(f"Calling {func.__name__}({signature})")
value = func(*args, **kwargs)
print(f"{func.__name__!r} returned {value!r}")
print(f"{func.__name__}() returned {repr(value)}")
return value

return wrapper_debug


def slow_down_1sec(func):
def slow_down_one_second(func):
"""Sleep 1 second before calling the function"""

@functools.wraps(func)
def wrapper_slow_down(*args, **kwargs):
def wrapper_slow_down_one_second(*args, **kwargs):
time.sleep(1)
return func(*args, **kwargs)

return wrapper_slow_down
return wrapper_slow_down_one_second


def register(func):
Expand All @@ -73,8 +62,6 @@ def register(func):


def repeat(_func=None, *, num_times=2):
"""Run the decorated function the given number of times"""

def decorator_repeat(func):
@functools.wraps(func)
def wrapper_repeat(*args, **kwargs):
Expand All @@ -91,29 +78,25 @@ def wrapper_repeat(*args, **kwargs):


def count_calls(func):
"""Count the number of calls made to the decorated function"""

@functools.wraps(func)
def wrapper_count_calls(*args, **kwargs):
wrapper_count_calls.num_calls += 1
print(f"Call {wrapper_count_calls.num_calls} of {func.__name__!r}")
print(f"Call {wrapper_count_calls.num_calls} of {func.__name__}()")
return func(*args, **kwargs)

wrapper_count_calls.num_calls = 0
return wrapper_count_calls


class CountCalls:
"""Count the number of calls made to the decorated function"""

def __init__(self, func):
functools.update_wrapper(self, func)
self.func = func
self.num_calls = 0

def __call__(self, *args, **kwargs):
self.num_calls += 1
print(f"Call {self.num_calls} of {self.func.__name__!r}")
print(f"Call {self.num_calls} of {self.func.__name__}()")
return self.func(*args, **kwargs)


Expand All @@ -139,7 +122,7 @@ def singleton(cls):

@functools.wraps(cls)
def wrapper_singleton(*args, **kwargs):
if not wrapper_singleton.instance:
if wrapper_singleton.instance is None:
wrapper_singleton.instance = cls(*args, **kwargs)
return wrapper_singleton.instance

Expand All @@ -157,15 +140,5 @@ def wrapper_cache(*args, **kwargs):
wrapper_cache.cache[cache_key] = func(*args, **kwargs)
return wrapper_cache.cache[cache_key]

wrapper_cache.cache = dict()
wrapper_cache.cache = {}
return wrapper_cache


def set_unit(unit):
"""Register a unit on a function"""

def decorator_set_unit(func):
func.unit = unit
return func

return decorator_set_unit
42 changes: 0 additions & 42 deletions primer-on-python-decorators/decorators_flask.py

This file was deleted.

Loading
Loading