itertools --- توابعی برای ایجاد پیمایشگرها جهت حلقهزنی کارآمد¶
این ماژول تعدادی از بلوکهای سازندهی iterator را پیادهسازی میکند که از ساختارهایی در APL، Haskell و SML الهام گرفتهاند. هر یک به شکلی مناسب برای Python بازطراحی شده است.
این ماژول مجموعهای اصلی از ابزارهای سریع و کارآمد از نظر مصرف حافظه را استاندارد میکند که بهتنهایی یا در ترکیب با یکدیگر مفید هستند. این ابزارها در کنار هم، یک «جبر پیمایشگر» (iterator algebra) را تشکیل میدهند که امکان ساخت ابزارهای تخصصی بهصورت مختصر و کارآمد در پایتون خالص را فراهم میکند.
برای نمونه، SML یک ابزار جدولبندی (tabulation) ارائه میکند: tabulate(f) که دنبالهی f(0), f(1), ... را تولید میکند. همان اثر را میتوان در پایتون با ترکیب map() و count() و تشکیل map(f, count()) به دست آورد.
پیمایشگرهای عمومی:
پیمایشگر |
آرگومانها |
نتایج |
مثال |
|---|---|---|---|
p [,func] |
p0, p0+p1, p0+p1+p2, ... |
|
|
p, n |
(p0, p1, ..., p_n-1), ... |
|
|
p, q, ... |
p0, p1, ... plast, q0, q1, ... |
|
|
پیمایشپذیر |
p0, p1, ... plast, q0, q1, ... |
|
|
داده، انتخابگرها |
(d[0] if s[0]), (d[1] if s[1]), ... |
|
|
[start[, step]] |
start, start+step, start+2*step, ... |
|
|
p |
p0, p1, ... plast, p0, p1, ... |
|
|
predicate, seq |
seq[n]، seq[n+1]، شروع از زمانی که محمول برقرار نباشد |
|
|
predicate, seq |
عناصری از seq که predicate(elem) برای آنها ناموفق است |
|
|
iterable[, key] |
زیرپیمایشگرهای گروهبندیشده بر اساس مقدار key(v) |
|
|
seq, [start,] stop [, step] |
عناصر از seq[start:stop:step] |
|
|
پیمایشپذیر |
(p[0], p[1]), (p[1], p[2]) |
|
|
elem [,n] |
elem، elem، elem، ... بهطور بیپایان یا تا n بار |
|
|
تابع، دنباله |
func(*seq[0]), func(*seq[1]), ... |
|
|
predicate, seq |
seq[0]، seq[1]، تا زمانی که محمول (predicate) شکست نخورد |
|
|
it, n |
it1, it2, ... itn یک پیمایشگر را به n تقسیم میکند |
|
|
p, q, ... |
(p[0], q[0]), (p[1], q[1]), ... |
|
پیمایشگرهای ترکیبی:
پیمایشگر |
آرگومانها |
نتایج |
|---|---|---|
p, q, ... [repeat=1] |
حاصلضرب دکارتی، معادل یک حلقه for تودرتو |
|
p[, r] |
تاپلهای به طول r، تمام ترتیبهای ممکن، بدون عناصر تکراری |
|
p, r |
تاپلهای به طول r، بهصورت مرتبشده، بدون عناصر تکراری |
|
p, r |
تاپلهایی به طول r، بهصورت مرتبشده، با عناصر تکراری |
مثالها |
نتایج |
|---|---|
|
|
|
|
|
|
|
|
توابع Itertool¶
همهی توابع زیر، پیمایشگرها را ایجاد میکنند و برمیگردانند. برخی از آنها جریانهایی با طول بینهایت فراهم میکنند، بنابراین باید فقط از طریق توابع یا حلقههایی که جریان را کوتاه میکنند، به آنها دسترسی پیدا کرد.
- itertools.accumulate(iterable[, function, *, initial=None])¶
یک پیمایشگر میسازد که مجموعهای انباشته یا نتایج انباشته از سایر توابع دودویی را بازمیگرداند.
function بهطور پیشفرض جمع است. function باید دو آرگومان بپذیرد: یک مجموع انباشته و یک مقدار از iterable.
اگر یک مقدار اولیه ارائه شود، انباشت با آن مقدار آغاز میشود و خروجی یک عنصر بیشتر از پیمایشپذیر ورودی خواهد داشت.
تقریباً معادل با:
def accumulate(iterable, function=operator.add, *, initial=None): 'Return running totals' # accumulate([1,2,3,4,5]) → 1 3 6 10 15 # accumulate([1,2,3,4,5], initial=100) → 100 101 103 106 110 115 # accumulate([1,2,3,4,5], operator.mul) → 1 2 6 24 120 iterator = iter(iterable) total = initial if initial is None: try: total = next(iterator) except StopIteration: return yield total for element in iterator: total = function(total, element) yield total
برای محاسبهی کمینهی جاری، function را روی
min()تنظیم کنید. برای بیشینهی جاری، function را رویmax()تنظیم کنید. یا برای حاصلضرب جاری، function را رویoperator.mul()تنظیم کنید. برای ساخت یک جدول استهلاک، سود را انباشته کنید و پرداختها را اعمال کنید:>>> data = [3, 4, 6, 2, 1, 9, 0, 7, 5, 8] >>> list(accumulate(data, max)) # running maximum [3, 4, 6, 6, 6, 9, 9, 9, 9, 9] >>> list(accumulate(data, operator.mul)) # running product [3, 12, 72, 144, 144, 1296, 0, 0, 0, 0] # Amortize a 5% loan of 1000 with 10 annual payments of 90 >>> update = lambda balance, payment: round(balance * 1.05) - payment >>> list(accumulate(repeat(90, 10), update, initial=1_000)) [1000, 960, 918, 874, 828, 779, 728, 674, 618, 559, 497]
برای دیدن تابع مشابهی که تنها مقدار انباشتهشده نهایی را برمیگرداند،
functools.reduce()را ببینید.اضافه شده در نسخهی 3.2.
تغییر یافته در نسخهی 3.3: پارامتر اختیاری function افزوده شد.
تغییر یافته در نسخهی 3.8: پارامتر اختیاری initial افزوده شد.
- itertools.batched(iterable, n, *, strict=False)¶
دادهها را از iterable بهصورت تاپلهایی به طول n دستهبندی میکند. ممکن است آخرین دسته کوتاهتر از n باشد.
اگر strict درست باشد، در صورتی که آخرین دسته کوتاهتر از n باشد، یک
ValueErrorپرتاب میشود.Loops over the input iterable and accumulates data into tuples up to size n. The input is consumed lazily, just enough to fill a batch. The result is yielded as soon as the batch is full or when the input iterable is exhausted:
>>> flattened_data = ['roses', 'red', 'violets', 'blue', 'sugar', 'sweet'] >>> unflattened = list(batched(flattened_data, 2)) >>> unflattened [('roses', 'red'), ('violets', 'blue'), ('sugar', 'sweet')]
تقریباً معادل با:
def batched(iterable, n, *, strict=False): # batched('ABCDEFG', 3) → ABC DEF G if n < 1: raise ValueError('n must be at least one') iterator = iter(iterable) while batch := tuple(islice(iterator, n)): if strict and len(batch) != n: raise ValueError('batched(): incomplete batch') yield batch
اضافه شده در نسخهی 3.12.
تغییر یافته در نسخهی 3.13: گزینهی strict افزوده شد.
- itertools.chain(*iterables)¶
Make an iterator that returns elements from the first iterable until it is exhausted, then proceeds to the next iterable, until all of the iterables are exhausted. This combines multiple data sources into a single iterator. Roughly equivalent to:
def chain(*iterables): # chain('ABC', 'DEF') → A B C D E F for iterable in iterables: yield from iterable
- classmethod chain.from_iterable(iterable)¶
سازنده جایگزین برای
chain(). ورودیهای زنجیرهشده را از یک آرگومان پیمایشپذیر واحد که بهصورت تنبلانه ارزیابی میشود، دریافت میکند. تقریباً معادل است با:def from_iterable(iterables): # chain.from_iterable(['ABC', 'DEF']) → A B C D E F for iterable in iterables: yield from iterable
- itertools.combinations(iterable, r)¶
بازگرداندن زیردنبالههایی به طول r از عناصرِ iterable ورودی.
خروجی زیردنبالهای از
product()است که تنها آیتمهایی را نگه میدارد که زیردنبالههایی از پیمایشپذیر هستند. طول خروجی توسطmath.comb()داده میشود کهn! / r! / (n - r)!را هنگامی که0 ≤ r ≤ nیا صفر را هنگامی کهr > nمحاسبه میکند.تاپلهای ترکیبی بر اساس ترتیب پیمایشپذیر ورودی، به ترتیب لغتنامهای تولید میشوند. اگر پیمایشپذیر ورودی مرتب باشد، تاپلهای خروجی به ترتیب مرتبشده تولید میشوند.
عناصر بر اساس موقعیتشان یکتا در نظر گرفته میشوند، نه بر اساس مقدارشان. اگر عناصر ورودی یکتا باشند، هیچ مقدار تکراری در هر ترکیب وجود نخواهد داشت.
تقریباً معادل با:
def combinations(iterable, r): # combinations('ABCD', 2) → AB AC AD BC BD CD # combinations(range(4), 3) → 012 013 023 123 pool = tuple(iterable) n = len(pool) if r > n: return indices = list(range(r)) yield tuple(pool[i] for i in indices) while True: for i in reversed(range(r)): if indices[i] != i + n - r: break else: return indices[i] += 1 for j in range(i+1, r): indices[j] = indices[j-1] + 1 yield tuple(pool[i] for i in indices)
- itertools.combinations_with_replacement(iterable, r)¶
زیردنبالههایی به طول r از عناصر iterable ورودی برمیگرداند و اجازه میدهد هر عنصر بیش از یک بار تکرار شود.
خروجی، زیردنبالهای از
product()است که فقط آیتمهایی را نگه میدارد که زیردنبالههایی از پیمایشپذیر (با امکان تکرار عناصر) هستند. تعداد زیردنبالههای برگرداندهشده هنگامی کهn > 0باشد،(n + r - 1)! / r! / (n - 1)!است.تاپلهای ترکیبی بر اساس ترتیب پیمایشپذیر ورودی، به ترتیب واژگانی تولید میشوند. اگر پیمایشپذیر ورودی مرتب باشد، تاپلهای خروجی بهصورت مرتب تولید خواهند شد.
عناصر بر اساس جایگاهشان یکتا در نظر گرفته میشوند، نه بر اساس مقدارشان. اگر عناصر ورودی یکتا باشند، ترکیبهای تولیدشده نیز یکتا خواهند بود.
تقریباً معادل با:
def combinations_with_replacement(iterable, r): # combinations_with_replacement('ABC', 2) → AA AB AC BB BC CC pool = tuple(iterable) n = len(pool) if not n and r: return indices = [0] * r yield tuple(pool[i] for i in indices) while True: for i in reversed(range(r)): if indices[i] != n - 1: break else: return indices[i:] = [indices[i] + 1] * (r - i) yield tuple(pool[i] for i in indices)
اضافه شده در نسخهی 3.1.
- itertools.compress(data, selectors)¶
Make an iterator that returns elements from data where the corresponding element in selectors is true. Stops when either the data or selectors iterables have been exhausted. Roughly equivalent to:
def compress(data, selectors): # compress('ABCDEF', [1,0,1,0,1,1]) → A C E F return (datum for datum, selector in zip(data, selectors) if selector)
اضافه شده در نسخهی 3.1.
- itertools.count(start=0, step=1)¶
پیمایشگری میسازد که مقادیر با فاصلههای یکسان را از start برمیگرداند. میتوان از آن همراه با
map()برای تولید نقاط داده متوالی یا همراه باzip()برای افزودن شمارههای ترتیب استفاده کرد. تقریباً معادل است با:def count(start=0, step=1): # count(10) → 10 11 12 13 14 ... # count(2.5, 0.5) → 2.5 3.0 3.5 ... n = start while True: yield n n += step
هنگام شمارش با اعداد ممیز شناور، گاهی میتوان با جایگزین کردن کد ضربی مانند:
(start + step * i for i in count())، به دقت بهتری دست یافت.تغییر یافته در نسخهی 3.1: آرگومان step افزوده شد و آرگومانهای غیر عدد صحیح مجاز شدند.
- itertools.cycle(iterable)¶
Make an iterator returning elements from the iterable and saving a copy of each. When the iterable is exhausted, return elements from the saved copy. Repeats indefinitely. Roughly equivalent to:
def cycle(iterable): # cycle('ABCD') → A B C D A B C D A B C D ... saved = [] for element in iterable: yield element saved.append(element) while saved: for element in saved: yield element
این ابزار تکرار (itertool) ممکن است به فضای ذخیرهسازی کمکی قابلتوجهی نیاز داشته باشد (بسته به طول پیمایشپذیر).
- itertools.dropwhile(predicate, iterable)¶
یک پیمایشگر بسازید که تا زمانی که predicate درست است، عناصر را از iterable حذف میکند و پس از آن همه عناصر را برمیگرداند. تقریباً معادل:
def dropwhile(predicate, iterable): # dropwhile(lambda x: x<5, [1,4,6,3,8]) → 6 3 8 iterator = iter(iterable) for x in iterator: if not predicate(x): yield x break for x in iterator: yield x
توجه داشته باشید که این ابزار پیمایش (itertool) تا پیش از آنکه شرط برای نخستین بار نادرست شود، هیچ خروجی تولید نمیکند، بنابراین ممکن است زمان راهاندازی طولانی داشته باشد.
- itertools.filterfalse(predicate, iterable)¶
پیمایشگری میسازد که عناصر را از پیمایشپذیر پالایش میکند و فقط آنهایی را برمیگرداند که محمول برای آنها مقدار نادرست برمیگرداند. اگر محمول
Noneباشد، آیتمهایی که نادرست هستند را برمیگرداند. تقریباً معادل است با:def filterfalse(predicate, iterable): # filterfalse(lambda x: x<5, [1,4,6,3,8]) → 6 8 if predicate is None: predicate = bool for x in iterable: if not predicate(x): yield x
- itertools.groupby(iterable, key=None)¶
یک پیمایشگر ایجاد کنید که کلیدها و گروههای متوالی را از iterable بازمیگرداند. key تابعی است که مقدار کلید هر المان را محاسبه میکند. اگر مشخص نشده باشد یا
Noneباشد، key بهطور پیشفرض یک تابع همانی است و المان را بدون تغییر بازمیگرداند. معمولاً، iterable باید از قبل بر اساس همان تابع key مرتب شده باشد.عملکرد
groupby()مشابه فیلترuniqدر یونیکس است. این تابع هر بار که مقدار تابع کلید تغییر کند، یک شکست یا گروه جدید ایجاد میکند (به همین دلیل معمولاً لازم است دادهها را با استفاده از همان تابع کلید مرتب کرده باشید). این رفتار با GROUP BY در SQL متفاوت است که عناصر مشترک را صرفنظر از ترتیب ورودیشان تجمیع میکند.گروه برگرداندهشده خودش یک پیمایشگر است که پیمایشپذیر زیربنایی را با
groupby()به اشتراک میگذارد. از آنجا که منبع مشترک است، هنگامی که شیءgroupby()پیشروی کند، گروه پیشین دیگر قابل مشاهده نیست. بنابراین، اگر بعداً به آن دادهها نیاز باشد، باید بهصورت یک فهرست ذخیره شود:groups = [] uniquekeys = [] data = sorted(data, key=keyfunc) for k, g in groupby(data, keyfunc): groups.append(list(g)) # Store group iterator as a list uniquekeys.append(k)
groupby()تقریباً معادل است با:def groupby(iterable, key=None): # [k for k, g in groupby('AAAABBBCCDAABBB')] → A B C D A B # [list(g) for k, g in groupby('AAAABBBCCD')] → AAAA BBB CC D keyfunc = (lambda x: x) if key is None else key iterator = iter(iterable) exhausted = False def _grouper(target_key): nonlocal curr_value, curr_key, exhausted yield curr_value for curr_value in iterator: curr_key = keyfunc(curr_value) if curr_key != target_key: return yield curr_value exhausted = True try: curr_value = next(iterator) except StopIteration: return curr_key = keyfunc(curr_value) while not exhausted: target_key = curr_key curr_group = _grouper(target_key) yield curr_key, curr_group if curr_key == target_key: for _ in curr_group: pass
- itertools.islice(iterable, stop)¶
- itertools.islice(iterable, start, stop[, step])
پیمایشگری میسازد که عناصر انتخابشده از پیمایشپذیر را برمیگرداند. مانند اسلایس دنباله کار میکند، اما از مقادیر منفی برای start، stop یا step پشتیبانی نمیکند.
اگر start صفر یا
Noneباشد، تکرار از صفر آغاز میشود. در غیر این صورت، عناصر پیمایشپذیر تا رسیدن به start نادیده گرفته میشوند.If stop is
None, iteration continues until the input is exhausted, if at all. Otherwise, it stops at the specified position.اگر step برابر
Noneباشد، گام بهطور پیشفرض ۱ خواهد بود. المانها بهصورت متوالی برگردانده میشوند، مگر اینکه step بزرگتر از ۱ تنظیم شده باشد که در این صورت آیتمها رد میشوند.تقریباً معادل با:
def islice(iterable, *args): # islice('ABCDEFG', 2) → A B # islice('ABCDEFG', 2, 4) → C D # islice('ABCDEFG', 2, None) → C D E F G # islice('ABCDEFG', 0, None, 2) → A C E G s = slice(*args) start = 0 if s.start is None else s.start stop = s.stop step = 1 if s.step is None else s.step if start < 0 or (stop is not None and stop < 0) or step <= 0: raise ValueError indices = count() if stop is None else range(max(start, stop)) next_i = start for i, element in zip(indices, iterable): if i == next_i: yield element next_i += step
اگر ورودی یک پیمایشگر باشد، مصرف کامل islice، پیمایشگر ورودی را صرفنظر از مقدار step به اندازهی
max(start, stop)گام پیش میبرد.
- itertools.pairwise(iterable)¶
جفتهای همپوشان متوالی گرفتهشده از پیمایشپذیر ورودی را برمیگرداند.
تعداد تاپلهای دوتایی (2-tuples) در پیمایشگر خروجی، یکی کمتر از تعداد ورودیها خواهد بود. اگر پیمایشپذیر ورودی کمتر از دو مقدار داشته باشد، این پیمایشگر خالی خواهد بود.
تقریباً معادل با:
def pairwise(iterable): # pairwise('ABCDEFG') → AB BC CD DE EF FG iterator = iter(iterable) a = next(iterator, None) for b in iterator: yield a, b a = b
اضافه شده در نسخهی 3.10.
- itertools.permutations(iterable, r=None)¶
جایگشتهای عناصر را بهصورت متوالی و به طول r از iterable برمیگرداند.
اگر r مشخص نشده باشد یا
Noneباشد، r بهطور پیشفرض برابر با طول iterable خواهد بود و تمام جایگشتهای ممکن با طول کامل تولید میشوند.خروجی، زیردنبالهای از
product()است که در آن آیتمهای دارای عناصر تکراری حذف شدهاند. طول خروجی از طریقmath.perm()به دست میآید، کهn! / (n - r)!را وقتی0 ≤ r ≤ nیا صفر را وقتیr > nمحاسبه میکند.تاپلهای جایگشت به ترتیب واژگانی، مطابق با ترتیب پیمایشپذیر ورودی تولید میشوند. اگر پیمایشپذیر ورودی مرتب باشد، تاپلهای خروجی به ترتیب مرتب تولید خواهند شد.
عناصر بر اساس جایگاهشان یکتا در نظر گرفته میشوند، نه بر اساس مقدارشان. اگر عناصر ورودی یکتا باشند، هیچ مقدار تکراری در یک جایگشت وجود نخواهد داشت.
تقریباً معادل با:
def permutations(iterable, r=None): # permutations('ABCD', 2) → AB AC AD BA BC BD CA CB CD DA DB DC # permutations(range(3)) → 012 021 102 120 201 210 pool = tuple(iterable) n = len(pool) r = n if r is None else r if r > n: return indices = list(range(n)) cycles = list(range(n, n-r, -1)) yield tuple(pool[i] for i in indices[:r]) while n: for i in reversed(range(r)): cycles[i] -= 1 if cycles[i] == 0: indices[i:] = indices[i+1:] + indices[i:i+1] cycles[i] = n - i else: j = cycles[i] indices[i], indices[-j] = indices[-j], indices[i] yield tuple(pool[i] for i in indices[:r]) break else: return
- itertools.product(*iterables, repeat=1)¶
ضرب دکارتی پیمایشپذیرهای ورودی.
تقریباً معادل حلقههای for تودرتو در یک عبارت تولیدگر است. برای مثال،
product(A, B)همان((x,y) for x in A for y in B)را برمیگرداند.حلقههای تودرتو مانند مسافتسنج میچرخند و راستترین عنصر در هر تکرار جلو میرود. این الگو یک ترتیب واژگانی ایجاد میکند، بهطوریکه اگر پیمایشپذیرهای ورودی مرتب باشند، تاپلهای حاصلضرب بهصورت مرتب تولید میشوند.
برای محاسبهی ضرب یک پیمایشپذیر در خودش، تعداد تکرارها را با آرگومان کلیدواژهای اختیاری repeat مشخص کنید. برای مثال،
product(A, repeat=4)به همان معنایproduct(A, A, A, A)است.این تابع تقریباً معادل کد زیر است، با این تفاوت که پیادهسازی واقعی نتایج میانی را در حافظه ایجاد نمیکند:
def product(*iterables, repeat=1): # product('ABCD', 'xy') → Ax Ay Bx By Cx Cy Dx Dy # product(range(2), repeat=3) → 000 001 010 011 100 101 110 111 if repeat < 0: raise ValueError('repeat argument cannot be negative') pools = [tuple(pool) for pool in iterables] * repeat result = [[]] for pool in pools: result = [x+[y] for x in result for y in pool] for prod in result: yield tuple(prod)
پیش از اجرای
product()، این تابع پیمایشپذیرهای ورودی را بهطور کامل مصرف میکند و مجموعههایی از مقادیر را در حافظه نگه میدارد تا حاصلضربها را تولید کند. بنابراین، فقط برای ورودیهای متناهی به کار میآید.
- itertools.repeat(object[, times])¶
یک پیمایشگر میسازد که object را بارها و بارها برمیگرداند. بهطور نامحدود اجرا میشود، مگر اینکه آرگومان times مشخص شده باشد.
تقریباً معادل با:
def repeat(object, times=None): # repeat(10, 3) → 10 10 10 if times is None: while True: yield object else: for i in range(times): yield object
یک کاربرد رایج repeat، فراهم کردن جریانی از مقادیر ثابت برای map یا zip است:
>>> list(map(pow, range(10), repeat(2))) [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
- itertools.starmap(function, iterable)¶
پیمایشگری میسازد که function را با استفاده از آرگومانهای بهدستآمده از iterable محاسبه میکند. به جای
map()زمانی استفاده میشود که پارامترهای آرگومان از پیش بهصورت تاپلها زیپ (pre-zipped) شدهاند.تفاوت میان
map()وstarmap()مشابه تمایز میانfunction(a,b)وfunction(*c)است. تقریباً معادل است با:def starmap(function, iterable): # starmap(pow, [(2,5), (3,2), (10,3)]) → 32 9 1000 for args in iterable: yield function(*args)
- itertools.takewhile(predicate, iterable)¶
یک پیمایشگر میسازد که عناصر را از پیمایشپذیر تا زمانی که شرط درست باشد برمیگرداند. تقریباً معادل است با:
def takewhile(predicate, iterable): # takewhile(lambda x: x<5, [1,4,6,3,8]) → 1 4 for x in iterable: if not predicate(x): break yield x
Note, the element that first fails the predicate condition is consumed from the input iterator and there is no way to access it. This could be an issue if an application wants to further consume the input iterator after takewhile has been run to exhaustion. To work around this problem, consider using more-itertools before_and_after() instead.
- itertools.tee(iterable, n=2)¶
n پیمایشگرٔ مستقل از یک پیمایشپذیر واحد برمیگرداند.
تقریباً معادل با:
def tee(iterable, n=2): if n < 0: raise ValueError if n == 0: return () iterator = _tee(iterable) result = [iterator] for _ in range(n - 1): result.append(_tee(iterator)) return tuple(result) class _tee: def __init__(self, iterable): it = iter(iterable) if isinstance(it, _tee): self.iterator = it.iterator self.link = it.link else: self.iterator = it self.link = [None, None] def __iter__(self): return self def __next__(self): link = self.link if link[1] is None: link[0] = next(self.iterator) link[1] = [None, None] value, self.link = link return value
هنگامی که iterable ورودی از پیش یک شیء پیمایشگر tee باشد، همهی اعضای تاپل بازگشتی چنان ایجاد میشوند که گویی توسط فراخوانی بالادستی
tee()تولید شده باشند. این «مرحلهی تختسازی» اجازه میدهد که فراخوانیهای تودرتویtee()زنجیرهی دادهی زیربنایی یکسانی را به اشتراک بگذارند و بهجای زنجیرهای از فراخوانیها، یک مرحلهی بهروزرسانی واحد داشته باشند.ویژگی تختسازی باعث میشود پیمایشگرهای tee بهطور کارآمد قابل پیشنگری (peekable) باشند:
def lookahead(tee_iterator): "Return the next value without moving the input forward" [forked_iterator] = tee(tee_iterator, 1) return next(forked_iterator)
>>> iterator = iter('abcdef') >>> [iterator] = tee(iterator, 1) # Make the input peekable >>> next(iterator) # Move the iterator forward 'a' >>> lookahead(iterator) # Check next value 'b' >>> next(iterator) # Continue moving forward 'b'
پیمایشگرهای
teeنخایمن نیستند. ممکن است هنگام استفادهی همزمان از پیمایشگرهایی که توسط همان فراخوانیtee()برگردانده شدهاند،RuntimeErrorپرتاب شود، حتی اگر پیمایشپذیر اصلی نخایمن باشد.این ابزار تکرار (itertool) ممکن است به فضای ذخیرهسازی کمکی قابلتوجهی نیاز داشته باشد (بسته به اینکه چه مقدار داده موقت نیاز است ذخیره شود). بهطور کلی، اگر یک پیمایشگر بیشتر یا تمام دادهها را پیش از شروع پیمایشگر دیگر مصرف کند، استفاده از
list()بهجایtee()سریعتر است.
- itertools.zip_longest(*iterables, fillvalue=None)¶
یک پیمایشگر میسازد که المانها را از هر یک از پیمایشپذیرها تجمیع میکند.
اگر پیمایشپذیرها طولهای نابرابری داشته باشند، مقادیر جاافتاده با fillvalue پر میشوند. اگر مشخص نشده باشد، fillvalue بهطور پیشفرض
Noneخواهد بود.Iteration continues until the longest iterable is exhausted.
تقریباً معادل با:
def zip_longest(*iterables, fillvalue=None): # zip_longest('ABCD', 'xy', fillvalue='-') → Ax By C- D- iterators = list(map(iter, iterables)) num_active = len(iterators) if not num_active: return while True: values = [] for i, iterator in enumerate(iterators): try: value = next(iterator) except StopIteration: num_active -= 1 if not num_active: return iterators[i] = repeat(fillvalue) value = fillvalue values.append(value) yield tuple(values)
اگر یکی از پیمایشپذیرها بهطور بالقوه بینهایت باشد، باید تابع
zip_longest()در پوششی قرار گیرد که تعداد فراخوانیها را محدود کند (برای مثالislice()یاtakewhile()).
دستورپختهای Itertools¶
این بخش دستورالعملهایی برای ایجاد یک مجموعهابزار گسترشیافته با استفاده از itertools موجود بهعنوان بلوکهای سازنده ارائه میدهد.
هدف اصلی دستورالعملهای itertools، آموزشی است. این دستورالعملها روشهای گوناگونی برای اندیشیدن دربارهی ابزارهای منفرد نشان میدهند — برای مثال، اینکه chain.from_iterable به مفهوم تختسازی مربوط است. این دستورالعملها همچنین ایدههایی دربارهی شیوههای ترکیب ابزارها ارائه میدهند — برای مثال، اینکه چگونه starmap() و repeat() میتوانند با هم کار کنند. این دستورالعملها همچنین الگوهایی برای استفاده از itertools با ماژولهای operator و collections و همچنین با itertoolsهای توکار مانند map()، filter()، reversed() و enumerate() نشان میدهند.
هدف ثانویهی دستورالعملها این است که بهعنوان یک مرکز رشد عمل کنند. توابع accumulate()، compress() و pairwise() در itertools در ابتدا بهصورت دستورالعمل معرفی شدند. در حال حاضر، دستورالعملهای sliding_window()، derangements() و sieve() در حال آزمایش هستند تا مشخص شود که آیا ارزش خود را اثبات میکنند یا خیر.
تقریباً تمام این دستورپختها و بسیاری، بسیاری دیگر را میتوان از پروژهی more-itertools موجود در Python Package Index نصب کرد:
python -m pip install more-itertools
بسیاری از دستورپختها همان کارایی بالای مجموعهابزار زیرین را ارائه میدهند. کارایی برتر حافظه با پردازش المانها بهصورت یکییکی حفظ میشود، نه با آوردن کل پیمایشپذیر به حافظه بهصورت یکجا. حجم کد با پیوند دادن ابزارها به یکدیگر در یک سبک تابعی کوچک نگه داشته میشود. سرعت بالا با ترجیح بلوکهای سازندهی «بردارسازیشده» بر استفاده از حلقههای for و تولیدگرها حفظ میشود، زیرا این موارد سربار مفسر را تحمیل میکنند.
from itertools import (accumulate, batched, chain, combinations, compress,
count, cycle, filterfalse, groupby, islice, permutations, product,
repeat, starmap, tee, zip_longest)
from collections import Counter, deque
from contextlib import suppress
from functools import reduce
from heapq import heappush, heappushpop, heappush_max, heappushpop_max
from math import comb, isqrt, prod, sumprod
from operator import getitem, is_not, itemgetter, mul, neg, truediv
# ==== Basic one liners ====
def take(n, iterable):
"اولین n آیتم از پیمایشپذیر را بهعنوان یک فهرست برمیگرداند."
return list(islice(iterable, n))
def prepend(value, iterable):
"یک مقدار واحد را در ابتدای یک پیمایشپذیر قرار میدهد."
# prepend(1, [2, 3, 4]) → 1 2 3 4
return chain([value], iterable)
def repeatfunc(function, times=None, *args):
"فراخوانیهای یک تابع را با آرگومانهای مشخصشده تکرار میکند."
if times is None:
return starmap(function, repeat(args))
return starmap(function, repeat(args, times))
def flatten(list_of_lists):
"یک سطح از تودرتویی را صاف میکند."
return chain.from_iterable(list_of_lists)
def ncycles(iterable, n):
"عناصر دنباله را n بار برمیگرداند."
return chain.from_iterable(repeat(tuple(iterable), n))
def loops(n):
"n بار حلقه میزند. مانند range(n) اما بدون ایجاد اعداد صحیح."
# for _ in loops(100): ...
return repeat(None, n)
def tail(n, iterable):
"یک پیمایشگر روی آخرین n آیتم برمیگرداند."
# tail(3, 'ABCDEFG') → E F G
return iter(deque(iterable, maxlen=n))
def consume(iterator, n=None):
"پیمایشگر را n مرحله جلو میبرد. اگر n برابر None باشد، آن را بهطور کامل مصرف میکند."
# Use functions that consume iterators at C speed.
if n is None:
deque(iterator, maxlen=0)
else:
next(islice(iterator, n, n), None)
def nth(iterable, n, default=None):
"آیتم n-اُم یا یک مقدار پیشفرض را برمیگرداند."
return next(islice(iterable, n, None), default)
def quantify(iterable, predicate=bool):
"با توجه به یک شرط که True یا False برمیگرداند، نتایج True را میشمارد."
return sum(map(predicate, iterable))
def first_true(iterable, default=False, predicate=None):
"اولین مقدار درست یا *default* را در صورت نبود مقدار درست برمیگرداند."
# first_true([a, b, c], x) → a or b or c or x
# first_true([a, b], x, f) → a if f(a) else b if f(b) else x
return next(filter(predicate, iterable), default)
def all_equal(iterable, key=None):
"اگر همه عناصر با یکدیگر برابر باشند، True برمیگرداند."
# all_equal('4٤௪౪໔', key=int) → True
return len(take(2, groupby(iterable, key))) <= 1
# ==== Data pipelines ====
def unique_justseen(iterable, key=None):
"عناصر یکتا را با حفظ ترتیب تولید میکند. فقط عنصری را که همین حالا دیده شده است به خاطر میسپارد."
# unique_justseen('AAAABBBCCDAABBB') → A B C D A B
# unique_justseen('ABBcCAD', str.casefold) → A B c A D
if key is None:
return map(itemgetter(0), groupby(iterable))
return map(next, map(itemgetter(1), groupby(iterable, key)))
def unique_everseen(iterable, key=None):
"عناصر یکتا را با حفظ ترتیب تولید میکند. همه عناصری را که تاکنون دیده شدهاند به خاطر میسپارد."
# unique_everseen('AAAABBBCCDAABBB') → A B C D
# unique_everseen('ABBcCAD', str.casefold) → A B c D
seen = set()
if key is None:
for element in filterfalse(seen.__contains__, iterable):
seen.add(element)
yield element
else:
for element in iterable:
k = key(element)
if k not in seen:
seen.add(k)
yield element
def unique(iterable, key=None, reverse=False):
"عناصر یکتا را به ترتیب مرتبشده تولید میکند. از ورودیهای هشناپذیر (unhashable) پشتیبانی میکند."
# unique([[1, 2], [3, 4], [1, 2]]) → [1, 2] [3, 4]
sequenced = sorted(iterable, key=key, reverse=reverse)
return unique_justseen(sequenced, key=key)
def sliding_window(iterable, n):
"دادهها را به تکهها یا بلوکهای ثابتطول همپوشان تبدیل میکند."
# sliding_window('ABCDEFG', 3) → ABC BCD CDE DEF EFG
iterator = iter(iterable)
window = deque(islice(iterator, n - 1), maxlen=n)
for x in iterator:
window.append(x)
yield tuple(window)
def grouper(iterable, n, *, incomplete='fill', fillvalue=None):
"دادهها را به تکهها یا بلوکهای ثابتطول غیرهمپوشان تبدیل میکند."
# grouper('ABCDEFG', 3, fillvalue='x') → ABC DEF Gxx
# grouper('ABCDEFG', 3, incomplete='strict') → ABC DEF ValueError
# grouper('ABCDEFG', 3, incomplete='ignore') → ABC DEF
iterators = [iter(iterable)] * n
match incomplete:
case 'fill':
return zip_longest(*iterators, fillvalue=fillvalue)
case 'strict':
return zip(*iterators, strict=True)
case 'ignore':
return zip(*iterators)
case _:
raise ValueError('Expected fill, strict, or ignore')
def roundrobin(*iterables):
"پیمایشپذیرهای ورودی را بهصورت چرخهای پیمایش میکند تا هر کدام تمام شوند."
# roundrobin('ABC', 'D', 'EF') → A D E B F C
# Algorithm credited to George Sakkis
iterators = map(iter, iterables)
for num_active in range(len(iterables), 0, -1):
iterators = cycle(islice(iterators, num_active))
yield from map(next, iterators)
def subslices(seq):
"همه زیراسلایسهای پیوسته و غیرخالی یک دنباله را برمیگرداند."
# subslices('ABCD') → A AB ABC ABCD B BC BCD C CD D
slices = starmap(slice, combinations(range(len(seq) + 1), 2))
return map(getitem, repeat(seq), slices)
def derangements(iterable, r=None):
"جایگشتهایی به طول r بدون نقاط ثابت تولید میکند."
# derangements('ABCD') → BADC BCDA BDAC CADB CDAB CDBA DABC DCAB DCBA
# Algorithm credited to Stefan Pochmann
seq = tuple(iterable)
pos = tuple(range(len(seq)))
have_moved = map(map, repeat(is_not), repeat(pos), permutations(pos, r=r))
valid_derangements = map(all, have_moved)
return compress(permutations(seq, r=r), valid_derangements)
def iter_index(iterable, value, start=0, stop=None):
"اندیسهایی را که یک مقدار در یک دنباله یا پیمایشپذیر در آنها ظاهر میشود برمیگرداند."
# iter_index('AABCADEAF', 'A') → 0 1 4 7
seq_index = getattr(iterable, 'index', None)
if seq_index is None:
iterator = islice(iterable, start, stop)
for i, element in enumerate(iterator, start):
if element is value or element == value:
yield i
else:
stop = len(iterable) if stop is None else stop
i = start
with suppress(ValueError):
while True:
yield (i := seq_index(value, i, stop))
i += 1
def iter_except(function, exception, first=None):
"یک رابط فراخوانی-تا-استثنا را به یک رابط پیمایشگر تبدیل میکند."
# iter_except(d.popitem, KeyError) → non-blocking dictionary iterator
with suppress(exception):
if first is not None:
yield first()
while True:
yield function()
# ==== Mathematical operations ====
def multinomial(*counts):
"تعداد آرایشهای متمایز یک چندمجموعه."
# Counter('abracadabra').values() → 5 2 2 1 1
# multinomial(5, 2, 2, 1, 1) → 83160
return prod(map(comb, accumulate(counts), counts))
def powerset(iterable):
"زیردنبالههای پیمایشپذیر از کوتاهترین تا بلندترین."
# powerset([1,2,3]) → () (1,) (2,) (3,) (1,2) (1,3) (2,3) (1,2,3)
s = list(iterable)
return chain.from_iterable(combinations(s, r) for r in range(len(s)+1))
def sum_of_squares(iterable):
"مربعهای مقادیر ورودی را با هم جمع میکند."
# sum_of_squares([10, 20, 30]) → 1400
return sumprod(*tee(iterable))
# ==== Matrix operations ====
def reshape(matrix, columns):
"شکل یک ماتریس دوبعدی را تغییر میدهد تا تعداد ستونهای دادهشده را داشته باشد."
# reshape([(0, 1), (2, 3), (4, 5)], 3) → (0, 1, 2) (3, 4, 5)
return batched(chain.from_iterable(matrix), columns, strict=True)
def transpose(matrix):
"ردیفها و ستونهای یک ماتریس دوبعدی را جابهجا میکند."
# transpose([(1, 2, 3), (11, 22, 33)]) → (1, 11) (2, 22) (3, 33)
return zip(*matrix, strict=True)
def matmul(m1, m2):
"دو ماتریس را ضرب میکند."
# matmul([(7, 5), (3, 5)], [(2, 5), (7, 9)]) → (49, 80) (41, 60)
n = len(m2[0])
return batched(starmap(sumprod, product(m1, transpose(m2))), n)
# ==== Polynomial arithmetic ====
def convolve(signal, kernel):
"""کانولوشن خطی گسسته (discrete linear convolution) دو پیمایشپذیر.
معادل ضرب چندجملهای.
کانولوشنها از نظر ریاضی جابهجاییپذیرند؛ با این حال، ورودیها بهشکل
متفاوتی ارزیابی میشوند. سیگنال بهصورت تنبل مصرف میشود و میتواند
بینهایت باشد. هسته پیش از آغاز محاسبات بهطور کامل مصرف میشود.
مقاله: https://betterexplained.com/articles/intuitive-convolution/
ویدیو: https://www.youtube.com/watch?v=KuXjwB4LzSA
"""
# convolve([1, -1, -20], [1, -3]) → 1 -4 -17 60
# convolve(data, [0.25, 0.25, 0.25, 0.25]) → Moving average (blur)
# convolve(data, [1/2, 0, -1/2]) → 1st derivative estimate
# convolve(data, [1, -2, 1]) → 2nd derivative estimate
kernel = tuple(kernel)[::-1]
n = len(kernel)
padded_signal = chain(repeat(0, n-1), signal, repeat(0, n-1))
windowed_signal = sliding_window(padded_signal, n)
return map(sumprod, repeat(kernel), windowed_signal)
def polynomial_from_roots(roots):
"""ضرایب یک چندجملهای را از ریشههای آن محاسبه میکند.
(x - 5) (x + 4) (x - 3) بسط مییابد به: x³ -4x² -17x + 60
"""
# polynomial_from_roots([5, -4, 3]) → [1, -4, -17, 60]
factors = zip(repeat(1), map(neg, roots))
return list(reduce(convolve, factors, [1]))
def polynomial_eval(coefficients, x):
"""یک چندجملهای را در یک مقدار مشخص ارزیابی میکند.
با پایداری عددی بهتر از روش هورنر محاسبه میکند.
"""
# Evaluate x³ -4x² -17x + 60 at x = 5
# polynomial_eval([1, -4, -17, 60], x=5) → 0
n = len(coefficients)
if not n:
return type(x)(0)
powers = map(pow, repeat(x), reversed(range(n)))
return sumprod(coefficients, powers)
def polynomial_derivative(coefficients):
"""مشتق اول یک چندجملهای را محاسبه میکند.
f(x) = x³ -4x² -17x + 60
f'(x) = 3x² -8x -17
"""
# polynomial_derivative([1, -4, -17, 60]) → [3, -8, -17]
n = len(coefficients)
powers = reversed(range(1, n))
return list(map(mul, coefficients, powers))
# ==== Number theory ====
def sieve(n):
"اعداد اول کوچکتر از n."
# sieve(30) → 2 3 5 7 11 13 17 19 23 29
if n > 2:
yield 2
data = bytearray((0, 1)) * (n // 2)
for p in iter_index(data, 1, start=3, stop=isqrt(n) + 1):
data[p*p : n : p+p] = bytes(len(range(p*p, n, p+p)))
yield from iter_index(data, 1, start=3)
def factor(n):
"عوامل اول n."
# factor(99) → 3 3 11
# factor(1_000_000_000_000_007) → 47 59 360620266859
# factor(1_000_000_000_000_403) → 1000000000000403
for prime in sieve(isqrt(n) + 1):
while not n % prime:
yield prime
n //= prime
if n == 1:
return
if n > 1:
yield n
def is_prime(n):
"اگر n اول باشد، True برمیگرداند."
# is_prime(1_000_000_000_000_403) → True
return n > 1 and next(factor(n)) == n
def totient(n):
"تعداد اعداد طبیعی تا n که با n متباین هستند."
# https://mathworld.wolfram.com/TotientFunction.html
# totient(12) → 4 because len([1, 5, 7, 11]) == 4
for prime in set(factor(n)):
n -= n // prime
return n
# ==== Running statistics ====
def running_mean(iterable):
"میانگین مقادیری که تاکنون دیده شدهاند."
# running_mean([37, 33, 38, 28]) → 37 35 36 34
return map(truediv, accumulate(iterable), count(1))
def running_min(iterable):
"کوچکترین مقدار در میان مقادیری که تاکنون دیده شدهاند."
# running_min([37, 33, 38, 28]) → 37 33 33 28
return accumulate(iterable, func=min)
def running_max(iterable):
"بزرگترین مقدار در میان مقادیری که تاکنون دیده شدهاند."
# running_max([37, 33, 38, 28]) → 37 37 38 38
return accumulate(iterable, func=max)
def running_median(iterable):
"میانه مقادیری که تاکنون دیده شدهاند."
# running_median([37, 33, 38, 28]) → 37 35 37 35
read = iter(iterable).__next__
lo = [] # max-heap
hi = [] # min-heap the same size as or one smaller than lo
with suppress(StopIteration):
while True:
heappush_max(lo, heappushpop(hi, read()))
yield lo[0]
heappush(hi, heappushpop_max(lo, read()))
yield (lo[0] + hi[0]) / 2
def running_statistics(iterable):
"آمار تجمعی برای مقادیری که تاکنون دیده شدهاند."
# Generate tuples: (size, minimum, median, maximum, mean)
t0, t1, t2, t3 = tee(iterable, 4)
return zip(
count(1),
running_min(t0),
running_median(t1),
running_max(t2),
running_mean(t3),
)