Какво се случва, когато напишем object.attr
?
object.__dict__['attr']
object.__class__,
ако това е функция, се връща специален обект (bound method), на който може да извикате ().object.__class__
не е функция, то просто се връщаobject.__getattr__('attr')
class A:
pass
def __str__(something):
return "hax0r"
a = A()
a.__str__ = __str__
print(a)
Обикновено колекциите съдържат данни, които искаме да обхождаме.
Понякога тези данни не са подредени и няма начин да ги достъпваме директно.(set
-ове, dict
-ове)
с for
for cheese in cheeses:
print('-{0}?'.format(cheese))
print('-No...')
Итеруеми
list
set
tuple
dict
range
map
и filter
обекти__getitem__
Някои обекти могат да се итерират многократно(списъци, множества...). Но не всички.
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
iter
се опитва да извика __iter__
метода на аргумента си, но ако се окаже, че такъв няма конструира итератор, като просто извиква __getitem__
с последователни естествени числа, започвайки от нула, докато не се хвърли StopIteration
class IterableThingie:
def __getitem__(self, index):
if index < 10:
return index * 2
else:
raise StopIteration()
it = IterableThingie()
for i in it:
print(i)
Принтира 0, 2, 4, …, 18
class IterableThingie:
def __getitem__(self, index):
if index < 10:
return index * 2
else:
raise StopIteration()
def __iter__(self):
return iter('ⰰⰱⰲⰳⰴⰵⰶⰷⰸⰹⰺⰻ')
it = IterableThingie()
for i in it:
print(i)
Принтира ⰰ, ⰱ, ⰲ, ⰳ, ⰴ, ⰵ, ⰶ, ⰷ, ⰸ, ⰹ, ⰺ, ⰻ
Има втора форма на iter
, в която приема два аргумента, един callable обект и обект-страж.
В този случай върнатият итератор ще връща стойностите от последователните извиквания на callable обекта, докато той не върне обекта-страж. (по-скоро обект, който при сравнение със стража връща True
)
counter = 0
def clbl():
# WRITING CODE LIKE THIS WILL LIKELY RESULT
# IN THE AGONIZING UNTIMELY DEATHS OF MANY
# CUTE FURRY ANIMALS
global counter
counter += 1
return counter
iter(clbl, 23)
list(_)
# [1, 2, 3, …, 22]
Казахме, че 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 SquaresUpTo:
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 = SquaresUpTo(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)