在类中只读的 Python 属性
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/43236938/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Python attribute that is read only within class
提问by gionni
I have a class:
我有一堂课:
class Portfolio:
def __init__(self, value):
self.value = value
class GenericStrategy:
def __init__(self, portfolio: Portfolio):
self.portfolio = portfolio
def modify_ptf_value(new_value):
self.portfolio.value = new_value
# This should return an error
I'will write some strategies which will inherit from GenericStrategy
. I'd like their methods to be able to read the attribute portfolio but not to modify it, nor its attributes.
我会写一些从GenericStrategy
. 我希望他们的方法能够读取属性组合但不能修改它,也不能修改它的属性。
I read something about the @properties
decorator, but it only works if I don't want the attribute (and its attributes) to be accessible from the outside, i can still modify the attribute (and its attributes) from methods 'inside' the object.
Is there a way to make the attribute (and its attributes) 'read-only' except for the __init__
method? Is my design wrong and should start over? I know it is up to the user not to modify "protected" attributes, but I would like to make it bullet proof. Any idea is well accepted, even if it requires a substantial change in the class design.
我读了一些关于@properties
装饰器的东西,但它只有在我不希望从外部访问属性(及其属性)时才有效,我仍然可以从对象“内部”的方法修改属性(及其属性)。除了__init__
方法之外,有没有办法使属性(及其属性)“只读” ?我的设计是错误的,应该重新开始吗?我知道用户不能修改“受保护”的属性,但我想让它防弹。任何想法都被广泛接受,即使它需要对类设计进行重大更改。
Thanks
谢谢
回答by CristiFati
As opposed to other (commonly used) programming languages Pythoncomes with a new approach regarding accessing class/instance members. For example, nothing is really private, the fields/methods that:
与其他(常用)编程语言相反,Python提供了一种关于访问类/实例成员的新方法。例如,没有什么是真正私有的,字段/方法:
- start with an
_
, are regular fields - start with
__
(and end with at most one_
), are just name mangled, but they still can be accessed (even modified/deleted) from outside the class
- 以 an 开头
_
,是常规字段 - 以
__
(最多以一个结束_
),只是名称错位,但它们仍然可以从类外部访问(甚至修改/删除)
So, at the end it's a matter of convention, and it relies that it will be followed by those who write code. Bottom line is there's nothingthat would prevent an user gaining access to a class/instance's internals.
所以,最后这是一个约定问题,它依赖于编写代码的人会遵循它。底线是没有什么可以阻止用户访问类/实例的内部结构。
Note: In other language it's possible too to access private members: there are methods officially supported (like Reflection([Oracle]: Trail: The Reflection API) for Java), or not so officially supported (which require some "tricks" - e.g.: reinterpret_cast
ing a class
to a struct
with the same structure for C++). Nowadays, more and more languages tend to offer a way to alter an instance structure.
注意:在其他语言中,也可以访问私有成员:有官方支持的方法(如Java 的Reflection( [Oracle]: Trail: The Reflection API) ),或者没有官方支持的方法(需要一些“技巧” - 例如: 将a转换为与C++具有相同结构的a )。如今,越来越多的语言倾向于提供一种改变实例结构的方法。reinterpret_cast
class
struct
Anyway, there is the so called Descriptor Protocol([Python]: Descriptor HowTo Guide) which is one of the Python's most powerful (and also most misunderstood) features.
无论如何,有所谓的描述符协议([Python]: Descriptor HowTo Guide),它是Python最强大(也是最容易被误解)的特性之一。
Using descriptors(as a side comment, propertiesrely on them), I wrote a piece of code that achieves (on some degree) what you're asking for:
使用描述符(作为旁注,属性依赖于它们),我写了一段代码来实现(在某种程度上)你所要求的:
class LockedAttribute(object):
def __init__(self, name):
self._name = name
self._set_count = 0
self._set_treshold = 1
def __get__(self, instance, cls):
return instance.__dict__[self._name]
def __set__(self, instance, value):
if self._set_count >= self._set_treshold:
raise AttributeError("Can't set attribute '{}'".format(self._name))
else:
instance.__dict__[self._name] = value
self._set_count += 1
def __delete__(self, instance):
raise AttributeError("Can't delete attribute '{}'".format(self._name))
class GenericStrategy(object):
portfolio = LockedAttribute("portfolio")
def __init__(self, portfolio):
self.portfolio = portfolio
try:
self.portfolio = portfolio
except AttributeError as e:
print(" ERROR: {}".format(e))
def set_portfolio(self, new_value):
self.portfolio = new_value
if __name__ == "__main__":
strategy = GenericStrategy("some portfolio name")
print("Portfolio: {}".format(strategy.portfolio))
try:
del strategy.portfolio
except AttributeError as e:
print(" ERROR: {}".format(e))
try:
strategy.set_portfolio("some 2nd portfolio name")
except AttributeError as e:
print(" ERROR: {}".format(e))
try:
strategy.portfolio = "some 3rd portfolio name"
except AttributeError as e:
print(" ERROR: {}".format(e))
print("Portfolio: {}".format(strategy.portfolio))
Notes:
注意事项:
- I removed
__
from the private attribute's name (portfolio
) to avoid using the mangling that I was talking about, in my code (would make it more difficult to read) - For Py27compatibility, the classes inherit
object
. If the compatibility isn't mandatory, the inheritance relation can be deleted (in Py3xit is by default) - I added the
try/except
blocks to illustrate the behavior, in productionthey should be deleted - As I stated above, if anyone wants to alter this behavior, can
- 我
__
从私有属性的名称 (portfolio
) 中删除,以避免在我的代码中使用我正在谈论的重整(会使其更难以阅读) - 为了Py27兼容性,类继承
object
. 如果兼容性不是强制性的,可以删除继承关系(Py3x中默认是) - 我添加了
try/except
块来说明行为,在生产中它们应该被删除 - 如上所述,如果有人想改变这种行为,可以