このプロパティが派生クラスで上書きされている場合、基本クラスのプロパティを呼び出すにはどうすればよいですか?


87

私のいくつかのクラスを、ゲッターとセッターの広範な使用から、プロパティのよりPython的な使用に変更しています。

しかし、以前のゲッターまたはセッターの一部が基本クラスの対応するメソッドを呼び出してから、別のことを実行するため、今は行き詰まっています。しかし、これはどのようにプロパティで達成できますか?親クラスでプロパティゲッターまたはセッターを呼び出す方法は?

もちろん、属性自体を呼び出すだけで、無限の再帰が得られます。

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

回答:


100

プロパティによって呼び出される基本クラス関数を呼び出すことができると思うかもしれません。

class FooBar(Foo):

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

これは試してみるのが最も明白なことですが、barはプロパティであり、呼び出し可能ではないため、機能しません。

ただし、プロパティは単なるオブジェクトであり、対応する属性を見つけるためのgetterメソッドがあります。

class FooBar(Foo):

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

Foo.bar.fset(self, c)継承されたセッターを呼び出す必要があるのはなぜですか?なぜFoo.bar.fset(c)-「自己」なしで-これは暗黙のうちに渡されると思いましたか?
nerdoc 2013年

2
TypeErrorが発生します: 'property'オブジェクトを
呼び出す

@nerdoc selfgetはチェーンからどこを意味しFoo.bar.fsetますか?
Tadhg McDonald-Jensen 2017

ただの考え-AFAIKの自己は常に暗黙のうちに渡されます。foo = Foo()\を実行するfoo.bar(c)と、自己渡しはありませんが、bar()はPythonからそれを受け取ります。私はPythonの専門家ではなく、多かれ少なかれ初心者でもあります。それはただの考えです。
nerdoc 2017年

selfクラスのインスタンスでメソッドが呼び出されたときにのみ暗黙的に渡されます。たとえばA、メソッドを持つクラスがありb(self, arg)、インスタンスを作成する場合c = A()、呼び出しc.b(arg)は次のようになりますA.b(c, arg)
TallChuck 2018年

53

スーパーはトリックを行う必要があります:

return super().bar

Python 2.xでは、より詳細な構文を使用する必要があります。

return super(FooBar, self).bar

1
TypeError:super()は少なくとも1つの引数を取ります(0が指定されています)
Aaron Maenpaa 2009年

4
私は推測します(まあ、リンク:Pで判断すると)、この答えはpython3関連です。python3では、super()は引数をゼロにすることができます。
shylent 2009年

6
super()。barは、ゲッターでは正常に機能するようですが、オーバーライドされたセッターのbaseプロパティを介した割り当てでは機能しません。super()。bar = 3を実行すると、AttributeErrorが発生します: 'super'オブジェクトに属性 'bar'がありません
Rob Smallshire 2014年

1
良い点ロブ、それを知りませんでした。ここではより多くの情報があります:stackoverflow.com/questions/10810369/...
Pankrat

2
これは取得には機能しますが、で設定すると失敗しますAttributeError
イーサンファーマン2016

24

super基本クラス名を明示的に参照する必要のない別の使用法があります。

基本クラス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では、親クラスAのプロパティゲッターにアクセスします。

他の人がすでに答えているように、それは:

super(B, self).prop

またはPython3:

super().prop

これは、ゲッター自体ではなく、プロパティのゲッターによって返される値を返しますが、ゲッターを拡張するのに十分です。

Bでは、親クラスAのプロパティセッターにアクセスします。

私がこれまでに見た中で最も良い推奨事項は次のとおりです。

A.prop.fset(self, value)

私はこれがより良いと信じています:

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

この例では、両方のオプションは同等ですが、superを使用すると、の基本クラスから独立しているという利点がありBます。プロパティも拡張BするCクラスから継承する場合は、Bのコードを更新する必要はありません。

Aのプロパティを拡張するBの完全なコード:

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)

1つの警告:

プロパティにセッターがない場合を除き、どちらBか一方の動作のみを変更した場合でも、セッターとゲッターの両方を定義する必要があります。


こんにちは、これはとても役に立ちます。これに対するフォローアップの質問があります。この設定はうまく機能します。ただし、Bにinitを設定して追加のプロパティを定義すると、機能しなくなります。Bに個別の初期化を行う方法はありますか?ありがとう
2017年

super(B, self.__class__)正確にどのように機能するか説明していただけますsuper(class, class)か?どこに文書化されていますか?
アート

私の答えでは
Eric

4

試してみてください

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

Pythonが基本クラスプロパティの呼び出しをサポートしているかどうかはわかりませんが。プロパティは実際には呼び出し可能なオブジェクトであり、指定された関数で設定され、クラス内のその名前を置き換えます。これは、利用可能なスーパー機能がないことを簡単に意味する可能性があります。

ただし、property()関数を使用するように構文をいつでも切り替えることができます。

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

基本クラスを作成する場合、これは正常に機能します。しかし、プロパティとゲッターに同じ名前を使用するサードパーティの基本クラスを拡張するとどうなるでしょうか。
akaihola 2013年

1

Maximeの答えに対するいくつかの小さな改善:

  • __class__書き込みを避けるために使用しBます。ノートself.__class__の実行時の型であるselfが、__class__ なし self囲むクラス定義の名前です。super()の省略形ですsuper(__class__, self)
  • __set__代わりに使用しますfset。後者はpropertysに固有ですが、前者はすべてのプロパティのようなオブジェクト(記述子)に適用されます。
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)

0

次のテンプレートを使用できます。

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)

注意!すべてのプロパティメソッドを一緒に再定義する必要があります。すべてのメソッドを再定義したくない場合は、代わりに次のテンプレートを使用してください。

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()

(それは私があなたの説明から何かを逃していない限りです)


1
あなたは:彼は単純な方法ではなく、プロパティについて話している
akaihola 2013年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.