Zəhmli təkrarlayıcılar və bunları necə etmək olar

John Matychuk'un Unsplash'daki şəkli

Problem

Make School-da oxuyarkən iş yoldaşlarının əşyaların siyahısını yaradan funksiyalar yazdıqlarını gördüm.

s = 'baacabcaab' p = 'a'
def find_char (sətir, simvol): indekslər = siyahı ()
indeks üçün, saymaqda str_char (string): əgər str_char == simvol: Indices.append (index)
İndeksləri qaytarın
çap (find_char (s, p)) # [1, 2, 4, 7, 8]

Bu tətbiq işləyir, ancaq bir neçə problem yaradır:

  • Yalnız ilk nəticəni istəsək nə etməli? Tamamilə yeni bir iş görməliyik?
  • Nəticəni yalnız bir dəfə təkrarlasaq, hər bir elementi yaddaşa qoymalıyıq?

Bu problemlərin ideal həlli təkrarlayıcıdır. Yaratdığı hər dəyərin siyahısını qaytarmaq və hər bir maddəni ayrı-ayrılıqda qaytarmaq əvəzinə "tənbəl siyahılar" kimi işləyirlər.

Təkrarlayıcılar ləng dəyərləri qaytarır. yaddaşı saxla.

Beləliklə, onlar haqqında daha çox məlumat əldə etmək üçün dalaq!

Daxili təkrarlayıcılar

Ən çox istifadə edilən təkrarlayıcılar () və zip () sayılır. Hər ikisi də tənbəlliklə next () dəyərlərini özləri ilə qaytarır.

range () təkrarlayıcı deyil, "tənbəl iterator" dur. - İzahat

Aralığı () iter () ilə iteratora çevirə bilərik. Buna görə nümunələrimizin bir şey öyrənməsi üçün etdiyimiz şey budur.

my_iter = iter (sahə (10)) çap (sonrakı (my_iter)) # 0 çap (sonrakı (my_iter)) # 1

Next () hər dəfə çağırıldıqda, aralığımızdakı növbəti dəyəri əldə edirik. Anlamı düz edir? Bir iteratoru bir siyahıya çevirmək istəyirsinizsə, sadəcə siyahı konstruktorunu verin.

my_iter = iter (sıra (10)) çap (siyahı (my_iter)) # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

Bu davranışı təqlid etsək, iteratorların necə işlədiyini daha çox başa düşərik.

my_iter = iter (sıra (10)) my_list = list ()
Cəhd edin: while true: my_list.append (sonrakı (my_iter)) istisna olmaqla StopIteration: mövcuddur
çap et (siyahım) # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

Görürsən ki, bir cəhd tutmaq ifadəsi ilə bağlamalı olduq. Bunun səbəbi təkrarlayıcıların tükəndikləri zaman StopIteration-ı işə salmasıdır.

Beləliklə, növbəti dəfə tükənmiş sıra iteratorumuzu çağırırıq, bu səhvi görəcəyik.

növbəti (my_iter) # Tetikleyiciler: StopIteration

Bir iterator yaradın

Üç ümumi iterator növündən istifadə edərək yalnız dayandırma arqumenti ilə bir sıra kimi davranan təkrarlayıcı yaratmağa çalışaq: siniflər, generator funksiyaları (gəlir) və generator ifadələri

əla

Bir iterator yaratmağın köhnə yolu açıq şəkildə müəyyən edilmiş bir sinif idi. Bir obyektin təkrarlayıcı olması üçün özünü qaytaran __iter __ () və növbəti dəyəri qaytaran __next __ () tətbiq etməlidir.

Sınıfın_aralığı: _ cərəyan = -1
def __init __ (self, stop): self._stop = stop
def __iter __ (özünü): özünüzü qaytarın
def __next __ (özünü): self._current + = 1
əgər self._current> = self._stop: StopIteration tetikler
self._current qayıt
r = aralığım (10) çap (siyahı (r)) # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

Bu o qədər də çətin deyildi, amma təəssüf ki, next () zəngləri arasındakı dəyişənlərə diqqət yetirməliyik. Şəxsən mən qazan plitəsini sevmirəm və döngələr barədə düşündüyümü dəyişdirirəm, çünki bu düşmə həlli deyil, ona görə də generatorlara üstünlük verirəm

Əsas üstünlüyü ondadır ki, daxili dəyişənləri dəyişdirən əlavə funksiyalar əlavə edə bilərik. B. dayandırın və ya yeni təkrarlayıcılar yaradın.

Sinif təkrarlayıcılarının qazan plitəsinə ehtiyac duyduqları dezavantaj var. Bununla birlikdə, vəziyyəti dəyişdirən əlavə funksiyalara sahib ola bilərlər.

Generatorlar

PEP 255-də gəlir sözlü “sadə generatorlar” tətbiq edilmişdir.

Bu gün generatorlar sinif yoldaşlarına nisbətən daha asan yaradılan təkrarlayıcılardır.

Generator funksiyası

Generator funksiyaları bu PEP-də müzakirə olunan şeylər idi və ən çox sevdiyim təkrarlayıcı növlərimdir. Buna görə başlayaq.

def my_range (stop): index = 0
indeks isə
r = aralığım (10) çap (siyahı (r)) # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

Görün bu 4 sətir kod nə qədər gözəldir? Üstə qoymaq üçün siyahımızın tətbiqindən bir qədər qısadır.

Generator normal məntiq axını olan siniflərdən daha az qazan plitəsi olan təkrarlayıcıları işləyir.

Generator funksiyaları icrasını avtomatik olaraq dayandırır və hər dəfə () deyiləndə göstərilən dəyəri qaytarır. Bu o deməkdir ki, () üçün ilk zəng qədər heç bir kod icra olunmur.

Bu, axının aşağıdakı kimidir:

  1. next () deməkdir
  2. Kod növbəti gəlir bildiriminə qədər icra olunur.
  3. Gəlir haqqındakı dəyər geri qaytarılır.
  4. İcra dayandırılacaq.
  5. Kodun son sətrinə çatana qədər hər növbəti () zəng üçün 1-5 təkrar.
  6. StopIteration tetiklenir.

Jeneratör funksiyaları ilə, gələcək sözcükdən əldə olunan gəliri də istifadə edə bilərsiniz, bu da təkrarlana bilən bitənə qədər başqa bir təkrarlana bilər.

def verimli_range (): my_range verimi (10)
çap et (siyahı (verildi_range ())) # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

Bu, xüsusilə mürəkkəb bir nümunə deyildi. Ancaq bunu təkrarlanan şəkildə də edə bilərsiniz!

def my_range_recursive (dayandır, cari = 0): əgər cari> = dayan: qayıt
My_range_recursive-dən axın axını verməsi (dayan, cari + 1)
r = my_range_recursive (10) print (list (r)) # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

Generator ifadəsi

Generator ifadələri tək təklər kimi təkrarlayıcılar yaratmağımızı təmin edir. Xarici funksionallıq tələb olunmadıqda yaxşıdırlar. Təəssüf ki, bir ifadə ilə başqa bir my_range yarada bilmərik, ancaq son my_range funksiyamız kimi təkrarlananları idarə edə bilərik.

my_doubled_range_10 = (mənim aralığımdakı x üçün x * 2 (10)) çap (siyahı (my_doubled_range_10)) # 0, 2, 4, 6, 8, 10, 12, 14, 16, 18]

Bununla əlaqədar ən yaxşısı aşağıdakıları etməkdir:

  1. Siyahıda, my_doubled_range_10-dan növbəti dəyər istənir.
  2. my_doubled_range_10 mənim aralığımdan növbəti dəyəri tələb edir.
  3. my_doubled_range_10, my_range dəyərini 2-yə vurur.
  4. Siyahı dəyəri özünə əlavə edir.
  5. My_doubled_range_10 StopIteration tetikleyene qədər 1-5-i təkrarlayın, my_range bunu etdikdə nə olur.
  6. Siyahı, my_doubled_range tərəfindən qaytarılmış hər bir dəyər ilə qaytarılır.

Hətta generator ifadələri ilə süzə bilərik!

my_even_range_10 = (mənim aralığımdakı x üçün x (10), x% 2 == 0 olduqda) çap (siyahı (my_even_range_10)) # # [0, 2, 4, 6, 8]

Bu, əvvəlkinə çox oxşardır, yalnız my_even_range_10 yalnız göstərilən şərtə uyğun dəyərləri, yəni yalnız [0, 10] arasındakı dəyərləri qaytarır.

Bu arada yalnız bir siyahı hazırladığımız üçün siyahı hazırlayırıq.

Üstünlük

mənbə

Generatorlar təkrarlayıcı olduğundan, təkrarlayıcılar təkrarlana biləndir və təkrarlayıcılar ləng dəyərlər verir. Bu o deməkdir ki, bu biliklə biz yalnız obyektləri istədikdə və nə qədər istədiyimizi verəcək obyektlər yarada bilərik.

Bu o deməkdir ki, generatorları bir-birini azaldan funksiyalara çevirə bilərik.

çap (cəmi (aralığım (10))) # 45

Cəmi bu şəkildə hesablamaq, sadəcə siyahı əlavə edib sonra atsanız siyahı yaratmaqdan çəkinir.

Bir generator funksiyası ilə daha yaxşı olmaq üçün ilk nümunəni yenidən yaza bilərik!

s = 'baacabcaab' p = 'a'
def find_char (sətir, simvollar): indeks üçün, sadalamaqda str_char (string): əgər str_char == simvollar: gəlir göstəricisi
çap (siyahı (find_char (s, s))) # [1, 2, 4, 7, 8]

Dərhal aşkar bir fayda olmaya bilər, amma gəlin ilk sualımı davam etdirək, “Yalnız ilk nəticəni istəyiriksə? Tamamilə yeni bir rol oynamalıyıq? "

Bir generator funksiyası ilə o qədər məntiqi yenidən yazmaq məcburiyyətində deyilik.
çap et (sonrakı (find_char (s, s))) # 1

İndi orijinal həllimizin verdiyi siyahıda ilk dəyəri əldə edə bildik. Bununla belə, bu yolla yalnız ilk matçı əldə edirik və siyahıda aşağıya çıxmağı dayandırırıq. Bundan sonra generator atılır və başqa heç nə yaradılmır. kütləvi yaddaşa qənaət edin.

Nəticə

Heç bir funksiya yaratsanız, belə bir siyahıda dəyərlər əlavə olunur.

def foo (bar): dəyərlər = []
barda x üçün: # bəzi məntiq dəyərləri. (x) əlavə edin
Dəyərləri qaytarın

Bir iteratoru sinif, generator funksiyası və ya generator ifadəsi ilə bu kimi qaytarmağı düşünün:

def foo (bar): barda x üçün: # bəzi məntiqi nəticə x

Resurslar və mənbələr

PEPlər

  • Generatorlar
  • Generator ifadələri PEP
  • PEP-dən gəlir

Məqalələr və mövzular

  • Təkrarlayıcılar
  • İterable və Iterator
  • Generator sənədləri
  • Generatorlara qarşı təkrarlayıcılar
  • Generator ifadəsi və funksiyası
  • Recrusive generatorları

Təriflər

  • Təkrarlanır
  • Təkrarlayıcı
  • generator
  • Generator iterator
  • Generator ifadəsi

İlk olaraq https://blog.dacio.dev/2019/05/03/python-iterators-and-generators/ saytında yayımlandı.