„ Програмиране с Python“, ФМИ
Стефан Кънев & Николай Бачийски
28.03.2007г.
За приятелите
- Митьо Питона не е много паметлив.
- Пази телефонните номера на приятелите си в прост текстов файл:
062600400
+1800500
061830647
062633733 - Една вечер Митьо решил да организира джакузи-парти и трябвало да се обади на всичките си приятели
- За целта трябвало да прочете всички номера от файла и да им се обади един по един
- Не забравяйте: Митьо не е много паметлив — може да помни само един номер в главата си
Първа реализация
numbers = open('friends', 'r').readlines()
for number in numbers:
mityo.invite(number.strip())
open
отваря файл и връща файлов обектreadlines
прочита целия файл и ни връща списък от низове (заедно с новите редове)strip
маха белите полета от двата края на низа (+ новите редове)
- Виждате ли проблем с тази реализация?
- Памет: четем целия файл в на Митьо паметта
Втора реализация
numbers = open('friends')
while (True):
number = numbers.readline()
if not number:
break
mityo.invite(number.strip())
readline
чете един ред от файла, ако няма — връща празен низ
- Виждате ли проблем с тази реализация?
- Простота: то не са while-ове, то не са if-ове
Трета реализация (любима)
for number in open('friends'):
mityo.invite(number.strip())
- Виждате ли проблем с тази?
- Функционално е еквивалентна на предишната!
- Памет: ок
- Четимост: да, да
- Магия: да
Каква е магията?
итератор — обект, с метод next
, който при всяко извикване или връща пореден елемент или вдига StopIteration
, ако няма повече
генератор — обект, за който вградената функция iter
връща итератор
Как работи for
for target in sequence:
блок
- Опитва се да се добере то итератор
- извиква iter(sequence) — на практика проверява дали обекта ни има метод
__iter__
и взима неговия резултат. Ако няма такова животно — отива на по-следващия слайд - на всяка стъпка от цикъла в target слага резултата от
next
метода на получения по-горе обект - … и така докато не прихване
StopIteration
- извиква iter(sequence) — на практика проверява дали обекта ни има метод
Пример
class MityoIter:
def __init__(self, filename = 'friends'):
self.numbers = open(filename)
def next(self):
number = self.numbers.readline()
if not number:
raise StopIteration
return number
def __iter__(self):
return self
for number in MityoIter():
mityo.invite(number.strip())
Как работи for (2)
for target in sequence:
блок
- Цикли по стандартния начин:
- Проверява дали sequence поддържа оператора
[]
(__getitem__
) - Започвайки от индекс
0
се опитва да достъпи елементите един по един, като увеличава на всяка стъпка индекса с1
- Свършва, когато прихване
IndexError
- Проверява дали sequence поддържа оператора
Пример
class MityoBeard:
def __init__(self, step = 1, maxage = 33):
self.step = step
self.maxage = maxage
def immortalise(self):
self.maxage = None
def __getitem__(self, day):
if self.maxage != None and day > self.maxage:
raise IndexError("Mityo's beard is already gone.")
return self.step*day
beard = MityoBeard(2)
for l in beard:
print "Mityo's beard is %d cm. long." % l
beard.immortalise()
for l in beard:
print "Mityo's beard is %d cm. long." % l
Обратно към итераторите
- итерацията е нещо съвсем отнесено
- не винаги можем да разберем лесно дали използваме списък или итератор
- в повечето случаи не ни е нужно да знаем
- от многото знания боли глава
Генераторите обичат питоните
range
vs.xrange
dict.keys
vs.dict.iterkeys
vs.dict
os.walk
enumerate
sorted
list(генератор)
Примери
>>> list(enumerate(xrange(37, 43)))
[(0, 37), (1, 38), (2, 39), (3, 40), (4, 41), (5, 42)]
>>> map(len, MityoIter()) # map и подобните са умни и разбират
>>> partners = {'Pena': 'Mityo', 'Kuna': 'Mityo', 'Boca': 'Mityo'}
>>> partners.keys()
['Kuna', 'Boca', 'Pena']
>>> partners.iterkeys()
>>> iter(partners)
>>> for woman in partners:
print "%s is with %s." % (woman, partners[woman])
Само вградените ли са лесни?
- вградените генератори се ползват лесно
- досега не видяхме лесен начин да си правим сами — само с класове и
__iter__
- е да, ама не
ша та yield-на
Магията не спира дотук:
def mityo_iter(filename='friends'):
numbers = open(filename)
while(True):
number = numbers.readline()
if number:
yield number
else:
return
>>> mityo_iter # най-обикновена ф-я
>>> mityo_iter() # обикновена-чушки
>>> for number in mityo_iter():
mityo.invite(number.strip())
Под капака
Функциите, които съдържат yield
връщат генератори.
yield
връща стойността на поредната стъпка, а итерацията завършва когато функцията свърши
yield
приспива изпълнението на функцията и след това продължава от същото място
Подробен пример
def mityo_rabbits(n):
x, y = 0, 1
for i in xrange(n):
yield y
x, y = y, x + y
>>> list(mityo_rabits(7))
[1, 1, 2, 3, 5, 8, 13]
Няма коментари:
Публикуване на коментар