Свойства и наследование 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())
Итак, это правильное решение для использования свойств и перезаписывания их в подклассе, или есть другие предпочтительные способы сделать это?
Ответы
Ответ 1
Я просто предпочитаю повторять 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
Ответ 2
Я не согласен с тем, что выбранный ответ является идеальным способом, позволяющим переопределить методы свойства. Если вы ожидаете, что геттеры и сеттеры будут переопределены, то вы можете использовать лямбду, чтобы обеспечить доступ к себе с чем-то вроде lambda self: self.<property func>
.
Это работает (по крайней мере) для Python версий от 2.4 до 3.6.
Если кто-нибудь знает способ сделать это, используя свойство в качестве декоратора, а не прямой вызов свойства(), я бы хотел услышать это!
Пример:
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"
Я обнаружил это на вундеркинде в игре.
Ответ 3
Другой способ сделать это, не создавая дополнительных классов. Я добавил метод 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)
Это довольно надуманный пример, но вы должны получить эту идею.
Ответ 4
Да, это способ сделать это; объявление свойства выполняется во время выполнения определения родительского класса, что означает, что он может только "видеть" версии методов, существующих в родительском классе. Поэтому, когда вы переопределяете один или несколько из этих методов в дочернем классе, вам нужно повторно объявить свойство, используя версию (-и) метода дочернего класса.
Ответ 5
Я согласен с вашим решением, которое кажется методом "на лету".
Эта статья посвящена вашей проблеме и обеспечивает именно ваше решение.
Ответ 6
Что-то вроде этого будет работать
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
Ответ 7
То же, что @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'
Ответ 8
У меня возникли проблемы с установкой свойства в родительском классе из дочернего класса. Следующий workround расширяет свойство родителя, но делает это, напрямую вызывая метод _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)
Ответ 9
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
То же, что и Низам Мохамед, только упомянуть, что руководство по стилю 2.13.4 использует метод и свойство шаблона