【发布时间】:2008-09-24 02:15:21
【问题描述】:
在 Python 中,我想让一个类的 selected 实例属性对类外的代码是只读的。我希望外部代码无法更改属性,除非间接通过调用实例上的方法。我希望语法简洁。什么是最好的方法? (我在下面给出我目前的最佳答案......)
【问题讨论】:
标签: python attributes readonly
在 Python 中,我想让一个类的 selected 实例属性对类外的代码是只读的。我希望外部代码无法更改属性,除非间接通过调用实例上的方法。我希望语法简洁。什么是最好的方法? (我在下面给出我目前的最佳答案......)
【问题讨论】:
标签: python attributes readonly
您应该使用 @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
【讨论】:
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
即,在属性上使用装饰器。会很干净……
【讨论】:
方法如下:
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)
(假设 foobar 和 blahblah 是您希望只读的属性。)在属性名称前添加 两个 下划线可以有效地将其隐藏在类外部,因此内部版本将无法从外部访问。这仅适用于从对象继承的新型类,因为它依赖于property。
另一方面...这是一件非常愚蠢的事情。保持变量私有似乎是来自 C++ 和 Java 的一种痴迷。您的用户应该使用您的类的公共接口,因为它设计得很好,而不是因为您强迫他们这样做。
编辑:看起来 Kevin 已经发布了类似的版本。
【讨论】:
没有真正的方法可以做到这一点。有办法让它变得更“难”,但没有完全隐藏、无法访问的类属性的概念。
如果不能信任使用您的课程的人遵循 API 文档,那么这是他们自己的问题。保护人们不做愚蠢的事情只是意味着他们会做更精细、更复杂和更具破坏性的愚蠢事情来尝试做他们一开始就不应该做的事情。
【讨论】:
您可以使用元类将遵循命名约定的方法(或类属性)自动包装到属性中(无耻地取自 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
【讨论】:
我知道 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")
很遗憾,此解决方案无法处理私有变量(__ 命名变量)。
【讨论】: