Python 对不可变对象的引用集合

Python collection of references to immutables

python 中有没有办法获取参考文献列表?意思是,当我改变其中一个内部事物时,一组事物会自动改变,而不需要引用集合本身。

允许我在示例中显示的东西

# What happens
a = 1
b = 2
L = [a, b]
print(L) # [1, 2]

b = 3
print(L) # [1, 2]


# I would like to have something like
a = 1
b = 2
ra = ref(a)
rb = ref(b)
L = [ra, rb]
print(L) # [1, 2]

b = 3
print(L) # [1, 3]

编辑 如果数字在 python 中可变,我的第一个示例 起作用。 IE。如果重新分配(例如:b = 3)将意味着 "mutate the memory of the object which we are referring to via the label 'b'".

我的第一个示例使用列表的可变元素(例如,如果我构建一个列表列表,并改变 - 无需重新分配! - 内部列表)。

因此,更具体地说,我的问题是:当集合包含不可变元素时,我们是否有机会发生这种行为?

(我尤其对可调用列表感兴趣)。

在打印L的新值之前,在L中再次存储值b,请看下面的代码:

a = 1
b = 2
L = [a, b]
print(L) # [1, 2]
b = 3
L=[a, b]
print(L) # [1, 3]

但是这种方法效率不高。更新List item的方式只有index.As如下:

a = 1
b = 2
L = [a, b]
print(L) # [1, 2]
b = 3
L[1]=b
print(L) # [1, 3]

我们无法通过您尝试使用 Python 中的引用的方式来做到这一点。 当您将一个整数放入列表中时,该列表会保存该整数的副本。整数最初是变量、文字值、函数调用的结果还是其他什么都无关紧要;当列表看到它时,它只是一个整数值。

好的,找到方法了。我只是用可变包装器包装不可变对象。

例如,如果我们想要可变整数:

class mut_int:
    def __init__(self, data):
        self.data = data

    def mutate(self, new_data):
        self.data = new_data

    def __repr__(self):
        return repr(self.data)

a = mut_int(1)
L = [a, a]
print(L) # [1, 1]

# from now on, we assume we don't have access to L anymore

a.mutate(2) # simulating the mutable a = 2
print(L) # [2, 2] # we changed L without accessing it directly, nice!

对于可变的可调用对象,类似地:

class mut_callable:
    def __init__(self, func):
        self.func = func

    def mutate(self, new_func):
        self.func = new_func

    def __repr__(self):
        return repr(self.func)

    def __call__(self, *args, **kwargs):
        return self.func(*args, **kwargs)

f = mut_callable(lambda x: x)
L = [f, lambda x: f(x) + f(x), lambda x: f(f(x))]
print([g(2) for g in L])
# Out: [2, 4, 2] 
# i.e. [identity(2), 2+2, identity(identity(2))]

# from now on, we assume we don't have access to L anymore

f.mutate(lambda x: x**2)
print([g(2) for g in L])
# Out: [4, 8, 16]
# i.e. [square(2), 2+2, square(square(2))]

不幸的是,由于 python 的动态特性和不可能重载赋值 (a = 3) 运算符,这有点脆弱。一旦对可变包装器使用通常的赋值,包装器就会丢失,我们回到原点。

a = mut_int(1)
L = [a, a]
print(L) # [1, 1]
a.mutate(2)
print(L) # [2, 2] good so far

a = 3 # !ACHTUNG! We did not stick to the api, and a is no more a mut_int!
print(L) # still [2, 2] :(
a.mutate(4) # AttributeError

如果有人找到更优雅的and/or符合人体工程学的and/or稳健的解决方案,我仍然很期待!