(Этот материал — для начинающих пользователей.)
Декораторы в Питоне конфликтуют с функцией документации help — если применить декоратор, вывод функции help изменится. Чтобы увидеть, пишем простой декоратор:
И 2 функции, с и без декоратора:
Теперь проверяем:
Вместо y мы видим описание внутренней функции декоратора.
Если вы делаете свой модуль и хотите снова использовать его через некоторое время, то внутренняя документация будет очень полезной — чтобы узнать, как вызвать конкретную функцию, не надо будет читать исходный код модуля. Но если нужны декораторы, придётся искать какое-то решение…
У функций есть свойства func_doc, func_name, которые, в принципе, можно установить принудительно:
Но список аргументов всё равно остался от внутренней функции. (Ответ на замечание читателя kmike: декоратор functools.wraps делает то же самое с тем же результатом)
Есть 2 решения: «чистое» в виде модуля и «грязное» в виде хака от Алекса Мартелли:
Суть хака в том, чтобы записать все декорируемые функции в массив lookaside, а функцию inspect.getargspec (которая выдаёт данные о функции для help) заменяем другой, которая выдаёт записанные в lookaside данные, либо вызывает исходную getargspec.
Декораторы в Питоне конфликтуют с функцией документации help — если применить декоратор, вывод функции help изменится. Чтобы увидеть, пишем простой декоратор:
- def deco(fn):
- def z(*args, **kwargs):
- return fn(*args, **kwargs)
- return z
И 2 функции, с и без декоратора:
- def x(c, d):
- """Это X"""
- pass
- @deco
- def y(a, b = 20):
- """Это Y"""
- pass
Теперь проверяем:
Help on function x: >>> help(x) x(c, d) Это X >>> help(y) Help on function z: z(*args, **kwargs)
Вместо y мы видим описание внутренней функции декоратора.
Если вы делаете свой модуль и хотите снова использовать его через некоторое время, то внутренняя документация будет очень полезной — чтобы узнать, как вызвать конкретную функцию, не надо будет читать исходный код модуля. Но если нужны декораторы, придётся искать какое-то решение…
У функций есть свойства func_doc, func_name, которые, в принципе, можно установить принудительно:
- def deco(fn):
- def z(*args, **kwargs):
- return fn(*args, **kwargs)
- z.func_doc = fn.func_doc
- z.func_name = fn.func_name
- return z
- @deco
- def y(a, b = 20):
- """Это Y"""
- pass
>>> help(y) Help on function y: y(*args, **kwargs) # а должно быть (a, b = 20) Это Y</code>
Но список аргументов всё равно остался от внутренней функции. (Ответ на замечание читателя kmike: декоратор functools.wraps делает то же самое с тем же результатом)
Есть 2 решения: «чистое» в виде модуля и «грязное» в виде хака от Алекса Мартелли:
Модуль decorator
- from decorator import decorator
- @decorator
- def deco(fn):
- def z(*args, **kwargs):
- return fn(*args, **kwargs)
- return z
- @deco
- def y(a, b = 20):
- pass
>>> help(y) Help on function y: y(a, b = 20)
Хак Алекса Мартелли
Суть хака в том, чтобы записать все декорируемые функции в массив lookaside, а функцию inspect.getargspec (которая выдаёт данные о функции для help) заменяем другой, которая выдаёт записанные в lookaside данные, либо вызывает исходную getargspec.
- import functools, inspect
- realgas = inspect.getargspec
- lookaside = dict()
- def fakegas(f):
- if f in lookaside:
- return lookaside[f]
- return realgas(f)
- inspect.getargspec = fakegas
- def deco(fn):
- @functools.wraps(fn)
- def z(*args, **kwargs):
- return fn(*args, **kwargs)
- lookaside[z] = realgas(fn) # обратите внимание, что
- return z
- @deco
- def x(a, b=23):
- """Это X"""
- return a + b
help(x) Help on function x in module __main__: x(a, b=23) Some doc for x.