Kurs
Python’ı bu kadar güçlü bir programlama dili yapan unsurlardan biri sihirli metotlardır. Bazı Python topluluklarında, çift alt çizgi yapıları sebebiyle bunların ‘dunder methods’ olarak adlandırıldığını da duyabilirsiniz.
Python’daki rolleri, ‘operatör aşırı yükleme’ olarak bilinen bir işlemi gerçekleştirmektir; bu da basitçe, önceden tanımlı bir operatöre genişletilmiş bir anlam verilmesi demektir. Başka bir deyişle, operatör aşırı yükleme, sınıflarımıza özel davranışlar ekleyerek bunların Python’un operatörleri ve yerleşik işlevselliğiyle kullanılmasını sağlar.
Bu yazıda Python sihirli metotlarını daha derinlemesine inceleyerek:
- Metotlar ve fonksiyonlar arasındaki farkı ayırt edeceğiz
- Sihirli metotların ne olduğunu açıklayacağız
- Bazı Python örnekleri sunacağız.
Bu eğitimde kullanılan kodu adım adım takip etmek için şu DataLab çalışma kitabına göz atın.
Python Metotları ve Fonksiyonları
Sihirli metotların ne olduğuna geçmeden önce, Python’da metot ile fonksiyon arasındaki farkları anlamamız gerekir.
Python, bir nesnenin davranışını temsil etmek için metotları kullanır. Dolayısıyla metotlar bir nesneyle ilişkilidir ve bir nesnenin içindeki veriler üzerinde bazı işlemler gerçekleştirir. Bir metodu ayırt eden bir diğer önemli unsur, metoda geçirilen ilk parametre olarak her zaman yer alması gereken self parametresidir.
Not: İlk parametre olarak self kelimesi olmak zorunda değildir, ancak Python geliştiricileri bunun en iyi uygulama olduğu konusunda genel bir uzlaşıya sahiptir.
# Example of a method
class Human:
# This is a method
def walk(self):
print("I am walking")
Metotlar ilişkilendirildikleri sınıfa bağımlıdır. Bu nedenle bir metodu çağırmak için, yönteme nokta operatörüyle erişmeden önce sınıfın bir örneğini oluşturmak gerekir.
# This will work
joe = Human()
joe.walk()
"""
I am walking
"""
# This will throw a NameError
walk()
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
/tmp/ipykernel_153/2327344440.py in <module>
4
5 # This will throw a NameError
----> 6 walk()
NameError: name 'walk' is not defined
Öte yandan bir fonksiyon farklıdır: bir sınıfın örneğini oluşturmamız gerekmez.
Python fonksiyonları, yalnızca çağrıldıklarında bir kod bloğu olarak yürütülen adımlardır. Herhangi bir sınıf nesnelerinden bağımsız olarak tanımlanırlar; yani doğrudan adlarıyla çağrılabilirler. Herhangi bir sınıfla ilişkili olmadıklarından self parametresini geçmemiz gerekmez – parametreler isteğe bağlıdır.
Özetle, bir fonksiyon yorumlayıcının bir görevi gerçekleştirmek için kullandığı, iyi tanımlanmış talimatlar bütünüdür.
# Example of a function
def run():
print("I am running.")
run()
"""
I am running.
"""
Fonksiyonlar nasıl çağrılır ve yazılır başlıklı yazımızda ele alındığı gibi, fonksiyonlar iki farklı türde olabilir: yerleşik ve kullanıcı tanımlı.
- Yerleşik fonksiyonlar: Python standart kütüphanesine dahil edilen fonksiyonlar.
- Kullanıcı tanımlı fonksiyonlar: Kullanıcı tarafından tanımlanan fonksiyonlar – Python, kullanıcıların kendi fonksiyonlarını tanımlamalarına ve bir Python programında kullanmalarına izin verir.
Birçok bakımdan metotlar ve fonksiyonlar aynıdır; örn., ikisi de def ifadesiyle tanımlanır. Temel fark, metotların bir nesneyle ilişkili olması ve bir sınıfın içinde yer alan veriler üzerinde işlem yapmak için kullanılması, fonksiyonların ise bağımsız olarak tanımlanması ve programın herhangi bir yerinde adlarıyla çağrılabilmesidir.
Python sihirli metotları nelerdir?
Eğitimin başında, çift alt çizgi yapıları nedeniyle sihirli metotların dunder metotlar olarak da adlandırıldığından bahsettik. Bununla kastettiğimiz, tüm Python sihirli metotlarının iki alt çizgiyle başlayıp bitmesidir.
En bilinen sihirli metot, bir sınıfın belirli özniteliklerle başlatılmasını sağlayan __init__ metodudur.
class Employee:
def __init__(self, name, age, role):
self.name = name
self.age = age
self.role = role
Bu sınıfın bir örneğini oluşturmak için, örneğin ad, yaş ve rol özniteliklerini belirtmemiz gerekir. Örneğin:
user_1 = Employee(name="John", age=24, role="Data Scientist")
Bunu yapmamak bir TypeError oluşturur.
__init__ metodu, başlatma ve kurucu sihirli metotları kategorisine girer, ancak bunlardan yalnızca biridir. Aşağıdaki tabloda diğer bazı başlatma ve kurucu sihirli metotlar yer almaktadır.
|
__new__(cls, other) |
Bir sınıfın yeni bir örneğini oluşturmak için kullanılır |
|
__init__(self, other) |
Yeni bir örnek oluşturulduktan sonra çağrılır; sınıf kurucu ifadesine geçirilen tüm öznitelikler öznitelik olarak kullanılacaktır. |
|
__del__(self) |
Yıkıcı (destructor) olarak da bilinir; bir örnek yok edileceği zaman çağrılır. |
Sihirli metotları kavramsallaştırmanın en basit yolu, uygulamanız ile Python yorumlayıcısı arasındaki bir sözleşme olarak görmektir. Sözleşmenin maddelerinden biri, belirli nesnelerle bir sınıf örneğinin başlatılması gibi belirli durumlarda Python’un perde arkasında bazı işlemleri gerçekleştirmesini gerektirir.
Başka bir deyişle, bir sihirli metodu doğrudan çağırarak sizin tetiklemeniz gerekmez – Python bunu sizin yerinize arka planda yapar.
Dize gösterimi metotları
Python bir nesne yönelimli programlama (OOP) dilidir – Python’daki her şey bir nesnedir. Yeni bir nesne oluşturduğumuzda, tüm sınıflar object’ten kalıtım aldığı için dolaylı olarak ilişkili bir nesne de yaratırız.
Not: Kalıtımı, sınıfınıza class object’i geçirerek açıkça da yapabilirsiniz.
# This works
class Car:
pass
# This also works
class Vehicle(object):
pass
car = Car()
vehicle = Vehicle()
print(car, vehicle, sep="\n")
"""
<__main__.Car object at 0x7f80449dc0a0>
<__main__.Vehicle object at 0x7f80449dc1f0>
"""
Yukarıdaki kodda iki boş sınıf oluşturduk, ancak print ifadesini çağırdığımızda Python yorumlayıcısı yine de ne yazdıracağını biliyordu. Bu, tüm sınıfların Python’daki tüm sınıfların ebeveyni olan object sınıfından kalıtım alması ve print ifadesinin, değeri ana programımıza geri döndürmek için çağırdığı __repr__ adlı bir sihirli metoda sahip olması sayesinde mümkündür.
Kalıtımla aşina olanlar, bir alt sınıfın, aynı isimde kendi metodunu tanımlayarak üst sınıfını geçersiz kılabileceğini bilir.
# Overriding the object __repr__
class Vehicle:
def __repr__(self):
return f"{self.__class__.__qualname__}"
vehicle = Vehicle()
print(vehicle)
"""
Vehicle
"""
Print ifadesiyle çağrılan bir dize gösterimi oluşturmak için __str__ sihirli metodunu da kullanabilirsiniz.
Aslında, __repr__ sihirli metodu __str__ sihirli metodunun yedek davranışı olarak hizmet eder. Dolayısıyla print ifadesini çağırdığımızda Python, önce sınıfınızda tanımlı bir __str__ metodu arar, bulamazsa __repr__ metoduna geri döner.
# Using __str__ for string representation
class Vehicle:
def __str__(self):
return f"{self.__class__.__qualname__}"
vehicle = Vehicle()
print(vehicle)
"""
Vehicle
"""
Aşağıdaki tabloda diğer dize gösterimi sihirli metotlarının bir listesi yer almaktadır.
|
__str__(self) |
Python’un yerleşik str() fonksiyonu ve print ifadesi tarafından, bir nesnenin biçimsel olmayan dize gösterimini göstermek için çağrılır. Geçerli bir Python ifadesi olması gerekmediğini unutmayın. |
|
__repr__(self) |
Python’un yerleşik repr() fonksiyonu ve dize dönüşümleri tarafından, bir nesnenin resmi dize gösterimini göstermek için çağrılır. Bunun geçerli bir Python ifadesi gibi görünmesi gerekir; ancak bu mümkün değilse, kullanışlı bir açıklama içeren bir dize tercih edilir. |
|
__unicode__(self) |
Python’un yerleşik unicode() fonksiyonunu uygulamak için çağrılır ve bir Unicode nesnesi döndürür. |
|
__hash__(self) |
Python’un yerleşik hash() fonksiyonu ve karma koleksiyon üyeleri üzerindeki işlemler tarafından çağrılır. Metot bir tamsayı döndürmelidir. |
|
__nonzero__(self) |
Python’un yerleşik bool() işlemi tarafından, doğruluk değeri testini uygulamak için çağrılır. Metot True veya False (ya da bunların tamsayı karşılığı 1 veya 0) döndürmelidir. |
Enkapsülasyon “sihirli” metotları
Enkapsülasyon, nesne yönelimli programlamanın (OOP) temel kavramlarından biridir. Verilerin, bu veriler üzerinde işlem yapan metotlarla birlikte paketlenmesini veya bir nesne içindeki belirli bileşenlere doğrudan erişimin kısıtlanmasını ifade eder.
Diğer OOP dillerinden gelen geliştiriciler, Python’un sınıflar için enkapsülasyonu gerçekten benimsememesini sıklıkla eleştirir. Bu kişiler, metotlar veya alanlar için açık belirleyicilere, örneğin herkese açık alıcılar (getter) ve ayarlayıcılarla (setter) özel öznitelikler tanımlamalarına olanak tanıyan yapılara alışkındır. Python’daki fark, mevcut enkapsülasyonun büyük kısmının sihirli metotlar aracılığıyla gerçekleşmesidir.
Örneğin __setattr__, bir özniteliğe atama için, o öznitelik mevcut olsa da olmasa da, davranış tanımlamamızı sağlayan bir enkapsülasyon çözümüdür. Başka bir deyişle, öznitelik değerlerindeki değişiklikler için kurallar tanımlanabilir.
import string
class Alphabet():
def __setattr__(self, name, value):
self.__dict__[name] = value.upper()
a = Alphabet()
# Assinging an attribute that does not exist
# The setattr should convert it to caps despite being set as lowercase
a.alphabet = string.ascii_lowercase
print(a.alphabet)
Yukarıdaki kodda, hiçbir özniteliği olmayan Alphabet adlı bir sınıf oluşturduk, ancak Python’a, sınıf örneğimizde ayarladığımız herhangi bir özniteliği büyük harfe çevirmek istediğimizi söyledik.
OOP’nin bazı okuyucular için biraz uzak bir konu olabileceğini anlıyoruz. Python’da OOP’ye hızla hakim olmak için DataCamp’in Python Programcısı Öğrenim Yolu’na göz atın.
Matematik sihirli metotları
Bir diğer Python sihirli metot kategorisi de matematik sihirli metotlarıdır (normal aritmetik operatörler olarak da bilinir). İfadeler, Python’da ‘operatör’ olarak adlandırdığımız özel semboller kullanılarak oluşturulur. Bir ifadede bu tür operatörleri kullanmak, matematik sihirli metotlarına sahip nesneler oluşturmayı içerir. Bu kurala uyulmaması bir TypeError ile sonuçlanır.
class Numbers:
def __init__(self, a, b):
self.a = a
self.b = b
set_a = Numbers(2, 4)
set_b = Numbers(3, 5)
print(set_a + set_b)
"""
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
/tmp/ipykernel_152/3639634723.py in <module>
7 set_b = Numbers(3, 5)
8
----> 9 print(set_a + set_b)
TypeError: unsupported operand type(s) for +: 'Numbers' and 'Numbers'
"""
Yukarıdaki kodda Numbers sınıfından birden fazla örnek oluşturup bunları birbirine eklemeye çalıştık ve beklendiği gibi bir hata aldık. Bu hataya bir çözüm, sınıfımızda sayıları toplamak için bir metot oluşturmaktır; ancak bu, ifadeler oluşturmamızı engellerdi.
Daha iyi bir çözüm, aşağıdaki gibi bir __add__ sihirli metodu uygulamaktır:
# Using add operator
class Numbers:
def __init__(self, a, b):
self.a = a
self.b = b
def __add__(self, other):
# Only permit Numbers objects to be added
if not isinstance(other, Numbers):
return NotImplemented
return Numbers(other.a + self.a, other.b + self.b)
def __repr__(self):
return f"{self.__class__.__qualname__}({self.a}, {self.b})"
a = Numbers(2, 4)
b = Numbers(3, 5)
print(a + b) # Should result in Numbers(5, 9)
"""
RandomNumbers(5, 9)
"""
Yukarıdaki kod örneği yalnızca iki Numbers nesnesini birbirine eklememizi sağlar. Bir Numbers nesnesini bir tamsayıyla çarpmamıza izin veren başka bir matematik sihirli metodunu uygulayalım.
# Multiplying Numbers with an integer
class Numbers:
def __init__(self, a, b):
self.a = a
self.b = b
def __add__(self, other):
# Only permit Numbers objects to be added
if not isinstance(other, Numbers):
return NotImplemented
return Numbers(other.a + self.a, other.b + self.b)
def __mul__(self, other):
if not isinstance(other, int):
return NotImplemented
return Numbers(self.a * other, self.b * other)
def __repr__(self):
return f"{self.__class__.__qualname__}({self.a}, {self.b})"
a = Numbers(2, 4)
print(a * 5) # Should return 10, 20
"""
RandomNumbers(10, 20)
"""
Yukarıdaki kodda, özel sınıfımızdaki sayıları bir tamsayıyla çarpmamıza olanak tanımak için bir __mul__ sihirli metodu ekledik.
Dikkat ederseniz Numbers nesnesi * operatörünün sol tarafında yer alıyor – bu tesadüf değil. Onu işlecin sağ tarafına koyalım ve ne olduğuna bakalım.
- snip --
a = Numbers(2, 4)
print(3 * a)
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
/tmp/ipykernel_152/2857655049.py in <module>
1 a = Numbers(2, 4)
----> 2 print(3 * a)
TypeError: unsupported operand type(s) for *: 'int' and 'Numbers'
Python bir TypeError fırlatır.
Uyguladığımız __mul__ sihirli metodu, yalnızca Numbers nesnesinin * operatörünün sol tarafında olduğu durumları kapsıyor. Bu işlevselliği genişletmek için ters sihirli metodu, __rmul__ eklememiz gerekir.
# Enabling reverse multiplication
class Numbers:
def __init__(self, a, b):
self.a = a
self.b = b
def __add__(self, other):
# Only permit Numbers objects to be added
if not isinstance(other, Numbers):
return NotImplemented
return Numbers(other.a + self.a, other.b + self.b)
def __mul__(self, other):
if not isinstance(other, int):
return NotImplemented
return Numbers(self.a * other, self.b * other)
def __rmul__(self, other):
return self.__mul__(other)
def __repr__(self):
return f"{self.__class__.__qualname__}({self.a}, {self.b})"
a = Numbers(2, 4)
print(5 * a) # Should return 10, 20
"""
Numbers(10, 20)
"""
Artık Numbers nesnesinin işlecin hangi tarafında göründüğünü düşünmeden bir çarpma ifadesi oluşturabiliriz.
Kapanış
Python sihirli metotlarından yararlanmanın temel ilkesi aynıdır: bir sınıf oluşturup object sınıfının sihirli metodunu geçersiz kılın. Python’da o kadar çok sihirli metot vardır ki, hepsini tek tek ele almaya kalksak bu son derece uzun bir eğitim olurdu. Python belgelerine göz atmanızı ve Python programlama becerilerinizi geliştirmek için kullanabileceğiniz sihirli metotların kapsamına dair fikir edinmenizi şiddetle öneririz.
