Yield from deyimi kullanımı

Merhaba arkadaşlar.

Genellikle from sözcüğünü, bir kütüphaneyi import sözcüğü ile programa yüklerken kullanıyoruz. Ancak from sözcüğünün kullanıldığı başka bir yer daha var. Bu başlıkta bundan bahsetmek isterim.

Diyelim aşağıdaki gibi bir fonksiyonumuz var. Bu aşağıdaki fonksiyon ne yapıyor bilmeyenler için anlatayım, izninizle.

def f(*args): pass

f(*args) ifadesi, f fonksiyonunun -255’e kadar- bir sürü argüman alabileceğini gösteriyor. Ana konudan sapmadan, isterseniz, 255 tane argüman alabileceğini nasıl öğrenebiliriz bulmaya çalışalım.

Yukarıdaki fonksiyona 255 tane argüman vererek çağırmaya çalışalım.

exec(f"f({', '.join(map(str, range(255)))})")

Bir de 256 tane argüman vererek çağırmaya çalışalım.

exec(f"f({', '.join(map(str, range(256)))})")

Şöyle bir hata almış olmamız lazım.

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<string>", line 1
SyntaxError: more than 255 arguments

Gördüğünüz gibi bir fonksiyona en fazla 255 tane argüman verilebiliyor.

Şimdi izninizle from sözcüğünün farklı kullanımını sizlere anlatmaya çalışmak isterim.

Örneğin, aşağıdaki gibi bir fonksiyonumuz olsun.

def f(*args):
    for i in args:
        yield i

Yukarıdaki fonksiyonun ne yaptığını henüz bilmeyen arkadaşlar için kısaca bir açıklamada bulunayım.

Öncelikle yield deyimi tıpkı return gibi bir fonksiyondan değer döndürmeye yarar ancak burada bir değerden çok bir çok değer içeren bir üreteç elde edersiniz. Yukarıdaki fonksiyonda yield yerine return kullanılmış olsaydı, ilk i değeri geri döndürülecekti, oysa yield sözcüğü burada bütün i’lere ulaşma imkanı tanıyor.

Şimdi basit bir tane dizi tanımlayalım.

dizi = ["elma", "armut", "çilek", "karpuz"]

dizi’yi f(*args) fonksiyonuna argüman olarak verelim. Ve aşağıdaki gibi ekrana yazdıralım.

print(*f(dizi))

Yukarıdaki print fonksiyonunun ekrana bastıracağı çıktı aşağıdaki gibi olacaktır.

['elma', 'armut', 'çilek', 'karpuz']

f() fonksiyonunun argümanı dizi değil de *dizi olsun bu sefer.

print(*f(*dizi))

Bu kez alacağımız çıktı aşağıdaki gibi olacaktır.

elma, armut, çilek, karpuz

Şimdi gelin yukarıdaki fonksiyonu biraz değiştirelim:

def f(*args):
    yield from args
    
    
print(*f(dizi))

f fonksiyonunun argümanına dizi’yi yazalım ve bakalım nasıl bir sonuç elde ediyoruz.

['elma', 'armut', 'çilek', 'karpuz']

Gördüğünüz gibi bu fonksiyon da ilk f(dizi) fonksiyonuna benzer bir çıktı verdi. f fonksiyonuna argüman olarak bir de *dizi’yi verelim.

print(*f(*dizi))

Şöyle bir çıktı almamız gerekir.

elma, armut, çilek, karpuz

Yine diğer fonkiyonun ürettiği sonuca benzer bir sonuç üretildi. Peki bu her zaman böyle mi olur? Gelin fonksiyonu biraz daha değiştirelim ve bu sorunun cevabını bulmaya çalışalım.

def f(*args):
    for i in args:
        yield i.upper() if isinstance(i, str) else i      

Şimdi f(*args) fonksiyonununa önce dizi sonra *dizi’yi argüman olarak verip, döndürdüğü değerleri ekrana yazdırmaya çalışalım…

print(*f(dizi))

Aşağıdaki gibi bir çıktı almamız gerekiyor:

['elma', 'armut', 'çilek', 'karpuz']

Argümanı *dizi olduğunda nasıl bir çıktı veriyor ona bakalım.

print(*f(*dizi))
ELMA ARMUT ÇILEK KARPUZ

Yazdığımız i.upper() if isinstance(i, str) else i deyimi sayesinde, tipi str olan argüman büyük harflerle ekrana yazdırıldı.
Bu son f fonksiyonumuzu from yield deyimiyle birlikte tekrar yazalım.

def f(*args):
    yield from args.upper() if isinstance(args, str) else args

Fonksiyona önce dizi’yi argüman olarak verelim ve nasıl bir çıktı aldığımıza bakalım.

print(*f(dizi))
['elma', 'armut', 'çilek', 'karpuz']

Gördüğünüz gibi diğer fonksiyon ile benzer bir sonuç aldık. Peki argümana *dizi’yi yazsak, o da benzer bir sonuç üretiyor mu bakalım.

print(*f(*dizi))
elma armut çilek karpuz

Bakın bu sefer farklı bir sonuç aldık. Yani ilk örnekte her iki argüman için de benzer sonuçlar almıştık ama bu örnekte son sonuç farklı çıktı. Acaba from yield kullandığımız fonksiyonda nasıl bir değişiklik yapılmalı ki print(*f(*dizi)) fonksiyonu string değerlerini büyük harflerle ekrana bastırsın?

Şöyle yapabilirdik herhalde:

def f(*args):
    yield from [i.upper() for i in args] \
        if isinstance(args, tuple) else args
    

print(*f(*dizi))

Yukarıdaki kodları çalıştırdığımızda alacağımız çıktı şöyle olacaktır.

ELMA ARMUT ÇILEK KARPUZ

Peki, aynı fonksiyon print(*f(dizi)) şeklinde çağrılabilir miydi?

print(*f(dizi))

Bu kodları çalıştırdığımızda aşağıdaki gibi bir çıktı almamız gerekiyor:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in f
  File "<stdin>", line 2, in <listcomp>
AttributeError: 'list' object has no attribute 'upper'

Peki, nasıl bir f fonksiyonu yazılmalı ki, fonksiyonu print(*f(dizi)) şeklinde çağırdığımızda, bir önceki print(*f(*dizi)) fonksiyonunun ürettiği çıktı olan ELMA ARMUT ÇILEK KARPUZ ile benzer olsun? Aşağıdakini bir deneyelim.

def f(*args):
    yield from [i.upper() for i in args[0]] \
        if isinstance(args, tuple) else arg

        
print(*f(dizi))

Bu kodları çalıştırdığımızda alacağımız çıktı aşağıdaki gibi olacaktır.

ELMA ARMUT ÇILEK KARPUZ

Son f fonksiyonunun argümanı *dizi şeklinde olursa alacağımız çıktı da şöyle olacaktır:

E L M A

Örneği bu kadar uzatmamın nedeni kafa karışıklığı yaratmak değildi, eğer bir kafa karışıklığına yol açtıysam şimdiden özür dilerim. Hatırlıyorsanız yukarıda şöyle bir şey yazmıştım:

İşte bu soruya cevap aramaya çalıştım ve gördüğüm kadarıyla,

def f(*args):
    yield from args

ile

def f(*args):
    for i in args:
        yield args

ifadeleri benzer sonuçlar üretiyor üretmesine ancak yukarıdaki diğer fonksiyon örneklerini bu yapıya uydurarak çağırmaya çalıştığımızda farklı sonuçlar alabildiğimizi gördük ve benzer sonuçlar almak için fonksiyonları değiştirmek zorunda kaldık.

Bu konuyla alakalı söyleyebileceklerimin sonuna geldim. Okuduğunuz için teşekkür ederim.

Sevgiler, saygılar.

11 Beğeni

Ornek string’ler olarak feci sekilde unutulabilen rastgele karakterler (kgjekgj) yerine elma, armut gibi akilda kalabilecek degerler kullanmani oneriyorum. Ornek bulmakta zorlaniyorsan NATO fonetik alfabesinde 26 tane guzel kelime var :slight_smile:

2 Beğeni

Tamam, örnek stringler değiştirildi. :slight_smile:

2 Beğeni

Çok değerli bir örnek teşekkürler.

Yukarıdaki iki fonksiyona alternatif olarak şöyle bir fonksiyon da yazılabilirdi.

def f(*args):
    yield from map(lambda i: i.upper(), map(str, args))
1 Beğeni

map zaten lazy, dogrudan dondurulebilir :​)

1 Beğeni

Iki ardisik map'e de gerek yok:

def f(*args):
    return map(lambda i: str(i).upper(), args)
1 Beğeni

İki ardışık map'e gerek yok tabi.

Eğitim kitaplarında böyle bir anlatım görmedim, çok teşekkür ederim. Ellerinize sağlık.

:smiley: bende görmemiştim

yield from zor bir konu değil, nasıl çalıştığı buradaki gibi özetlenebilir:
https://python-istihza.yazbel.com/ileri_fonksiyonlar.html#yield-from-deyimi

@dildeolupbiten kullanımına örnekler vermiş.

1 Beğeni