Raf*_*ski 74 python properties private readonly python-2.7
我不知道什么时候属性应该是私有的,如果我应该使用属性.
我最近读到,setter和getter不是pythonic,我应该使用属性装饰器.没关系.
但是,如果我有属性,那么不能从类外部设置但可以读取(只读属性).这个属性应该是私有的,私有的我是指下划线,就像那样self._x?如果是,那么如何在不使用getter的情况下阅读它?我现在唯一知道的方法是写
@property
def x(self):
return self._x
Run Code Online (Sandbox Code Playgroud)
这样我可以读取属性obj.x但我无法设置它obj.x = 1所以它没关系.
但是我真的应该关心设置不能设置的对象吗?也许我应该离开它.但是我再也不能使用下划线,因为obj._x用户读取很奇怪,所以我应该使用obj.x然后再次用户不知道他不能设置这个属性.
你有什么看法和实践?
sie*_*z0r 56
只是我的两分钱,西拉斯雷在正确的轨道上,但我想添加一个例子.;-)
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
Run Code Online (Sandbox Code Playgroud)
Sil*_*Ray 55
通常,Python程序应该假设所有用户都同意成人,因此负责自己正确使用事物.但是,在极少数情况下,对于可设置的属性(例如派生值或从某些静态数据源读取的值)没有意义,getter-only属性通常是首选模式.
Oz1*_*123 45
这是一种避免假设的方法
所有用户都同意成年人,因此负责自己正确使用事物.
使用@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
Run Code Online (Sandbox Code Playgroud)
运用
没有下划线:它是一个公共变量.
一个下划线:它是一个受保护的变量.
两个下划线:它是一个私有变量.
除了最后一个,它是一个惯例.如果你真的努力,你仍然可以使用双下划线访问变量.
唉,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
Run Code Online (Sandbox Code Playgroud)
你问:
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
Run Code Online (Sandbox Code Playgroud)
我从没想过这个答案会受到如此多的关注.令人惊讶的是.这鼓励我创建一个可以使用的包.
$ pip install read-only-properties
Run Code Online (Sandbox Code Playgroud)
在你的python shell中:
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
Run Code Online (Sandbox Code Playgroud)
这是只读属性的一种稍微不同的方法,它可能应该被称为一次写入属性,因为它们必须被初始化,不是吗?对于我们中间那些担心能够通过直接访问对象的字典来修改属性的偏执狂,我引入了“极端”名称修改:
from uuid import uuid4
class ReadOnlyProperty:
def __init__(self, name):
self.name = name
self.dict_name = uuid4().hex
self.initialized = False
def __get__(self, instance, cls):
if instance is None:
return self
else:
return instance.__dict__[self.dict_name]
def __set__(self, instance, value):
if self.initialized:
raise AttributeError("Attempt to modify read-only property '%s'." % self.name)
instance.__dict__[self.dict_name] = value
self.initialized = True
class Point:
x = ReadOnlyProperty('x')
y = ReadOnlyProperty('y')
def __init__(self, x, y):
self.x = x
self.y = y
if __name__ == '__main__':
try:
p = Point(2, 3)
print(p.x, p.y)
p.x = 9
except Exception as e:
print(e)
Run Code Online (Sandbox Code Playgroud)