【问题标题】:What is the easiest, most concise way to make selected attributes in an instance be readonly?使实例中的选定属性成为只读的最简单、最简洁的方法是什么?
【发布时间】:2008-09-24 02:15:21
【问题描述】:

在 Python 中,我想让一个类的 selected 实例属性对类外的代码是只读的。我希望外部代码无法更改属性,除非间接通过调用实例上的方法。我希望语法简洁。什么是最好的方法? (我在下面给出我目前的最佳答案......)

【问题讨论】:

    标签: python attributes readonly


    【解决方案1】:

    您应该使用 @property 装饰器。

    >>> class a(object):
    ...     def __init__(self, x):
    ...             self.x = x
    ...     @property
    ...     def xval(self):
    ...             return self.x
    ... 
    >>> b = a(5)
    >>> b.xval
    5
    >>> b.xval = 6
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    AttributeError: can't set attribute
    

    【讨论】:

    • 这不是和我做的一样吗,“readonly = property(lambda self: self.__readonly)”?
    【解决方案2】:
    class C(object):
    
        def __init__(self):
    
            self.fullaccess = 0
            self.__readonly = 22 # almost invisible to outside code...
    
        # define a publicly visible, read-only version of '__readonly':
        readonly = property(lambda self: self.__readonly)
    
        def inc_readonly( self ):
            self.__readonly += 1
    
    c=C()
    
    # prove regular attribute is RW...
    print "c.fullaccess = %s" % c.fullaccess
    c.fullaccess = 1234
    print "c.fullaccess = %s" % c.fullaccess
    
    # prove 'readonly' is a read-only attribute
    print "c.readonly = %s" % c.readonly
    try:
        c.readonly = 3
    except AttributeError:
        print "Can't change c.readonly"
    print "c.readonly = %s" % c.readonly
    
    # change 'readonly' indirectly...
    c.inc_readonly()
    print "c.readonly = %s" % c.readonly
    

    这个输出:

    $ python ./p.py
    c.fullaccess = 0
    c.fullaccess = 1234
    c.readonly = 22
    无法更改 c.readonly
    c.readonly = 22
    c.readonly = 23

    我的手指痒得不能说话了

        @readonly
        self.readonly = 22
    

    即,在属性上使用装饰器。会很干净……

    【讨论】:

    • 你可以! @property 可以,但是你必须使用 def readonly(self): return readonly 它仍然可以避免 lambda 噪声。
    • 这不是只读属性,因为其他代码仍然可以执行“c._C__readonly = 4”
    • 是的,我知道那是用于“__”前缀的“修饰”。这真的被外部代码欺骗了,如果他们那么绝望,我屈服了! :)
    【解决方案3】:

    方法如下:

    class whatever(object):
      def __init__(self, a, b, c, ...):
        self.__foobar = 1
        self.__blahblah = 2
    
      foobar = property(lambda self: self.__foobar)
      blahblah = property(lambda self: self.__blahblah)
    

    (假设 foobarblahblah 是您希望只读的属性。)在属性名称前添加 两个 下划线可以有效地将其隐藏在类外部,因此内部版本将无法从外部访问。这仅适用于从对象继承的新型类,因为它依赖于property

    另一方面...这是一件非常愚蠢的事情。保持变量私有似乎是来自 C++ 和 Java 的一种痴迷。您的用户应该使用您的类的公共接口,因为它设计得很好,而不是因为您强迫他们这样做。

    编辑:看起来 Kevin 已经发布了类似的版本。

    【讨论】:

      【解决方案4】:

      没有真正的方法可以做到这一点。有办法让它变得更“难”,但没有完全隐藏、无法访问的类属性的概念。

      如果不能信任使用您的课程的人遵循 API 文档,那么这是他们自己的问题。保护人们不做愚蠢的事情只是意味着他们会做更精细、更复杂和更具破坏性的愚蠢事情来尝试做他们一开始就不应该做的事情。

      【讨论】:

      • 好吧,有一种方法,只是不像我们想要的那样干净简洁...... -k
      • 你可以设置属性。到 property(),然后什么都没有,甚至实例本身都无法访问它!
      • (虽然我还想指出你的第二点是有效的!)
      • 特殊扩展模块可能使更改选定属性变得更加“困难”
      【解决方案5】:

      您可以使用元类将遵循命名约定的方法(或类属性)自动包装到属性中(无耻地取自 Unifying Types and Classes in Python 2.2

      class autoprop(type):
          def __init__(cls, name, bases, dict):
              super(autoprop, cls).__init__(name, bases, dict)
              props = {}
              for name in dict.keys():
                  if name.startswith("_get_") or name.startswith("_set_"):
                      props[name[5:]] = 1
              for name in props.keys():
                  fget = getattr(cls, "_get_%s" % name, None)
                  fset = getattr(cls, "_set_%s" % name, None)
                  setattr(cls, name, property(fget, fset))
      

      这允许您使用:

      class A:
          __metaclass__ = autosuprop
          def _readonly(self):
              return __x
      

      【讨论】:

        【解决方案6】:

        我知道 William Keller 是迄今为止最干净的解决方案.. 但这是我想出的东西..

        class readonly(object):
            def __init__(self, attribute_name):
                self.attribute_name = attribute_name
        
            def __get__(self, instance, instance_type):
                if instance != None:
                    return getattr(instance, self.attribute_name)
                else:
                    raise AttributeError("class %s has no attribute %s" % 
                                         (instance_type.__name__, self.attribute_name))
        
            def __set__(self, instance, value):
                raise AttributeError("attribute %s is readonly" % 
                                      self.attribute_name)
        

        这是使用示例

        class a(object):
            def __init__(self, x):
                self.x = x
            xval = readonly("x")
        

        很遗憾,此解决方案无法处理私有变量(__ 命名变量)。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2017-12-30
          • 2023-03-30
          • 2017-05-21
          • 1970-01-01
          • 2011-03-22
          • 2023-02-10
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多