Python 3.x'in süper () büyüsü neden?


159

Python 3.x'te, super()bağımsız değişkenler olmadan çağrılabilir:

class A(object):
    def x(self):
         print("Hey now")

class B(A):
    def x(self):
        super().x()
>>> B().x()
Hey now

Bu işi yapmak için, derleme zamanı sihri gerçekleştirilir, bunun bir sonucu aşağıdaki kodun (isyan supereden super_) başarısız olmasıdır:

super_ = super

class A(object):
    def x(self):
        print("No flipping")

class B(A):
    def x(self):
        super_().x()
>>> B().x()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in x
RuntimeError: super(): __class__ cell not found

Neden super()üst sınıf derleyiciden yardım almadan çalışma zamanında çözülemiyor? Bu davranışın veya bunun altında yatan nedenin, dikkatsiz bir programcıyı ısırdığı pratik durumlar var mı?

... ve bir yan soru olarak: Python'da fonksiyonların, yöntemlerin vb. farklı bir isme yeniden bağlanmasıyla kırılabilecek başka örnekler var mı?


6
Ben Armin bu konuda açıklayan yapalım edeceğiz biri . Bu da başka bir iyi yazı
Oyunlar Brainiac

Yanıtlar:


217

super()KURU (Kendinizi Tekrarlama) ilkesini ihlal etmemek için yeni büyü davranışı eklendi, bkz. PEP 3135 . Sınıfı bir küresel olarak referans alarak açık bir şekilde adlandırmak zorunda kaldığınızda, super()kendi keşfettiğiniz aynı yeniden bağlama sorunlarına da eğilimlidir :

class Foo(Bar):
    def baz(self):
        return super(Foo, self).baz() + 42

Spam = Foo
Foo = something_else()

Spam().baz()  # liable to blow up

Aynısı, dekoratörün sınıf adını yeniden hatırlatan yeni bir nesne döndürdüğü sınıf dekoratörleri kullanmak için de geçerlidir:

@class_decorator_returning_new_class
class Foo(Bar):
    def baz(self):
        # Now `Foo` is a *different class*
        return super(Foo, self).baz() + 42

Sihirli super() __class__hücre, orijinal sınıf nesnesine erişmenizi sağlayarak bu sorunları güzelce kaldırır.

PEP, başlangıçta superbir anahtar kelime olmayı öngören Guido tarafından başlatıldı ve mevcut sınıfa bakmak için bir hücre kullanma fikri de onun oldu . Elbette, onu bir anahtar kelime yapma fikri PEP'in ilk taslağının bir parçasıydı .

Bununla birlikte, aslında Guido , anahtar kelime fikrinden 'çok büyülü' olarak uzaklaştı ve bunun yerine mevcut uygulamayı önerdi. O için farklı bir ad kullanarak tahmin super()bir sorun olabilir :

Düzeltme ekim bir ara çözüm kullanıyor: __class__ adlı bir değişkeni kullandığınızda ihtiyacınız olduğunu varsayar 'super'. Bu nedenle, (genel olarak) yeniden adlandırmak superiçin supperve kullanım supperancak super, bu bağımsız değişken olmadan çalışmaz (ama olacak yine iş bunu başarılı olursa, ya __class__ya da gerçek sınıf nesnesi) adında ilgisiz bir değişkeniniz varsa superişler işe yarar ancak yöntem, hücre değişkenleri için kullanılan biraz daha yavaş çağrı yolunu kullanır.

Böylece, sonunda, bir superanahtar kelime kullanmanın doğru olmadığını ve sihirli bir __class__hücre sağlamanın kabul edilebilir bir uzlaşma olduğunu ilan eden Guido'nun kendisi oldu.

Uygulamanın sihirli, örtük davranışının biraz şaşırtıcı olduğunu, ancak super()dilde en yanlış uygulanan işlevlerden biri olduğunu kabul ediyorum . Sadece internette bulunan tüm yanlış uygulamalara super(type(self), self)veya super(self.__class__, self) çağrılara bir göz atın ; bu kodlardan herhangi biri türetilmiş bir sınıftan çağrılmış olsaydı, sonsuz bir özyineleme istisnasıyla karşılaşırsınız . En azından basitleştirilmiş super()çağrı, argüman olmadan, bu problemden kaçınır .

Yeniden adlandırılmış gelince super_; Sadece referans __class__senin yöntemde de ve tekrar çalışacağız. Yönteminizdeki super veya __class__ adlarına başvurursanız hücre oluşturulur :

>>> super_ = super
>>> class A(object):
...     def x(self):
...         print("No flipping")
... 
>>> class B(A):
...     def x(self):
...         __class__  # just referencing it is enough
...         super_().x()
... 
>>> B().x()
No flipping

1
İyi yazma. Ancak yine de çamur kadar açıktır. Super () ifadesinin def super(of_class=magic __class__), a self.super(); def super(self): return self.__class__?
Charles Merriam

17
@CharlesMerriam: Bu yazı, super()argüman olmadan nasıl çalıştığı ile ilgili değil ; çoğunlukla neden var olduğu ile ilgilenir . super()Bir sınıf yöntemi, eşdeğerdir için super(ReferenceToClassMethodIsBeingDefinedIn, self)burada, ReferenceToClassMethodIsBeingDefinedInderleme zamanında tespit adlandırılan, bir kapak olarak yönteme bağlı olduğu __class__ve super()zamanında çağrı çerçeveden hem bakacaktır. Ama aslında tüm bunları bilmenize gerek yok.
Martijn Pieters

1
@CharlesMerriam: ama otomatik olarak başlatılan bir fonksiyonsuper() olmaya yakın değil , hayır.
Martijn Pieters

1
@ chris.leonard: anahtar cümle, süper () veya __class__yönteminizde kullanırsanız hücre oluşturulur . Adı superişlevinizde kullandınız. Derleyici bunu görür ve __class__kapanışı ekler .
Martijn Pieters

4
@Alexey: öyle değil yeter. yöntemin tanımlandığı türle aynı olmayan geçerli türü type(self)verir . Bir sınıfın Yani yöntem ile ihtiyaçları o kadar sınıflandırma olabilir, çünkü nokta hangi, olduğu ve size sonsuz döngü verecekti. Cevabımdaki bağlantıya bakın: Türetilmiş bir sınıfta super () çağırırken self .__ class__'yi geçebilir miyim? Foobazsuper(Foo, self).baz()class Ham(Foo):type(self)Hamsuper(type(self), self).baz()
Martijn Pieters
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.