Python只读属性
我不知道属性何时应该是私有的,是否应该使用属性。 我最近读到setter和getter不是pythonic,我应该使用属性装饰器。 没关系 但若我有一个属性,那个么该属性不能从类外设置,但可以读取(只读属性)。这个属性是否应该是private,我所说的private是指下划线,比如Python只读属性,python,properties,python-2.7,private,readonly,Python,Properties,Python 2.7,Private,Readonly,我不知道属性何时应该是私有的,是否应该使用属性。 我最近读到setter和getter不是pythonic,我应该使用属性装饰器。 没关系 但若我有一个属性,那个么该属性不能从类外设置,但可以读取(只读属性)。这个属性是否应该是private,我所说的private是指下划线,比如self.\ux? 如果是,那么我如何在不使用getter的情况下阅读它? 我现在知道的唯一方法就是写作 @property def x(self): return self._x 这样,我可以通过obj.x
self.\ux
?
如果是,那么我如何在不使用getter的情况下阅读它?
我现在知道的唯一方法就是写作
@property
def x(self):
return self._x
这样,我可以通过obj.x
读取属性,但我不能设置它obj.x=1
,所以它很好
但我真的应该关心设置不能设置的对象吗?也许我应该离开它。但我不能使用下划线,因为读取obj.\ux
对用户来说很奇怪,所以我应该使用obj.x
,然后用户又不知道他不能设置这个属性
你的观点和做法是什么 一般来说,编写Python程序时应该假设所有用户都是同意的成年人,因此他们自己负责正确使用东西。然而,在极少数情况下,属性设置不合理(例如派生值或从某个静态数据源读取的值),getter-only属性通常是首选模式。只要我的两分钱,就在正确的轨道上,但我想添加一个示例。;-) Python是一种类型不安全的语言,因此您必须始终信任代码的用户,让他们像理性(明智)的人一样使用代码 Per: 仅对非公共方法和实例变量使用一个前导下划线 若要在类中具有“只读”属性,可以使用
@property
装饰,在使用新样式的类时,需要从对象继承
示例:
>>> 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
>>A类(对象):
... 定义初始化(self,a):
... 自我。_a=a
...
... @财产
... def a(自我):
... 回归自我
...
>>>a=a(‘测试’)
>>>a.a
“测试”
>>>a.a='pleh'
回溯(最近一次呼叫最后一次):
文件“”,第1行,在
AttributeError:无法设置属性
请注意,实例方法也是(类的)属性,如果您真的想成为一个坏蛋,可以在类或实例级别设置它们。或者,您可以设置一个类变量(它也是类的一个属性),在这个类中,方便的只读属性无法直接使用。我想说的是,“只读属性”问题实际上比人们通常认为的更普遍。幸运的是,工作中有一些传统的期望,这些期望太强了,以至于我们无法理解这些其他情况(毕竟,几乎所有东西都是python中的某种属性)
基于这些期望,我认为最通用和轻量级的方法是采用约定,即“public”(无前导下划线)属性是只读的,除非明确记录为可写。这包含了通常的期望,即方法不会被修补,指示实例默认值的类变量更好,更不用说了。如果您真的对某些特殊属性感到怀疑,请使用只读描述符作为最后一个资源度量
所有用户都是同意的成年人,因此有责任使用
事情本身是正确的
请看下面我的更新
使用@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('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
你问:
只读属性
来自哪里
很高兴您这么问,以下是以下信息的来源:
更新
我没想到这个答案会得到如此多的关注。令人惊讶的是,确实如此。这鼓励我创建一个可以使用的包
$ pip install read-only-properties
在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
[1]中的:从rop导入只读属性
在[2]中:@read_only_属性('a'))
…:类Foo:
…:定义初始化(self,a,b):
…:self.a=a
…:self.b=b
...:
在[3]中:f=Foo('explodes','ok to overwrite')
在[4]中:f.b=5
在[5]中:f.a=‘动臂’
---------------------------------------------------------------------------
AttributeError回溯(最近一次呼叫上次)
在()
---->1 f.a=‘动臂’
/home/oznt/.virtualenvs/tracker/lib/python3.5/site-packages/rop.py in.\uuuuu setattr\uuuuu(self、name、value)
116通行证
117其他:
-->118 raise AttributeError(“无法触摸{}”。格式(名称))
119
120 super()
属性错误:不能碰一个
虽然我喜欢Oz123中的类装饰器,但您也可以执行以下操作,即使用显式类包装器和带有类工厂方法的“new”在闭包中返回类:
class B(object):
def __new__(cls, val):
return cls.factory(val)
@classmethod
def factory(cls, val):
private = {'var': 'test'}
class InnerB(object):
def __init__(self):
self.variable = val
pass
@property
def var(self):
return private['var']
return InnerB()
我知道我是在死而复生,但我正在研究如何使属性为只读,在找到这个主题后,我对已经共享的解决方案不满意 那么,回到最初的问题,如果您从以下代码开始:
@property
def x(self):
return self._x
如果要使X为只读,只需添加:
@x.setter
def x(self, value):
raise Exception("Member readonly")
然后,如果运行以下命令:
print (x) # Will print whatever X value is
x = 3 # Will raise exception "Member readonly"
对于只读属性,这里有一种稍微不同的方法,它可能应该被称为一次写入属性,因为它们确实需要初始化,不是吗?对于我们当中那些担心自己被
print (x) # Will print whatever X value is
x = 3 # Will raise exception "Member readonly"
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)
@property
def language(self):
return self._language
@language.setter
def language(self, value):
# WORKAROUND to get a "getter-only" behavior
# set the value only if the attribute does not exist
try:
if self.language == value:
pass
print("WARNING: Cannot set attribute \'language\'.")
except AttributeError:
self._language = value
def final(cls):
clss = cls
@classmethod
def __init_subclass__(cls, **kwargs):
raise TypeError("type '{}' is not an acceptable base type".format(clss.__name__))
cls.__init_subclass__ = __init_subclass__
return cls
def methoddefiner(cls, method_name):
for clss in cls.mro():
try:
getattr(clss, method_name)
return clss
except(AttributeError):
pass
return None
def readonlyattributes(*attrs):
"""Method to create readonly attributes in a class
Use as a decorator for a class. This function takes in unlimited
string arguments for names of readonly attributes and returns a
function to make the readonly attributes readonly.
The original class's __getattribute__, __setattr__, and __delattr__ methods
are redefined so avoid defining those methods in the decorated class
You may create setters and deleters for readonly attributes, however
if they are overwritten by the subclass, they lose access to the readonly
attributes.
Any method which sets or deletes a readonly attribute within
the class loses access if overwritten by the subclass besides the __new__
or __init__ constructors.
This decorator doesn't support subclassing of these classes
"""
def classrebuilder(cls):
def __getattribute__(self, name):
if name == '__dict__':
from types import MappingProxyType
return MappingProxyType(super(cls, self).__getattribute__('__dict__'))
return super(cls, self).__getattribute__(name)
def __setattr__(self, name, value):
if name == '__dict__' or name in attrs:
import inspect
stack = inspect.stack()
try:
the_class = stack[1][0].f_locals['self'].__class__
except(KeyError):
the_class = None
the_method = stack[1][0].f_code.co_name
if the_class != cls:
if methoddefiner(type(self), the_method) != cls:
raise AttributeError("Cannot set readonly attribute '{}'".format(name))
return super(cls, self).__setattr__(name, value)
def __delattr__(self, name):
if name == '__dict__' or name in attrs:
import inspect
stack = inspect.stack()
try:
the_class = stack[1][0].f_locals['self'].__class__
except(KeyError):
the_class = None
the_method = stack[1][0].f_code.co_name
if the_class != cls:
if methoddefiner(type(self), the_method) != cls:
raise AttributeError("Cannot delete readonly attribute '{}'".format(name))
return super(cls, self).__delattr__(name)
clss = cls
cls.__getattribute__ = __getattribute__
cls.__setattr__ = __setattr__
cls.__delattr__ = __delattr__
#This line will be moved when this algorithm will be compatible with inheritance
cls = final(cls)
return cls
return classrebuilder
def setreadonlyattributes(cls, *readonlyattrs):
return readonlyattributes(*readonlyattrs)(cls)
if __name__ == '__main__':
#test readonlyattributes only as an indpendent module
@readonlyattributes('readonlyfield')
class ReadonlyFieldClass(object):
def __init__(self, a, b):
#Prevent initalization of the internal, unmodified PrivateFieldClass
#External PrivateFieldClass can be initalized
self.readonlyfield = a
self.publicfield = b
attr = None
def main():
global attr
pfi = ReadonlyFieldClass('forbidden', 'changable')
###---test publicfield, ensure its mutable---###
try:
#get publicfield
print(pfi.publicfield)
print('__getattribute__ works')
#set publicfield
pfi.publicfield = 'mutable'
print('__setattr__ seems to work')
#get previously set publicfield
print(pfi.publicfield)
print('__setattr__ definitely works')
#delete publicfield
del pfi.publicfield
print('__delattr__ seems to work')
#get publicfield which was supposed to be deleted therefore should raise AttributeError
print(pfi.publlicfield)
#publicfield wasn't deleted, raise RuntimeError
raise RuntimeError('__delattr__ doesn\'t work')
except(AttributeError):
print('__delattr__ works')
try:
###---test readonly, make sure its readonly---###
#get readonlyfield
print(pfi.readonlyfield)
print('__getattribute__ works')
#set readonlyfield, should raise AttributeError
pfi.readonlyfield = 'readonly'
#apparently readonlyfield was set, notify user
raise RuntimeError('__setattr__ doesn\'t work')
except(AttributeError):
print('__setattr__ seems to work')
try:
#ensure readonlyfield wasn't set
print(pfi.readonlyfield)
print('__setattr__ works')
#delete readonlyfield
del pfi.readonlyfield
#readonlyfield was deleted, raise RuntimeError
raise RuntimeError('__delattr__ doesn\'t work')
except(AttributeError):
print('__delattr__ works')
try:
print("Dict testing")
print(pfi.__dict__, type(pfi.__dict__))
attr = pfi.readonlyfield
print(attr)
print("__getattribute__ works")
if pfi.readonlyfield != 'forbidden':
print(pfi.readonlyfield)
raise RuntimeError("__getattr__ doesn't work")
try:
pfi.__dict__ = {}
raise RuntimeError("__setattr__ doesn't work")
except(AttributeError):
print("__setattr__ works")
del pfi.__dict__
raise RuntimeError("__delattr__ doesn't work")
except(AttributeError):
print(pfi.__dict__)
print("__delattr__ works")
print("Basic things work")
main()
def attr_proxy(obj):
""" Use dynamic class definition to bind obj and proxy_attrs.
If you can extend the target class constructor that is
cleaner, but its not always trivial to do so.
"""
proxy_attrs = dict()
class MyObjAttrProxy():
def __getattr__(self, name):
if name in proxy_attrs:
return proxy_attrs[name] # overloaded
return getattr(obj, name) # proxy
def __setattr__(self, name, value):
""" note, self is not bound when overloading methods
"""
proxy_attrs[name] = value
return MyObjAttrProxy()
myobj = attr_proxy(Object())
setattr(myobj, 'foo_str', 'foo')
def func_bind_obj_as_self(func, self):
def _method(*args, **kwargs):
return func(self, *args, **kwargs)
return _method
def mymethod(self, foo_ct):
""" self is not bound because we aren't using object __new__
you can write the __setattr__ method to bind a self
argument, or declare your functions dynamically to bind in
a static object reference.
"""
return self.foo_str + foo_ct
setattr(myobj, 'foo', func_bind_obj_as_self(mymethod, myobj))