Обикновено колекциите съдържат данни, които искаме да обхождаме.
Понякога тези данни не са подредени и няма начин да ги достъпваме директно.(set-ове, dict-ове)
с for
for cheese in cheeses:
print('-{0}?'.format(cheese))
print('-No...')
Итеруеми
list
set
tuple
dict
range
map
и filter
обектиНякои обекти могат да се итерират многократно(списъци, множества...). Но не всички.
squares = map(lambda x: x ** 2, range(5))
for number in squares:
print(number)
# 0 1 4 9 16
for number in squares:
print(number)
#
Обикновено мързеливите се итерират по веднъж.
Индексирането не винаги има смисъл, въпреки че обекта може да се итерира
__iter__
Връща обект-итератор, с който можем да обходим нашата "колекция"
Итератора е обект, пазещ позицията на текущо обхождане на колекция
(обект, който има __next__
метод)
StopIteration
) когато елементите свършатiter(a)
<=> a.__iter__()
next(a)
<=>a.__next__()
Забележка: трябва да имате МНОГО ДОБРА основателна причина, за да ползватеa.__method__()
вместо method(a)
НЯМАТЕ такава причина
С разликата, че for ще обработиStopIteration
грешката:
interjections = [
'Ring-ding-ding-ding-dingeringeding!',
'Wa-pa-pa-pa-pa-pa-pow!',
'Hatee-hatee-hatee-ho!',
'Joff-tchoff-tchoffo-tchoffo-tchoff!'
]
iterator = iter(interjections)
while True:
interjection = next(iterator)
print(interjection)
Итераторите на стандартните обекти в python също имат__iter__
метод, който не прави нищо особено зашеметяващо
>>> iterable = iter([1, 2, 3])
>>> iter(iterable) is iterable
True
Казахме, че map
, filter
и range
са мързеливи. Това означава, че всеки елемент се генерира чак когато е необходим.
>>> odd = filter(lambda num: num % 2, range(10))
>>> iter(odd) is odd
True
Какво ще се случи тук?
>>> loud_names = ['JEFF', 'STONE', 'MIKE', 'EDDIE', 'MATT']
>>> quiet_names = map(lambda name: name.lower(), loud_names)
>>> loud_names[3] = 'VEDDER'
>>> print(list(quiet_names))
['jeff', 'stone', 'mike', 'vedder', 'matt']
list
обектите имат метод sort
>>> numbers = [12, 15, 14, 10, 5, 7, 6]
>>> print(numbers.sort())
None
>>> print(numbers)
[5, 6, 7, 10, 12, 14, 15]
mutable нещата често пъти внасят усложнения
Има вградена функцияsorted
>>> numbers = [12, 15, 14, 10, 5, 7, 6]
>>> print(sorted(numbers))
[5, 6, 7, 10, 12, 14, 15]
>>> print(numbers)
[12, 15, 14, 10, 5, 7, 6]
sorted
приема keyword аргумент(?) key
, който оказва как да се извлекат сравними стойности от елементите.
>>> points = [(10, 3), (4, 8), (5, 9), (2, 3), (12, 6), (7, 4)]
>>> sorted(points)
[(2, 3), (4, 8), (5, 9), (7, 4), (10, 3), (12, 6)]
>>> sorted(points, key=lambda point: point[1])
[(10, 3), (2, 3), (7, 4), (12, 6), (4, 8), (5, 9)]
Аналогично
reverse
- метод на list
, който обръща списъка на мястоreversed
- вградена функция, която връща ново итеруемо>>> numbers = [12, 15, 14, 10, 5, 7, 6]
>>> reversed(numbers)
<list_reverseiterator object at 0x7f14ff534490>
>>> list(_)
[6, 7, 5, 10, 14, 15, 12]
Ако добавяте/махате елементи докато итерирате резултата отreversed
няма да останете доволни
def actors_generator():
yield 'Graham Chapman'
yield 'John Cleese'
yield 'Terry Gilliam'
yield 'Eric Idle'
yield 'Terry Jones'
yield 'Michael Palin'
actors = actors_generator()
for actor in actors:
print(actor + ' as seen on British TV')
Iterator pattern
class squares_up_to:
def __init__(self, up_to):
self.up_to = up_to
self.num = 0
def __iter__(self):
return self
def __next__(self):
if self.num > self.up_to:
raise StopIteration
square = self.num ** 2
self.num += 1
return square
Можем да го използваме ето така
squares = squares_up_to(100)
for square in squares:
print(square)
def squares_up_to(number):
value = 0
while value <= number:
yield value ** 2
value += 1
raise StopIteration
Като list comprehension, но с обли скоби и "мързелив":
squares_up_to_ten = (number ** 2 for number in range(10))
any
, all
map
, filter
list
, tuple
, set
enumerate
zip
Ако искаме да проверим дали елементите на итеруемо отговарят на условие
>>> all([True, True])
True
>>> all([True, False])
False
>>> any([True, False])
True
Приемат итеруеми и връщат итератори.(2 vs. 3)
def numbers():
num = 0
while True:
yield num
num += 1
doulbes = map(lambda num: num*2, numbers())
Когато индексите ни интересуват
>>> exclamations = ['кòли', 'бèси', 'сèчи']
>>> for index, exclamation in enumerate(exclamations):
... print('{0}. {1}!'.format(index, exclamation))
...
0. кòли!
1. бèси!
2. сèчи!
Итерира едновременно няколко итеруеми
titles = ['Ænima', 'Lateralus', '10,000 Days']
positions_US = [2, 1, 1]
positions_UK = [108, 16, 4]
template = 'Tool\'s {0} was at {1} in the US and at {2} in the UK'
for title, us_pos, uk_pos in zip(titles, positions_US, positions_UK):
print(template.format(title, us_pos, uk_pos))
Удобства за работа с итеруеми обекти.
Всички функции в него са "мързеливи".
>>> from itertools import accumulate
>>> sums = accumulate(range(1, 101), lambda a, b: a + b)
>>> print(sums)
<itertools.accumulate object at 0x7ff61d24b518>
>>> next(sums)
1
>>> next(sums)
3
>>> next(sums)
6
>>> list(sums)[-1]
5050
Конкатенира итеруеми
>>> from itertools import chain
>>> all_to_15 = chain(range(10), range(11, 15))
>>> list(all_to_15)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 13, 14]
Връща част от итеруемо според маска
>>> from itertools import compress
>>> list(compress(range(10), [True, False]*5)
[0, 2, 4, 6, 8]
Групира сортирана последователност от елементи по ключ
>>> from itertools import groupby
>>> from collections import defaultdict
>>> data = [ ('John', 'Tilsit'), ('Eric', 'Cheshire'), ('Michael', 'Camembert'),
... ('Terry', 'Gouda'), ('Terry', 'Port Salut'), ('Michael', 'Edam'),
... ('Eric', 'Ilchester'), ('John', 'Fynbo') ]
>>> data = sorted(data, key=lambda record: record[0])
>>> by_owner = defaultdict(list)
>>> for key, group in groupby(data, lambda record: record[0]):
... for record in group:
... by_owner[key].append(record[1])
...
>>> by_owner['Terry']
['Gouda', 'Port Salut']
itertools.repeat(objects[, times])
- връща итеруемо с опредлен брой(или безкрайно много) повторения на един обектitertools.cycle(iterable)
- безкрайна конкатенация на един итеруем обектitertools.filterfalse(function, iterable)
- filter, тълкуващ предиката на обратно(ако function е None връща falsy елементите)itertools.permutations(iterable)
- генерира пермутациите на елементите в итеруемотоitertools.product(*iterables [,repeat=1])
- връща декартово произведение на итеруемиitertools.takewhile(function, iterable)
- генерира елементите на итеруемото, до първото което не отговаря на предикатаitertools.dropwhile(function, iterable)
- генерира елементите на итеруемото, от първото което не отговаря на предиката нататъкitertools.tee(iterable, n)
- връща кортеж от n независими итеруемиEXPLORE!
import itertools
dir(itertools)
help(itertools)