16 ноября 2012 г.

Аргументы, углубляемся, продолжение


Передача аргументов, доходим до конца

Исчезнувшая встроенная функция apply (Python 2.6):
До появления версии Python 3.0 того же эффекта, который дает использование синтаксиса
*args и **args в вызовах функций, можно было добиться с помощью встроенной функции apply. Данная возможность убрана в версии 3.0, но осталась в версии 2.х и 2.6. Проще говоря, следующие две инструкции являются эквивалентными в версиях Python ниже версии 3.0:


func(*pargs, **kargs)     # Новейший синтаксис вызова:
                          #func(*sequence, **dict)

apply(func, pargs, kargs) # Устаревшая функция: 
                          #apply(func, sequence, dict)

В  качестве  примера  рассмотрим  следующую  функцию,  которая  принимает произвольное число позиционных и именованных аргументов:
>>> def echo(*args, **kwargs): print(args, kwargs)
...
>>> echo(1, 2, a=3, b=4)
(1, 2) {‘a’: 3, ‘b’: 4}

С использование функции  apply предыдущий код выглядел бы так:

>>> pargs = (1, 2)
>>> kargs = {‘a’:3, ‘b’:4}

>>> apply(echo, pargs, kargs)
(1, 2) {‘a’: 3, ‘b’: 4}

>>> echo(*pargs, **kargs)
(1, 2) {‘a’: 3, ‘b’: 4}



-----------------------------------------------------------------


Python 3.0: аргументы, которые могут передаваться только по именам
*args - синтаксис аргумента который может передаваться только по именам
Например, в следующем фрагменте аргумент a может передаваться как именованный или как позиционный аргумент, в b собираются все дополнительные позиционные аргументы и аргумент c может передаваться только как именованный аргумент:
>>> def kwonly(a, *b, c):
...     print(a, b, c)
...
>>> kwonly(1, 2, c=3)
1 (2,) 3
>>> kwonly(a=1, c=3)
1 () 3
>>> kwonly(1, 2, 3)
TypeError: kwonly() needs keyword-only argument c
-----------------------------------------------------------------


Чтобы показать, что функция не принимает списки аргументов произвольной длины, можно использовать одиночный символ *, при этом она  ожидает, что все следующие за звездочкой аргументы будут передаваться по именам.

>>> def kwonly(a, *, b, c):
...     print(a, b, c)
...
>>> kwonly(1, c=3, b=2)
1 2 3
>>> kwonly(c=3, b=2, a=1)
1 2 3
>>> kwonly(1, 2, 3)
TypeError: kwonly() takes exactly 1 positional argument (3 given)
>>> kwonly(1)
TypeError: kwonly() needs keyword-only argument b
В данном примере показано что аргументы b и c, могут передаваться только по именам, так как в функции они стоят после аргумента *. 
-----------------------------------------------------------------

Для именованных аргументов возможно использовать значения по умолчанию, не смотря на то что они стоят после *:
>>> def kwonly(a, *, b=’spam’, c=’ham’):
...     print(a, b, c)
...
>>> kwonly(1)
1 spam ham
>>> kwonly(1, c=3)
1 spam 3
>>> kwonly(a=1)
1 spam ham
>>> kwonly(c=3, b=2, a=1)
1 2 3
>>> kwonly(1, 2)
TypeError: kwonly() takes exactly 1 positional argument (2 given)
-----------------------------------------------------------------

Именованные аргументы которым значение не задано по умолчанию, являются обязательными для вызова в функции:
>>> def kwonly(a, *, b, c=’spam’):
...     print(a, b, c)
...
>>> kwonly(1, b=’eggs’)
1 eggs spam
>>> kwonly(1, c=’eggs’)
TypeError: kwonly() needs keyword-only argument b
>>> kwonly(1, 2)
TypeError: kwonly() takes exactly 1 positional argument (2 given)

>>> def kwonly(a, *, b=1, c, d=2):
...     print(a, b, c, d)
...
>>> kwonly(3, c=4)
3 1 4 2
>>> kwonly(3, c=4, b=5)
3 5 4 2
>>> kwonly(3)
TypeError: kwonly() needs keyword-only argument c
>>> kwonly(1, 2, 3)
TypeError: kwonly() takes exactly 1 positional argument (3 given)
В данном примере именованному аргументу b нужно обязательно давать значение при вызове функции kwonly(1, b=’eggs’).
-----------------------------------------------------------------

Правила, определяющие порядок следования:
Важно отметить, что аргументы, которые передаются только по именам, должны указываться  после одиночного символа звездочки, но не двойного; эти аргументы не могут располагаться после формы **args представления списка именованных аргументов произвольной длины, и пара символов ** без следующего за ними имени аргумента также не может появляться в списке аргументов. В обоих случаях будет выведено сообщение о синтаксической ошибке:
>>> def kwonly(a, **pargs, b, c):
SyntaxError: invalid syntax
>>> def kwonly(a, **, b, c):
SyntaxError: invalid syntax


Для того чтоб не было данных ошибок, следует расставлять именованные аргументы перед аргументом с символом **.

>>> def f(a, *b, **d, c=6): print(a, b, c, d) # Только #именованные аргументы 
SyntaxError: invalid syntax  # должны предшествовать **!
>>> def f(a, *b, c=6, **d): print(a, b, c, d) # Коллекции #аргументов 
...                                           # в заголовке
>>> f(1, 2, 3, x=4, y=5)# Используется значение по умолчанию
1 (2, 3) 6 {‘y’: 5, ‘x’: 4}
>>> f(1, 2, 3, x=4, y=5, c=7)# Переопределение значения по #умолчанию
1 (2, 3) 7 {‘y’: 5, ‘x’: 4}
>>> f(1, 2, 3, c=7, x=4, y=5)# Среди именованных аргументов
1 (2, 3) 7 {‘y’: 5, ‘x’: 4}
>>> def f(a, c=6, *b, **d): print(a, b, c, d)  # c не является #только
...                                 # именованным аргументом!
>>> f(1, 2, 3, x=4)
1 (3,) 2 {‘x’: 4}

-----------------------------------------------------------------


Аргументы, которые  могут  передаваться  только  по  именам,  могут располагаться  как перед формой *args, так  и после нее,  а также могут  включаться в словарь **args:
>>> def f(a, *b, c=6, **d): print(a, b, c, d) # Только #именованные аргументы
...                                           # между * и **

>>> f(1, *(2, 3), **dict(x=4, y=5))# Распаковывание аргументов 
1 (2, 3) 6 {‘y’: 5, ‘x’: 4}                   # при вызове

>>> f(1, *(2, 3), **dict(x=4, y=5), c=7)# Именованные аргументы 
SyntaxError: invalid syntax     # после **args!

>>> f(1, *(2, 3), c=7, **dict(x=4, y=5))#Переопределение значений 
1 (2, 3) 7 {‘y’: 5, ‘x’: 4}                   # по умолчанию

>>> f(1, c=7, *(2, 3), **dict(x=4, y=5)) # Перед * или после нее

1 (2, 3) 7 {‘y’: 5, ‘x’: 4}

>>> f(1, *(2, 3), **dict(x=4, y=5, c=7))# Только именованные #аргументы 
1 (2, 3) 7 {‘y’: 5, ‘x’: 4}                   # внутри **
-----------------------------------------------------------------

Когда используются аргументы,  которые могут передаваться только по именам? 
Если говорить кратко, они упрощают создание  функций,  которые  принимают  произвольное  количество  позиционных аргументов  и  параметры  настройки,  передаваемые  в  виде  именованных  аргументов. Аргументы, которые передаются только по именам, можно и не использовать, но без их использования может потребоваться выполнить лишнюю работу, чтобы определить значения по умолчанию для таких параметров и проверять, что не было передано лишних именованных аргументов.
Представьте функцию, которая обрабатывает множество передаваемых ей объектов и дополнительно принимает флаг трассировки:
process(X, Y, Z)           # Используется значение флага по умолчанию
process(X, Y, notify=True) # значение флага определяется явно
Без использования аргумента, который может передаваться только по имени, 
нам пришлось бы использовать обе формы, *args и **args, и вручную проверять 
именованные аргументы, а благодаря аргументу, который может передаваться 
только по имени, программный код получится компактнее.
-----------------------------------------------------------------
На этом мы заканчиваем рассмотрение темы аргументов, простите за то, что в посте много букофф... до скорого!

Комментариев нет:

Отправить комментарий