koddla

Yazılımcıları bilgi ile güçlendirir.

Python’da if __name__ == __main__ ne işe yarar?

Birden fazla modülü olan bir projenin kaynak kodunu incelerseniz aşağıdakine benzer bir ifade görmüşsünüzdür. Peki bu ifade ne işe yarıyor?

if __name__ == "__main__":
    print("Hello world!")

Kısa cevap

Kullanıcıların yanlışlıkla bir kodu çalıştırmalarını önleyen standard bir koddur. Peki bu önlem olmasaydı ne olurdu?

  • Güvenliğini test etmediğimiz bir scripti kodumuza dahil ettiğimizde, kodumuz import edilen scripti önce import zamanında sonrada çağrı sırasında çalıştırır. Bu durum neredeyse her zaman hata olacaktır.
  • Güvenliğini test etmediğimiz bir scripti çağırdığımızda, bu script içinde de bir import ifadesi bulunabilir. Bu durum da yine yukarıdaki problemi oluşturacaktır.

Uzun cevap

Bu konuyu daha iyi anlamak için, Python’un komut dosyalarını nasıl başlattığını ve modül import mekanizması ile nasıl etkileşime girdiğini incelemek gerekir.

Python çalıştırıcısı bir kaynak dosyasını okurken iki şey yapar:

  • birkaç özel değişken oluşturur __name__ gibi
  • dosyada bulduğu tüm kodu çalıştırır

Bu işlemin nasıl çalıştığını ve Python komutlarında gördüğümüz __name__ kontrollerine daha yakından bakalım.

Örnek kod

Import ve komut dosyalarının nasıl çalıştığını anlamak için biraz farklı bir kod örneği kullanalım. Aşağıdaki foo.py kodunu inceleyelim:

# foo.py.

print("import öncesi")
import math

print("functionA öncesi")
def functionA():
    print("Function A")

print("functionB öncesi")
def functionB():
    print("Function B {}".format(math.sqrt(100)))

print("__name__ kontrol öncesi")
if __name__ == '__main__':
    functionA()
    functionB()
print(" __name__ kontrol sonrası")

Özel değişkenler

Python çalıştırıcısı bir kodu okuduğunda önce özel değişkenleri atar. Bu örnekte sadece __name__ değişkeni ile ilgileniyoruz.

Kodunuz ana program içerisinde ise

Eğer kodunuzu ana program olarak çalıştırıyorsanız (aşağıdaki gibi):

python foo.py

çalıştırıcı "__main__" değişkenini __name__ değişkenine atar:

# çalıştırıcı bu ifadeyi programın başına eklemiş gibi düşünebiliriz
__name__ = "__main__" 

Kodunuz başka bir script tarafından import edilirse

Diğer taraftan, başka bir modülün ana program olduğunu ve kodumuzun bu modül tarafından import edildiğini düşünelim. Bu durumda, yukarıdaki ifade başka bir modül için – scriptimizi import eden ana modül için – geçerli olur:

# foo'nun başka bir script tarafından import edildiğini varsayalım
import foo

Çalıştırıcı foo.py dosyasına bakar ve bunu çalıştırmadan önce "foo" değerini import ifadesinde __name__ değişkenine atar 

# Çalıştırıcının aşağıdaki kodu import esnasında scriptimizin üstüne yerleştirdiğini düşünebiliriz
 __name__ = "foo"

Kodun çalışması

Özel değişkenler atandığında, çalıştırıcı, tüm kodu teker teker çalıştırır. Yukarıdaki kodu bir yandan da kendi bilgisayarınızda test edebilirsiniz. Bu şekilde açıklamalar daha anlaşılır olur.

Her zaman uygulanır:

  1. "import öncesi" yazdırır.
  2. math modülünü import eder ve math isimli bir değişkene atar. Bu aşağıdaki yöntem ile aynı sonucu verir. Yani import math yerine daha alt düzey bir yöntem olan __import__‘a modül ismini yazma ile eş değerdir
# string olarak ismi verilen modülü arar, "math",
# daha sonra bunu yerel değişken math'e atar
math = __import__("math")
  1. "functionA öncesi" ifadesini yazdırır.
  2. def kod bloğunu çalıştırır, bir fonksiyon objesi oluşturur, ve bu objeyi functionA adındaki değişkene atar.
  3. "functionB öncesi" ifadesini yazdırır.
  4. def kod bloğunu çalıştırır, başka bir fonksiyon objesi oluşturur, ve bu objeyi functionB adındaki değişkene atar.
  5. "__name__ kontrol öncesi" ifadesini yazdırır.

Sadece ana programda iken çalıştırılır

  1. Eğer ana programda isek, çalıştırıcı, __name__ değişkenine "__main__" değerini atar ve iki fonksiyon çalıştırır. Bu fonksiyonlar "Function A" ve "Function B 10.0" yazdırırlar.

Kod sadece başka bir modülden çağrılırsa

  1. Eğer modülümüz ana program değil de başka bir modül tarafından çalıştırılırsa, bu sefer __name__ değişkeni "foo" değerini alır, "__main__" değerini almaz. Böylece if ifadesi içerisindeki kod bloğu da görmezden gelinir.

Her zaman uygulanır

  1. "__name__ kontrol sonrası" yazdırılır – iki durumda da.

Özet

Özet olarak aşağıdaki çıktıları iki durum için de alırız:

# Eğer foo main program ise
import öncesi
functionA öncesi
functionB öncesi
__name__ kontrol öncesi
Function A
Function B 10.0
__name__ kontrol sonrası
# Eğer foo başka bir modül tarafından çağrılırsa 
import öncesi
functionA öncesi
functionB öncesi
__name__ kontrol öncesi
__name__ kontrol sonrası

Bu neden böyle uygulanır?

Bunun neden böyle çalıştığının doğal olarak merak edebilirsiniz. Bazen bir modül olarak diğer programlar ve/veya modüller tarafından kullanılabilen bir .py dosyası yazmayı düşünürüz. Aynı zamanda bu py dosyasının ana program olarak kendisi de çalıştırılabilir olsun isteriz. Örneğin:

  • Modülünüz bir kütüphanedir, ancak bazı birim testlerini veya demoları çalıştıran bir komut dosyası moduna da sahiptir.
  • Modülünüz sadece ana bir program olarak kullanılır, ancak bazı birim testine sahiptir ve bu testler ancak import edildiğinde çalışır.
  • Modülünüz çoğunlukla ana bir program olarak kullanılır, aynı zamanda gelişmiş kullanıcılar için programcı dostu bir API sağlar.

Peki bu bilgi ile neler yapabiliriz?

Birden fazla __name__ kontrol blogu kullanabilir miyiz?

  • Her ne kadar bunu yapmak garip olsa da, pythonun dil yapısı sizi bunun için sınırlamaz.
  • Aşağıdaki dosyanın foo2.py olduğunu varsayalım. python foo2.py ifadesini komut satırında çalıştırsanız ne olur? ve neden?
# foo2.py.
import os, sys; sys.path.insert(0, os.path.dirname(__file__)) # needed for some interpreters

def functionA():
    print("a1")
    from foo2 import functionB
    print("a2")
    functionB()
    print("a3")

def functionB():
    print("b")

print("t1")
if __name__ == "__main__":
    print("m1")
    functionA()
    print("m2")
print("t2")
      
  • Şimdi de foo3.py scriptinden __name__ kontrolunu kaldırdığımızda ne olacağını düşünelim:
# foo3.py.
import os, sys; sys.path.insert(0, os.path.dirname(__file__)) # bazı çalıştırıcılar için gerekli

def functionA():
    print("a1")
    from foo3 import functionB
    print("a2")
    functionB()
    print("a3")

def functionB():
    print("b")

print("t1")
print("m1")
functionA()
print("m2")
print("t2")
  • Bu ifade bir script tarafından çalıştırıldığında ne olur? Örneğin import edildiğinde?
# foo4.py
__name__ = "__main__"

def bar():
    print("bar")
    
print("__name__ kontrol öncesi")
if __name__ == "__main__":
    bar()
print("__name__ kontrol sonrası")

Bir cevap yazın

E-posta hesabınız yayımlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir

Back to top