web-dev-qa-db-ja.com

pythonプロパティと継承

サブクラスで上書きするプロパティ(getメソッド)を持つ基本クラスがあります。私の最初の考えは次のようなものでした:

class Foo(object):
    def _get_age(self):
        return 11

    age = property(_get_age)


class Bar(Foo):
    def _get_age(self):
        return 44

これは機能しません(サブクラスbar.ageは11を返します)。動作するラムダ式を使用したソリューションを見つけました:

age = property(lambda self: self._get_age())

だから、これはプロパティを使用してサブクラスでそれらを上書きするための正しいソリューションですか、またはこれを行う他の好ましい方法はありますか?

57
Peter Hoffmann

property()を繰り返すだけでなく、@classmethodクラスメソッドをオーバーライドするときのデコレータ。

これは非常に冗長に見えますが、少なくともPython標準については、次のことに気付くでしょう:

1)読み取り専用プロパティの場合、propertyをデコレーターとして使用できます。

class Foo(object):
    @property
    def age(self):
        return 11

class Bar(Foo):
    @property
    def age(self):
        return 44

2)Python 2.6、 プロパティはメソッドのペアを増やしましたsetterおよびdeleterは一般プロパティに適用するために使用できます読み取り専用のショートカットは既に利用可能です:

class C(object):
    @property
    def x(self):
        return self._x

    @x.setter
    def x(self, value):
        self._x = value
65
piro

選択した回答が、プロパティメソッドのオーバーライドを可能にする理想的な方法であることに同意しません。ゲッターとセッターがオーバーライドされることが予想される場合は、lambda self: self.<property func>などのように、lambdaを使用してselfへのアクセスを提供できます。

これは(少なくとも)Pythonバージョン2.4から3.6で機能します。

直接property()呼び出しとしてではなく、デコレータとしてpropertyを使用してこれを行う方法を知っている人がいれば、それを聞きたいです!

例:

class Foo(object):
    def _get_meow(self):
        return self._meow + ' from a Foo'
    def _set_meow(self, value):
        self._meow = value
    meow = property(fget=lambda self: self._get_meow(),
                    fset=lambda self, value: self._set_meow(value))

これにより、オーバーライドを簡単に実行できます。

class Bar(Foo):
    def _get_meow(self):
        return super(Bar, self)._get_meow() + ', altered by a Bar'

そのため:

>>> foo = Foo()
>>> bar = Bar()
>>> foo.meow, bar.meow = "meow", "meow"
>>> foo.meow
"meow from a Foo"
>>> bar.meow
"meow from a Foo, altered by a Bar"

これは geek at play で発見しました。

16
Mr. B

追加のクラスを作成せずに行う別の方法。 2つのうち1つだけをオーバーライドする場合に何をするかを示すために、setメソッドを追加しました。

class Foo(object):
    def _get_age(self):
        return 11

    def _set_age(self, age):
        self._age = age

    age = property(_get_age, _set_age)


class Bar(Foo):
    def _get_age(self):
        return 44

    age = property(_get_age, Foo._set_age)

これは非常に不自然な例ですが、アイデアを得る必要があります。

11
Kamil Kisiel

はい、これはそれを行う方法です。プロパティ宣言は、親クラスの定義が実行されるときに実行されます。つまり、親クラスに存在するメソッドのバージョンのみを「見る」ことができます。したがって、子クラスでこれらのメソッドの1つ以上を再定義する場合、子クラスのバージョンのメソッドを使用してプロパティを再宣言する必要があります。

7
James Bennett

私はあなたの解決策に同意します。これはオンザフライのテンプレート方法のようです。 この記事 あなたの問題に対処し、まさにあなたの解決策を提供します。

2

@ mr-b と同じですが、デコレータを使用します。

class Foo(object):
    def _get_meow(self):
        return self._meow + ' from a Foo'
    def _set_meow(self, value):
        self._meow = value
    @property
    def meow(self):
        return self._get_meow()
    @meow.setter
    def meow(self, value):
        self._set_meow(value)

これにより、オーバーライドを簡単に実行できます。

class Bar(Foo):
    def _get_meow(self):
        return super(Bar, self)._get_meow() + ', altered by a Bar'
2
Nizam Mohamed

このような何かが動作します

class HackedProperty(object):
    def __init__(self, f):
        self.f = f
    def __get__(self, inst, owner):    
        return getattr(inst, self.f.__name__)()

class Foo(object):
    def _get_age(self):
        return 11
    age = HackedProperty(_get_age)

class Bar(Foo):
    def _get_age(self):
        return 44

print Bar().age
print Foo().age
2
Kozyarchuk

子クラスから親クラスにプロパティを設定する際に問題が発生しました。次の回避策は、親のプロパティを拡張しますが、親の_set_ageメソッドを直接呼び出して拡張します。しわは常に正しいはずです。それは少しjavathonicです。

import threading


class Foo(object):
    def __init__(self):
        self._age = 0

    def _get_age(self):
        return self._age

    def _set_age(self, age):
        self._age = age

    age = property(_get_age, _set_age)


class ThreadsafeFoo(Foo):

    def __init__(self):
        super(ThreadsafeFoo, self).__init__()
        self.__lock = threading.Lock()
        self.wrinkled = False

    def _get_age(self):
        with self.__lock:
             return super(ThreadsafeFoo, self).age

    def _set_age(self, value):
        with self.__lock:
            self.wrinkled = True if value > 40 else False
            super(ThreadsafeFoo, self)._set_age(value)

    age = property(_get_age, _set_age)
0
andrew pate
class Foo:
    # Template method
    @property
    def age(self):
        return self.dothis()
    # Hook method of TM is accessor method of property at here
    def dothis(self):
        return 11
class Bar(Foo):
    def dothis(self):
        return 44

Nizam Mohamedと同じですが、テンプレートメソッドとプロパティの両方を使用して スタイルガイド 2.13.4

0
Robin