Кортежи (tuples) очень похожи на списки, но являются неизменяемыми. Как мы видели, использование изменяемых объектов может приводить к неприятным сюрпризам.
Кортежи пишутся в круглых скобках. Если элементов $>1$ или 0, это не вызывает проблем. Но как записать кортеж с одним элементом? Конструкция (x)
абсолютно легальна в любом месте любого выражения, и означает просто x
. Чтобы избежать неоднозначности, кортеж с одним элементом x
записывается в виде (x,)
.
(1,2,3)
()
(1,)
Скобки ставить не обязательно, если кортеж - единственная вещь в правой части присваивания.
t=1,2,3
t
Работать с кортежами можно так же, как со списками. Нельзя только изменять их.
len(t)
t[1]
u=4,5
t+u
2*u
В левой части присваивания можно написать несколько переменных через запятую, а в правой кортеж. Это одновременное присваивание значений нескольким переменным.
x,y=1,2
x
y
Сначала вычисляется кортеж в правой части, исходя из старых значений переменных (до этого присваивания). Потом одновременно всем переменным присваиваются новые значения из этого кортежа. Поэтому так можно обменять значения двух переменных.
x,y=y,x
x
y
Это проще, чем в других языках, где приходится использовать третью переменную.
В соответствии с математическими обозначениями, множества пишутся в фигурных скобках. Элемент может содержаться в множестве только один раз. Порядок элементов в множестве не имеет значения, поэтому питон их сортирует. Элементы множества могут быть любых типов. Множества используются существенно реже, чем списки. Но иногда они бывают весьма полезны. Например, когда я собирался делать апгрейд системы на сервере, я написал на питоне программу, которая строила множество пакетов, установленных в системе до апгрейда; множество пакетов, имеющихся на инсталляционных CD; имеющихся на основных сайтах с дополнительными пакетами, и т.д. И она мне помогла восстановить функциональность после апгрейда.
s={0,1,0,5,5,1,0}
s
Принадлежит ли элемент множеству?
1 in s, 2 in s, 1 not in s
Множество можно получить из списка, или строки, или любого объекта, который можно использовать в for
цикле (итерабельного).
l=[0,1,0,5,5,1,0]
set(l)
set('абба')
Как записать пустое множество? Только так.
set()
Дело в том, что в фигурных скобках в питоне пишутся также словари (мы будем их обсуждать в следующем параграфе). Когда в них есть хоть один элемент, можно отличить словарь от множества. Но пустые фигурные скобки означают пустой словарь.
{}
Работать с множествами можно как со списками.
len(s)
for x in s:
print(x)
Это генератор множества (set comprehension).
{i for i in range(5)}
Объединение множеств.
s2=s|{2,5}
s2
Проверка того, является ли одно множество подмножеством другого.
s<s2, s>s2, s<=s2, s>=s2
Пересечение.
s2&{1,2,3}
Разность и симметричная разность.
s2-{1,3,5}
s2^{1,3,5}
Множества (как и списки) являются изменяемыми объектами. Добавление элемента в множество и исключение из него.
s2.add(4)
s2
s2.remove(1)
s2
Как и в случае +=
, можно скомбинировать теоретико-множественную операцию с присваиванием.
s2|={1,2}
s2
Существуют также неизменяемые множества. Этот тип данных называется frozenset
. Операции над такими множествами подобны обычным, только невозможно изменять их (добавлять и исключать элементы).
Словарь содержит пары ключ - значение (их порядок несущественен). Это один из наиболее полезных и часто используемых типов данных в питоне.
d={'one':1,'two':2,'three':3}
d
Можно узнать значение, соответствующее некоторому ключу. Словари реализованы как хэш-таблицы, так что поиск даже в больших словарях очень эффективен. В языках низкого уровня (например, C) для построения хэш-таблиц требуется использовать внешние библиотеки и писать заметное количество кода. В скриптовых языках (perl, python, php) они уже встроены в язык, и использовать их очень легко.
d['two']
d['four']
Можно проверить, есть ли в словаре данный ключ.
'one' in d, 'four' in d
Можно присваивать значения как имеющимся ключам, так и отсутствующим (они добавятся к словарю).
d['one']=-1
d
d['four']=4
d
Длина - число ключей в словаре.
len(d)
Можно удалит ключ из словаря.
del d['two']
d
Метод get
, если он будет вызван с отсутствующим ключом, не приводит к ошибке, а возвращает специальный объект None
. Он используется всегда, когда необходимо указать, что объект отсутствует (в какой-то мере он аналогичен null
в C). Если передать методу get
второй аргумент - значение по умолчанию, то будет возвращаться это значение, а не None
.
d.get('one'),d.get('five')
d.get('one',0),d.get('five',0)
Словари обычно строят последовательно: начинают с пустого словаря, а затем добавляют ключи со значениями.
d={}
d
d['zero']=0
d
d['one']=1
d
А это генератор словаря (dictionary comprehension).
d={i:i**2 for i in range(5)}
d
Ключами могут быть любые неизменяемые объекты, например, целые числа, строки, кортежи.
d={}
d[0,0]=1
d[0,1]=0
d[1,0]=0
d[1,1]=-1
d
d[0,0]+d[1,1]
Словари, подобно спискам, можно использовать в for
циклах. Перебираются имеющиеся в словаре ключи (в каком-то непредсказуемом порядке).
d={'one':1,'two':2,'three':3}
for x in d:
print(x,' ',d[x])
Метод keys
возвращает список ключей, метод values
- список соответствующих значений (в том же порядке), а метод items
- список пар (ключ,значение). Точнее говоря, это не списки, а некоторые объекты, которые можно использовать в for
циклах или превратить в списки функцией list
. Если хочется написать цикл по упорядоченному списку ключей, то можно использовать sorted(d.keys))
.
d.keys(),d.values(),d.items()
for x in sorted(d.keys()):
print(x,' ',d[x])
for x,y in d.items():
print(x,' ',y)
del x,y
Что есть истина? И что есть ложь? Подойдём к этому философскому вопросу экспериментально.
bool(False),bool(True)
bool(None)
bool(0),bool(123)
bool(''),bool(' ')
bool([]),bool([0])
bool(set()),bool({0})
bool({}),bool({0:0})
На варажения, стоящие в булевых позициях (после if
, elif
и while
), неявно напускается функция bool
. Некоторые объекты интерпретируются как False
: число 0, пустая строка, пустой список, пустое множество, пустой словарь, None
и некоторые другие. Все остальные объекты интерпретируются как True
. В операторах if
или while
очень часто используется список, словарь или что-нибудь подобное, что означает делай что-то если этот список (словарь и т.д.) не пуст.
Заметим, что число с плавающей точкой 0.0 тоже интерпретируется как False
. Это использовать категорически не рекомендуется: вычисления с плавающей точкой всегда приближённые, и неизвестно, получите Вы 0.0 или 1.234E-12
.
Лучше напишите if abs(x)<epsilon:
.
Это простейшая в мире функция. Она не имеет параметров, ничего не делает и ничего не возвращает. Оператор pass
означает "ничего не делай"; он используется там, где синтаксически необходим оператор, а делать ничено не нужно (после if
или elif
, после def
и т.д.).
def f():
pass
f
pass
type(f)
r=f()
print(r)
Эта функция более полезна: она имеет параметр и что-то возвращает.
def f(x):
return x+1
f(1),f(1.0)
f('abc')
Если у функции много параметров, то возникает желание вызывать её попроще в наиболее часто встречающихся случаях. Для этого в операторе def
можно задать значения некоторых параметров по умолчанию (они должны размещаться в конце списка параметров). При вызове необходимо указать все обязательные параметры (у которых нет значений по умолчанию), а необязательные можно и не указывать. Если при вызове указывать параметры в виде имя=значение
, то это можно делать в любом порядке. Это гораздо удобнее, чем вспоминать, является данный параметр восьмым или девятым при вызове какой-нибудь сложной функции.
def f(x,a=0,b='b'):
print(x,' ',a,' ',b)
f(1.0)
f(1.0,1)
f(1.0,b='a')
f(1.0,b='a',a=2)
f(a=2,x=2.0)
Переменные, использующиеся в функции, являются локальными. Присваивание им не меняет значений глобальных переменных с такими же именами.
a=1
def f():
a=2
return a
f()
a
Если в функции нужно использовать какие-нибудь глобальные переменные, их нужно описать как global
.
def f():
global a
a=2
return a
f()
a
Пространство имён устанавливает соответствие между именами переменных и объектами - их значениями. Есть пространство имён локальных переменных функции, пространство имён глобальных переменных программы и пространство имён встроенных функций языка питон. Для реализации пространств имён используются словари.
Если функции передаётся в качестве аргумента какой-нибудь изменяемый объект, и функция его изменяет, то это изменение будет видно снаружи после этого вызова. Мы уже обсуждали эту ситуацию, когда две переменные (в данном случае глобальная переменная и параметр функции) указывают на один и тот же изменяемый объект объект.
def f(x,l):
l.append(x)
return l
l=[1,2,3]
f(0,l)
l
Если в качестве значения какого-нибудь параметра по умолчанию используется изменяемый объект, то это может приводить к неожиданным последствиям. В данном случае исполнение определения функции приводит к созданию двух объектов: собственно функции и объекта-списка, первоначально пустого, который используется для инициализации параметра функции при вызове. Функция изменяет этот объект. При следующем вызове он опять используется для инициализации параметра, но его значение уже изменилось.
def f(x,l=[]):
l.append(x)
return l
f(0)
f(1)
f(2)
Чтобы избежать таких сюрпризов, в качестве значений по умолчанию лучше использовать только неизменяемые объекты.
def f(x,l=None):
if l is None:
l=[]
l.append(x)
return l
f(0)
f(1)
f(2,[0,1])
Эта функция имеет один обязательный параметр плюс произвольное число необязательных. При вызове все такие дополнительные аргументы объединяются в кортеж, который функция может использовать по своему усмотрению.
def f(x,*l):
print(x,' ',l)
f(0)
f(0,1)
f(0,1,2)
f(0,1,2,3)
Звёздочку можно использовать и при вызове функции. Можно заранее построить список (или кортеж) аргументов, а потом вызвать функцию с этими аргументами.
l=[1,2]
c=('a','b')
f(*l,0,*c)
Такую распаковку из списков и кортежей можно использовать не только при вызове функции, но и при построении списка или кортежа.
(*l,0,*c)
[*l,0,*c]
[*l,3]
Эта функция имеет два обязательных параметра плюс произвольное число необязательных ключевых параметров. При вызове они должны задаваться в виде имя=значение
. Они собираются в словарь, который функция может использовать по своему усмотрению.
def f(x,y,**d):
print(x,' ',y,' ',d)
f(0,1,foo=2,bar=3)
Двойную звёздочку можно использовать и при вызове функции. Можно заранее построить словарь аргументов, сопоставляющий значения именам параметров, а потом вызвать функцию с этими ключевыми аргументами.
d={'foo':2,'bar':3}
f(0,1,**d)
d['x']=0
d['y']=1
f(**d)
Вот любопытный способ построить словарь с ключами-строками.
def f(**d):
return d
f(x=0,y=1,z=2)
Двойную звёздочку можно использовать не только при вызове функции, но и при построении словаря.
d={0:'a',1:'b'}
{**d,2:'c'}
Вот простой способ объединить два словаря.
d1={0:'a',1:'b'}
d2={2:'c',3:'d'}
{**d1,**d2}
Если один и тот же ключ встречается несколько раз, следующее значение затирает предыдущее.
d2={1:'B',2:'C'}
{**d1,3:'D',**d2,3:'d'}
Это наиболее общий вид списка параметров функции. Сначала идут обязательные параметры (в данном случае два), затем произвольное число необязательных (при вызове они будут объединены в кортеж), а затем произвольное число ключевых параметров (при вызове они будут объединены в словарь).
def f(x,y,*l,**d):
print(x,' ',y,' ',l,' ',d)
f(0,1,2,3,foo=4,bar=5)
В питоне функции являются гражданами первого сорта. Они могут присутствовать везде, где допустимы объекты других типов - среди элементов списков, значений в словарях и т.д.
def f0(x):
return x+2
def f1(x):
return 2*x
l=[f0,f1]
l
x=2.0
n=1
l[n](x)
Если Вы пишете функцию не для того, чтобы один раз её вызвать и навсегда забыть, то нужна документация, объясняющая, что эта функция делает. Для этого сразу после строчки def
пишется строка. Она называется док-строкой, и сохраняется при трансляции исходного текста на питоне в байт-код (в отличие от комментариев, которые при этом отбрасываются). Обычно эта строка заключается в тройные кавычки и занимает несколько строчек. Док-строка доступна как атрибут __doc__
функции, и используется функцией help
. Вот пример культурно написанной функции, вычисляющей $n$-е число Фибоначчи.
Для проверки типов аргументов, переданных функции, удобно использовать оператор assert
. Если условие в нём истинно, всё в порядке, и он ничего не делает; если же оно ложно, выдаётся сообщение об ошибке.
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
fib.__doc__
help(fib)
[fib(n) for n in range(1,10)]
fib(-1)
fib(2.0)