Python只读类属性

7 投票
7 回答
3347 浏览
提问于 2025-04-17 01:06

有没有办法在Python中创建只读的类属性?比如在Unity3d中你可以这样做:

transform.position = Vector3.zero

Vector3.zero会返回一个Vector3类的实例,其中x、y和z都是0。这基本上和下面的代码是一样的:

transform.position = Vector3(0, 0, 0)

我尝试过这样做:

class Vector3(object):
    zero = Vector3(0, 0, 0)
    ...

但是我遇到了一个未定义变量的错误,因为这个类还没有被定义。那么,如何创建不需要类实例的只读类属性呢?

7 个回答

6

使用一个描述符

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__)

应该能满足你的需求:

>>> 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}
10

最明显的方法可能是在事后修改类对象:

class Vector3(object):
    # ...
Vector3.zero = Vector3(0, 0, 0)

这样做的主要问题是,只有一个零对象,如果这个对象是可变的,就可能在各处造成意外的损坏。使用一个动态描述符,每次访问时都创建一个零向量,可能会更简单(而且感觉不那么像是临时解决方案),这可以通过创建一个ClassProperty类来实现:

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)

不过,我认为这些都不算真正的“Python风格”。想想Python里的其他数学类型:整数、浮点数和复数。这些类型都没有“零”类属性,也没有零构造函数,而是当没有参数时调用它们会返回零。所以,也许最好是这样做:

class Vector3(object):
    def __init__(self, x=0, y=0, z=0):
        self.x = x
        self.y = y
        self.z = z 

这更像是Python,而不是Unity3D,如果你明白我的意思。

8

使用元类

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)

撰写回答