Zen of Python, işleri yapmanın yalnızca bir yolu olması gerektiğini belirtir - ancak sıklıkla bir işlevi ne zaman kullanacağıma ve bir yöntemi ne zaman kullanacağıma karar verme sorunuyla karşılaşıyorum.
Önemsiz bir örneği ele alalım - bir ChessBoard nesnesi. Diyelim ki tahtadaki tüm yasal King hamlelerini almak için bir yola ihtiyacımız var. ChessBoard.get_king_moves () yazıyor muyuz yoksa get_king_moves (chess_board) mı?
İşte baktığım bazı ilgili sorular:
- Python neden 'sihirli yöntemler' kullanır?
- Python dizelerinin bir dizi uzunluğu yöntemine sahip olmamasının bir nedeni var mı?
Aldığım cevaplar büyük ölçüde yetersizdi:
Python neden bazı işlevler için yöntemler kullanır (ör. List.index ()) ama diğerleri için işlev görür (ör. Len (liste))?
Bunun başlıca nedeni tarih. Fonksiyonlar, bir grup tip için jenerik olan ve hiç metodu olmayan nesneler için bile çalışması amaçlanan işlemler için kullanıldı (örn. Tuple). Python'un (map (), apply () ve diğerleri) işlevsel özelliklerini kullandığınızda, amorf bir nesne koleksiyonuna kolayca uygulanabilecek bir işleve sahip olmak da uygundur.
Aslında, yerleşik bir işlev olarak len (), max (), min () uygulamak, aslında bunları her tür için yöntem olarak uygulamaktan daha az koddur. Bireysel vakalar hakkında tartışılabilir ama bu Python'un bir parçası ve bu tür temel değişiklikler yapmak için artık çok geç. Büyük kod kırılmalarını önlemek için işlevler kalmalıdır.
İlginç olsa da, yukarıdakiler hangi stratejinin benimseneceği konusunda pek bir şey söylemiyor.
Bunun nedenlerinden biri budur - özel yöntemlerle geliştiriciler, getLength (), length (), getlength () veya herhangi bir şekilde farklı bir yöntem adı seçmekte özgür olabilirler. Python, len () ortak işlevinin kullanılabilmesi için katı adlandırma uygular.
Biraz daha ilginç. Benim aldığım şey, işlevlerin bir anlamda arayüzlerin Pythonic versiyonu olduğudur.
Son olarak, Guido'nun kendisinden :
Yetenekler / Arayüzler hakkında konuşmak, bana bazı "haydut" özel yöntem isimlerimizi düşündürdü. Dil Referansında, "Bir sınıf, yöntemleri özel adlarla tanımlayarak (aritmetik işlemler veya alt simge oluşturma ve dilimleme gibi) özel sözdizimi tarafından çağrılan belirli işlemleri uygulayabilir." Ancak , sözdizimini desteklemek yerine yerleşik işlevlerin yararına sağlanmış gibi görünen
__len__
veya gibi özel adlara sahip tüm bu yöntemler vardır__unicode__
. Böylece Muhtemelen bir arayüz tabanlı Python bu yöntemler, bir ABC düzenli adlandırılmış yöntemlerle dönüşeceğini__len__
olacakclass container: ... def len(self): raise NotImplemented
Yine de, biraz daha düşünürsek, neden tüm sözdizimsel işlemlerin belirli bir ABC'de normal olarak adlandırılan uygun yöntemi çağırmadığını
<
object.lessthan
comparable.lessthan
anlamıyorum . Örneğin " ", muhtemelen " " (veya belki " ") anlamına gelir. Bu yüzden bir başka fayda, Python'u bu karışık addaki tuhaflıktan uzaklaştırma yeteneği olacaktır ki bu bana bir HCI iyileştirmesi gibi görünüyor .Hm. Katıldığımdan emin değilim (bunu düşün :-).
İlk olarak açıklamak istediğim iki bitlik "Python mantığı" var.
Her şeyden önce, HCI nedenleriyle x.len () yerine len (x) 'i seçtim (
def __len__()
çok sonra geldi). Aslında iç içe geçmiş iki neden var, her ikisi de HCI:(a) Bazı işlemler için, önek gösterimi sadece sonekten daha iyi okunur - önek (ve ek!) işlemleri matematikte, matematikçinin bir problem hakkında düşünmesine yardımcı olan görsellerin gösterimlerden hoşlandığı uzun bir matematik geleneğine sahiptir. Biz böyle bir formül yeniden hangi ile kolay karşılaştırın
x*(a+b)
içinex*a + x*b
ham OO gösterimi kullanarak aynı şeyi yapmanın sakarlık.(b) Ben diyor kod okuduğumda
len(x)
ben biliyorum bir şeyin uzunluğu istiyor söyledi. Bu bana iki şey anlatıyor: sonuç bir tamsayı ve argüman bir tür kapsayıcı. Aksine, okuduğumdax.len()
, bununx
bir arayüz uygulayan veya standardı olan bir sınıftan miras alan bir tür konteyner olduğunu zaten bilmem gerekiyorlen()
. Eşleme uygulamayan bir sınıfın birget()
veyakeys()
yöntemi olduğunda veya dosya olmayan bir şeyin birwrite()
yöntemi olduğunda zaman zaman yaşadığımız kafa karışıklığına tanık olun .Aynı şeyi başka bir şekilde söyleyerek, 'len'i yerleşik bir işlem olarak görüyorum . Bunu kaybetmekten nefret ederim. Bunu kast edip etmediğini kesin olarak söyleyemem ama 'def len (self): ...' kesinlikle kulağa sıradan bir yönteme indirgemek istiyormuşsun gibi geliyor. Ben kesinlikle -1'im.
Açıklamaya söz verdiğim Python mantığının ikinci kısmı,
__special__
sadece bakmak için değil , özel yöntemler seçmemin nedenidirspecial
. Sınıfların geçersiz kılmak isteyebileceği birçok işlem bekliyordum, bazı standartlar (örneğin__add__
veya__getitem__
), bazıları çok standart değil (örneğin__reduce__
uzun süredir turşuların C kodunda hiçbir desteği yoktu). Bu özel işlemlerin sıradan yöntem adlarını kullanmasını istemedim, çünkü o zaman önceden var olan sınıflar veya tüm özel yöntemler için ansiklopedik bir belleği olmayan kullanıcılar tarafından yazılan sınıflar, uygulamak istemedikleri işlemleri yanlışlıkla tanımlayabilirler. , muhtemelen feci sonuçlarla. Ivan Krstić, tüm bunları yazdıktan sonra gelen mesajında bunu daha kısa bir şekilde açıkladı.- --Guido van Rossum (ana sayfa: http://www.python.org/~guido/ )
Bunu anladığım kadarıyla, bazı durumlarda ön ek notasyonu daha mantıklıdır (yani Duck.quack, dilbilimsel açıdan vakadan (Duck) daha mantıklıdır) ve yine, işlevler "arayüzlere" izin verir.
Böyle bir durumda, benim tahminim get_king_moves'u yalnızca Guido'nun ilk noktasına dayanarak uygulamak olacaktır. Ancak bu yine de, benzer push ve pop yöntemleriyle bir yığın ve kuyruk sınıfının uygulanmasıyla ilgili pek çok açık soru bırakıyor - bunlar işlev mi yoksa yöntem mi? (burada fonksiyonları tahmin ediyorum, çünkü gerçekten bir push-pop arayüzünü işaret etmek istiyorum)
TLDR: Biri, fonksiyonları ne zaman kullanacağına karar verme stratejisinin ne olması gerektiğini açıklayabilir mi?
X.frob
yaX.__frob__
ve ayaklıfrob
.