Python dekoratörleri, Python’da inanılmaz derecede kullanışlı bir yapıdır. Python’da dekoratörleri kullanarak, bir fonksiyonun davranışını başka bir fonksiyonun içine sararak değiştirebiliriz. Dekoratörler, daha temiz kod yazmamızı ve işlevsellik paylaşmamızı sağlar. Bu makale, yalnızca dekoratörlerin nasıl kullanılacağına değil, nasıl yaratılacağına dair bir öğreticidir.
ön koşul bilgisi
Python’daki dekoratörler konusu biraz arka plan bilgisi gerektirir. Aşağıda, bu öğreticiyi anlamlandırmak için zaten aşina olmanız gereken bazı kavramları listeledim. Gerekirse kavramları tazeleyebileceğiniz kaynakları da bağlantılandırdım.
Temel Python
Bu konu daha orta/ileri düzey bir konudur. Sonuç olarak, öğrenmeye çalışmadan önce, veri türleri, işlevler, nesneler ve sınıflar gibi Python’un temellerine zaten aşina olmalısınız.
Alıcılar, ayarlayıcılar ve yapıcılar gibi bazı nesne yönelimli kavramları da anlamalısınız. Python programlama diline aşina değilseniz, başlamanız için bazı kaynakları burada bulabilirsiniz.
İşlevler birinci sınıf vatandaşlardır
Temel Python’a ek olarak, Python’daki bu daha gelişmiş kavramın da farkında olmalısınız. İşlevler ve Python’daki hemen hemen her şey, int veya string gibi nesnelerdir. Nesne oldukları için onlarla birkaç şey yapabilirsiniz:
- Bir işlevi, bir dizgeyi veya int’yi işlev bağımsız değişkeni olarak ilettiğiniz gibi, başka bir işleve bağımsız değişken olarak iletebilirsiniz.
- İşlevler, diğer dize veya int değerlerini döndürdüğünüz gibi diğer işlevler tarafından da döndürülebilir.
- Fonksiyonlar değişkenlerde saklanabilir
Aslında, işlevsel nesneler ile diğer nesneler arasındaki tek fark, işlevsel nesnelerin __call__() sihirli yöntemini içermesidir.
Umarım, bu noktada, ön koşul bilgisi konusunda rahatsınızdır. Ana konuyu tartışmaya başlayabiliriz.
Python Dekoratörü nedir?
Bir Python dekoratörü, bir fonksiyonu bağımsız değişken olarak alan ve iletilen işlevin değiştirilmiş bir sürümünü döndüren bir işlevdir. Diğer bir deyişle, foo işlevi, argüman olarak işlev çubuğunu alırsa bir dekoratördür. ve başka bir fonksiyon baz döndürür.
Baz işlevi, baz’ın gövdesi içinde işlev çubuğuna bir çağrı olması anlamında bar’ın bir modifikasyonudur. Ancak bara çağrılmadan önce ve sonra baz her şeyi yapabilir. Bu bir ağız dolusuydu; durumu göstermek için bazı kodlar:
# Foo is a decorator, it takes in another function, bar as an argument def foo(bar): # Here we create baz, a modified version of bar # baz will call bar but can do anything before and after the function call def baz(): # Before calling bar, we print something print("Something") # Then we run bar by making a function call bar() # Then we print something else after running bar print("Something else") # Lastly, foo returns baz, a modified version of bar return baz
Python’da Dekoratör Nasıl Oluşturulur?
Python’da dekoratörlerin nasıl yaratıldığını ve kullanıldığını göstermek için bunu basit bir örnekle göstereceğim. Bu örnekte, süslediği işlevin adını, işlev her çalıştığında günlüğe kaydedecek bir günlük düzenleyici işlevi oluşturacağız.
Başlamak için dekoratör işlevini oluşturduk. Dekoratör, argüman olarak func’u alır. func, süslediğimiz fonksiyondur.
def create_logger(func): # The function body goes here
Dekoratör işlevinin içinde, işlevi çalıştırmadan önce işlev adını günlüğe kaydedecek değiştirilmiş işlevimizi oluşturacağız.
# Inside create_logger def modified_func(): print("Calling: ", func.__name__) func()
Ardından, create_logger işlevi, değiştirilen işlevi döndürür. Sonuç olarak, tüm create_logger işlevimiz şöyle görünecektir:
def create_logger(func): def modified_func(): print("Calling: ", func.__name__) func() return modified_function
Dekoratörü oluşturmayı bitirdik. create_logger işlevi, dekoratör işlevinin basit bir örneğidir. Süslemekte olduğumuz işlev olan func’u alır ve başka bir işlev olan replace_func döndürür. replace_func, func’u çalıştırmadan önce func’un adını günlüğe kaydeder.
Python’da dekoratörler nasıl kullanılır?
Dekoratörümüzü kullanmak için @ sözdizimini şu şekilde kullanırız:
@create_logger def say_hello(): print("Hello, World!")
Artık betiğimizde say_hello()’yu çağırabiliriz ve çıktı aşağıdaki metin olmalıdır:
Calling: say_hello "Hello, World"
Ama @create_logger ne yapıyor? Dekoratörü say_hello işlevimize uyguluyor. Ne yapıldığını daha iyi anlamak için, bu paragrafın hemen altındaki kod, @create_logger’ı say_hello’nun önüne koymakla aynı sonucu verecektir.
def say_hello(): print("Hello, World!") say_hello = create_logger(say_hello)
Başka bir deyişle, Python’da dekoratörleri kullanmanın bir yolu, yukarıdaki kodda yaptığımız gibi, işlevde geçen dekoratörü açıkça çağırmaktır. Diğer ve daha özlü yol, @ sözdizimini kullanmaktır.
Bu bölümde, Python dekoratörlerinin nasıl oluşturulacağını ele aldık.
Biraz daha karmaşık örnekler
Yukarıdaki örnek basit bir durumdu. Dekorasyonunu yaptığımız fonksiyonun bağımsız değişkenler alması gibi biraz daha karmaşık örnekler var. Daha karmaşık bir durum da, tüm sınıfı dekore etmek istediğiniz zamandır. Burada bu iki durumu da ele alacağım.
İşlev bağımsız değişkenleri aldığında
Süslemekte olduğunuz işlev bağımsız değişkenleri aldığında, değiştirilen işlev bağımsız değişkenleri almalı ve sonunda değiştirilmemiş işleve çağrı yaptığında bunları iletmelidir. Bu kafa karıştırıcı geliyorsa, foo-bar terimleriyle açıklamama izin verin.
Foo’nun dekoratör işlevi olduğunu, bar’ın dekore ettiğimiz işlev olduğunu ve baz’ın dekore edilmiş çubuk olduğunu hatırlayın. Bu durumda bar, baz’a yapılan çağrı sırasında argümanları alır ve baz’a iletir. İşte kavramı sağlamlaştırmak için bir kod örneği:
def foo(bar): def baz(*args, **kwargs): # You can do something here ___ # Then we make the call to bar, passing in args and kwargs bar(*args, **kwargs) # You can also do something here ___ return baz
*args ve **kwargs yabancı görünüyorsa; sırasıyla konumsal ve anahtar kelime argümanlarına işaretçilerdir.
Baz’ın argümanlara erişimi olduğunu ve bu nedenle bar’ı çağırmadan önce argümanların bazı doğrulamalarını yapabileceğini not etmek önemlidir.
Bir dekoratör fonksiyonumuz olsaydı, bir dekoratör fonksiyonumuz olsaydı, dekore ettiği bir fonksiyona iletilen bağımsız değişkenin bir dize olmasını sağlayacak olansure_string buna bir örnek olurdu; bunu şu şekilde uygulardık:
def ensure_string(func): def decorated_func(text): if type(text) is not str: raise TypeError('argument to ' + func.__name__ + ' must be a string.') else: func(text) return decorated_func
say_hello işlevini şu şekilde dekore edebiliriz:
@ensure_string def say_hello(name): print('Hello', name)
Sonra bunu kullanarak kodu test edebiliriz:
say_hello('John') # Should run just fine say_hello(3) # Should throw an exception
Ve aşağıdaki çıktıyı üretmelidir:
Hello John Traceback (most recent call last): File "/home/anesu/Documents/python-tutorial/./decorators.py", line 20, in <module> say hello(3) # should throw an exception File "/home/anesu/Documents/python-tu$ ./decorators.pytorial/./decorators.py", line 7, in decorated_func raise TypeError('argument to + func._name_ + must be a string.') TypeError: argument to say hello must be a string. $0
Beklendiği gibi, komut dosyası ‘Hello John’ yazdırmayı başardı çünkü ‘John’ bir dizedir. ‘Merhaba 3’ü yazdırmaya çalışırken bir istisna attı çünkü ‘3’ bir dizi değildi. sure_string dekoratörü, bir dize gerektiren herhangi bir işlevin bağımsız değişkenlerini doğrulamak için kullanılabilir.
Bir sınıfı dekore etmek
Sadece dekorasyon fonksiyonlarına ek olarak sınıfları da dekore edebiliriz. Bir sınıfa bir dekoratör eklediğinizde, dekore edilmiş yöntem, sınıfın yapıcı/başlatıcı yönteminin (__init__) yerini alır.
foo-bar’a geri dönersek, foo’nun dekoratörümüz olduğunu ve Bar’ın dekore ettiğimiz sınıf olduğunu varsayalım, sonra foo Bar.__init__’yi süsleyecek. Bu, Bar türündeki nesnelerin somutlaştırılmasından önce herhangi bir şey yapmak istiyorsak faydalı olacaktır.
Bu, aşağıdaki kodun olduğu anlamına gelir
def foo(func): def new_func(*args, **kwargs): print('Doing some stuff before instantiation') func(*args, **kwargs) return new_func @foo class Bar: def __init__(self): print("In initiator")
eşdeğerdir
def foo(func): def new_func(*args, **kwargs): print('Doing some stuff before instantiation') func(*args, **kwargs) return new_func class Bar: def __init__(self): print("In initiator") Bar.__init__ = foo(Bar.__init__)
Aslında, iki yöntemden biri kullanılarak tanımlanan Bar sınıfı bir nesneyi başlatmak size aynı çıktıyı vermelidir:
Doing some stuff before instantiation In initiator
Python’da Örnek Dekoratörler
Kendi dekoratörlerinizi tanımlayabilseniz de, Python’da zaten yerleşik olan bazıları vardır. Python’da karşılaşabileceğiniz yaygın dekoratörlerden bazıları şunlardır:
@statikyöntem
Statik yöntem, dekore ettiği yöntemin statik bir yöntem olduğunu belirtmek için bir sınıfta kullanılır. Statik yöntemler, sınıfı başlatmaya gerek kalmadan çalışabilen yöntemlerdir. Aşağıdaki kod örneğinde, statik bir bark yöntemiyle bir Dog sınıfı oluşturuyoruz.
class Dog: @staticmethod def bark(): print('Woof, woof!')
Artık bark yöntemine şu şekilde erişilebilir:
Dog.bark()
Ve kodu çalıştırmak aşağıdaki çıktıyı üretecektir:
Woof, woof!
Dekoratörler nasıl kullanılır bölümünde bahsettiğim gibi dekoratörler iki şekilde kullanılabilir. @ sözdizimi, ikisinden biri olarak daha özlüdür. Diğer yöntem ise, dekore etmek istediğimiz fonksiyonu bir argüman olarak ileterek dekoratör fonksiyonunu çağırmak. Anlamı, yukarıdaki kod aşağıdaki kodla aynı şeyi gerçekleştirir:
class Dog: def bark(): print('Woof, woof!') Dog.bark = staticmethod(Dog.bark)
Ve yine aynı şekilde bark yöntemini kullanabiliriz.
Dog.bark()
Ve aynı çıktıyı üretecekti
Woof, woof!
Gördüğünüz gibi, ilk yöntem daha temiz ve daha kodu okumaya başlamadan önce işlevin statik bir işlev olduğu daha açık. Sonuç olarak, kalan örnekler için ilk yöntemi kullanacağım. Ancak ikinci yöntemin bir alternatif olduğunu unutmayın.
@sınıf yöntemi
Bu dekoratör, dekore ettiği yöntemin bir sınıf yöntemi olduğunu belirtmek için kullanılır. Sınıf yöntemleri, çağrılabilmeleri için sınıfın somutlaştırılmasını gerektirmemeleri bakımından statik yöntemlere benzer.
Bununla birlikte, temel fark, sınıf yöntemlerinin sınıf özniteliklerine erişimi varken statik yöntemlerin olmamasıdır. Bunun nedeni, Python’un sınıfı, her çağrıldığında bir sınıf yöntemine ilk argüman olarak otomatik olarak iletmesidir. Python’da bir sınıf yöntemi oluşturmak için sınıf yöntemi dekoratörünü kullanabiliriz.
class Dog: @classmethod def what_are_you(cls): print("I am a " + cls.__name__ + "!")
Kodu çalıştırmak için, sınıfı başlatmadan basitçe yöntemi çağırıyoruz:
Dog.what_are_you()
Ve çıktı:
I am a Dog!
@mülk
Özellik dekoratörü, bir yöntemi özellik ayarlayıcı olarak etiketlemek için kullanılır. Dog örneğimize dönersek, Dog’un adını alan bir metot oluşturalım.
class Dog: # Creating a constructor method that takes in the dog's name def __init__(self, name): # Creating a private property name # The double underscores make the attribute private self.__name = name @property def name(self): return self.__name
Artık köpeğin adına normal bir özellik gibi erişebiliriz,
# Creating an instance of the class foo = Dog('foo') # Accessing the name property print("The dog's name is:", foo.name)
Ve kodu çalıştırmanın sonucu şöyle olur:
The dog's name is: foo
@özellik.setter
property.setter dekoratörü, özelliklerimiz için bir ayarlayıcı yöntemi oluşturmak için kullanılır. @property.setter dekoratörünü kullanmak için, property’yi özelliğin adıyla değiştiriyorsunuz, için bir ayarlayıcı oluşturuyorsunuz. Örneğin, foo özelliğinin yöntemi için bir ayarlayıcı oluşturuyorsanız, dekoratörünüz @foo.setter olacaktır. İşte göstermek için bir Köpek örneği:
class Dog: # Creating a constructor method that takes in the dog's name def __init__(self, name): # Creating a private property name # The double underscores make the attribute private self.__name = name @property def name(self): return self.__name # Creating a setter for our name property @name.setter def name(self, new_name): self.__name = new_name
Ayarlayıcıyı test etmek için aşağıdaki kodu kullanabiliriz:
# Creating a new dog foo = Dog('foo') # Changing the dog's name foo.name="bar" # Printing the dog's name to the screen print("The dog's new name is:", foo.name)
Kodun çalıştırılması aşağıdaki çıktıyı üretecektir:
The dogs's new name is: bar
Python’da dekoratörlerin önemi
Artık dekoratörlerin ne olduğunu ele aldığımıza ve bazı dekoratör örnekleri gördüğünüze göre, Python’da dekoratörlerin neden önemli olduğunu tartışabiliriz. Dekoratörler birkaç nedenden dolayı önemlidir. Bazıları, aşağıda listeledim:
- Kodun yeniden kullanılabilirliğini sağlarlar: Yukarıda verilen günlük kaydı örneğinde, @create_logger’ı istediğimiz herhangi bir işlevde kullanabiliriz. Bu, her işlev için manuel olarak yazmadan tüm işlevlerimize günlük işlevselliği eklememizi sağlar.
- Modüler kod yazmanıza izin verirler: Yine günlük kaydı örneğine geri dönersek, dekoratörlerle çekirdek işlevi, bu durumda say_hello’yu ihtiyacınız olan diğer işlevsellikten, bu durumda günlük kaydından ayırabilirsiniz.
- Çerçeveleri ve kitaplıkları geliştirirler: Dekoratörler, ek işlevsellik sağlamak için Python çerçevelerinde ve kitaplıklarında yaygın olarak kullanılır. Örneğin, Flask veya Django gibi web çerçevelerinde, yolları tanımlamak, kimlik doğrulamayı gerçekleştirmek veya belirli görünümlere ara yazılım uygulamak için dekoratörler kullanılır.
Son sözler
Dekoratörler inanılmaz derecede faydalıdır; işlevlerini değiştirmeden işlevleri genişletmek için bunları kullanabilirsiniz. Bu, işlevlerin performansını zamanlamak, bir işlev çağrıldığında günlüğe kaydetmek, bir işlevi çağırmadan önce bağımsız değişkenleri doğrulamak veya bir işlev çalıştırılmadan önce izinleri doğrulamak istediğinizde kullanışlıdır. Dekoratörleri anladığınızda, daha temiz bir şekilde kod yazabileceksiniz.
Daha sonra, demetler ve Python’da cURL kullanımı hakkındaki makalelerimizi okumak isteyebilirsiniz.