02. Колекции

02. Колекции

02. Колекции

7 март 2016

Предизвикателството

Всъщност сте се справили доста добре

40 от 53 човека имат 10 минаващи теста

Предизвикателството

Но все пак сте ни оставили малко материал за първите няколко слайда днес

Naming things

Трудно е, определено няма го оспорим

За някои неща обаче не е простичко

stoynosti и stoynost с нищо не са по-добри от values и value. Даже са откровено по-лош избор.

return vs. print

Молим ви, нека това е първия и единствен път, в който трябва да говорим за това

def answer():
    return 42

def antwoord():
    print(42)

a = answer() # 42
b = antwoord() # None

Един въпрос

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

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

Още един

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

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

  1. Запазете вашето решение във файл с името `solution.py`.
  2. Свалете `sample_test.py` в същата директория, в която е решението ви
  3. През командния ред на операционната си система отидете във въпросната директория
  4. Изпълнете `python3.5 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_i_like[1] # True
things_i_like[0] is things_i_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 ' + str(number))

range

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

numbers = range(10, 13)

for number in numbers:
    print('We can count to ' + str(number))

range

range може и в обратен ред

numbers = range(13, 0, -1)

for number in numbers:
    print('We can count to ' + str(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

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

Въпроси?