Skip to content Skip to sidebar Skip to footer

Python Read-only Class Properties

Is there a way to make read-only class properties in Python? Ex. in Unity3d you can do this: transform.position = Vector3.zero Vector3.zero returns an instance of the Vector3 clas

Solution 1:

The most obvious way might be to alter the class object after the fact:

classVector3(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):

classClassProperty(property):
    def__get__(self, cls, owner):
        return self.fget.__get__(None, owner)()

classVector3(object):
    @ClassProperty    @classmethoddefzero(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:

classVector3(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.

Solution 2:

Use a metaclass

classMetaVector3(type):

    @propertydefzero(cls):
        return cls(0,0,0)

classVector3(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)

Solution 3:

Use a descriptor:

classZero(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  

classVector3(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}

Solution 4:

What you're imagining is possible, but not necessary in this case. Just wait until after your class is defined to assign the attribute

classVector3(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.

Solution 5:

That's a really interesting question, the workaround I would go with is to do a classmethod as a "getter" for the zero object:

classVector3(object):
    __zero = Nonedef__init__(self, x,y,z):
        self.x = x
        self.y = y
        self.z = z

    @classmethoddefzero(cls):
        ifnot cls.__zero:
            cls.__zero = Vector3(0,0,0) 
        return cls.__zero

myzerovec = Vector3.zero()

Post a Comment for "Python Read-only Class Properties"