何时应该将属性设为私有并设置为只读?
我不太清楚什么时候属性应该是私有的,以及我是否应该使用 property
。
我最近看到有人说,使用设置器和获取器在 Python 中并不算优雅,但使用 property
装饰器是可以的。
但是如果我有一个属性,它不应该从类外部被设置,但可以被读取(只读属性)。这个属性应该是私有的吗?我说的私有是指用下划线开头,比如 self._x
?
如果是的话,那我该怎么读取它而不使用获取器呢?我现在知道的唯一方法是这样写:
@property
def x(self):
return self._x
这样我可以通过 obj.x
来读取这个属性,但我不能用 obj.x = 1
来设置它,所以这样是可以的。
但是我真的需要在意一个不应该被设置的对象吗?也许我应该就这样放着不管。但另一方面,我不能用下划线,因为读取 obj._x
对用户来说很奇怪,所以我应该用 obj.x
,但这样用户又不知道他不应该设置这个属性。
你有什么看法和做法呢?
10 个回答
这里有一种方法可以避免假设
所有用户都是成年人,因此他们应该自己负责正确使用这些东西。
请查看我下面的更新
使用 @property
的时候,代码会显得很啰嗦,例如:
class AClassWithManyAttributes:
'''refactored to properties'''
def __init__(a, b, c, d, e ...)
self._a = a
self._b = b
self._c = c
self.d = d
self.e = e
@property
def a(self):
return self._a
@property
def b(self):
return self._b
@property
def c(self):
return self._c
# you get this ... it's long
使用
没有下划线:这是一个公共变量。
一个下划线:这是一个受保护的变量。
两个下划线:这是一个私有变量。
除了最后一种,其他的都是一种约定。如果你真的很努力,还是可以访问带有双下划线的变量。
那么我们该怎么办呢?难道就放弃在Python中使用只读属性吗?
看这里! read_only_properties
装饰器来帮忙了!
@read_only_properties('readonly', 'forbidden')
class MyClass(object):
def __init__(self, a, b, c):
self.readonly = a
self.forbidden = b
self.ok = c
m = MyClass(1, 2, 3)
m.ok = 4
# we can re-assign a value to m.ok
# read only access to m.readonly is OK
print(m.ok, m.readonly)
print("This worked...")
# this will explode, and raise AttributeError
m.forbidden = 4
你可能会问:
read_only_properties
是从哪里来的?
很高兴你问了,这里是 read_only_properties 的源代码:
def read_only_properties(*attrs):
def class_rebuilder(cls):
"The class decorator"
class NewClass(cls):
"This is the overwritten class"
def __setattr__(self, name, value):
if name not in attrs:
pass
elif name not in self.__dict__:
pass
else:
raise AttributeError("Can't modify {}".format(name))
super().__setattr__(name, value)
return NewClass
return class_rebuilder
更新
我从没想到这个回答会引起这么多关注。出乎意料的是,它确实引起了关注。这让我有了动力去创建一个你可以使用的包。
$ pip install read-only-properties
在你的Python环境中:
In [1]: from rop import read_only_properties
In [2]: @read_only_properties('a')
...: class Foo:
...: def __init__(self, a, b):
...: self.a = a
...: self.b = b
...:
In [3]: f=Foo('explodes', 'ok-to-overwrite')
In [4]: f.b = 5
In [5]: f.a = 'boom'
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-5-a5226072b3b4> in <module>()
----> 1 f.a = 'boom'
/home/oznt/.virtualenvs/tracker/lib/python3.5/site-packages/rop.py in __setattr__(self, name, value)
116 pass
117 else:
--> 118 raise AttributeError("Can't touch {}".format(name))
119
120 super().__setattr__(name, value)
AttributeError: Can't touch a
我想说几句,Silas Ray说得很对,不过我觉得可以加个例子。;-)
Python是一种类型不安全的语言,所以你总是得相信使用你代码的人会像个正常人一样使用它。
根据PEP 8的规定:
只有在非公开的方法和实例变量前加一个下划线。
如果你想在一个类里创建一个“只读”的属性,可以使用@property
这个装饰器。使用这个装饰器时,你需要从object
继承,以便使用新式类。
例子:
>>> class A(object):
... def __init__(self, a):
... self._a = a
...
... @property
... def a(self):
... return self._a
...
>>> a = A('test')
>>> a.a
'test'
>>> a.a = 'pleh'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: can't set attribute
一般来说,写Python程序的时候,我们应该假设所有用户都是成年人,能够自己负责正确使用这些程序。不过,有一些特殊情况,比如某些属性的值是计算出来的,或者是从某个固定的数据源读取的,这种情况下就不应该让用户随便修改这些属性。对于这种情况,通常我们会使用只读属性,也就是只提供获取值的功能,不让用户去设置它。