Python类实例变量隔离

这个问题在这里已经有了答案:

  • “最小的惊讶”和可变的默认参数30的答案

  • 你只是陷入了一个不为人所知的境地,但是却以两个着名的“陷阱”出现在新人面前。

    这种行为是可以预料的,为了解决这个问题,你应该将类声明的开头改为:

    from typing import Optional 
    
    
    class Test:
        def __init__(self, tags: Optional(dict)=None, fields: Optional(dict)=None):
            self.__tags = tags or {}
            self.__fields = fields or {}
            ...
        ...
    

    现在了解“为什么这样?”:
    Python代码 - 包括表达式,出现在模块级别,或者在类体内,或者函数或方法声明中,都只会被处理一次 - 当该模块首次被加载时。

    这意味着您在类体中创建的空字典以及此时作为字典创建的__init__级别的缺省参数,并在每次实例化类时重新使用。

    第一部分是直接在Python的类体上声明的属性是类属性 - 这意味着它们将在该类的所有实例中共享。 如果您在方法内为self.attribute = XXX指定了一个属性,那么您将创建一个实例属性。

    第二个问题是函数/方法参数的默认值与函数代码一起保存 - 所以您在每个方法调用之后声明为空的字典是相同的 - 并且在您的类的所有实例之间共享。

    避免这种情况的通常模式是将默认参数设置为None或其他选择的sentinel值,并在函数体内进行测试:如果没有值发送到这些参数,只需创建一个新的新字典(或其他可变对象)实例。 这是在函数实际执行时创建的,并且对于该运行是唯一的。 (并且,如果将它们分配给具有self.attr = {}的实例属性,当然,该实例是唯一的)

    而对于or关键字我在答复中提出的self.__tags = tags or {} -从旧Python中常见的模式乞求(我们有一个inine之前if ),但仍是有用的,在其中的“或”操作的快捷键,和在像obj1 or obj2这样的表达式中,返回第一个操作数,如果它评估为“truish”值或返回第二个属性(如果它不是真的,没关系,第二个参数的真值无论如何都是重要的) 。 使用内联“if”表达式的相同表达式是: self.__tags = tags if tags else {}

    另外,很高兴提及尽管为了使旧教程中提到的“私有”属性具有两个__属性名称的模式,但这不是一个好的编程模式,应该避免。 Python实际上并未实现私有或受保护的属性访问 - 我们所使用的约定是,如果某个属性,方法或函数名以_ (单个下划线)开始,那么它就是私有的,在未来版本的控制这些属性的代码中更改或调用这些属性可能会有未曾行为的行为 - 但代码中的任何内容都不会阻止您这样做。

    但是,对于双下划线前缀,实际上有一个副作用:在编译时,以__为前缀的类属性被重命名,并且__xxx被重命名为_<classname>__xxx - 类体内的所有出现都被重命名为相同时尚和代码以外的代码可以正常访问它,只需编写完整的名称即可。 这个特性的目的是允许基类拥有属性和方法,这些属性和方法不会在子类中被重写,或者是由于错误或易于使用属性名称(但不是为了“安全”目的)。

    旧的语言教程和文本通常将此功能解释为在Python中执行“私有属性”的一种方式 - 实际上这是不正确的。

    链接地址: http://www.djcxy.com/p/28511.html

    上一篇: Python class instance variable isolation

    下一篇: Change email on git commits?