List comprehension ও iterator
এই পাঠে যা শিখবেন
- List comprehension — for loop-এর Pythonic পরিবর্ত
- Filtering ও nested comprehension
- Dict ও set comprehension
- Generator expression — lazy iteration
- Iterator protocol ও
iter(),next() - কখন comprehension, কখন loop — ভারসাম্য
১ · List comprehension — কেন?
একই কাজ — তিন উপায়ে। কোনটা Pythonic?
nums = [1, 2, 3, 4, 5]
# ❶ For loop — verbose
squared_v1 = []
for n in nums:
squared_v1.append(n ** 2)
# ❷ map + lambda — functional, কিন্তু কম পঠনযোগ্য
squared_v2 = list(map(lambda x: x ** 2, nums))
# ❸ List comprehension — Pythonic ✓
squared_v3 = [n ** 2 for n in nums]
print(squared_v1)
print(squared_v2)
print(squared_v3)
[expression for item in iterable if condition]
১) expression — প্রতিটি item কী হবে output-এ।
২) for ... in ... — কোন iterable-এ ঘুরবে।
৩) if condition — ঐচ্ছিক filter।
২ · Filter ও condition
nums = list(range(20))
# জোড় সংখ্যার বর্গ
even_sq = [n ** 2 for n in nums if n % 2 == 0]
print(even_sq)
# Conditional expression — ternary
labels = ["pass" if n >= 50 else "fail" for n in [45, 67, 89, 32, 78]]
print(labels)
# Nested filter
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
flat_odd = [x for row in matrix for x in row if x % 2 == 1]
print(flat_odd) # [1, 3, 5, 7, 9]
for row in matrix for x in row পড়ুন as: for row in matrix: for x in row:। ক্রম গুরুত্বপূর্ণ।
৩ · Dict ও set comprehension
# Dict comprehension — word → length
words = ["AI", "Python", "ML", "data"]
lengths = {w: len(w) for w in words}
print(lengths)
# Vocabulary — id mapping
vocab = ["king", "queen", "man", "woman"]
word_to_id = {w: i for i, w in enumerate(vocab)}
id_to_word = {i: w for w, i in word_to_id.items()}
print(word_to_id)
print(id_to_word)
# Set comprehension — unique
text = "the quick brown fox jumps over the lazy dog"
unique_chars = {c for c in text if c.isalpha()}
print(sorted(unique_chars))
৪ · Generator — lazy iteration
List comprehension সব result একসাথে memory-তে তৈরি করে। ১০ কোটি item — RAM শেষ। GeneratorGeneratorএকটি iterator যা lazily মান তৈরি করে — চাইলে দেয়, না চাইলে নয়। yield keyword বা (expr for x in ...)। বিশাল ডেটা-তে অপরিহার্য। — চাইলে দেয়, না চাইলে নয়।
# List vs Generator
list_comp = [n ** 2 for n in range(10)] # সব এখনই তৈরি
gen_exp = (n ** 2 for n in range(10)) # () — generator
print(list_comp) # [0, 1, 4, ...]
print(gen_exp) # <generator object ...>
# Generator iterate
for sq in gen_exp:
print(sq, end=" ")
print()
# একবারই — আবার লাগলে আবার তৈরি
print(list(gen_exp)) # [] — exhausted
# Memory পার্থক্য
import sys
big_list = [i for i in range(100_000)]
big_gen = (i for i in range(100_000))
print(f"List: {sys.getsizeof(big_list)} bytes")
print(f"Gen: {sys.getsizeof(big_gen)} bytes")
৫ · yield — function-কে generator বানানো
def fibonacci(n):
a, b = 0, 1
for _ in range(n):
yield a # value দাও, পরে আবার আস
a, b = b, a + b
# Generator function-এর ব্যবহার
for f in fibonacci(10):
print(f, end=" ") # 0 1 1 2 3 5 8 13 21 34
print()
# Batch generator — AI-তে আদর্শ
def batches(data, size):
for i in range(0, len(data), size):
yield data[i:i + size]
samples = list(range(1, 11))
for b in batches(samples, 3):
print("Batch:", b)
৬ · Iterator protocol
# for loop যা ভেতরে করে
fruits = ["আম", "জাম", "কাঁঠাল"]
it = iter(fruits) # iterator তৈরি
print(next(it)) # আম
print(next(it)) # জাম
print(next(it)) # কাঁঠাল
# print(next(it)) # StopIteration
# zip — দু'টি iterable একসাথে
names = ["রহিম", "করিম", "ফাতেমা"]
ages = [20, 22, 19]
for n, a in zip(names, ages):
print(f"{n}: {a}")
৭ · AI-তে comprehension/generator-এর প্রয়োগ
- Feature engineering:
[normalize(x) for x in data] - Token-id mapping:
{w: i for i, w in enumerate(vocab)} - Batch loader: generator function — disk → tensor stream।
- One-hot encoding: nested comprehension।
- Pipeline:
(transform(x) for x in source)— chain। - PyTorch DataLoader: internal-এ iterator protocol।
ভাবনার প্রশ্ন
প্রতিটি প্রশ্ন নিজে কিছুক্ষণ ভাবুন — তারপর "→ উত্তর" চাপুন।
প্র ০১ List comprehension সবসময় explicit for loop-এর চেয়ে ভাল — সত্য নাকি মিথ? Performance ও readability — দু'টি দৃষ্টিকোণ থেকে কখন কোনটা?
এই বিতর্ক Python community-তে চিরন্তন। সঠিক উত্তর: "depends" — কিন্তু কখন কোনটা — এর সঠিক বোঝা ভাল kod ও অস্পষ্ট কোডের মধ্যে পার্থক্য।
Comprehension-এর performance:
- ~২০-৩০% দ্রুত explicit loop-এর চেয়ে।
- কারণ — bytecode-level optimization, append() call এড়ানো।
- NumPy vectorize-এর তুলনায় তুচ্ছ — ১০০× ধীর।
Comprehension কখন বাছবেন:
- Simple transform:
[x*2 for x in lst]— clear। - Filter + transform:
[f(x) for x in lst if cond(x)]— concise। - Dict/set creation:
{k: v for k, v in pairs} - One-line, simple expression।
Loop কখন বাছবেন:
- Side effect (print, log, db write) — comprehension এ বেমানান।
- Multiple statements — comprehension একটি expression।
- Try/except — loop ছাড়া উপায় নেই।
- Multiple output — দুটি list একসাথে তৈরি — loop সহজ।
- ৩+ nested loop — comprehension অপাঠ্য।
উদাহরণ — কী খারাপ comprehension:
# খারাপ
result = [
[transform(x, y) for y in row if validate(y)]
for row in matrix if any(check(y) for y in row)
]
# কয়েক sec বুঝতে যায় — loop-এ পরিষ্কার হত
"Side effect-এ comprehension" anti-pattern:
# খুব খারাপ
[print(x) for x in data] # list তৈরির অপচয়, print-এর জন্য
# ভাল
for x in data:
print(x)
NumPy/Pandas-এর সাথে priorities:
- Numerical data → NumPy/Pandas vectorize, comprehension না।
- Mixed type / Python objects → comprehension।
- Dict construction → dict comprehension সবচেয়ে ভাল।
"Walrus" (Python 3.8+):
# যদি repeated computation এড়াতে চান
result = [y for x in data if (y := f(x)) > 0]
# y শুধু একবার compute
Readability heuristic:
- One-line line ৭০-৮০ char-এর কম → comprehension OK।
- Nested ২ level → debatable।
- Nested ৩ level → ভুল।
- আপনি ৬ মাস পরে নিজে পড়লে বুঝবেন? — yes → keep।
মূল উপলব্ধি: Comprehension = transform-এর syntactic sugar, না magic। সঠিক জায়গায় — elegant। অপব্যবহারে — illegible। "Readability counts" (Zen of Python)। সন্দেহ থাকলে — explicit loop।
প্র ০২ "Generator + yield" pattern — Python-এর memory-saving secret। Streaming data, infinite sequence, pipeline — কেন এটা AI/data engineering-এর backbone?
Generator — Python 2.2 (২০০১)-এ PEP 255-এ আসে। এটি functional programming-এর "lazy evaluation" idiom Python-এ আনে। আজকের AI data infra এর উপর দাঁড়িয়ে।
Lazy evaluation কী?
- Eager: সব value এখনই compute, memory-তে।
- Lazy: চাইলে এক-একটা দেয়, না চাইলে কিছু না।
- Generator = Python-এর lazy stream।
Memory analysis:
# ১০ কোটি sample
import sys
list_data = [i for i in range(10**8)] # ~৮ GB RAM!
gen_data = (i for i in range(10**8)) # ~২০০ bytes
# স্মৃতি save: ৪ কোটি গুণ
Pipeline composition — Unix pipe-এর মতো:
def read_lines(path):
with open(path) as f:
for line in f:
yield line.strip()
def parse(lines):
for line in lines:
yield json.loads(line)
def filter_active(records):
for r in records:
if r["active"]:
yield r
# Pipeline — কোন ধাপেই memory-তে full data নেই
pipeline = filter_active(parse(read_lines("huge.jsonl")))
for record in pipeline:
process(record)
Infinite sequence:
def counter():
i = 0
while True:
yield i
i += 1
c = counter()
for n in c:
if n > 5: break
print(n)
# 0 1 2 3 4 5
AI/ML-এ critical applications:
- PyTorch DataLoader: internal-এ iterator। Disk → batch তৈরি করে stream।
- TensorFlow tf.data: generator-based pipeline।
- HuggingFace datasets: streaming mode — terabyte ডেটা-ও memory-তে আনে না।
- Image augmentation: on-the-fly transform — pre-compute না।
- Tokenization: stream tokens through layers।
- Sliding window: sequence model-এ context window।
Generator-এর advanced features:
- send(): generator-এ value পাঠানো — coroutine-এর প্রাচীন রূপ।
- throw(): exception inject।
- yield from: sub-generator-এ delegate।
- async generator (3.6+): await + yield।
itertools — generator-এর ভাণ্ডার:
chain,cycle,repeat— combine।islice,takewhile,dropwhile— slice।product,permutations,combinations— combinatorics।groupby,tee— group ও duplicate।
সাবধানতা:
- Single-pass — exhaustion-এর পর reset লাগে।
- len() নেই — explicit count।
- Index access নেই — only sequential।
- Debug কঠিন — print স্থায়ী state বদলায়।
মূল উপলব্ধি: Generator = Python-এর scaling secret। ছোট লেনদেন list, বিশাল data generator। AI-তে — ১ TB log file process? Generator without thinking। DataLoader internals মাস্টার করতে — generator অপরিহার্য।
প্র ০৩
"Iterator protocol" Python-এর dunder magic-এর একটি। for loop ভেতরে কীভাবে কাজ করে? Custom iterator class কখন তৈরি করবেন?
Iterator protocol — Python-এর data model-এর foundation। এটা না বুঝলে — Python কীভাবে কাজ করে — অস্পষ্ট থাকবে।
Iterator protocol:
__iter__()— iterator object ফেরত।__next__()— পরবর্তী value, না থাকলেStopIterationraise।
For loop ভেতরে যা করে:
# আপনি লিখলেন
for x in collection:
use(x)
# Python ভেতরে
it = iter(collection) # __iter__()
while True:
try:
x = next(it) # __next__()
except StopIteration:
break
use(x)
Iterable vs Iterator:
- Iterable:
__iter__আছে — list, tuple, dict, str, file। - Iterator:
__next__আছে — iter()-এর result। - Iterator নিজেও iterable (
__iter__ফেরায়self)।
Custom iterator class:
class CountDown:
def __init__(self, start):
self.current = start
def __iter__(self):
return self
def __next__(self):
if self.current <= 0:
raise StopIteration
self.current -= 1
return self.current + 1
for n in CountDown(5):
print(n) # 5 4 3 2 1
সাধারণত generator function সহজ:
def countdown(start):
while start > 0:
yield start
start -= 1
for n in countdown(5):
print(n)
কখন class বাছবেন?
- State management complex।
- Multiple methods দরকার (reset, peek, etc.)।
- Inheritance ও extensibility।
- Type checking —
isinstance(x, MyIterator)।
AI-তে এই pattern:
- PyTorch Dataset + DataLoader:
class MyDataset(Dataset): def __getitem__(self, idx): ... def __len__(self): ... # DataLoader auto-creates iterator wrapping this - Streaming HuggingFace dataset: internal iterator।
- Random sampler: custom
__iter__with shuffling। - Curriculum learning: difficulty-ordered iterator।
Common built-in iterables:
range,enumerate,zip,map,filteropen()file — line-by-line iterator।dict.keys(),.values(),.items()
iter() with sentinel:
# ফাইল chunked read
with open("big.bin", "rb") as f:
for chunk in iter(lambda: f.read(4096), b""):
process(chunk)
# b"" পেলে stop
মূল উপলব্ধি: Iterator protocol = Python-এর "duck typing" of iteration। যেকোনো object যা __iter__+__next__ দেয় — for loop-এ কাজ করে। PyTorch Dataset, NumPy ndarray, Pandas DataFrame — সব এই protocol-এ। বুঝলে — Python ecosystem-এর design গভীরে দেখা যায়।
প্র ০৪
NumPy array-এ arr * 2 কেন list comprehension-এর চেয়ে ১০০× দ্রুত? Vectorization ও comprehension-এর philosophical পার্থক্য কী? কখন কোনটা?
এই প্রশ্ন AI-র performance-এর হৃদয়ে নিয়ে যায়। List comprehension পrocessor-এর বন্ধু — কিন্তু GPU-র শত্রু।
List comprehension overhead:
- প্রতি iteration — Python interpreter dispatch।
- প্রতি object — type check, reference count।
- প্রতি element — Python heap allocation।
- Memory fragmentation — cache miss।
NumPy vectorization:
- Contiguous C array — cache-friendly।
- SIMD instruction — CPU একসাথে ৪/৮ float।
- BLAS/LAPACK — হাজার মানুষ-বছরের optimization।
- GIL release — multi-thread possible।
Concrete benchmark:
import numpy as np
import time
n = 10_000_000
# Comprehension
data = list(range(n))
t = time.time()
result = [x * 2 for x in data]
print(f"Comp: {time.time()-t:.3f}s") # ~০.৫s
# NumPy
arr = np.arange(n)
t = time.time()
result = arr * 2
print(f"NumPy: {time.time()-t:.3f}s") # ~০.০১s
Philosophical পার্থক্য:
- Comprehension: "প্রতিটি element-এর জন্য কী?" — element-thinking।
- Vectorization: "পুরো array-কে কী করব?" — array-thinking।
- Mind shift গুরুত্বপূর্ণ।
Vectorization-এর সীমা:
- Numerical-only — string operations চলে না (NumPy-তে limited)।
- Branching —
ifper element কঠিন (np.where ব্যবহার)। - Sequential dependency — RNN-এর hidden state cumulative।
- Mixed type collection — list comprehension।
Vectorization patterns:
# Conditional - np.where
arr_relu = np.where(arr > 0, arr, 0)
# Boolean mask
positives = arr[arr > 0]
# Reduce
mean = arr.mean()
# Outer product
xy = x[:, None] * y[None, :] # broadcasting
কখন কোনটা:
- Numerical, large data → NumPy/Pandas/PyTorch
- Mixed types, small data, complex logic → comprehension
- Side effects → for loop
- Streaming, infinite → generator
Hybrid approach:
# Step 1: comprehension to clean
clean = [parse(x) for x in raw if valid(x)]
# Step 2: NumPy for math
arr = np.array(clean)
result = (arr - arr.mean()) / arr.std()
GPU-এ আরো দ্রুত:
- PyTorch
tensor.cuda()→ ১০০০× speedup typical। - JAX
jitcompile → XLA optimization। - Numba
@njit→ Python loop → C speed।
মূল উপলব্ধি: Comprehension = Python idiom। Vectorization = math idiom। AI = math-heavy → vectorization default। কিন্তু comprehension-ও জানতে হবে — preprocessing, filtering, dict creation এ। দুটি tool — দুটি use case।
অনুশীলন
-
One-liner: 1 থেকে 100-এর মধ্যে যেসব সংখ্যা ৩-এ বিভাজ্য কিন্তু ৫-এ নয় — list comprehension-এ বের করুন।
result = [n for n in range(1, 101) if n % 3 == 0 and n % 5 != 0] print(result) # [3, 6, 9, 12, 18, 21, 24, 27, ...] -
Vocabulary: একটি বাক্যে — প্রতিটি unique word-কে index দিন (০ থেকে শুরু) — dict comprehension।
text = "AI is fun and AI is the future" unique_words = sorted(set(text.lower().split())) vocab = {w: i for i, w in enumerate(unique_words)} print(vocab) # {'ai': 0, 'and': 1, 'fun': 2, 'future': 3, ...} -
Generator: infinite even number generator লিখুন। প্রথম ১০টি নিয়ে print করুন।
def even_gen(): n = 0 while True: yield n n += 2 import itertools first_10 = list(itertools.islice(even_gen(), 10)) print(first_10) # [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ
- পাঠ ০৭ · ফাইল পড়া ও লেখা পরবর্তী পাঠ Disk-এ data save ও load — generator-এর সাথে natural জুড়ি।
- পাঠ ০৫ · Function ও Lambda আগের পাঠ
- পাঠ ০৩ · List, Tuple ও Dictionary সম্পর্কিত এই data structure-গুলোর উপর comprehension।
- সব AI Courses দেখুন ABCL TECH