Skip to content

Instantly share code, notes, and snippets.

@simonseo
Last active December 29, 2022 03:59
Show Gist options
  • Save simonseo/50fe06f467dcac9d1dc0c4922c62e8bd to your computer and use it in GitHub Desktop.
Save simonseo/50fe06f467dcac9d1dc0c4922c62e8bd to your computer and use it in GitHub Desktop.
# examples from https://realpython.com/primer-on-python-decorators/
import time
import functools
import random
######### Common decorators #########
def timer(func):
"""Print the runtime of the decorated function"""
@functools.wraps(func)
def wrapper_timer(*args, **kwargs):
start_time = time.perf_counter() # 1
value = func(*args, **kwargs)
end_time = time.perf_counter() # 2
run_time = end_time - start_time # 3
print(f"Finished {func.__name__!r} in {run_time:.4f} secs")
return value
return wrapper_timer
def debug(func):
"""Print the function signature and return value"""
@functools.wraps(func)
def wrapper_debug(*args, **kwargs):
args_repr = [repr(a) for a in args] # 1
kwargs_repr = [f"{k}={v!r}" for k, v in kwargs.items()] # 2
signature = ", ".join(args_repr + kwargs_repr) # 3
print(f"Calling {func.__name__}({signature})")
value = func(*args, **kwargs)
print(f"{func.__name__!r} returned {value!r}") # 4
return value
return wrapper_debug
def repeat(num_times):
def decorator_repeat(func):
@functools.wraps(func)
def wrapper_repeat(*args, **kwargs):
for _ in range(num_times):
value = func(*args, **kwargs)
return value
return wrapper_repeat
return decorator_repeat
def singleton(cls):
"""Make a class a Singleton class (only one instance)"""
@functools.wraps(cls)
def wrapper_singleton(*args, **kwargs):
if not wrapper_singleton.instance:
wrapper_singleton.instance = cls(*args, **kwargs)
return wrapper_singleton.instance
wrapper_singleton.instance = None
return wrapper_singleton
######### General Template #########
# basic form
def decorator(func):
"""Docstring for decorator"""
# do something upon function definition
return func
# advanced form
def decorator(func):
"""Docstring for decorator"""
# do something upon function definition
@functools.wraps(func)
def wrapper(*args, **kwargs):
# do something before running function
value = func(*args, **kwargs)
# do something after running function
return value
# do something upon wrapper definition like `wraps`
return wrapper
# advanced form with arguments
def create_decorator(*decorator_args, **decorator_kwargs):
# do something before creating decorator
def decorator(func):
# do something upon function definition
@functools.wraps(func)
def wrapper(*args, **kwargs):
# do something before running function
value = func(*args, **kwargs)
# do something after running function
# do something upon wrapper definition
return wrapper
# do something after creating decorator
return decorator
# advanced form that can either be argument-less or accept keyword arguments
def decorator_or_create_decorator(_func=None, *,
the_parameter='default value when not called with kwargs'):
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
if _func is None: # there are keywords
return decorator # return decorator
else: # no keywords
return decorator(_func) # return wrapper
# Stateful form
def stateful_decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
# update wrapper.some_property
print(f"Property value {wrapper.num_calls} of {func.__name__!r}")
return func(*args, **kwargs)
wrapper.some_property = 0
return wrapper
# stateful class form
class CountCalls:
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}")
return self.func(*args, **kwargs)
######### Registering functions ########
PLUGINS = dict()
def register(func):
"""Register a function as a plug-in"""
PLUGINS[func.__name__] = func
return func
@register
def say_hello(name):
return f"Hello {name}"
@register
def be_awesome(name):
return f"Yo {name}, together we are the awesomest!"
def randomly_greet(name):
greeter, greeter_func = random.choice(list(PLUGINS.items()))
print(f"Using {greeter!r}")
return greeter_func(name)
######### Flask ########
#### (better to use Flask-Login extension than to use this)
from flask import Flask, g, request, redirect, url_for
app = Flask(__name__)
def login_required(func):
"""Make sure user is logged in before proceeding"""
@functools.wraps(func)
def wrapper_login_required(*args, **kwargs):
if g.user is None:
return redirect(url_for("login", next=request.url))
return func(*args, **kwargs)
return wrapper_login_required
@app.route("/secret")
@login_required
def secret():
pass
######## Built-in class decorators ########
#### @property, @classmethod, @staticmethod
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 positive")
@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.
Class methods take the class as first parameter."""
return cls(1)
@staticmethod
def pi():
"""Value of π, could use math.pi instead though.
Static methods don't use anything inside the class."""
return 3.1415926535
######## Dataclass ########
from dataclasses import dataclass
@dataclass
class PlayingCard:
rank: str
suit: str
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment