Kodomo

Пользователь

Учебная страница курса биоинформатики,
год поступления 2011

Словари, кортежи, параметры командной строки

Словари

Синонимов названию масса: словари (dict, dictionary), отображения (map, mapping), хэш-таблицы (hash table), ассоциативные массивы (associative array) – все эти названия понимаются более-менее одинаково.

Суть простая: питон хранит список пар ключ '(уникальный!)' -> значение, и умеет по ключу добыть значение, записать новое значение, добавить в список этих пар новую или удалить существующую.

Пишется это так:

   1 >>> x = dict() # так оно называется с т.з. питона
   2 >>> x = {} # лучше писать всегда так
   3 >>> x[1] = 2 # добавили элемент в словарь
   4 >>> print(x)
   5 { 1: 2 }
   6 >>> x["hello"] = "world" # добавили ещё элемент в словарь
   7 >>> print(x)
   8 { 'hello': 'world', 1: 2 }
   9 >>> print(x[1])
  10 2
  11 >>> x[1] = 3 # изменили элемент словаря
  12 { 1: 3, 'hello': 'world' }
  13 >>> # мы никогда не знаем, в каком порядке хранятся значения в словаре
  14 >>> del x['hello'] # удалили элемент словаря
  15 >>> y = { 1: 2, 'hello': "world" } # а можно создавать словарь сразу так

Если мы хотим перебрать всё содержимое словаря, мы можем воспользоваться циклом for. Когда словарь попадает в то место, где питон ожидает списка, то он интерпретируется как список всех ключей (в произвольном порядке). Примеры:

   1 >>> for key in y:
   2 ...     print key, y[key]
   3 hello world
   4 1 2
   5 >>> list(y) # список ключей словаря
   6 ['hello', 1]
   7 >>> 1 in y # является ли 1 ключом в словаре y?
   8 True
   9 >>> 2 in y
  10 False
  11 >>> "hello" in y
  12 True
  13 >>> "world" in y
  14 False

<!> Вопрос на засыпку: как распечатать содержимое словаря в порядке возрастания ключей?

Питон старается преследовать насколько это осмысленно (но не более) простоту и универсальность записи. Обратите внимание, что основной синтаксис работы со словарями такой же, как и со списками: хранилище[место в хранилище] = значение или значение = хранилище[место в хранилище]:

   1 >>> x = [1, 2, 3]
   2 >>> y = { 0: 1, 1: 2, 2: 3 }
   3 >>> print(x[0])
   4 1
   5 >>> print(y[0])
   6 1
   7 >>> x[1] = 5
   8 >>> y[1] = 5
   9 >>> print(x[1])
  10 5
  11 >>> print(y[1])
  12 5
  13 >>> print(x)
  14 [1, 5, 3]
  15 >>> print(y)
  16 { 1: 5, 0: 1, 2: 3 }

<!> Вопрос на засыпку: чем в данном случае отличается dict от list? (Два простых примера: как выглядят присваивание за границы существующего, цикл? Какие ещё отличия можете придумать?)

Метод get()

За дальнейшей информацией о том, как работать со словарями, я вас отправляю к хелпам (интересного там разве что update, get, setdefault, pop), но про один метод всё-таки расскажу:

Метод d.get(key,value) для словаря читает элемент по ключу key. (То же самое, что квадратные скобочки после имени словаря). Однако, если элемента в словаре нету, то он не выдаст ошибку, а вернёт свой второй аргумент value (или None, если он опущен).

   1 >>> d = { 1: 2, 3: 4 }
   2 >>> d.get(1, 5) # то же самое, что d[1], так как 1 в d есть
   3 2
   4 >>> d.get(2, 5) # ключа 2 в словаре нету
   5 5

Соответственно, довольно часто встречается такая идиома:

   1 def f(d, k):
   2   d[k] = d.get(k, []) + [1]
   3 # Что делает функция f? Ответьте на примере:
   4 >>> x = { 1: [1] }
   5 >>> f(x, 1)
   6 >>> f(x, 2)
   7 >>> print(x)

Метод get никогда не меняет словарь. Аналогичная штука, которая записывает значение в словарь, зовётся setdefault.

Тупли (кортежи, tuples)

По сути это то же самое, что и список, только тупли нельзя менять.

   1 >>> x = (1, 2, 3) # чаще всего кортежи создают как список значений в скобках
   2 >>> x = 1, 2, 3 # но на самом деле, скобки здесь имеют тот же смысл, что и в арифметических выражениях

Если нам нужен тупль из этого элемента, то это можно сделать только так:

   1 >>> x = (1,)
   2 >>> x = (1, 2, 3)
   3 >>> for i in x:
   4 ...     print(i)
   5 1
   6 2
   7 3
   8 >>> 1 in x
   9 True
  10 >>> x[0]
  11 1
  12 >>> x[0] = 2
  13 ...
  14 TypeError: 'tuple' object does not support item assignment

Такая вот ограниченная и простая штука.

Тупли можно использовать в качестве ключей в словарях (пример — разреженная матрица).

Двойное присваивание

Есть такая удобная штука - tuple unpacking:

   1 >>> x = [1, 2, 3]
   2 >>> a, b, c = x

или:

   1 >>> a, b, c = 1, 2, 3

Тут полезно понимать следующее: сначала питон полностью исполняет то, что в правой части присваивания (собирает кортеж), а потом распаковывает получившийся котеж слева. Это значит, что мы можем в правой и в левой части использовать одни и те же переменные:

   1 >>> x = 1
   2 >>> y = 2
   3 >>> x, y = y, x
   4 >>> print(x)
   5 2
   6 >>> print(y)
   7 1

Возврат нескольких значений из функции

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

   1 def coords(time):
   2     return sin(time), cos(time)
   3 ...
   4 x, y = coords(10)

Разбор командной строки с помощью optparse

Когда мы вызываем программу через командную строку, мы можем также указывать ее параметры (если они есть).

Например, вспомним команду ls. Ее можно вызвать с различными параметрами.

ls -lR
ls --long --recursive
ls -l -R

Все три записи выше равнозначны между собой.

ls --sort=time # параметры могут принимать какое-то значение
ls a.txt b.txt

Если параметр написан через знак дефиса, то он называется именованным, иначе – позиционным.

В переменной sys.argv содержится список строк - все то, что было написано в командной строке (разделенное по пробелам).

Например, если мы напишем маленькую программку test_argv.py:

   1 import sys
   2 print sys.argv

И запустим ее с какими-нибудь параметрами, то получится так:

   1 $ python test_argv.py file.txt -r -w 5
   2 ['test_argv.py', 'file.txt', '-r', '-w', '5']

Для того, чтобы автоматизировать разбор командной строки подобно программе ls, надо импортировать библиотеку optparse.

   1 import optparse
   2 parser=optparse.OptionParser()

parser - это так называемый объект-разборщик (далее будем называть его просто парсером).

Разберем это на примере программы сортировки (пусть будет называться sort.py).

Пусть для нее можно будет указывать три параметра:

-r --reverse
-w --word
-a --sort-alg

Для этого надо прописать:

   1 import optparse
   2 import sys
   3 parser=optparse.OptionParser()
   4 parser.add_option('-w', '--word', help='word number to sort by', type='int')
   5 parser.add_option('-r', '--reverse', action='store_true', help='reverse sort order')
   6 parser.add_option('-a', '--sort-alg', dest='alg', help='sorting algorithm; can be "qsort" (qsort) or "merge" (merge-sort)', type='string', default='qsort')
   7 options, args=parser.parse_args()
   8 print options.word
   9 print options.reverse
  10 print options.alg # значение положилось в options.alg, как это указано в 'dest'
  11 print args
  12 print sys.argv

Итак, парсер будет разбирать слово, стоящее после записи '-w' ('--word'), '-a' ('--sort-alg') или '-r' ('--reverse'). Для '-w' оно должно быть целым числом, для '-a' - строкой и по умолчанию равно 'qsort'.

Фраза action='store_true' означает, что данный параметр является флагом. Если бы этого не было, то параметр нёс бы какое-то значение.

В args будут записываться все то, что парсер не смог разобрать.

Рассмотрим диалог с командной строкой:

$ python sort.py
None
None
qsort
[]
['sort.py']
$ python sort.py -r -w 1 -a merge a.txt
1
True
merge
['a.txt']
['sort.py', '-r', '-w', '1', '-a', 'merge', 'a.txt']
$ python sort.py --help
Usage: sort.py [options]

Options:
  -h, --help            show this help message and exit
  -w WORD, --word=WORD  word number to sort by
  -r, --reverse         reverse sort order
  -a ALG, --sort-alg=ALG
                        sorting algorithm; can be "qsort" (qsort) or "merge"
                        (merge-sort)
$ python sort.py -x
sort.py [options]
sort.py: error: no such option: -x

Мы можем захотеть как-то реагировать на отсутствие значения для обязательного параметра (например, параметра '-w'):

   1 import optparse
   2 import sys
   3 parser=optparse.OptionParser()
   4 parser.add_option('-w', '--word', help='word number to sort by', type='int')
   5 parser.add_option('-r', '--reverse', action='store_true', help='reverse sort order')
   6 parser.add_option('-a', '--sort-alg', dest='alg', help='sorting algorithm; can be "qsort" (qsort) or "merge" (merge-sort)', type='string', default='qsort')
   7 options, args=parser.parse_args()
   8 print options.word
   9 if not options.word:
  10     parser.error('Word number is not given')

Тогда диалог с командной строкой будет выглядеть так:

$ python sort.py
None
Usage: sort.py [options]

sort.py: error: Word number is not given

$ python sort.py -w 3
3