【问题标题】:python read-only class propertiespython 只读类属性
【发布时间】:2011-11-08 13:16:42
【问题描述】:

有没有办法在 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)
    ...

但我得到一个未定义的变量错误,因为该类尚未定义。那么如何创建不需要类实例的只读类属性呢?

【问题讨论】:

    标签: python properties


    【解决方案1】:

    您的想象是可能的,但在这种情况下不是必需的。等到你的类被定义之后再分配属性

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

    或使其成为模块级常量。


    出于任何实际目的,您很有可能只想使用形状 (3,) numpy 数组而不是编写此类。

    【讨论】:

      【解决方案2】:

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

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

      这样做的主要问题是只有一个零对象,如果它是可变的,您可能会在整个地方造成意外损坏。使用每次访问时创建零向量的动态描述符可能更容易(并且感觉不那么hacky)(这是通过创建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)
      

      不过,我认为这些都不是真正的“pythonic”。考虑 Python 中的其他数学类型:整数、浮点数和复数。这些都没有“零”类属性或零构造函数,而是在不带参数调用时返回零。所以也许最好这样做:

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

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

      【讨论】:

      • 第一个答案是显而易见的,但它不是只读的。第二个需要类的实例才能使用该属性。第三个并没有真正做到我想要它做的事情。我更多的是在寻找 Unity 的工作方式。
      • @topher 已修复,但以与环境无关的方式编写代码很奇怪。 Python 类通常不会以这种方式工作。
      • @topher,在 Python 中,我们不会习惯性地在程序级别强制执行只读操作。如果您没有给某人任何重新分配此值的理由并且他们没有恶意,他们就不会这样做。如果他们打算做坏事,您无法通过特殊描述符/元类的事情来帮助他们。
      • “考虑 Python 中的其他数学类型:整数、浮点数和复数。这些类型都没有“零”类属性或零构造函数,而是在调用时返回零没有争论。” 这是一个绝妙的见解。
      • @topher,您对只读的坚持是错误的。这个是名义上是只读的,另外我可以很容易地用元类解决方案来改变它。 在 Python 中,尝试在语言级别强制执行“只读”之类的操作是浪费精力。
      【解决方案3】:

      至于只读部分,this 是一个很好的资源。

      template = property(lambda self: self.__template)
      

      【讨论】:

        【解决方案4】:

        使用元类

        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)
        

        【讨论】:

        • 元类不能被多重继承或组合,所以如果你想要另一个元类或另一个超类,你就是 SOL。我通常会避开它们。
        • 元类可能非常有用,但它肯定不是适合这项工作的工具。
        • @Devin - 这是一个自我挫败的论点。如果你从不使用元类,你就不会从元类中获得任何价值。即使你只能使用一个元类,你也会获得一些价值——比仅仅因为在某些情况下不能使用多个元类而根本不使用它们要多。 eryksun,一如既往的美丽答案。
        • @agf 隐含在“我避免他们”是“除非他们是必要的”。情况并非如此,因为还有其他东西会产生相同的行为但不涉及元类(特别是描述符)。
        • 此方法的另一个好处是zero 属性不会出现在实例上。
        【解决方案5】:

        这是一个非常有趣的问题,我会采用的解决方法是将类方法作为零对象的“getter”:

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

        【讨论】:

        • 这几乎是我要找的,但这是一种方法。有没有办法让它成为一个类属性?
        • 据我所知,除了上面的元类答案之外,但这会导致零在您每次引用它时实际上返回一个不同的 Vector3 实例,我认为这不是您想要的(a单个静态定义的对象)。老实说,“Vector3.zero()”与“Vector3.zero”相比真的有那么大的区别吗?
        【解决方案6】:
        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
        

        无耻地盗取here

        【讨论】:

        • 仔细看看德文的回答,他已经在你面前发布了这个。
        • @agf:我看到了,但你可以注意到他编辑了他的答案(见revisions),当我开始写我的答案时,情况并非如此。无论如何,感谢您指出...
        【解决方案7】:

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

        应该做你想做的:

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

        【讨论】:

        • 我认为描述符的__get__方法中的owner参数是所属类,所以可以重写为return owner(0, 0, 0)。
        猜你喜欢
        • 1970-01-01
        • 2018-07-04
        • 2016-02-25
        • 2021-11-24
        • 2011-06-08
        • 2016-02-02
        • 2010-11-22
        • 2010-10-23
        相关资源
        最近更新 更多