gpt4 book ai didi

python - 如何在python中重新分配变量而不更改其ID?

转载 作者:行者123 更新时间:2023-12-04 16:38:12 25 4
gpt4 key购买 nike

当将一个变量分配给另一个变量时,它们指向同一个对象。那么,如何在变量仍然指向同一个对象的情况下更改其中一个的值?

a = 10
b = a
a -= 1
print(b) #expect to print 9 but it print 10
如何在python中重新分配变量而不更改其ID?

最佳答案

我不确定您是否对 Python 中的变量或不可变值感到困惑。所以我将解释两者,一半的答案可能看起来像“不,我已经知道了”,但另一半应该有用。

在 Python 中——与 C 不同——变量不是值所在的位置。这只是一个名字。值(value)观随心所欲。1 所以,当你这样做时:

a = 10
b = a

你不是在制造 b成对 a 的引用.这个想法在 Python 中甚至没有意义。您正在制作 a10 取名,然后制作 b 10的另一个名称.如果你以后这样做:
a = 11

……你做了 a11 取名,但这对 b 没有影响——它仍然只是 10的一个名字.

这也意味着 id(a)没有给你变量的 ID a ,因为没有这样的东西。 a只是在某个命名空间(例如,模块的全局字典)中查找的名称。这是值(value), 11 (或者,如果您之前运行过它,则是具有 ID 的不同值 10 )。 (虽然我们在这里:输入的也是值,而不是变量。这里不相关,但值得了解。)

当谈到可变性时,事情变得有点棘手。例如:
a = [1, 2, 3]
b = a

这仍然使 ab列表的两个名称。
a[0] = 0

这不会分配给 a , 所以 ab仍然是同一个列表的名称。它确实分配给 a[0] ,这是该列表的一部分。所以,列表 ab现在两个名字都成立 [0, 2, 3] .
a.extend([4, 5])

这显然做了同样的事情: ab现在命名列表 [0, 2, 3, 4, 5] .

这就是事情变得困惑的地方:
a += [6]

是重新绑定(bind)的作业 a ,或者它只是改变了 a 的值是一个名字吗?事实上,两者兼而有之。这意味着,在幕后,是:
a = a.__iadd__([6])

……或者,大致:
_tmp = a
_tmp.extend([6])
a = _tmp

所以,我们分配给 a ,但我们正在为它分配与它已经命名的相同的值。同时,我们也在改变那个值,它仍然是 b 的值。名称。

所以现在:
a = 10
b = 10
a += 1

你可能会猜到最后一行做了这样的事情:
a = a.__iadd__(1)

这并不完全正确,因为 a没有定义 __iadd__方法,所以它回退到这个:
a = a.__add__(1)

但这不是重要的一点。2 重要的一点是,因为整数与列表不同,是不可变的。你不能把数字 10 变成数字 11,就像你在 INTERCAL 或(某种)Fortran 中那样,或者你做最奇怪的 X 战警时所做的那个奇怪的梦。并且没有可以设置为 11 的“保存数字 10 的变量”,因为这不是 C++。因此,这必须返回一个新值,即值 11 .

所以, a成为那个新 11 的名字.同时, b仍然是 10 的名字.这就像第一个例子。

但是,毕竟这告诉你做你想做的事是多么不可能,我要告诉你做你想做的事是多么容易。

还记得之前,当我提到您可以对列表进行变异时,该列表的所有名称都会看到新值吗?那么,如果你这样做:
a = [10]
b = a
a[0] += 1

现在 b[0]将是 11 .

或者你可以创建一个类:
class Num:
pass

a = Num()
a.num = 10
b = a
a.num += 1

现在, b.num11 .

或者你甚至可以创建一个实现 __add__ 的类。和 __iadd__all the other numeric methods ,因此它可以(几乎)透明地保存数字,但可以可变地这样做。
class Num:
def __init__(self, num):
self.num = num
def __repr__(self):
return f'{type(self).__name__}({self.num})'
def __str__(self):
return str(self.num)
def __add__(self, other):
return type(self)(self.num + other)
def __radd__(self, other):
return type(self)(other + self.num)
def __iadd__(self, other):
self.num += other
return self
# etc.

现在:
a = Num(10)
b = a
a += 1

b是相同的名称 Num(11)a .

但是,如果您真的想这样做,您应该考虑制作一些特定的东西,例如 Integer而不是通用 Num保存任何类似数字的东西,并在 numbers 中使用适当的 ABC模块来验证您是否涵盖了所有关键方法,获得许多可选方法的免费实现,并能够通过 isinstance类型检查。 (并且可能以 num.__int__ 的方式在其构造函数中调用 int,或者至少是特殊情况 isinstance(num, Integer) 这样您就不会以对引用的引用结束……除非这是您想要的。)

1. 好吧,他们住在译员希望他们住的地方,就像齐奥塞斯库治下的罗马尼亚人一样。但是如果你是一个用 C 编写的内置/扩展类型并且是 Party 的付费成员,你可以覆盖 __new__使用不依赖于 super 的构造函数分配,否则你别无选择。

2. 但这并非完全不重要。按照约定(当然所有内置和 stdlib 类型都遵循约定), __add__不会变异, __iadd__做。所以,像 list 这样的可变类型定义两者,这意味着它们获得了 a += b 的就地行为但复制行为 a + b , 而不可变类型如 tupleint仅定义 __add__ ,所以他们对两者都有复制行为。 Python 不会强制您以这种方式做事,但是如果它不选择这两种方式中的一种,您的类型就会很奇怪。如果你熟悉 C++,那也是一样的——你通常会实现 operator+=通过就地变异并返回对 this 的引用, 和 operator+通过复制然后返回 +=在副本上,但语言不会强制您这样做,如果您不这样做只会令人困惑。

关于python - 如何在python中重新分配变量而不更改其ID?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51336185/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com