python read-only class properties
Is there a way to make r开发者_StackOverflow中文版ead-only class properties in Python? Ex. in Unity3d you can do this:
transform.position = Vector3.zero
Vector3.zero returns an instance of the Vector3 class where x, y, and z are 0. This is basically the same as:
transform.position = Vector3(0, 0, 0)
I've tried doing something like this:
class Vector3(object):
zero = Vector3(0, 0, 0)
...
But I get an undefined variable error because the class hasn't been defined yet. So how do you make read-only class properties that don't require an instance of the class?
The most obvious way might be to alter the class object after the fact:
class Vector3(object):
# ...
Vector3.zero = Vector3(0, 0, 0)
The main problem with this is that there's then only one zero object, and if it's mutable you can cause accidental damage all over the place. It may be easier (and feel less hacky) to use a dynamic descriptor that creates a zero vector every time it's accessed (this is done by creating a 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)
I consider none of these really "pythonic", though. Consider the other mathematical types in Python: ints, floats, and complex numbers. None of these have a "zero" class attribute, or a zero constructor, instead they return zero when called with no arguments. So perhaps it might be best to do like so:
class Vector3(object):
def __init__(self, x=0, y=0, z=0):
self.x = x
self.y = y
self.z = z
This is less like Unity3D and more like Python, if you know what I mean.
Use a metaclass
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)
Use a 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__)
Should do what you want:
>>> 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}
What you're imagining is possible, but not necessary in this case. Just wait until after your class is defined to assign the attribute
class Vector3(object):
...
Vector3.zero = Vector3(0, 0, 0)
or make it a module level constant.
There is a good chance you want simply to use a shape (3,)
numpy array instead of writing this class, for any practical purposes.
That's a really interesting question, the workaround I would go with is to do a classmethod as a "getter" for the zero object:
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()
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
Shamelessly stolen from here
As for the read-only part, this is a good resource.
template = property(lambda self: self.__template)
精彩评论