Türetilmiş sınıfta bu özelliğin üzerine yazılıyorsa, temel sınıfın bir özelliği nasıl çağrılır?


88

Bazı sınıflarımı, alıcıların ve ayarlayıcıların yoğun kullanımından, özelliklerin daha pitonik kullanımına değiştiriyorum.

Ama şimdi sıkıştım çünkü önceki alıcılarımdan veya ayarlayıcılarımdan bazıları temel sınıfın karşılık gelen yöntemini çağıracak ve sonra başka bir şey gerçekleştirecek. Fakat bu, özelliklerle nasıl başarılabilir? Üst sınıfta özellik alıcı veya ayarlayıcı nasıl çağrılır?

Tabii ki sadece niteliğin kendisini çağırmak sonsuz özyineleme sağlar.

class Foo(object):

    @property
    def bar(self):
        return 5

    @bar.setter
    def bar(self, a):
        print a

class FooBar(Foo):

    @property
    def bar(self):
        # return the same value
        # as in the base class
        return self.bar # --> recursion!

    @bar.setter
    def bar(self, c):
        # perform the same action
        # as in the base class
        self.bar = c    # --> recursion!
        # then do something else
        print 'something else'

fb = FooBar()
fb.bar = 7

Yanıtlar:


103

Özellik tarafından çağrılan temel sınıf işlevini çağırabileceğinizi düşünebilirsiniz:

class FooBar(Foo):

    @property
    def bar(self):
        # return the same value
        # as in the base class
        return Foo.bar(self)

Bence bu denenecek en bariz şey - işe yaramıyor çünkü bar bir özelliktir, çağrılabilir değil.

Ancak bir özellik, karşılık gelen niteliği bulmak için bir alıcı yöntemine sahip yalnızca bir nesnedir:

class FooBar(Foo):

    @property
    def bar(self):
        # return the same value
        # as in the base class
        return Foo.bar.fget(self)

Neden Foo.bar.fset(self, c)kalıtsal bir ayarlayıcıyı çağırmalıyım ? Neden olmasın Foo.bar.fset(c)- "benlik" olmadan - Bunun dolaylı olarak geçtiğini sanıyordum?
nerdoc

2
Bir TypeError alıyorum: 'özellik' nesnesi
çağrılamıyor

@nerdoc self, zincirden nereden ima ediyor Foo.bar.fset?
Tadhg McDonald-Jensen

Sadece bir düşünce - AFAIK benliği her zaman örtük olarak aktarılır. Eğer bir foo = Foo()\ yaparsanız foo.bar(c), öz geçilmez, ancak bar () bunu Python'dan alır. Python uzmanı değilim, az çok yeni başlayanlar da. Bu sadece bir düşünce.
nerdoc

selfyalnızca yöntem bir sınıfın örneğinde çağrıldığında örtük olarak aktarılır. Örneğin, Ayöntemi olan bir sınıfım varsa b(self, arg)ve bir örnek oluşturursam c = A(), çağırma c.b(arg)A.b(c, arg)
şuna

55

Süper numara yapmalı:

return super().bar

Python 2.x'te daha ayrıntılı sözdizimini kullanmanız gerekir:

return super(FooBar, self).bar

1
TypeError: super () en az 1 argüman alır (0 verilen)
Aaron Maenpaa

4
Sanırım (bağlantıya bakılırsa: P), bu cevap python3 ile ilgilidir. Python3'te super () sıfır argüman alabilir, evet.
shylent

6
super (). bar alıcı için iyi çalışıyor gibi görünüyor, ancak geçersiz kılınmış bir ayarlayıcıda temel özellik aracılığıyla atama için çalışmıyor. Super (). Bar = 3 yaparsam AttributeError: 'süper' nesnesinin 'bar' niteliği yok
Rob Smallshire

1
İyi nokta Rob, bunu bilmiyordu. İşte daha fazla bilgi: stackoverflow.com/questions/10810369/…
Pankrat

2
Bu almak için çalışsa da, bir AttributeError.
Ethan Furman

27

superTemel sınıf adına açıkça atıfta bulunmayı gerektirmeyen alternatif bir kullanım vardır .

Temel sınıf A:

class A(object):
    def __init__(self):
        self._prop = None

    @property
    def prop(self):
        return self._prop

    @prop.setter
    def prop(self, value):
        self._prop = value

class B(A):
    # we want to extend prop here
    pass

B'de, üst sınıf A'nın özellik alıcısına erişim:

Diğerlerinin zaten yanıtladığı gibi, bu:

super(B, self).prop

Veya Python 3'te:

super().prop

Bu, alıcının kendisi tarafından değil, mülkün alıcısı tarafından döndürülen değeri döndürür, ancak alıcıyı genişletmek için yeterlidir.

B'de, üst sınıf A'nın özellik belirleyicisine erişim:

Şimdiye kadar gördüğüm en iyi öneri şudur:

A.prop.fset(self, value)

Bunun daha iyi olduğuna inanıyorum:

super(B, self.__class__).prop.fset(self, value)

Bu örnekte her iki seçenek de eşdeğerdir, ancak süper kullanmanın temel sınıflarından bağımsız olma avantajı vardır B. Eğer Bbir devralmak vardı Cda mülkiyet uzanan sınıfının, güncelleştirmenin olmazdı B'ın kodu.

A'nın özelliğini genişleten B'nin tam kodu:

class B(A):
    @property
    def prop(self):
        value = super(B, self).prop
        # do something with / modify value here
        return value

    @prop.setter
    def prop(self, value):
        # do something with / modify value here
        super(B, self.__class__).prop.fset(self, value)

Bir uyarı:

Mülkünüzde ayarlayıcı yoksa, Byalnızca birinin davranışını değiştirseniz bile hem ayarlayıcıyı hem de alıcıyı tanımlamanız gerekir.


Merhaba, bu çok yardımcı oldu. Buna bir devam sorum var. Bu kurulum iyi çalışıyor; ancak, ek özellikleri tanımlaması için B için init ayarladığımda çalışmayı durdurur . B için ayrı bir init'e sahip olmanın bir yolu var mı? Eğer ederiz
Sang

super(B, self.__class__)Tam olarak nasıl çalıştığını açıklar mısınız lütfen super(class, class)? Nerede belgeleniyor?
Sanat

Ben bu cevaba bazı küçük geliştirmeler önerdiğimiz Cevabıma
Eric

4

Deneyin

@property
def bar:
    return super(FooBar, self).bar

Python'un temel sınıf özelliğini çağırmayı destekleyip desteklemediğinden emin değilim. Bir özellik, aslında belirtilen işlevle ayarlanan ve daha sonra sınıfta bu adı değiştiren çağrılabilir bir nesnedir. Bu, süper işlevin mevcut olmadığı anlamına gelebilir.

Yine de property () işlevini kullanmak için söz diziminizi her zaman değiştirebilirsiniz:

class Foo(object):

    def _getbar(self):
        return 5

    def _setbar(self, a):
        print a

    bar = property(_getbar, _setbar)

class FooBar(Foo):

    def _getbar(self):
        # return the same value
        # as in the base class
        return super(FooBar, self)._getbar()

    def bar(self, c):
        super(FooBar, self)._setbar(c)
        print "Something else"

    bar = property(_getbar, _setbar)

fb = FooBar()
fb.bar = 7

Temel sınıfı yazarsanız bu iyi çalışır. Ancak mülk ve alıcı için aynı adı kullanan bir üçüncü taraf temel sınıfını genişletirseniz ne olur?
akaihola

2

Maxime'in cevabında bazı küçük iyileştirmeler :

  • __class__Yazmaktan kaçınmak için kullanma B. Bunun self.__class__çalışma zamanı türü olduğunu self, ancak __class__ içermeyen self , çevreleyen sınıf tanımının adı olduğunu unutmayın. super()için bir kısaltmadır super(__class__, self).
  • Kullanılması __set__yerine fset. İkincisi, 'lere özgüdür property, ancak birincisi tüm özellik benzeri nesneler (tanımlayıcılar) için geçerlidir.
class B(A):
    @property
    def prop(self):
        value = super().prop
        # do something with / modify value here
        return value

    @prop.setter
    def prop(self, value):
        # do something with / modify value here
        super(__class__, self.__class__).prop.__set__(self, value)

1

Aşağıdaki şablonu kullanabilirsiniz:

class Parent():
    def __init__(self, value):
        self.__prop1 = value

    #getter
    @property
    def prop1(self):
        return self.__prop1

    #setter
    @prop1.setter
    def prop1(self, value):
        self.__prop1 = value

    #deleter
    @prop1.deleter
    def prop1(self):
        del self.__prop1
  
class Child(Parent):

    #getter
    @property
    def prop1(self):
        return super(Child, Child).prop1.__get__(self)

    #setter
    @prop1.setter
    def prop1(self, value):
        super(Child, Child).prop1.__set__(self, value)

    #deleter
    @prop1.deleter
    def prop1(self):
        super(Child, Child).prop1.__delete__(self)

Not! Tüm özellik yöntemleri birlikte yeniden tanımlanmalıdır. Tüm yöntemleri yeniden tanımlamak istemiyorsanız, bunun yerine aşağıdaki şablonu kullanın:

class Parent():
    def __init__(self, value):
        self.__prop1 = value

    #getter
    @property
    def prop1(self):
        return self.__prop1

    #setter
    @prop1.setter
    def prop1(self, value):
        self.__prop1 = value

    #deleter
    @prop1.deleter
    def prop1(self):
        del self.__prop1


class Child(Parent):

    #getter
    @Parent.prop1.getter
    def prop1(self):
        return super(Child, Child).prop1.__get__(self)

    #setter
    @Parent.prop1.setter
    def prop1(self, value):
        super(Child, Child).prop1.__set__(self, value)

    #deleter
    @Parent.prop1.deleter
    def prop1(self):
        super(Child, Child).prop1.__delete__(self)

-4
    class Base(object):
      def method(self):
        print "Base method was called"

    class Derived(Base):
      def method(self):
        super(Derived,self).method()
        print "Derived method was called"

    d = Derived()
    d.method()

(eğer açıklamanızdan bir şey kaçırmıyorsam)


1
sen: o, basit yöntemlerden değil, mülklerden bahsediyor
akaihola
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.