Свойства и наследование 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())
Итак, является ли это правильным решением для использования свойств и перезаписи их в подклассе, или есть другие предпочтительные способы сделать это?
Решение
Я просто предпочитаю повторять 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
Другие советы
Я не согласен с тем, что выбранный ответ является идеальным способом переопределить методы свойств.Если вы ожидаете, что геттеры и сеттеры будут переопределены, вы можете использовать лямбда-выражение для предоставления доступа к себе, например 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"
Я обнаружил это на компьютерщик, играющий.
Еще один способ сделать это, не создавая никаких дополнительных классов. Я добавил метод 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)
Это довольно надуманный пример, но вы должны понять.
Да, это способ сделать это; объявление свойства выполняется в то время, когда выполняется определение родительского класса, что означает, что оно может только "см." версии методов, которые существуют в родительском классе. Поэтому, когда вы переопределяете один или несколько из этих методов в дочернем классе, вам необходимо повторно объявить свойство, используя версию метода (ов) дочернего класса.
Я согласен с вашим решением, которое кажется методом на лету. В этой статье рассматривается ваша проблема и предоставляется именно ваше решение. р>
Примерно так будет работать
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 для родителя. Морщинистый всегда должен быть правильным. Это немного джаватонично, хотя.
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)