Iterators and generators

Колекции в Python

  • List
  • Tuple
  • Set
  • Dict

List

animals = ['panda', 'dog', 'notcat', 'hatecats']
for animal in animals:
    print('I like {}'.format(animal))

Има подредба! Може да индексираме!

List slicing

a[start:end] # items start through end-1
a[start:]    # items start through the rest of the array
a[:end]      # items from the beginning through end-1
a[start:end:step] # start through not past end, by step
a[:]         # a copy of the whole array

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

frameworks = ['django', 'angular', 'rails']
frameworks.append(frameworks)

frameworks[-1] is frameworks # True
print(frameworks) # ['django', 'angular', 'rails', [...]]
>>> python_frameworks = ["Django", "Flask"]
>>> frameworks = [python_frameworks, 'angular', 'rails']
>>> frameworks
[['Django', 'Flask'], 'angular', 'rails']
>>> python_frameworks.append("Pyramid")
>>> frameworks
[['Django', 'Flask', 'Pyramid'], 'angular', 'rails']

tuple

Като списък, но не може да се променя

python_frameworks = ("Django", "Flask")
python_frameworks[1] = Pyramid

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment
>>> web_framewokrs = (["Django", "Flask"], ["AngularJS", "Dojo"])
>>> web_framewokrs[0].append("Pyramid")
(['Django', 'Flask', 'Pyramid'], ['AngularJS', 'Dojo'])

Set

Просто множества!

hackers = {"Rado", "Kamen", "Ivo"}
set3 = set1 & set2        # Intersection
set4 = set1 | set2        # Union
set5 = set1 - set3        # Set difference
set6 = set1 ^ set2        # Symmetric difference
issubset = set1 <= set2   # Subset test
issuperset = set1 >= set2 # Superset test

Нямаме подредба!

dict

lotr_heroes = {
    'Gandalf': 'the grey',
    'Frodo': 'Baggins',
}
lotr_heroes['Saruman'] = 'the white'

Нямаме подредба!

Как итерираме?

class Panda:
    def __init__(self, name):
        self.name = name

pandas = [Panda("Ivo"), Panda("Rado"), Panda("Roza")]
for panda in pandas:
    print(panda.name)

Как итерираме?(2)

class Panda:
    def __init__(self, name):
        self.name = name

    def __str__(self):
        return self.name


class Pandas(object):
    def __init__(self, pandas):
        self.pandas = pandas

    def __iter__(self):
        return self
    
    def __next__(self):
        if not self.pandas:
            raise StopIteration
    
        return self.pandas.pop()

pandas = Pandas([Panda("Ivo"), Panda("Rado"), 
                 Panda("Roza")])
iterator = iter(pandas)
print(next(iterator)) # Ivo
print(next(iterator)) # Rado

for panda in pandas:
    print(panda)

>>> Ivo
    Rado
    Roza

__iter__ и __next__

  • __iter__  -> Връща итератор, с който можем да обходим нашата колекция
  • __next__ -> Връща следващата стойност в обхождането

 

  1.  извиква __iter__ метода на arg, но ако се окаже, че такъв няма:
  2.  извиква __getitem__ с последователни естествени числа, започвайки от нула, докато не се хвърли StopIteration, ако няма такъв
  3. Хвърля грешка,че обектът не е итеруем

Какво прави iter(arg)?

>>> team = dict([('Ivo', '22'), ('Rado', '24'), ('Fandalf', 400)])
>>> iterator = iter(team)
>>> next(iterator)
'Rado'
>>> next(iterator)
'Fandalf'
>>> next(iterator)
'Ivo'
>>> next(iterator)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration
>>> 

Пример 2

class MyMoney:
    def __getitem__(self, index):
        if index > 20:
            raise StopIteration()
        return 30 ** index

>>> ivo_money = MyMoney()
>>> for money in ivo_money:
>>>    print(money)
1
30
900
27000
810000
24300000
729000000
21870000000
656100000000
19683000000000
590490000000000
17714700000000000
531441000000000000
15943230000000000000
478296900000000000000
14348907000000000000000
430467210000000000000000
12914016300000000000000000
387420489000000000000000000
11622614670000000000000000000
348678440100000000000000000000
class FibUpTo:
    def __init__(self, up_to):
        self.up_to = up_to
        self.num = 0
        self.a = 1
        self.b = 1

    def __iter__(self):
        return self

    def __next__(self):
        if self.num > self.up_to:
            raise StopIteration

        if self.num < 2:
            self.num += 1
            return 1
        self.a, self.b = self.b, self.a + self.b
        self.num += 1
        return self.b

fib = FibUpTo(10)

for number in fib:
    print(number)

Мързеливост

Това означава, че всеки елемент се генерира чак когато е необходим.

>>> numbers = [[1,2,3], [1,2,3], [1,2,3]]
>>> sums = map(lambda x: sum(x), numbers)
>>> numbers[2].append(22)
>>> next(sums)
6
>>> next(sums)
6
>>> next(sums)
28

Други мързеливи функции:

  • any
  • all
  • map
  • filter
  • zip
  • enumerate

Генератори

def panda_meals():
    yield 'Bamboo One'
    yield 'Bamboo Two'
    yield 'Bamboo Three'
    yield 'Bamboo Four'
    yield 'Bamboo Five'
    yield 'Bamboo Six'

meals = panda_meals()
for meal in meals:
    print("Num num num " + meal)

>>> Num num num Bamboo One
    Num num num Bamboo Two
    Num num num Bamboo Three
    Num num num Bamboo Four
    Num num num Bamboo Five
    Num num num Bamboo Six

Пример 2

def degrees(number):
    i = 2
    while i < 5:
        yield number ** i
        i += 1
        print(i)

degrees = degrees(2)
for degree in degrees:
    print(str(degree) + "\n")

>>> 4
    8
    16

И тях ги мързи!

def fib():
    a, b = 0, 1
    while True:
        yield a
        a, b = b, a + b

for f in fib():
    print(f)

Можем ли да използваме итератор, за да итерираме генератор?

def foo():
    for i in range(3):
        print("before yield {}".format(i))
        yield i
        print("after yield {}".format(i))


f = foo()
print(next(f))
# before yield 0
# 0
print(next(f))
# after yield 0
# before yield 1
# 1

Iterators and Generators - 101 v5

By Hack Bulgaria

Iterators and Generators - 101 v5

  • 1,388