02. Колекции

02. Колекции

02. Колекции

9 март 2015

Въпрос

Какво ще изведе този код?

foods = ['spam', 'eggs', 'ham']
things = foods
things[1] = 'chips'
print(foods[1])

Втори въпрос

А този?

a = ['spam', 'eggs', 'ham']
b = a
c = a[:]
print(a is b, a is c)
print(a == b, a == c)
b[1] = 'milk'
print(a is b, a is c)
print(a == b, a == c)

Още един:

my_string = '1234567890'
>>my_string[10]

IndexError: string index out of range

И още един..

my_data = set(range(10))

for x in my_data:
    print(x)
    break

Ще изпринти един от елементите, без гаранция кой

Последен..

{}
set()
1
None
True
[1, 2, 3]
(1, 2, 3)
12.0
'абвг'
lambda x: x**0
print, 5, 6, 7, 8

Кои от тези са валидни ключове за dict?

1, None, True, (1, 2, 3), 12.0, 'абвг', lambda-та и (print, 5, 6, 7 ,8)

Излъгах :)

my_dict = dict('12 34 16'.split() + [(False, False)])
print(len(my_dict), my_dict[0])

3 False

Написахте ли си домашното?

Според нас не сте

Има регистрирани грубо около 150 човека, предадени са ~70 решения

Имате време до началото на следващата ни среща, откровено не ви съветваме да чакате последния момент.

Да си кажем отрано

Забелязахме някои подробности в това, което е предадено до момента, на които е важно да обърнем внимание, за да имате време да ги поправите преди края на срока на домашното

Интересни конструкции

if thingie == 'zero':
    return 0
elif thingie == 'one':
    return 1
elif thingie == 'THE ANSWER':
    return 6*7
else:
    return 'You are confusing me! STAHP!'

Само ако в python имаше някакъв вграден тип структура, която може да асоциира един обект с друг… Може би нещо, което да можем да индексираме с 'THE ANSWER' и то да ни върне 42… Би било супер яко!

Интересни конструкции 2

number_names = {
    0: 'zero',
    1: 'one',
    2: 'two',
    
    42: 'THE ANSWER'
}

В древните програмистки книги се говори за митична структура от данни, която подрежда елементите си линейно в паметта и може да бъде индексирана с числата 0, 1, 2 и т.н., за да получаваме елемента на съответната позиция в нея. Някои смели душѝ дори твърдят, че има имплементация на такова нещо в python.

Най-трудните работи

Казано е, че двете най-трудни неща в програмирането са

Кръщаване на неща

Ако една константна стойност се срещне повече от веднъж в кода ви, то тя задължително трябва да стане стойност на променлива или константа

Кръщаване на неща

# LO6O(mi e)!!!!
if argument == 41:
    return 'WRONG'
elif argument == 42:
    return 'CORRECT'
elif argument == 43:
    return 'WRONG'

Кръщаване на неща

# SLED ASPERINA
wrong = 'WRONG'
correct = 'CORRECT'
if argument == 41:
    return wrong
elif argument == 42:
    return correct
elif argument == 43:
    return wrong

Фермани

Едно добро решение на това домашно спокойно може да се случи в рамките на 50-60 реда при просторно подреждане на нещата, така че да се четат лесно

Има добри решения и на по 20, но не искаме да се стремите към такива неща

НЕ ПРЕДАВАЙТЕ ФУНКЦИИ, КОИТО НЕ СЕ ПОЛЗВАТ! Така решенията ви стават по-тежки за четене и осмисляне, без това да допринася с нищо

Организация на кода

Решението на тази задача се състои единствено от функции, които трябва да са „глобални“ във файла с решението. Можете да дефинирате всякакви спомагателни функции, променливи, класове и каквото си желаете(макар че други функции и класове по-скоро не са нужни), но функциите описани в условието трябва да са видими в глобалната област на файла с решението.

не

class solution:
    def interpret_western_sign(day, month):
        

    def interpret_chinese_sign(year):
        

да

def interpret_western_sign(day, month):
    

def interpret_chinese_sign(year):
    

Организация на кода

  1. Запазете вашето решение във файл с името `solution.py`.
  2. Свалете `sample_test.py` в същата директория, в която е решението ви
  3. През командния ред на операционната си система отидете във въпросната директория
  4. Изпълнете `python3.4 sample_test.py`
  5. Ако имате дълбока фундаментална грешка в решението си, това ще ви покаже каква е

Малко стил

PEP8! Това е конвенцията за стил на Python. Задължително спазване!

Още малко стил

PEP8! Това е конвенцията за стил на Python. Задължително спазване!

Още много от източника

PEP8

Никой не е запомнил целия pep8 само с четене

Всеки редактор може да бъде конфигуриран, така че да използва pep8, за да проверява кода ви и да се кара, когато не правите нещо правилно

Научете се как да ползвате добре редактора, който сте си избрали, това е много важно

pip install pep8

And now for something completely different

По същество за колекции

Да си подредим данните

Когато имаме данни, най-логично е да ги слагаме в колекции.

Какво е колекция?

списъци

nice_things = ['coffee', 'cheese', 'crackers', 'tea']
for thing in nice_things:
    print('I tend to like {}'.format(thing))

можем и просто да ги индексираме

print(nice_things[1]) # cheese
print(nice_things[-1]) # tea
print(nice_things[-3]) # cheese

списъци

cute_animals = ['cat', 'raccoon', 'panda', 'red panda', 'marmot']
cute_animals[1:3]  # ['raccoon', 'panda']
cute_animals[-1]  # 'marmot'
cute_animals[1:-1]  # ['raccoon', 'panda', 'red panda']
cute_animals[::-1]  # ['marmot', 'red panda', 'panda', 'raccoon', 'cat']
cute_animals[-1:0:-1]  # ['marmot', 'red panda', 'panda', 'raccoon']
cute_animals[-1:0:-2]  # ['marmot', 'panda']

списъци

Списъците съдържат "указатели" към елементи

coffee, cheese, crackers, tea = 'coffee', 'cheese', 'crackers', 'tea' # unpacking
things_i_like = [coffee, cheese, crackers]
things_you_like = [crackers, coffee, tea]

things_i_like[0] == things_you_like[1] # True
things_i_like[0] is things_you_like[1] # True

списъци

Това позволява някои интересни неща

cheeses = ['brie', 'bergkäse', 'kashkaval', 'leipäjuusto']
cheeses.append(cheeses)

cheeses[-1] is cheeses # True
print(cheeses) # ['brie', 'bergkäse', 'kashkaval', 'leipäjuusto', [...]]

списъци(-ception)

cheeses = ['brie', 'bergkäse', 'kashkaval', 'leipäjuusto']
teas = ['chai', 'earl grey', 'jasmine', 'oolong']

breakfast = [cheeses, teas]
print(breakfast[0][1]) # bergkäse

breakfast[1][2] = ['шкембе', 'люби чушки', 'оцет с чесън']
print(teas) # ?
['chai', 'earl grey', ['шкембе', 'люби чушки', 'оцет с чесън'], 'oolong']

методи на списъци

range

range връща итерируемо за интервал от числа

numbers = range(3)

for number in numbers:
    print('We can count to {}'.format(number))

range

range интервалът може да не започва от нула

numbers = range(10, 13)

for number in numbers:
    print('We can count to {}'.format(number))

tuple

последователност, n-торка(n-орка), тюпъл

като списък, но с постоянен състав

people = ('Niki', 'Kiro', 'Genata')
people[2] # Genata
people[1] # Kiro
people[0] # Niki

people[1] = 'бобър'

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment

кортеж

tuple

последователността от елементи в кортежа не може да се променя, самите елементи може да изменят вътрешната си структура

change_me = ([1, 2, 3], [4, 5, 6], [7, 8, 9])
change_me[1][1] = 0
change_me[2][0] = 'c'

print(change_me) # ([1, 2, 3], [4, 0, 6], ['c', 8, 9])

tuple

алтернативен синтаксис за кортежи

people = 'Niki', 'Kiro', 'Genata'
people = 'Niki',

people = ('Niki') # най-вероятно не е каквото очаквате

Има методите `index` и `count` като на списъците

Любопитни работи

Ако имате n-торка, съдържаща само имена от лявата страна на присвояване, може да постигнете интересни ефекти:

(a, b) = 1, 2
print(a)  # 1

Любопитни работи

Всъщност скобите изобщо не са задължителни

a, b = 1, 2
print(a)  # 1

Любопитни работи

Или

numbers = (1, 2, 3)
a, b, c = numbers

Любопитни работи

Можем да правим и така:

a, *b, c = 1, 2, 3, 4, 5
a = 1
b = [2, 3, 4]
c = 5

Сравняване на списъци и кортежи

Сравняват се лексикографски:

>>> (1, 2) < (1, 3)
True
>>> (1, 2) < (1, 2)
False
>>> (1, 2) < (1, 2, 3)
True
>>> [1, 2] < [1, 3]
True
>>> (1, 2) < [1, 3] # tuple vs. list
# поражда грешка:
#    TypeError: unorderable types: tuple() < list()

Популярни структури от данни

Опашка (queue, FIFO buffer) - можете да ползвате списък.

adjectives = []

def add_adjective(items):
    adjectives.append(items)

def get_adjective():
    return adjectives.pop(0)

add_adjective('Magic')
add_adjective('Woody Allen')
add_adjective('Zombie')
add_adjective('Superhero')

print(' '.join(adjectives) + ' Jesus!') # Magic Woody Allen Zombie Superhero Jesus!

Stack?

sets

Множества(за всякакви практически цели, неразличими от математическата абстракция със същото име)

favourite_numbers = set()
favourite_numbers.add(13)
favourite_numbers.add(73)
favourite_numbers.add(32)
favourite_numbers.add(73)
favourite_numbers.add(1024)
favourite_numbers.add(73)

print(favourite_numbers) # {32, 73, 666, 13, 1024}

sets

Множествата са итерируеми и НЕподредени

for num in favourite_numbers:
    print('I really like the number ' + str(num))

sets

можем да проверяваме за принадлежност

73 in favourite_numbers # True

sets

Има синтаксис за създаване на множества(както може би сте се досетили)

favourite_numbers = {32, 73, 666, 13, 1024}

{} не е празния set!

Операции с множества

>>> {1, 2, 3} | {2, 3, 4}
{1, 2, 3, 4}
>>> {1, 2, 3} & {2, 3, 4}
{2, 3}
>>> {1, 2, 3} - {2, 3, 4}
{1}
>>> {1, 2, 3} ^ {2, 3, 4}
{1, 4}
>>> {1, 2, 3} < {2, 3, 4}
False
>>> {2, 3} < {2, 3, 4} # < - подмножество
True
>>> {2, 3} == {2.0, 3}
True
>>> {1, 2}.isdisjoint({3, 4})
True

dict

Индексите не винаги са достатъчно информативни

artist_names = {
    'Eddie': 'Vedder',
    'Maynard': 'Keenan',
    'Matthew': 'Bellamy',
    'James': 'LaBrie',
}

print('Eddie\'s last names is ' + artist_names['Eddie'])

{}

{} е празен речник, по простата причина, речниците са доста по-често използвана структура от множествата

dict

можем да добавяме нови стойности във вече създаден речник

names['Devin'] = 'Townsend'

print(names) # {'Devin': 'Townsend', 'Matthew': 'Bellamy', 'Eddie': 'Vedder', 'James': 'LaBrie', 'Maynard': 'Keenan'}

речникът също е неподреден

Три други начина за създване на речник

Чрез наименовани параметри към конструктора (не питайте):

>>> dict(france="Paris", italy="Rome")
{'italy': 'Rome', 'france': 'Paris'}

Чрез списък от двойки

>>> dict([('One', 'I'), ('Two', 'II')])
{'Two': 'II', 'One': 'I'}

Чрез списък от ключове и стойност по подразбиране

>>> dict.fromkeys([1, 2, 3], 'Unknown')
{1: 'Unknown', 2: 'Unknown', 3: 'Unknown'}

Речници и хеш функции

кой какви сирена има

data = [('John', 'Tilsit'), ('Eric', 'Cheshire'), ('Michael', 'Camembert'),
        ('Terry', 'Gouda'), ('Terry', 'Port Salut'), ('Michael', 'Edam'),
        ('Eric', 'Ilchester'), ('John', 'Fynbo')]

def cheeses_by_owner(cheeses_data):
    by_owner = {}
    for owner, cheese in cheeses_data: # <- tuple unpacking
        if owner in by_owner:
            by_owner[owner].append(cheese)
        else:
            by_owner[owner] = [cheese]

    return by_owner

map/filter/reduce/all/any

за любознателните: map и filter са мързеливи

Comprehensions

List comprehension

[израз for променлива in поредица if условие]

>>> [x * x for x in range(0, 10)]
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

>>> [x * x for x in range(0, 10) if x % 2]
[1, 9, 25, 49, 81]

List comprehension

Един list comprehension може да се вложи в друг, защото връща нещо итерируемо

>>> [(x, y) for x in [1,2,3] for y in [3,1,4] if x != y]
[(1, 3), (1, 4), (2, 3), (2, 1), (2, 4), (3, 1), (3, 4)]

Generator expression

Set comprehension

>>> import math
>>> {int(math.sqrt(x)) for x in range(1,100)}
{1, 2, 3, 4, 5, 6, 7, 8, 9}

Dict comprehension

>>> {i : chr(65+i) for i in range(10)}
{0: 'A', 1: 'B', 2: 'C', 3: 'D', 4: 'E', 5: 'F', 6: 'G', 7: 'H', 8: 'I', 9: 'J'}

Влизат collections

deque

from collections import deque

adjectives = deque()


def add_adjective(items):
    adjectives.append(items)


def get_adjective():
    return adjectives.popleft()

add_adjective('Komodo Dragon')
add_adjective('Telepathic')
add_adjective('Vampire')
add_adjective('Quantum Hovercraft')

print(' '.join(adjectives) + ' Jesus') # Komodo Dragon Telepathic Vampire Quantum Hovercraft Jesus

defaultdict

from collections import defaultdict


data = [('John', 'Tilsit'), ('Eric', 'Cheshire'), ('Michael', 'Camembert'),
         ('Terry', 'Gouda'), ('Terry', 'Port Salut'), ('Michael', 'Edam'),
         ('Eric', 'Ilchester'), ('John', 'Fynbo')]


def cheeses_by_owner(cheeses_data):
    by_owner = defaultdict(list)
    for owner, cheese in cheeses_data:
        by_owner[owner].append(cheese)

    return by_owner

itertools

>>> import itertools
>>> help(itertools)

Още малко стил

Въпроси?