İç içe oluşturulan fonksiyonlar

İç içe oluşturulan bir fonksiyonda içerideki fonksiyonu parantezleri olmadan çağırdığımda neden doğru çalışıyor?
Örnek Kod:

def sarmala(current_v):
    
    def calculate():

        global content, v, operator
        
        if type(current_v) == int and content.get() != "NAND":
            content.set(content.get() + str(current_v))
            
        elif current_v == "." and content.get().find(".") == -1 and content.get() != "NAND":
            content.set(content.get() + current_v)

        elif current_v == "C":
            content.set("")

        elif current_v == "=" and content.get() != "NAND":
            
            if operator == "+" and content.get() != "":
                total = float(v) + float(content.get())
                content.set(total)

            if operator == "-" and content.get() != "":
                total = float(v) - float(content.get())
                content.set(total)
                
            if operator == "/" and content.get() != "":
                if float(content.get()) == 0:
                    content.set("NAND")
                else:
                    total = float(v) / float(content.get())
                    content.set(total)

            if operator == "*" and content.get() != "":
                total = float(v) * float(content.get())
                content.set(total)
            
        else :
            if content.get() != "" and content.get() != "NAND":
                v = float(content.get())
                content.set("")
                operator = current_v
            else:
                pass
            
    return calculate

Duruma göre parantezi kullanırsınız, duruma göre kullanmazsınız.
Mesela aşağıdaki fonksiyona bakın.

def f(*args):
    def g():
        print(*args)
    return g
    
    
print(f("hello", "world")

Bu kodları çalıştırırsanız alacağınız çıktı şöyle olacak:

<function f.<locals>.g at 0x7f9751e6f0d0>

Ama fonksiyonu şu şekilde yazarsanız farklı bir sonuç alacaksınız.

def f(*args):
    def g():
        print(*args)
    return g()
    
    
print(f("hello", "world"))

Bu kodları çalıştırırsanız alacağınız çıktı şöyle olacak:

hello world
None

Yani her iki şekilde de kodlar “doğru” çalışıyor. Ancak aşağıdaki başlıkta belirttiğiniz gibi bir durum söz konusu olduğunda içerdeki fonksiyonu parantezsiz bir şekile kullanırsınız. Bir fonksiyonu parantezsiz kullanmak aslında fonksiyonu çağırmamak demektir. Parantezsiz kullanımda bir fonksiyonu sadece referans göstermiş olursunuz.

Yani şöyle:

>>> def f(): pass
>>> f
<function f at 0x7f42f3d259d8>

Bazı durumlarda, fonksiyonu referans göstermeniz gerekir, mesela bir Thread nesnesi oluştururken, thread’in hedef alacağı fonksiyon çağrılmaz, referans gösterilir. Aynı durum Tkinter’in command parametresine yazılan fonksiyon için de geçerli.

2 Beğeni

Yanıtınız için çok tşkler çok açıklayıcı olmuş. Kafamda oturmaya başlıyor bu konu. Yine de bazı kısımlar karanlık kafamda. Mesela ben bahsi geçen kod parçasında "sarmala" fonksiyonunu bir butona "parantezler" olmadan göndermiştim. Bunu da yine siz çözüm yolu olarak göstermiştiniz önceki sorularımdan birinde. Eğer ki ben sarmala fonksiyonunu butonun "command" işlevine vermemiş olsaydım yine de "calculate" fonksiyonunu parantezler olmadan çağırdığımda aynı sonucu mu alacaktım. Yoksa parantezleri koymam mı gerekecekti? Bunun testini yaptığımda parantez koymam gerekiyor, yoksa adres veriyor bana dönüş değeri olarak. Ya da şöyle mi düşünmeliyim, "sarmala" fonksiyonunu bir butona veya threat a verdiğimde içteki fonksiyonu parantez olmadan mı çağırmam gerekir?

command kısmına eğer doğrudan bir fonksiyonu yazacaksanız, fonksiyonu referans gösterirsiniz.

tk.Button(master, command=fonksiyon)

Eğer bu fonksiyonun argümanları varsa, bir lambda fonksiyonu kullanabilirsiniz veya size daha önce gösterdiğim gibi bir sarıcı fonksiyon kullanabilirsiniz.

# Alternatif 1
tk.Button(master, command=lambda: fonksiyon(*args))

# Alternatif 2
tk.Button(master, command=sarici_fonksiyon(*args))

Mesela aşağıdaki kodu ele alalım:

def sarici(x):

    def artir():
        print(x + 5)

    return artir

Bu sarici fonksiyonu çağırırsanız size içteki fonksiyonu referans olarak verecektir. Dolayısıyla içteki fonksiyonu doğrudan çalıştırmak istiyorsanız, sarıcı fonksiyona ihtiyacınız olmaz.

Veya yukarıdaki sarici fonksiyonun içindeki artir fonksiyonunu kullanmak isterseniz şunu yapmanız gerekir:

sarici(10)()

Sarici fonksiyonu çağırmak size içteki fonksiyonun referans değerini verecektir. O halde içteki fonksiyonu çalıştırmak istiyorsanız sarici(10)() yazarak içteki fonksiyonu çağırabilirsiniz.
Bir fonksiyon bir thread’in veya bir command’ın hedefi olacaksa, sarici fonksiyonu kullanabilirsiniz.

O başlıktaki örneği tekrar yazayım:

import tkinter as tk

root = tk.Tk()


def sarici(x):

    def artir():
        print(x + 5)

    return artir


button = tk.Button(root, text="artır", command=sarici(5))
button.pack()

root.mainloop()

Burada sarici fonksiyonu kullanmasaydınız ve içteki artir fonksiyonu argüman alan bir fonksiyon olarak kalsaydı, onu bu şekilde argümanlarıyla birlikte çağıramazdınız. Yanlış anlamayın çağırırdınız ama fonksiyondaki görev siz daha butona basmadan çalışmaya başlardı.

Fonksiyonu parantezler olmadan cagiramiyorsun. Parantezsiz ifade fonksiyon objesini veriyor.

Butonun veya thread’in dokumentasyonunu okuyup fonksiyon istiyorsa fonksiyon, baska turde bir deger istiyorsa baska bir turde deger vermen gerekir.

Bu arada fonksiyonu “parantezli” olarak paslamak diye de bir sey yok. O durumda ana cagri yapilmadan once fonksiyon calisiyor, parantezli ifadenin yerine dondurdugu deger geliyor.

hede(a(), 43)

Once a fonksiyonu cagriliyor, sonra dondurdugu deger hede’ye paslaniyor. a soyle ise:

def a(): return 42

yukaridaki cagrinin hede(42, 43)'ten farki yok. hede’nin a() diye mi 42 diye mi cagrildigini bilmesinin bir yolu da yok bu arada.

Bu ifade sanırım epey aydınlattı beni. Yardımlarınız çok teşekkürler.

Yardımınız için tşkler. Daha da aydınlandım bu konuda.