Python propiedades y herencia
-
04-07-2019 - |
Pregunta
Tengo una clase base con una propiedad que (el método get) quiero sobrescribir en la subclase. Mi primer pensamiento fue algo como:
class Foo(object):
def _get_age(self):
return 11
age = property(_get_age)
class Bar(Foo):
def _get_age(self):
return 44
Esto no funciona (la subclase bar.age devuelve 11). Encontré una solución con una expresión lambda que funciona:
age = property(lambda self: self._get_age())
Entonces, ¿es esta la solución correcta para usar propiedades y sobrescribirlas en una subclase, o hay otras formas preferidas de hacerlo?
Solución
Simplemente prefiero repetir la propiedad ()
, así como usted repetirá el decorador @classmethod
al anular un método de clase.
Si bien esto parece muy detallado, al menos para los estándares de Python, puede notar:
1) para propiedades de solo lectura, propiedad
se puede utilizar como decorador:
class Foo(object):
@property
def age(self):
return 11
class Bar(Foo):
@property
def age(self):
return 44
2) en Python 2.6, las propiedades crecieron un par de métodos deleter
que se pueden usar para aplicar a las propiedades generales el acceso directo ya disponible para las de solo lectura:
class C(object):
@property
def x(self):
return self._x
@x.setter
def x(self, value):
self._x = value
Otros consejos
No estoy de acuerdo con que la respuesta elegida sea la forma ideal de permitir anular los métodos de propiedad. Si espera que se anulen a los captadores y establecedores, entonces puede usar lambda para proporcionar acceso a sí mismo, con algo como lambda self: self. & Lt; property func >
.
Esto funciona (al menos) para las versiones 2.4 a 3.6 de Python.
Si alguien sabe cómo hacerlo con el uso de la propiedad como decorador en lugar de como una llamada directa a la propiedad (), ¡me gustaría escucharlo!
Ejemplo:
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))
De esta manera, se puede realizar una anulación fácilmente:
class Bar(Foo):
def _get_meow(self):
return super(Bar, self)._get_meow() + ', altered by a Bar'
para que:
>>> 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"
Descubrí esto en geek en juego .
Otra forma de hacerlo, sin tener que crear ninguna clase adicional. He agregado un método de configuración para mostrar lo que haces si solo anulas uno de los dos:
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)
Este es un ejemplo bastante artificial, pero deberías tener una idea.
Sí, esta es la manera de hacerlo; la declaración de propiedad se ejecuta en el momento en que se ejecuta la definición de la clase padre, lo que significa que solo puede " ver " Las versiones de los métodos que existen en la clase padre. Por lo tanto, cuando redefine uno o más de esos métodos en una clase secundaria, deberá volver a declarar la propiedad utilizando la versión de la (s) método (s) de la clase secundaria.
Estoy de acuerdo con su solución, que parece ser un método de plantilla sobre la marcha. Este artículo se ocupa de su problema y proporciona exactamente su solución.
Algo como esto funcionará
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
Igual que los @ mr-b pero con decorador.
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)
De esta manera, se puede realizar una anulación fácilmente:
class Bar(Foo):
def _get_meow(self):
return super(Bar, self)._get_meow() + ', altered by a Bar'
Me encontré con problemas al establecer una propiedad en una clase padre desde una clase hija. La siguiente ronda de trabajo amplía una propiedad de un padre pero lo hace llamando directamente al método _set_age del padre. Arrugado siempre debe ser correcto. Aunque es un poco 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)