Kodomo

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

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

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

Словари

Синонимов названию масса: словари (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 >>> y = { 1: 2, 'hello': "world" } # а можно создавать словарь сразу так
  15 >>> del x[1] # удалили элемент словаря

Если мы хотим перебрать всё содержимое словаря, мы можем воспользоваться циклом 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
   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 {{file:///C:/DOCUME~1/dendik/LOCALS~1/Temp/msohtmlclip1/01/clip_image001.gif||height="15",width="15"}} Что делает функция 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 >>> for i in x:
   3 ...     print(i)
   4 1
   5 2
   6 3
   7 >>> 1 in x
   8 True
   9 >>> x[0]
  10 1
  11 >>> x[0] = 2
  12 ...
  13 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

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

   1 import optparse
   2 import sys
   3 parser=optparse.OptionParser()
   4 parser.add_option('-w', '--word', help='word number to sort by', type='int', default=0)
   5 parser.add_option('-r', '--reverse', action='store_true', help='reverse sort order')
   6 options, args=parser.parse_args()
   7 print options.word
   8 print options.reverse
   9 print args
  10 print sys.argv

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

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

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

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

$ python sort.py
0
None
[]
['sort.py']
$ python sort.py -r -w 1 a.txt
1
True
['a.txt']
['sort.py', '-r', '-w', '1', 'a.txt']
$ python sort.py --help
Options:
  -h, --help            show this help message and exit
  -w WORD, --word=WORD  word number to sort by
  -r, --reverse         reverse sort order
$ python sort.py -x
sort.py [options]
sort.py: error: no such option: -x