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:
"import öncesi"
yazdırır.math
modülünü import eder vemath
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")
"functionA öncesi"
ifadesini yazdırır.def
kod bloğunu çalıştırır, bir fonksiyon objesi oluşturur, ve bu objeyifunctionA
adındaki değişkene atar."functionB öncesi"
ifadesini yazdırır.def
kod bloğunu çalıştırır, başka bir fonksiyon objesi oluşturur, ve bu objeyifunction
B adındaki değişkene atar."__name__ kontrol öncesi"
ifadesini yazdırır.
Sadece ana programda iken çalıştırılır
- 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
- 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öyleceif
ifadesi içerisindeki kod bloğu da görmezden gelinir.
Her zaman uygulanır
"__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ı")