Python'da self, Demystified

Bir süredir Python'da (nesne yönelimli programlama) programlama yapıyorsanız, kesinlikle selfilk parametreleri olan yöntemlerle karşılaşmışsınızdır .

Önce bu yinelenen benlik parametresinin ne olduğunu anlamaya çalışalım.

Python'da self nedir?

Nesne yönelimli programlamada, bir sınıf için yöntem tanımladığımızda self, her durumda ilk parametre olarak kullanırız . Adlı bir sınıfın tanımına bakalım Cat.

 class Cat: def __init__(self, name, age): self.name = name self.age = age def info(self): print(f"I am a cat. My name is (self.name). I am (self.age) years old.") def make_sound(self): print("Meow")

Bu durumda, dahil olmak üzere tüm yöntemler, __init__ilk parametreye sahiptir self.

Sınıfın nesneler için bir plan olduğunu biliyoruz. Bu plan, birden fazla sayıda nesne oluşturmak için kullanılabilir. Yukarıdaki sınıftan iki farklı nesne oluşturalım.

 cat1 = Cat('Andy', 2) cat2 = Cat('Phoebe', 3)

selfAnahtar belirli bir sınıfın bir örneği (nesne) temsil etmek için kullanılır. Bu durumda, iki Catnesne cat1ve cat2kendi nameve ageniteliklerine sahiptir. Kendi kendine argüman olmasaydı, aynı sınıf bu iki nesne için bilgileri tutamazdı.

Bununla birlikte, sınıf sadece bir taslak olduğundan, python'daki selfher nesnenin özniteliklerine ve yöntemlerine erişime izin verir. Bu, her nesnenin kendi niteliklerine ve yöntemlerine sahip olmasını sağlar. Bu nedenle, bu nesneleri oluşturmadan çok önce bile, nesnelere selfsınıfı tanımlarken başvururuz.

Neden her seferinde kendini açıkça tanımlıyor?

Kullanımını anladığımızda bile self, özellikle başka dillerden gelen programcılar için, selfbir yöntemi her tanımladığımızda açıkça bir parametre olarak iletilen , yine de tuhaf görünebilir . As Python Zen gider, " Açık daha iyi örtülü daha ".

Öyleyse neden bunu yapmamız gerekiyor? Başlamak için basit bir örnek verelim. PointBaşlangıç distancenoktasına olan mesafeyi hesaplamak için bir yöntem tanımlayan bir sınıfımız var .

 class Point(object): def __init__(self,x = 0,y = 0): self.x = x self.y = y def distance(self): """Find distance from origin""" return (self.x**2 + self.y**2) ** 0.5

Şimdi bu sınıfı somutlaştıralım ve mesafeyi bulalım.

 >>> p1 = Point(6,8) >>> p1.distance() 10.0

Yukarıdaki örnekte, __init__()üç parametre tanımlıyor , ancak ikisini (6 ve 8) geçtik. Benzer şekilde distance()bir ancak sıfır bağımsız değişken geçirilmesini gerektirir. Python neden bu argüman numarası uyuşmazlığından şikayetçi değil?

Dahili Olarak Ne Olur?

Point.distanceve p1.distanceyukarıdaki örnekte farklıdır ve tamamen aynı değildir.

 >>> type(Point.distance) >>> type(p1.distance) 

Birincisinin bir fonksiyon, ikincisinin ise bir metot olduğunu görebiliriz. Yöntemlerle ilgili tuhaf bir şey (Python'da), nesnenin kendisinin karşılık gelen işleve ilk argüman olarak aktarılmasıdır.

Yukarıdaki örnek durumunda, yöntem çağrısı p1.distance()aslında ile eşdeğerdir Point.distance(p1).

Genel olarak, bazı argümanlara sahip bir metodu çağırdığımızda, metodun nesnesini ilk argümandan önce yerleştirerek karşılık gelen sınıf işlevi çağrılır. Yani, böyle bir şey obj.meth(args)olur Class.meth(obj, args). Çağrı süreci, alma işlemi otomatik değildir (açıkça).

Bu, sınıftaki bir işlevin ilk parametresinin nesnenin kendisi olması gerektiğinin nedenidir. Bu parametrenin yazılması selfsadece bir kuraldır. Bu bir anahtar kelime değildir ve Python'da özel bir anlamı yoktur. Başka isimler de kullanabilirdik (gibi this) ama bu kesinlikle önerilmez. selfÇoğu geliştirici tarafından hoş karşılanmayan adların kullanılması kodun okunabilirliğini azaltır ( Okunabilirlik sayılır ).

Kendinden Kaçınılabilir

Şimdiye kadar, nesnenin (örneğin) kendisinin otomatik olarak ilk argüman olarak iletildiğini biliyorsunuz. Statik bir yöntem yapılırken bu örtük davranış önlenebilir . Aşağıdaki basit örneği düşünün:

 class A(object): @staticmethod def stat_meth(): print("Look no self was passed")

İşte statik @staticmethodyapan bir fonksiyon dekoratörü stat_meth(). Bu sınıfı somutlaştıralım ve yöntemi çağıralım.

 >>> a = A() >>> a.stat_meth() Look no self was passed

Yukarıdaki örnekten, statik bir yöntem kullanılırken nesneyi ilk argüman olarak geçirmenin örtük davranışından kaçınıldığını görebiliriz. Sonuç olarak, statik yöntemler düz eski işlevler gibi davranır (Bir sınıfın tüm nesneleri statik yöntemleri paylaştığından).

 >>> type(A.stat_meth) >>> type(a.stat_meth) 

Ben Kalmak İçin Burada

Açık self, Python'a özgü değildir. Bu fikir Modula-3'ten ödünç alındı . Aşağıda, yardımcı olacağı bir kullanım örneği verilmiştir.

Python'da açık değişken bildirimi yoktur. İlk görevde harekete geçerler. Öğesinin kullanımı, selförnek nitelikleri (ve yöntemleri) yerel değişkenlerden ayırt etmeyi kolaylaştırır.

İlk örnekte self.x bir örnek niteliğidir, x ise yerel bir değişkendir. Aynı değiller ve farklı ad alanlarında yer alıyorlar.

Birçoğu, thisC ++ ve Java'da olduğu gibi Python'da self'i bir anahtar kelime yapmayı önerdi . Bu self, yöntemlerde biçimsel parametre listesinden fazlalık explicit kullanımını ortadan kaldıracaktır .

Bu fikir umut verici görünse de gerçekleşmeyecek. En azından yakın gelecekte. Ana neden geriye dönük uyumluluktur. İşte Python'un yaratıcısının kendisinin açık benliğin neden kalması gerektiğini açıklayan bir blogu.

__init __ () bir yapıcı değil

Şimdiye kadarki bilgilerden çıkarılabilecek önemli bir sonuç, __init__()yöntemin bir kurucu olmadığıdır. Birçok naif Python programcısı, __init__()bir nesne oluşturduğumuzda çağrıldığı için bununla karıştırılır .

Daha yakından bir inceleme, içindeki ilk parametrenin __init__()nesnenin kendisi olduğunu ortaya çıkaracaktır (nesne zaten mevcuttur). İşlev __init__(), nesne oluşturulduktan hemen sonra çağrılır ve onu başlatmak için kullanılır.

Teknik olarak konuşursak, bir kurucu, nesnenin kendisini oluşturan bir yöntemdir. Python'da bu yöntem __new__(). Bu yöntemin ortak imzası şudur:

 __new__(cls, *args, **kwargs)

Çağrıldığında __new__(), sınıfın kendisi otomatik olarak ilk argüman olarak aktarılır ( cls).

Yine tıpkı self gibi, cls de bir adlandırma kuralıdır. Ayrıca, * args ve ** kwargs Python'da yöntem çağrıları sırasında rastgele sayıda argüman almak için kullanılır.

Uygulama sırasında hatırlanması gereken bazı önemli noktalar __new__()şunlardır:

  • __new__()her zaman önceden çağrılır __init__().
  • İlk argüman, örtük olarak iletilen sınıfın kendisidir.
  • Daima içinden geçerli bir nesne döndür __new__(). Zorunlu değildir, ancak asıl kullanımı bir nesneyi oluşturmak ve geri döndürmektir.

Bir örneğe bakalım:

 class Point(object): def __new__(cls,*args,**kwargs): print("From new") print(cls) print(args) print(kwargs) # create our object and return it obj = super().__new__(cls) return obj def __init__(self,x = 0,y = 0): print("From init") self.x = x self.y = y

Şimdi, şimdi onu somutlaştıralım.

 >>> p2 = Point(3,4) From new (3, 4) () From init

Bu örnek __new__()daha önce çağrıldığını göstermektedir __init__(). Ayrıca cls in parametresinin __new__()sınıfın kendisi ( Point) olduğunu da görebiliriz . Son olarak, nesne, nesne temel sınıfındaki __new__()yöntemi çağırarak oluşturulur .

Python'da, objectdiğer tüm sınıfların türetildiği temel sınıftır. Yukarıdaki örnekte, bunu super () kullanarak yaptık.

__New__ veya __init__ kullanılsın mı?

You might have seen __init__() very often but the use of __new__() is rare. This is because most of the time you don't need to override it. Generally, __init__() is used to initialize a newly created object while __new__() is used to control the way an object is created.

We can also use __new__() to initialize attributes of an object, but logically it should be inside __init__().

One practical use of __new__(), however, could be to restrict the number of objects created from a class.

Suppose we wanted a class SqPoint for creating instances to represent the four vertices of a square. We can inherit from our previous class Point (the second example in this article) and use __new__() to implement this restriction. Here is an example to restrict a class to have only four instances.

 class SqPoint(Point): MAX_Inst = 4 Inst_created = 0 def __new__(cls,*args,**kwargs): if (cls.Inst_created>= cls.MAX_Inst): raise ValueError("Cannot create more objects") cls.Inst_created += 1 return super().__new__(cls)

Örnek bir çalışma:

 >>> p1 = SqPoint(0,0) >>> p2 = SqPoint(1,0) >>> p3 = SqPoint(1,1) >>> p4 = SqPoint(0,1) >>> >>> p5 = SqPoint(2,2) Traceback (most recent call last):… ValueError: Cannot create more objects

Ilginç makaleler...