Функции

Это простейшая в мире функция. Она не имеет параметров, ничего не делает и ничего не возвращает. Оператор pass означает "ничего не делай"; он используется там, где синтаксически необходим оператор, а делать ничено не нужно (после if или elif, после def и т.д.).

In [1]:
def f():
    pass
In [2]:
f
Out[2]:
<function __main__.f>
In [3]:
pass
In [4]:
type(f)
Out[4]:
function
In [5]:
r=f()
print(r)
None

Эта функция более полезна: она имеет параметр и что-то возвращает.

In [6]:
def f(x):
    return x+1
In [7]:
f(1),f(1.0)
Out[7]:
(2, 2.0)
In [8]:
f('abc')
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-8-410386031a44> in <module>()
----> 1 f('abc')

<ipython-input-6-e9c32f618734> in f(x)
      1 def f(x):
----> 2     return x+1

TypeError: must be str, not int

Если у функции много параметров, то возникает желание вызывать её попроще в наиболее часто встречающихся случаях. Для этого в операторе def можно задать значения некоторых параметров по умолчанию (они должны размещаться в конце списка параметров). При вызове необходимо указать все обязательные параметры (у которых нет значений по умолчанию), а необязательные можно и не указывать. Если при вызове указывать параметры в виде имя=значение, то это можно делать в любом порядке. Это гораздо удобнее, чем вспоминать, является данный параметр восьмым или девятым при вызове какой-нибудь сложной функции.

In [9]:
def f(x,a=0,b='b'):
    print(x,'  ',a,'  ',b)
In [10]:
f(1.0)
1.0    0    b
In [11]:
f(1.0,1)
1.0    1    b
In [12]:
f(1.0,b='a')
1.0    0    a
In [13]:
f(1.0,b='a',a=2)
1.0    2    a
In [14]:
f(a=2,x=2.0)
2.0    2    b

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

In [15]:
a=1
In [16]:
def f():
    a=2
    return a
In [17]:
f()
Out[17]:
2
In [18]:
a
Out[18]:
1

Если в функции нужно использовать какие-нибудь глобальные переменные, их нужно описать как global.

In [19]:
def f():
    global a
    a=2
    return a
In [20]:
f()
Out[20]:
2
In [21]:
a
Out[21]:
2

Пространство имён устанавливает соответствие между именами переменных и объектами - их значениями. Есть пространство имён локальных переменных функции, пространство имён глобальных переменных программы и пространство имён встроенных функций языка питон. Для реализации пространств имён используются словари.

Если функции передаётся в качестве аргумента какой-нибудь изменяемый объект, и функция его изменяет, то это изменение будет видно снаружи после этого вызова. Мы уже обсуждали эту ситуацию, когда две переменные (в данном случае глобальная переменная и параметр функции) указывают на один и тот же изменяемый объект объект.

In [22]:
def f(x,l):
    l.append(x)
    return l
In [23]:
l=[1,2,3]
f(0,l)
Out[23]:
[1, 2, 3, 0]
In [24]:
l
Out[24]:
[1, 2, 3, 0]

Если в качестве значения какого-нибудь параметра по умолчанию используется изменяемый объект, то это может приводить к неожиданным последствиям. В данном случае исполнение определения функции приводит к созданию двух объектов: собственно функции и объекта-списка, первоначально пустого, который используется для инициализации параметра функции при вызове. Функция изменяет этот объект. При следующем вызове он опять используется для инициализации параметра, но его значение уже изменилось.

In [25]:
def f(x,l=[]):
    l.append(x)
    return l
In [26]:
f(0)
Out[26]:
[0]
In [27]:
f(1)
Out[27]:
[0, 1]
In [28]:
f(2)
Out[28]:
[0, 1, 2]

Чтобы избежать таких сюрпризов, в качестве значений по умолчанию лучше использовать только неизменяемые объекты.

In [29]:
def f(x,l=None):
    if l is None:
        l=[]
    l.append(x)
    return l
In [30]:
f(0)
Out[30]:
[0]
In [31]:
f(1)
Out[31]:
[1]
In [32]:
f(2,[0,1])
Out[32]:
[0, 1, 2]

Эта функция имеет один обязательный параметр плюс произвольное число необязательных. При вызове все такие дополнительные аргументы объединяются в кортеж, который функция может использовать по своему усмотрению.

In [33]:
def f(x,*l):
    print(x,'  ',l)
In [34]:
f(0)
0    ()
In [35]:
f(0,1)
0    (1,)
In [36]:
f(0,1,2)
0    (1, 2)
In [37]:
f(0,1,2,3)
0    (1, 2, 3)

Звёздочку можно использовать и при вызове функции. Можно заранее построить список (или кортеж) аргументов, а потом вызвать функцию с этими аргументами.

In [38]:
l=[1,2]
f(*l)
1    (2,)
In [39]:
c=('a','b')
f(*l,0,*c)
1    (2, 0, 'a', 'b')

Такую распаковку из списков и кортежей можно использовать не только при вызове функции, но и при построении списка или кортежа.

In [40]:
(*l,0,*c)
Out[40]:
(1, 2, 0, 'a', 'b')
In [41]:
[*l,0,*c]
Out[41]:
[1, 2, 0, 'a', 'b']
In [42]:
[*l,3]
Out[42]:
[1, 2, 3]
In [43]:
[3,*l]
Out[43]:
[3, 1, 2]

Эта функция имеет два обязательных параметра плюс произвольное число необязательных ключевых параметров. При вызове они должны задаваться в виде имя=значение. Они собираются в словарь, который функция может использовать по своему усмотрению.

In [44]:
def f(x,y,**d):
    print(x,'  ',y,'  ',d)
In [45]:
f(0,1,foo=2,bar=3)
0    1    {'foo': 2, 'bar': 3}

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

In [46]:
d={'foo':2,'bar':3}
f(0,1,**d)
0    1    {'foo': 2, 'bar': 3}
In [47]:
d['x']=0
d['y']=1
f(**d)
0    1    {'foo': 2, 'bar': 3}

Вот любопытный способ построить словарь с ключами-строками.

In [48]:
def f(**d):
    return d
In [49]:
f(x=0,y=1,z=2)
Out[49]:
{'x': 0, 'y': 1, 'z': 2}

Двойную звёздочку можно использовать не только при вызове функции, но и при построении словаря.

In [50]:
d={0:'a',1:'b'}
{**d,2:'c'}
Out[50]:
{0: 'a', 1: 'b', 2: 'c'}

Вот простой способ объединить два словаря.

In [51]:
d1={0:'a',1:'b'}
d2={2:'c',3:'d'}
{**d1,**d2}
Out[51]:
{0: 'a', 1: 'b', 2: 'c', 3: 'd'}

Если один и тот же ключ встречается несколько раз, следующее значение затирает предыдущее.

In [52]:
d2={1:'B',2:'C'}
{**d1,3:'D',**d2,3:'d'}
Out[52]:
{0: 'a', 1: 'B', 2: 'C', 3: 'd'}

Это наиболее общий вид списка параметров функции. Сначала идут обязательные параметры (в данном случае два), затем произвольное число необязательных (при вызове они будут объединены в кортеж), а затем произвольное число ключевых параметров (при вызове они будут объединены в словарь).

In [53]:
def f(x,y,*l,**d):
    print(x,'  ',y,'  ',l,'  ',d)
In [54]:
f(0,1,2,3,foo=4,bar=5)
0    1    (2, 3)    {'foo': 4, 'bar': 5}

Функции можно передать функцию в качестве аргумента. Например, эта функция реализует численное интегрирование по формуле Симпсона. Её первый параметр - функция, которую надо проинтегрировать; далее задаются пределы интегрирования и число интервалов, на которое нужно разбить область интегрирования.

In [55]:
def simpson(f,a,b,n):
    h=(b-a)/(2*n)
    s=0.5*(f(a)+f(b))+2*f(a+h)
    x=a+2*h
    for i in range(n-1):
        s+=f(x)+2*f(x+h)
        x+=2*h
    return 2/3*h*s
In [56]:
from math import sin,pi
[simpson(sin,0,pi,n) for n in [1,10,100,1000]]
Out[56]:
[2.0943951023931953, 2.0000067844418012, 2.000000000676474, 2.000000000000091]

В питоне функции являются гражданами первого сорта. Они могут присутствовать везде, где допустимы объекты других типов - среди элементов списков, значений в словарях и т.д.

In [57]:
def f0(x):
    return x+2
In [58]:
def f1(x):
    return 2*x
In [59]:
l=[f0,f1]
l
Out[59]:
[<function __main__.f0>, <function __main__.f1>]
In [60]:
x=2.0
n=1
l[n](x)
Out[60]:
4.0

Если Вы пишете функцию не для того, чтобы один раз её вызвать и навсегда забыть, то нужна документация, объясняющая, что эта функция делает. Для этого сразу после строчки def пишется строка. Она называется док-строкой, и сохраняется при трансляции исходного текста на питоне в байт-код (в отличие от комментариев, которые при этом отбрасываются). Обычно эта строка заключается в тройные кавычки и занимает несколько строчек. Док-строка доступна как атрибут __doc__ функции, и используется функцией help. Вот пример культурно написанной функции, вычисляющей $n$-е число Фибоначчи.

Для проверки типов аргументов, переданных функции, удобно использовать оператор assert. Если условие в нём истинно, всё в порядке, и он ничего не делает; если же оно ложно, выдаётся сообщение об ошибке.

In [61]:
def fib(n):
    "вычисляет n-е число Фибоначчи"
    assert type(n) is int and n>0
    if n<=2:
        return 1
    x,y=1,1
    for i in range(n-2):
        x,y=y,x+y
    return y
In [62]:
fib.__doc__
Out[62]:
'вычисляет n-е число Фибоначчи'
In [63]:
help(fib)
Help on function fib in module __main__:

fib(n)
    вычисляет n-е число Фибоначчи

In [64]:
[fib(n) for n in range(1,10)]
Out[64]:
[1, 1, 2, 3, 5, 8, 13, 21, 34]
In [65]:
fib(-1)
---------------------------------------------------------------------------
AssertionError                            Traceback (most recent call last)
<ipython-input-65-b876e14fb318> in <module>()
----> 1 fib(-1)

<ipython-input-61-800ccd3a2d90> in fib(n)
      1 def fib(n):
      2     "вычисляет n-е число Фибоначчи"
----> 3     assert type(n) is int and n>0
      4     if n<=2:
      5         return 1

AssertionError: 
In [66]:
fib(2.0)
---------------------------------------------------------------------------
AssertionError                            Traceback (most recent call last)
<ipython-input-66-363564e722ae> in <module>()
----> 1 fib(2.0)

<ipython-input-61-800ccd3a2d90> in fib(n)
      1 def fib(n):
      2     "вычисляет n-е число Фибоначчи"
----> 3     assert type(n) is int and n>0
      4     if n<=2:
      5         return 1

AssertionError: