Kodomo

User

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

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

Словари

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

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

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

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

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

>>> for key in y:
...     print key, y[key]
hello world
1 2
>>> list(y) # список ключей словаря
['hello', 1]
>>> 1 in y # является ли 1 ключом в словаре y?
True
>>> 2 in y
False
>>> "hello" in y
True
>>> "world" in y
False

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

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

>>> x = [1, 2, 3]
>>> y = { 0: 1, 1: 2, 2: 3 }
>>> print(x[0])
1
>>> print(y[0])
1
>>> x[1] = 5
>>> y[1] = 5
>>> print(x[1])
5
>>> print(y[1])
5
>>> print(x)
[1, 5, 3]
>>> print(y)
{ 1: 5, 0: 1, 2: 3 }

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

Метод get()

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

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

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

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

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

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

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

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

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

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

>>> x = (1,)
>>> x = (1, 2, 3)
>>> for i in x:
...     print(i)
1
2
3
>>> 1 in x
True
>>> x[0]
1
>>> x[0] = 2
...
TypeError: 'tuple' object does not support item assignment

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

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

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

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

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

или:

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

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

>>> x = 1
>>> y = 2
>>> x, y = y, x
>>> print(x)
2
>>> print(y)
1

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

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

def coords(time):
    return sin(time), cos(time)
...
x, y = coords(10)

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

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

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

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

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

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

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

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

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

import sys
print sys.argv

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

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

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

import argparse
parser=argparse.ArgumentParser()

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

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

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

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

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

import argparse
import sys
parser=argparse.ArgumentParser()
parser.add_argument('-w', '--word', help='word number to sort by', type=int)
parser.add_argument('-r', '--reverse', action='store_true', help='reverse sort order')
parser.add_argument('-a', '--sort-alg', dest='alg', help='sorting algorithm; can be "qsort" (qsort) or "merge" (merge-sort)', type=str, default='qsort')
options=parser.parse_args()
print(options)
print(options.word)
print(options.alg)# значение положилось в options.alg, как это указано в 'dest'
print(options.reverse)
print sys.argv

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

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

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

$ python sort.py
Namespace(alg='qsort', reverse=False, word=None)
None
qsort
False
['test1.py']
$ 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: unrecognized arguments: -x

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

import argparse
import sys
parser=argparse.ArgumentParser()
parser.add_argument('-w', '--word', help='word number to sort by', type=int)
parser.add_argument('-r', '--reverse', action='store_true', help='reverse sort order')
parser.add_argument('-a', '--sort-alg', dest='alg', help='sorting algorithm; can be "qsort" (qsort) or "merge" (merge-sort)', type=str, default='qsort')
options=parser.parse_args()
print(options.word)
if not options.word:
    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

2012/4/Python/7/Record (last edited 2014-03-24 19:45:45 by stavrovskaya)