Yukarıdaki Armin Ronacher'den doğru açıklama, benim gibi yeni başlayanların iyi anlayabilmesi için cevaplarını genişletti:
Bir sınıfta tanımlanan yöntemlerdeki fark, statik veya örnek yöntemi (burada başka bir tür - sınıf yöntemi var - burada tartışılmadan tartışılsın), sınıf örneğine bir şekilde bağlı olup olmadıkları gerçeğinde yatmaktadır. Örneğin, yöntemin çalışma zamanı sırasında sınıf örneğine başvuru alıp almadığını varsayalım
class C:
a = []
def foo(self):
pass
C # this is the class object
C.a # is a list object (class property object)
C.foo # is a function object (class property object)
c = C()
c # this is the class instance
__dict__Sınıf nesnesi sözlük özelliği böylece tüm özellikleri ve bir sınıf nesnesi yöntem ve referans tutan
>>> C.__dict__['foo']
<function foo at 0x17d05b0>
foo yöntemine yukarıdaki gibi erişilebilir. Burada dikkat edilmesi gereken önemli bir nokta, python'daki her şeyin bir nesne olduğudur ve bu nedenle yukarıdaki sözlükteki referansların kendileri diğer nesnelere işaret ediyor olmasıdır. Onlara Class Property Objects diyeyim - ya da kısalık cevabım kapsamında CPO olarak.
Bir CPO bir tanımlayıcıysa, python yorumlayıcısı __get__()içerdiği değere erişmek için CPO yöntemini çağırır .
Bir CPO'nun tanımlayıcı olup olmadığını belirlemek için, python yorumlayıcısı tanımlayıcı protokolünü uygulayıp uygulamadığını kontrol eder. Tanımlayıcı protokol uygulamak 3 yöntem uygulamaktır
def __get__(self, instance, owner)
def __set__(self, instance, value)
def __delete__(self, instance)
örneğin
>>> C.__dict__['foo'].__get__(c, C)
nerede
self CPO (liste, str, işlev vb. bir örneği olabilir) ve çalışma zamanı tarafından sağlanır
instance bu CPO'nun tanımlandığı sınıfın örneğidir (yukarıdaki 'c' nesnesi) ve tarafımızdan açıklık sağlanması gerekir
ownerbu CPO'nun tanımlandığı sınıftır (yukarıdaki 'C' sınıf nesnesi) ve bizim tarafımızdan sağlanması gerekir. Ancak bunun sebebi onu CPO'da çağırıyoruz. bunu örnek üzerinde çağırdığımızda, çalışma zamanının örneği veya sınıfını (polimorfizm) sağlayabileceğinden bunu sağlamamız gerekmez.
value CPO için öngörülen değerdir ve tarafımızdan sağlanmalıdır
Tüm CPO'lar tanımlayıcı değildir. Örneğin
>>> C.__dict__['foo'].__get__(None, C)
<function C.foo at 0x10a72f510>
>>> C.__dict__['a'].__get__(None, C)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'list' object has no attribute '__get__'
Bunun nedeni liste sınıfının tanımlayıcı protokolünü uygulamamasıdır.
Bu nedenle, kendi kendine argüman c.foo(self)gereklidir çünkü yöntem imzası aslında budur C.__dict__['foo'].__get__(c, C)(yukarıda açıklandığı gibi, C bulunabileceği veya polimorflaştırılacağı için gerekli değildir) Ve bu da gerekli örnek argümanını geçmezseniz bir TypeError almanızın nedeni de budur.
Yönteme yine de Object C sınıfı üzerinden başvurulduğunu fark ederseniz ve class örneği ile bağlantı, örnek nesne biçimindeki bir bağlamın bu işleve geçirilmesiyle gerçekleştirilir.
Bu, bağlam için herhangi bir bağlamı veya bağlamı korumayı seçerseniz, gereken tek şey, tanımlayıcı CPO'yu sarmak için bir sınıf yazmak ve __get__()bağlam gerektirmeyen yöntemini geçersiz kılmaktı . Bu yeni sınıf, dekoratör olarak adlandırdığımız şeydir ve anahtar kelime ile uygulanır@staticmethod
class C(object):
@staticmethod
def foo():
pass
Yeni sarılmış CPO'da bağlamın bulunmaması foobir hata atmaz ve aşağıdaki gibi doğrulanabilir:
>>> C.__dict__['foo'].__get__(None, C)
<function foo at 0x17d0c30>
Statik bir yöntemin kullanılması daha çok bir ad alanı ve kod bakımıdır (bir sınıftan çıkarılması ve modül boyunca kullanılabilir hale getirilmesi vb.).
Yöntemleri bağlamsallaştırmanız gerekmediği sürece (erişim örneği değişkenleri, sınıf değişkenleri vb. Gibi) mümkün olduğunda örnek yöntemleri yerine statik yöntemler yazmak daha iyi olabilir. Bunun bir nedeni, nesnelere istenmeyen başvurular yapmadan çöp toplama işlemini kolaylaştırmaktır.