Python中如何避免可变类型带来的坑

class Example:
# 坑
container = {}

def __init__(self):
    # do init
    pass
def result(self):
    if self.container:
        return self.container()
    self.container["a"] = self._a()
    self.container["b"] = self._b()
    return self.container
def _a(self):
    # fetch data
    return "a"

def _b(self):
    # fetch data
    return "b"

今天用了一个这样的结构来处理部分数据.然后就出现了第二个实例返回的结果和上一个一样的情况. 反思了一下自己在写代码的时候根本没注意到这个 container 在两个实例里可能会指向同一个地址的问题.写 python 两年多一直在很小心 list 和 dict 作为参数时避免采坑,没想到今天换了个地方踩了一下,不知道别人是不是了解这个坑,发出来给大家看看吧


Python中如何避免可变类型带来的坑

21 回复

这…是常识吧,楼主写两年多 Python 写的都是啥?


在Python里,可变类型(比如列表、字典、集合)作为函数默认参数或者在不同对象间共享时,确实容易踩坑。核心就两点:默认参数只在函数定义时计算一次,以及赋值操作对于可变对象是引用传递。

最常见的就是这个经典错误:

def append_to_list(value, my_list=[]):
    my_list.append(value)
    return my_list

print(append_to_list(1))  # 输出 [1]
print(append_to_list(2))  # 输出 [1, 2] 而不是预期的 [2]

修复方法是用None作为默认值,在函数内部初始化:

def append_to_list(value, my_list=None):
    if my_list is None:
        my_list = []
    my_list.append(value)
    return my_list

另一个坑是多个变量引用同一个可变对象:

a = [1, 2, 3]
b = a  # b和a指向同一个列表
b.append(4)
print(a)  # 输出 [1, 2, 3, 4],a也被修改了

需要独立副本时,用copy()方法或者切片[:]

b = a.copy()  # 或者 b = a[:]

类属性共享也是类似问题:

class MyClass:
    items = []  # 这是类属性,所有实例共享
    
    def add(self, value):
        self.items.append(value)

obj1 = MyClass()
obj2 = MyClass()
obj1.add(1)
print(obj2.items)  # 输出 [1]

应该在__init__里初始化实例属性:

class MyClass:
    def __init__(self):
        self.items = []  # 每个实例有自己的列表

总结就是:默认参数用None,需要副本时显式拷贝,类属性在__init__里初始化。

静态变量

不过确实有很大部分直接入 python 并不了解这个东西。

因为类变量是实例共享的,不可变类型修改时会修改实例变量,可变类型就是这种情况

return self.container() 这里多了对括号

可变类型? 静态变量?

尽量避免使用类变量,使用大写定义常量,只读不写。

类变量也有并发冲突问题。

…两年真白写了

我估计单例你都没写过…

请问这是什么书

我在刚入行半年的时候,就修过之前老哥留下的这样的 bug= =

这也不是楼主所说的 “可变类型的坑” 吧。这都能理解错,楼主还是要严谨点。

Python 基础教程(第三版)

你需要的是把 self.container 变量定义在__init__函数中,和可变类型没关系

兄弟估计没学过 cpp 或者 java,面向对象底子不够

这应该怪 类属性 跟 实例属性 没搞懂

按理说两年了不应该还踩这个坑啊。再说了这个也不 可变类型的坑,
明显就是类变量和实例变量的概念和区别没弄明白。

你这个需求下创建实例变量应该把放到 init 方法中。
class Example:

◇◇◇◇def init(self):
◇◇◇◇◇◇◇◇# do init
◇◇◇◇◇◇◇◇self.container = {}
◇◇◇◇◇◇◇◇pass

我觉得某楼也不必这么说楼主,

这个东西,就是点与面的问题,没必要把话说绝。

老实说 Python 这个确实是个坑…不知道设计的时候咋想的,所以一般丢到 init 里面去比较好

我 Python 不熟,但这个跟可变类型有啥关系啊?看起来是成员变量跟静态变量的问题。

lz 从 java 来的吧; python 里面用 a, b = a(), b() 不需要写什么 pojo

的确和可变类型没啥关系,恐怕 java 也没写好,静态的 class 的属性和 obj 的属性用法都是一致的。

回到顶部