>>> Значение параметра по умолчанию
В следующем определении функции параметр c
имеет значение по умолчанию:
>>> def fun(a, b, c=True):
... return a if c else b
...
>>> fun(1, 2)
1
>>> fun(1, 2, 2<1)
2
Значения параметров по умолчанию вычисляются один раз в момент выполнения предложения def
. Из этого следует, что
- в них можно использовать видимые на этот момент имена и
- они могут изменяться уже после определения функции, если являются изменяемыми (mutable) объектами.
Например:
>>> def fun(a, b=[]):
... b.append(a)
... print(b)
...
>>> fun(1)
[1]
>>> fun(2)
[1, 2]
>>> fun(3)
[1, 2, 3]
Чтобы избежать такого эффекта, не используйте изменяемые объекты в качестве значений параметров по умолчанию.
>>> Цирк?
Используя возможности распаковки и упаковки аргументов при вызове функции, можно написать универсальную функцию для вызова любой функции с произвольными аргументами. Например, функция callfun
выводит имя вызываемой функции и значения агрументов, после чего вызывает функцию и возвращает результат:
>>> def callfun(fun, *args, **kwargs):
... print('Call %s with %s, %s' % (fun, args, kwargs))
... return fun(*args, **kwargs)
...
>>> callfun(iter, [])
Call <built-in function iter> with ([],), {}
<list_iterator object at 0x0000019E664B04A8>
>>> callfun(range, 10, 16)
Call <class 'range'> with (10, 16), {}
range(10, 16)
>>> callfun(print, 'hello', 'world', end='!\n')
Call <built-in function print> with ('hello', 'world'), {'end': '!\n'}
hello world!
Используя эти же возможности, можно организовать вызовы разных функций с разными аргументами в цикле:
>>> funs = (
... (iter, ([], ), {}),
... (range, (10, 16), {}),
... (print, ('hello', 'world'), {'end': '!\n'})
... )
>>>
>>> for f, a, k in funs:
... f(*a, **k)
...
<list_iterator object at 0x000001FFA3B80780>
range(10, 16)
hello world!
>>> Господин декоратор
Для всякой функции можно создать функцию-обертку, которая, помимо вызова этой функции, будет делать что-то еще, например, тот же вывод на печать имени и агрументов при вызове:
>>> def wrap(fun):
... def f(*args, **kwargs):
... print('Call %s with %s, %s' % (fun, args, kwargs))
... return fun(*args, **kwargs)
... return f
...
>>> def hello(name):
... return 'hello ' + name
...
>>> hello('Андрей')
'hello Андрей'
>>>
>>> wrapped = wrap(hello)
>>> wrapped('Андрей')
Call <function hello at 0x000001BAE285A950> with ('Андрей',), {}
'hello Андрей'
А если функцию-обертку присвоить имени, связанному с обернутой функцией, то произойдет по сути замена изначальной функции на функцию-обертку:
>>> hello = wrap(hello)
>>> hello('Андрей')
Call <function hello at 0x000001BAE285A950> with ('Андрей',), {}
'hello Андрей'
Именно это делают декораторы в Python.
>>> Разоблачение декоратора
Назначение декораторов в Python – добавить в ваши функции дополнительную функциональность или как-то иначе обработать ваши функции. Функция wrap
из предыдущего лмнта – это готовый декоратор:
>>> def wrap(fun):
... def f(*args, **kwargs):
... print('Call %s with %s, %s' % (fun, args, kwargs))
... return fun(*args, **kwargs)
... return f
Для декорирования функций используется такой синтаксис:
>>> @wrap
... def hi(name):
... return 'hi ' + name
...
>>> hi('Иван')
Call <function hi at 0x000001BAE285AAE8> with ('Иван',), {}
'hi Иван'
Обратите внимание, что после обработки декоратором функция, связанная с именем hi
, – это на самом деле функция-обертка f
:
>>> hi.__name__
'f'
>>> Маскировка декоратора
У функции-обёртки не только атрибут __name__
отличается от соответствующего атрибута обёрнутой функции, но и другие атрибуты, среди которых, в частности, __module__
, __doc__
и __dict__
. Для того, чтобы обновить значения этих атрибутов у функции-обёртки и сделать ее более похожей на обёрнутую функцию, модуль functools
стандартной библиотеки Python предлагает декоратор wraps
:
>>> from functools import wraps
>>>
>>> def deco(fun):
... @wraps(fun)
... def f(*args, **kwargs):
... """Wrap function"""
... print('Calling', fun)
... return fun(*args, **kwargs)
... return f
...
>>> @deco
... def hi(name):
... """Greet named person"""
... return 'hi, ' + name
...
>>> hi('Иван')
Calling <function hi at 0x000002987E2402F0>
'hi, Иван'
>>>
>>> hi.__name__
'hi'
>>> hi.__doc__
'Greet named person'
Комментариев нет:
Отправить комментарий