Pythonプロパティと継承
-
04-07-2019 - |
質問
サブクラスで上書きする(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())
それで、これはプロパティを使用してサブクラスでそれらを上書きするための適切なソリューションですか、またはこれを行う他の好ましい方法がありますか?
解決
クラスメソッドをオーバーライドするときに @classmethod
デコレータを繰り返すのと同様に、単に property()
を繰り返すことを好みます。
これは非常に冗長に思えますが、少なくとも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
他のヒント
選択した回答が、プロパティメソッドのオーバーライドを可能にする理想的な方法であることに同意しません。ゲッターとセッターがオーバーライドされると予想される場合は、lambdaを使用してselfへのアクセスを提供し、 lambda self:self。< property func>
のようなものを使用できます。
これは(少なくとも)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"
追加のクラスを作成せずに行う別の方法。 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)
これはかなり不自然な例ですが、アイデアを得る必要があります。
はい、これがその方法です。プロパティ宣言は、親クラスの定義が実行されるときに実行されます。つまり、「見る」ことしかできないということです。親クラスに存在するメソッドのバージョン。そのため、子クラスでこれらのメソッドの1つ以上を再定義するときは、子クラスのバージョンのメソッドを使用してプロパティを再宣言する必要があります。
私はあなたの解決策に同意します。これはオンザフライのテンプレート方法のようです。 この記事はあなたの問題を扱っており、まさにあなたの解決策を提供します。
このようなものは動作します
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
@ 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'
子クラスから親クラスにプロパティを設定する際に問題が発生しました。次の回避策は、親のプロパティを拡張しますが、親の_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)