Python类实例变量隔离 [英] Python class instance variable isolation

查看:145
本文介绍了Python类实例变量隔离的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我是一名自学成才的程序员,最近我正在学习python.我遇到了一个奇怪的问题,但我想这只是我不了解python语法和/或程序流程的结果.

I am a self-taught programmer, and I've recently been learning python. I have encountered a strange issue but I imagine it is just a result of me not knowing the python syntax and/or program flow.

我有一个名为Test的类,位于文件TestClass.py中. `

I have one class called Test which is in the file TestClass.py. `

class Test:

    __tags = {}
    __fields = {}

    def __init__(self, tags: dict={}, fields: dict={}):
        self.__tags = tags
        self.__fields = fields

    def setTag(self, key, value):
        self.__tags[key] = value

    def getTag(self, key):
        return self.__tags[key]

    def setField(self, key, value):
        self.__fields[key] = value

    def getField(self, key):
        return self.__fields[key]


    def getAll(self):
        return [
            {
                'tags': self.__tags,
                'fields': self.__fields
            }
        ]

我正在包含程序代码test.py

import TestClass

t1 = TestClass.Test()
t1.setTag('test1', 'value1')
t1.setField('testfield', 'fieldvalue')

t2 = TestClass.Test()
t2.setTag('test2', 'value2')

print(t1.getAll())
print(t2.getAll())

print语句使事情变得很奇怪.输出 应该 为:

The print statements are where things get weird. The output should be:

[{'tags': {'test1': 'value1'}, 'fields': {'testfield': 'fieldvalue'}}]
[{'tags': {'test2': 'value2'}, 'fields': {}}]

但是实际输出是...

But the actual output is...

[{'tags': {'test2': 'value2', 'test1': 'value1'}, 'fields': {'testfield': 'fieldvalue'}}]
[{'tags': {'test2': 'value2', 'test1': 'value1'}, 'fields': {'testfield': 'fieldvalue'}}]

为什么?

Python 3.5

Python 3.5

推荐答案

对于新手来说,您不是一个人,而是两个Python著名的陷阱".

You just fell not in one, but in two Python well known "traps" for newcomers.

此行为是预期的,要解决此问题,应将类声明的开头更改为:

This behavior is expected, and to fix it, you should change the beginning of your class declaration to:

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代码仅被处理一次.

Now understanding the "why so?":
The Python code - including expressions, present at either module level, or inside a class body, or at a function or method declaration is processed just once - when that module is first loaded.

这意味着您在类主体中以及在__init__级别的默认参数上创建的空字典,这些空字典在此时被创建为字典,并在每次实例化该类时重新使用.

This means the empty dictionaries you were creating in your class body and on the default parameters of the __init__ level where created as a dictionary at this time, and re-used every time the class was instantiated.

第一部分是直接在Python的类主体上声明的属性是 class 属性-这意味着它们将在该类的所有实例之间共享.如果在方法内部使用self.attribute = XXX分配属性,则将创建一个实例属性.

The first part is that attributes declared directly on the class body in Python are class attributes - which mean they will be shared across all instances of that class. If you assign an attribute with self.attribute = XXX inside a method, then you create an instance attribute.

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

The second problem is that default values for function/method parameters are saved along with the function code - so the dictionaries you declared as empty there were the same after each method call - and shared across all instances of your class.

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

The usual pattern to avoid this is to set default parameters to None or other sentinel value of choice, and within the function body to test: if no value was sent to those parameters, just create a fresh new dictionary (or other mutable object) instance. This is created when the function is actually executed and is unique for that run. (And, if you assign them to an instance attribute with self.attr = {}, unique to that instance, of course)

关于我在答案self.__tags = tags or {}中提出的or关键字-它源自旧Python中常见的模式(在使用inine if之前),但仍然有用,其中或"运算符快捷方式, 和 在像obj1 or obj2这样的表达式中,如果第一个操作数计算为"truish"值,则返回第一个操作数,或者返回第二个属性(如果它不是truish没关系,那么无论如何,第二个参数的真值都是重要的) .使用内联"if"表达式的相同表达式为:self.__tags = tags if tags else {}.

As for the or keyword I proposed in my answer self.__tags = tags or {} - it begs from a pattern common in old Python (before we had an inine if) but still useful, in which the "or" operator shortcuts, and in expressiions like obj1 or obj2, returns the first operand if it evaluates to a "truish" value, or returns the second attribute (if it is not truish, does not matter, the truth value of the second parameter is all that matters anyway). The same expression using an inline "if" expression would be: self.__tags = tags if tags else {}.

此外,很高兴提到,虽然为了使旧教程中提到的私有"属性具有两个在属性名称前加上__的模式,但这不是一个好的编程模式,应避免使用. Python实际上并没有实现私有或受保护的属性访问-我们使用的约定是,如果某个属性,方法或函数名称以_开头(单下划线),则表示供对其进行编码的任何人私有使用在那里,在控制这些属性的代码的未来版本中更改或调用这些属性可能会表现出异常行为-但是代码中的任何内容实际上都不能阻止您这样做.

Also, it is nice to mention that although the pattern of prepending two __ to attribute names in order to have what is mentioned in old tutorials as "private" attributes, that is not a good programing pattern and should be avoided. Python does not actually implements private or protected attribute access - what we do use is a convention that, if a certain attribute, method or function name starts with _ (a single underline), it is meant for private use of whoever coded it there, and changing or calling those might have unexcpted behaviors in future versions of the code which control those attributes - but nothing in the code actually prevents you from doing so.

但是对于双下划线前缀,实际上会产生副作用:在 compile 时,以__为前缀的类属性被重命名,而__xxx被重命名为_<classname>__xxx -类主体内的所有出现都以相同的方式重命名,并且类主体外的代码可以正常地访问它,只需编写完整的名称即可.此功能旨在允许基类保留属性和方法,这些属性和方法在错误或易于使用属性名称的情况下(但出于安全"目的)不能在子类中被覆盖.

For a double underscores prefix, however, there is an actuall side effect: at compile time, class attributes prefixed with __ are renamed, and the __xxx is renamed to _<classname>__xxx - all ocurrences within the class body are renamed in the same fashion, and code outside the class body can access it normally, just writing the full mangled name. This feature is meant to allow base classes to hold attributes and methods that are not to be overriden in sub-classes, either by mistake or ease of use of an attribute name, (but not for "security" purposes).

旧的语言教程和文本通常将这一功能解释为在Python中执行私有属性"的一种方法-实际上,这些是不正确的.

Old language tutorials and texts usually explain this feature as a way to do "private attributes" in Python - those are actually incorrect.

这篇关于Python类实例变量隔离的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆