在实例中将所选属性设置为只读的最简单,最简洁的方法是什么?
在Python中,我想使类的选定实例属性对该类外部的代码只读。我希望外部代码无法更改属性,除非通过在实例上调用方法来间接更改属性。我希望语法简洁。什么是最好的方法? (我在下面给出当前的最佳答案...)
解决方案
没有真正的方法可以做到这一点。有多种方法可以使其变得更加"困难",但是没有完全隐藏的,无法访问的类属性的概念。
如果不信任使用我们课程的人来遵循API文档,那是他们自己的问题。保护人们不做愚蠢的事情只是意味着他们将做更多复杂,复杂和破坏性的愚蠢事情,以尝试做本来不应该做的事情。
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
装饰器。
>>> 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
我们可以使用元类,该类自动将遵循命名约定的方法(或者类属性)包装到属性中(从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
这是如何做:
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是我们要只读的属性。)在属性名前面加上两个下划线会有效地将其从类外部隐藏,因此内部版本将无法从外部访问。这仅适用于从对象继承的新型类,因为它依赖于"属性"。
另一方面,这是一件很愚蠢的事情。保持变量私有似乎是C ++和Java的一种困扰。用户应该对课程使用公共接口,因为它是经过精心设计的,而不是因为我们强迫他们这样做。
编辑:看起来凯文已经发布了一个类似的版本。
我知道,威廉·凯勒(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")
不幸的是,该解决方案无法处理私有变量(__命名变量)。