ლექცია 13 · XIII კვირაLecture 13 · Week XIII
იტერატორები და
კონტეინერებიIterators and
containers
როგორ მუშაობს for სინამდვილეში · iter() და next() · გენერატორები
How for really works · iter() and next() · generators
დღეს ასევე: პრაქტიკული დავალება #4 Also today: practical assignment #4
გამეორება · ლექციები 11–12Review · lectures 11–12
შემოწმება — 9 კითხვაCheck yourself — 9 questions
1. როგორ შევქმნათ ცარიელი set და რატომ არა {}-ით?
1. How do you create an empty set, and why not with {}?
set()-ით. {} ქმნის ცარიელ ლექსიკონს.
With set(). {} creates an empty dictionary.
2. რას აბრუნებს {1,2,3} & {2,3,4} და {1,2,3} - {2,3,4}?
2. What do {1,2,3} & {2,3,4} and {1,2,3} - {2,3,4} return?
{2, 3} (თანაკვეთა) და {1} (სხვაობა).
{2, 3} (intersection) and {1} (difference).
3. რა განსხვავებაა d["x"]-სა და d.get("x")-ს შორის?
3. What is the difference between d["x"] and d.get("x")?
[] გასაღების არარსებობისას ისვრის KeyError-ს; get() აბრუნებს None-ს (ან მითითებულ ნაგულისხმევს).
[] raises KeyError if the key is missing; get() returns None (or a default you supply).
4. როგორ გავიაროთ ლექსიკონზე გასაღებთან და მნიშვნელობასთან ერთად? 4. How do you loop a dictionary with both key and value?
for k, v in d.items():5. დაწერე ერთი ხაზი, რომელიც სიიდან დუბლიკატებს აშორებს. 5. Write one line that removes duplicates from a list.
unique = list(set(data)). ⚠ თავდაპირველი რიგი არ შენარჩუნდება — თუ საჭიროა, list(dict.fromkeys(data)).
unique = list(set(data)). ⚠ The original order is lost — if you need it, use list(dict.fromkeys(data)).
6. რომელი ბლოკი სრულდება ყოველთვის — else თუ finally?
6. Which block always runs — else or finally?
finally. else სრულდება მხოლოდ მაშინ, თუ try-ში შეცდომა არ მოხდა.
finally. The else runs only if no error occurred in the try.
7. რატომ არის ცუდი except: pass?
7. Why is except: pass bad?
except ყველაფერს იჭერს (Ctrl+C-საც), pass კი პრობლემას ჩუმად მალავს — შეცდომა მოგვიანებით სულ სხვა ადგილას გამოჩნდება.
A bare except catches everything (including Ctrl+C), and pass hides the problem silently — the error resurfaces somewhere else entirely.
8. რომელი გამონაკლისი მოხდება? int("abc") · [1,2][9] · "5" + 5
8. Which exception is raised by int("abc") · [1,2][9] · "5" + 5?
ValueError · IndexError · TypeError.9. რას აკეთებს raise?
9. What does raise do?
raise ValueError("ასაკი უარყოფითი ვერ იქნება").
It deliberately throws an exception — used to reject invalid data immediately: raise ValueError("age cannot be negative").
ცნებაConcept
კონტეინერი — რა არისWhat a container is
ობიექტი, რომელიც სხვა ობიექტებს იტევს და შეუძლია პასუხი გასცეს კითხვას „ეს შენში არის?“
An object that holds other objects and can answer the question “is this one of yours?”
print(3 in [1, 2, 3]) # list
print("a" in ("a", "b")) # tuple
print(1 in {1, 2}) # set
print("key" in {"key": 1}) # dict — searches the keys
print("th" in "python") # str
# every one of them supports len()
print(len([1,2,3]), len("abc"), len({1,2}))
დალაგებულიOrdered
list · tuple · str · range — ინდექსით მუშაობს
list · tuple · str · range — support indexing
დაულაგებელიUnordered
set · dict — ინდექსი არ აქვს, სამაგიეროდ ძებნა სწრაფია
set · dict — no indexing, but lookups are fast
მთავარიThe core idea
რა ხდება for-ის შიგნითWhat happens inside a for
for x in [10, 20, 30]:
print(x)
it = iter([10, 20, 30]) # get an iterator
while True:
try:
x = next(it) # the next element
except StopIteration: # elements exhausted
break
print(x)
for-ით გავლა შეიძლება: list, str, dict, ფაილი…
Iterator (მიმდევრობის მკითხველი) — ობიექტი, რომელსაც ახსოვს, სად გაჩერდა, და next()-ზე შემდეგს აბრუნებს
Two concepts
An iterable is anything you can loop over with for: list, str, dict, a file…
An iterator is the object that remembers where it stopped and hands back the next item on next()
for „ჯადოსნური“ ეგონათ — აქ ხდება ცხადი, რომ ისიც ჩვეულებრივი მექანიზმია.
The key slide of the lecture. Until now for felt like magic — here it becomes an ordinary mechanism.
პრაქტიკაPractice
iter() და next() ხელითiter() and next() by hand
numbers = [10, 20, 30]
it = iter(numbers)
print(next(it)) # 10
print(next(it)) # 20
print(next(it)) # 30
print(next(it)) # StopIteration!
# with a default value — no error
it = iter(numbers)
print(next(it, "დასრულდა")) # 10
print(next(it, "დასრულდა")) # 20
print(next(it, "დასრულდა")) # 30
print(next(it, "დასრულდა")) # დასრულდა
it = iter([1, 2, 3])
print(list(it)) # [1, 2, 3]
print(list(it)) # [] — already exhausted!
სია ხელახლა გავლადია, იტერატორი — არა.
A list can be traversed again; an iterator cannot.
შექმნაBuilding one
საკუთარი იტერატორიYour own iterator
class Countdown:
"""უკუთვლა n-დან 1-მდე. / Counts down from n to 1."""
def __init__(self, start):
self.current = start
def __iter__(self):
return self # it is its own iterator
def __next__(self):
if self.current <= 0:
raise StopIteration # the signal: we are done
value = self.current
self.current -= 1
return value
for n in Countdown(5):
print(n, end=" ") # 5 4 3 2 1
__iter__() აბრუნებს იტერატორს, __next__() — შემდეგ ელემენტს ან ისვრის StopIteration-ს. ვინც ამ ორ მეთოდს ატარებს, მასზე for იმუშავებს.
Two methods — that is the whole contract__iter__() returns an iterator, __next__() returns the next item or raises StopIteration. Anything implementing both works with for.
კლასები კურსის პროგრამაში არაა — აქ ის მხოლოდ მექანიზმის საჩვენებლადაა. ქვემოთ ბევრად მარტივი გზაა. Classes are not part of this course — this is only to show the mechanism. A much simpler route follows.
მარტივი გზაThe simple route
გენერატორი — yieldGenerators — yield
class Countdown:
def __init__(self, start):
self.current = start
def __iter__(self):
return self
def __next__(self):
if self.current <= 0:
raise StopIteration
v = self.current
self.current -= 1
return v
def countdown(start):
while start > 0:
yield start
start -= 1
for n in countdown(5):
print(n, end=" ") # 5 4 3 2 1
yield vs returnreturn ფუნქციას ამთავრებს. yield მნიშვნელობას აბრუნებს და ფუნქციას „აპაუზებს“ — შემდეგი next() სწორედ იმ ადგილიდან გააგრძელებს.
yield vs returnreturn ends the function. yield hands back a value and pauses it — the next next() resumes from exactly that point.
yield shows execution returning to the same line.
უპირატესობაThe advantage
„ზარმაცი“ გამოთვლა (lazy evaluation)Lazy evaluation
import sys
# a list — every element is in memory at once
squares_list = [n ** 2 for n in range(1_000_000)]
print(sys.getsizeof(squares_list)) # ~8,400,000 bytes
# a generator — each element is produced on demand
squares_gen = (n ** 2 for n in range(1_000_000))
print(sys.getsizeof(squares_gen)) # ~200 bytes
print(sum(squares_gen)) # works, without the memory
ფრჩხილები მნიშვნელოვანიაThe brackets matter
[x for x in ...] → სია (list comprehension)(x for x in ...) → გენერატორი
[x for x in ...] → a list(x for x in ...) → a generator
უსასრულო მიმდევრობაAn endless sequence
def naturals():
n = 1
while True:
yield n
n += 1
სიად ეს შეუძლებელიაImpossible as a list
range(), enumerate(), zip(), map(), filter() და ფაილი — ყველა ზარმაცია. ამიტომაც კითხულობს for line in f გიგაბაიტიან ფაილს პრობლემის გარეშე.
Where you already meet thisrange(), enumerate(), zip(), map(), filter() and files are all lazy. That is precisely why for line in f handles a gigabyte file without trouble.
პრაქტიკაPractice
გენერატორები რეალურ ამოცანაშიGenerators in a real task
def read_lines(path):
"""ფაილს ხაზ-ხაზ კითხულობს, ცარიელებს ტოვებს.
Reads the file line by line, skipping blanks."""
with open(path, encoding="utf-8") as f:
for line in f:
line = line.strip()
if line:
yield line
def only_errors(lines):
"""მხოლოდ შეცდომების ხაზები. / Only the error lines."""
for line in lines:
if "ERROR" in line:
yield line
def first_words(lines):
for line in lines:
yield line.split()[0]
# a chain — not a single intermediate list is built
for word in first_words(only_errors(read_lines("app.log"))):
print(word)
ბიბლიოთეკაLibrary
itertools — მზა იტერატორებიitertools — ready-made iterators
from itertools import count, cycle, islice, chain, product, combinations
# an infinite counter
for n in islice(count(10, 5), 4):
print(n, end=" ") # 10 15 20 25
# repeating in a cycle
colors = cycle(["წითელი", "მწვანე"])
print(list(islice(colors, 5)))
# joining sequences
print(list(chain([1, 2], [3, 4]))) # [1, 2, 3, 4]
# the cartesian product
print(list(product("AB", [1, 2])))
# [('A',1), ('A',2), ('B',1), ('B',2)]
# combinations
print(list(combinations([1, 2, 3], 2)))
# [(1,2), (1,3), (2,3)]
itertools სტანდარტული ბიბლიოთეკის ნაწილია, დაყენება არ სჭირდება.
The principleBefore writing your own, check whether it already exists. itertools ships with Python — nothing to install.
შეფასებაAssessment
პრაქტიკული დავალება #4Practical assignment #4
8 ქულა · სრულდება აუდიტორიაში, დამოუკიდებლად 8 points · done in class, independently
iter/nextხელითსიაზე გაიარეwhile True-თი,next()-ითა დაStopIteration-ის დაჭერით- გენერატორი
even_numbers(limit)აბრუნებს ლუწ რიცხვებსyield-ით. შეადარე იმავე ლოგიკის სიისეულ ვარიანტსsys.getsizeof()-ით - ფიბონაჩის გენერატორიუსასრულო; პირველი 15 წევრი ამოიღე
islice-ით - ფაილის მილსადენისამი გენერატორი: ხაზების კითხვა → გაფილტვრა → გარდაქმნა. ჩააერთე ერთ ჯაჭვში
- ახსნადაწერე კომენტარად: რით განსხვავდება
[x for x in ...]და(x for x in ...)
iter/nextby handWalk a list withwhile True,next()and a caughtStopIteration- A generator
even_numbers(limit)Yields even numbers withyield. Compare it to the list version usingsys.getsizeof() - A Fibonacci generatorInfinite; take the first 15 terms with
islice - A file pipelineThree generators: read lines → filter → transform. Chain them together
- ExplainWrite as a comment: how
[x for x in ...]differs from(x for x in ...)
შემაჯამებელიWrap-up
რა უნდა დაგამახსოვრდესWhat to remember
- Iterable — რაზეც
for-ით გავლა შეიძლება; Iterator — ის, ვინც ახსოვს, სად გაჩერდა forსინამდვილეში აკეთებს:iter()→next()→StopIteration- იტერატორი ერთჯერადია — მეორედ გავლა ცარიელს დააბრუნებს
- საკუთარი იტერატორი:
__iter__+__next__; მარტივი გზა —yield - გენერატორი მეხსიერებას არ ჭამს — ელემენტს საჭიროებისას ქმნის
[...]სია ·(...)გენერატორი
- An iterable is what you can loop over; an iterator is what remembers the position
forreally performs:iter()→next()→StopIteration- An iterator is single-use — a second pass yields nothing
- Your own iterator:
__iter__+__next__; the easy way isyield - A generator uses almost no memory — it produces items on demand
[...]a list ·(...)a generator