¿Hay alguna manera de hacer propiedades de clase de solo lectura en Python? Ex. en Unity3d puedes hacer esto:
transform.position = Vector3.zero
Vector3.zero devuelve una instancia de la clase Vector3 donde x, y y z son 0. Esto es básicamente lo mismo que:
transform.position = Vector3(0, 0, 0)
He intentado hacer algo como esto:
class Vector3(object):
zero = Vector3(0, 0, 0)
...
Pero obtengo un error variable indefinido porque la clase aún no se ha definido. Entonces, ¿cómo se hacen las propiedades de clase de solo lectura que no requieren una instancia de la clase?
7 respuestas
Usa una metaclase
class MetaVector3(type):
@property
def zero(cls):
return cls(0,0,0)
class Vector3(object):
__metaclass__ = MetaVector3
def __init__(self, x, y, z):
self.x = x
self.y = y
self.z = z
>>> v = Vector3.zero
>>> v.x, v.y, v.z
(0, 0, 0)
Lo que estás imaginando es posible, pero no necesario en este caso. Solo espere hasta que se haya definido su clase para asignar el atributo
class Vector3(object):
...
Vector3.zero = Vector3(0, 0, 0)
O convertirlo en un nivel de módulo constante.
Hay una buena posibilidad de que desee simplemente usar una matriz numpy de forma (3,)
en lugar de escribir esta clase, para cualquier propósito práctico.
class ClassProperty(property):
def __get__(self, cls, owner):
return self.fget.__get__(None, owner)()
class Vector3(object):
_zero = None
def __init__(self, x, y, z):
self.x = x
self.y = y
self.z = z
@ClassProperty
@classmethod
def zero(cls):
if cls._zero is None:
cls._zero = cls(0,0,0)
return cls._zero
Robado descaradamente de aquí
La forma más obvia podría ser alterar el objeto de clase después del hecho:
class Vector3(object):
# ...
Vector3.zero = Vector3(0, 0, 0)
El principal problema con esto es que solo hay un objeto cero, y si es mutable puede causar daños accidentales en todo el lugar. Puede ser más fácil (y sentirse menos hacky) usar un descriptor dinámico que crea un vector cero cada vez que se accede (esto se hace creando un ClassProperty class):
class ClassProperty(property):
def __get__(self, cls, owner):
return self.fget.__get__(None, owner)()
class Vector3(object):
@ClassProperty
@classmethod
def zero(cls):
return cls(0, 0, 0)
Sin embargo, considero que ninguno de estos es realmente "pitónico". Considere los otros tipos matemáticos en Python: ints, flotantes y números complejos. Ninguno de estos tiene un atributo de clase "cero", o un constructor cero, en su lugar, devuelven cero cuando se les llama sin argumentos. Entonces quizás sea mejor hacerlo así:
class Vector3(object):
def __init__(self, x=0, y=0, z=0):
self.x = x
self.y = y
self.z = z
Esto es menos como Unity3D y más como Python, si sabes a lo que me refiero.
Utilice un descriptor:
class Zero(object):
def __get__(self, instance, owner):
return owner(0, 0, 0)
def __set__(self, instance, value):
#could raise an exception here or somethiing
#this gets called if the user attempts to overwrite the property
pass
class Vector3(object):
zero = Zero()
def __init__(self, x, y, z):
self.x = x
self.y = y
self.z = z
def __repr__(self):
return str(self.__dict__)
Debes hacer lo que quieras:
>>> v = Vector3(1, 2, 3)
>>> v
{'y': 2, 'x': 1, 'z': 3}
>>> v.zero
{'y': 0, 'x': 0, 'z': 0}
>>> v.zero = 'foo'
>>> v.zero
{'y': 0, 'x': 0, 'z': 0}
Esa es una pregunta realmente interesante, la solución que usaría es hacer un método de clase como "captador" para el objeto cero:
class Vector3(object):
__zero = None
def __init__(self, x,y,z):
self.x = x
self.y = y
self.z = z
@classmethod
def zero(cls):
if not cls.__zero:
cls.__zero = Vector3(0,0,0)
return cls.__zero
myzerovec = Vector3.zero()
En cuanto a la parte de solo lectura, este es un buen recurso.
template = property(lambda self: self.__template)
Preguntas relacionadas
Nuevas preguntas
python
Python es un lenguaje de programación multipropósito, de tipificación dinámica y de múltiples paradigmas. Está diseñado para ser rápido de aprender, comprender y usar, y hacer cumplir una sintaxis limpia y uniforme. Tenga en cuenta que Python 2 está oficialmente fuera de soporte a partir del 01-01-2020. Aún así, para preguntas de Python específicas de la versión, agregue la etiqueta [python-2.7] o [python-3.x]. Cuando utilice una variante de Python (por ejemplo, Jython, PyPy) o una biblioteca (por ejemplo, Pandas y NumPy), inclúyala en las etiquetas.