Napsat ručně dekorátor není těžké, ale je to pracné.
Je tam vhodné myslet i na takové věci jako správné předávání pozičních a jmenných argumentů nebo kopírování doc stringu a názvu funkce (kvůli helpu a REPL uživatelům).
Funcy to dělá takto: https://github.com/Suor/funcy/blob/master/funcy/decorators.py
Uvnitř se pak používá standardní functools.wraps: https://docs.python.org/3/library/functools.html#functools.wraps
jj a potom jim ty dekorátory vychází krásně čitelně, jako například ten retry (kterej je z nich nejsložitjěší a přitom pěkně pochopitelnej):
@decorator
def retry(call, tries, errors=Exception, timeout=0, filter_errors=None):
"""Makes decorated function retry up to tries times.
Retries only on specified errors.
Sleeps timeout or timeout(attempt) seconds between tries."""
errors = _ensure_exceptable(errors)
for attempt in range(tries):
try:
return call()
except errors as e:
if not (filter_errors is None or filter_errors(e)):
raise
# Reraise error on last attempt
if attempt + 1 == tries:
raise
else:
timeout_value = timeout(attempt) if callable(timeout) else timeout
if timeout_value > 0:
time.sleep(timeout_value)
Nejen kvůli helpu a REPLu, naposledy jsem zjistil, že mi v dekorátoru chybí wraps až ve chvíli, kdy mi sphinx tvrdil, že metoda má signaturu (*args, **kwargs) -> Any. Taková dokumentace je pak na ho-dně pečlivé zvážení, jestli jí vůbec dělat nebo ne.
V tomhle se mi funcy opravdu líbí, všechno to chodí už od výroby. Totéž silent. Odchytává Exception, zatímco "prázdný" except: vlastně znamená except BaseException. Co z toho budeme potřebovat častěji, námořníku? Kávu, nebo to sádlo?