在一个实例中选择属性的最简单,最简洁的方法是readonly?

时间:2008-09-24 02:15:21

标签: python attributes readonly

在Python中,我想让类的选定的实例属性只读取到类之外的代码。我希望外部代码无法改变属性,除非间接地通过调用实例上的方法。我希望语法简洁。什么是最好的方法? (我现在给出我目前最好的答案......)

6 个答案:

答案 0 :(得分:7)

您应该使用@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

答案 1 :(得分: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

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

答案 2 :(得分:2)

以下是:

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的一种痴迷。您的用户应该使用公共界面,因为它设计得很好,而不是因为你强迫它们。

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

答案 3 :(得分:1)

没有真正的方法可以做到这一点。有一些方法可以使它更“困难”,但是没有完全隐藏,无法访问的类属性的概念。

如果无法信任使用您班级的人员遵循API文档,那么这就是他们自己的问题。保护人们不做愚蠢的事情只意味着他们会做更复杂,复杂和破坏性的愚蠢的事情来尝试做他们本来不应该做的事情。

答案 4 :(得分:0)

您可以使用一个元类,它将遵循命名约定的方法(或类属性)自动包装到属性中(无耻地从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

答案 5 :(得分:0)

我知道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")

不幸的是,此解决方案无法处理私有变量(__命名变量)。