在类中只读的 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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-19 22:41:58  来源:igfitidea点击:

Python attribute that is read only within class

pythonattributes

提问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 @propertiesdecorator, 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_casting a classto a structwith 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_castclassstruct

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/exceptblocks 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块来说明行为,在生产中它们应该被删除
  • 如上所述,如果有人想改变这种行为,可以