初学Python,有一定C/C++基础。
看到Tuple的"immutable"的时候产生了这样一个疑问。
根据我的理解,如果Tuple的元素是普通变量(e.g. myInt=5),那创建
myTuple=(myInt,)
的意义就是
myTuple=(myInt的值,)
因此是不变的。
但是如果myTuple=(myInt,myList),虽然myTuple[1]永远都指向myList,但是myList中的内容是可以改变的。看到有人表示这Tuple里的List实际上是myList的引用。那能否用C++的指针/引用来类比统一解释一下这种Tuple元素的immutable特性呢?
【补充一下:比如对于myTuple=(myInt,myList),定义的时候,从意义上感觉(非严谨),就像是用C++写:
myTuple[0]=myInt; //myInt之后发生了什么都和myTuple[0]无关了,myTuple[0]只决定于myInt当前值
&myTuple[1]=myList; //而myTuple[1]更像是myList的一个引用。myList如果变化,myTuple[1]也会变化
于是这里就产生了“同样是对Tuple赋值,却有两种不同的意义”的矛盾。有什么解释方式能调和这种矛盾么?】
表述的不太清楚,希望能有理解了我的疑问的朋友呀。多谢了。
你的类比是错误的。
myTuple=(myInt,myList)
在任何时候都是:因为在Python里一切都是对象,
myInt
也是。tuple
是immutable
的意思是你无法为其元素重新赋值。也就是你不能改变myTuple[0]
和myTuple[1]
这两个指针变量的值,所以它们永远指向myInt
和myList
,这一点是确定的。但是你可以修改
tuple
中的每个元素所指向的对象本身,前提是这个对象必须是可变的。比如你能够修改myList
,因为这是一个可变对象。但是myInt
不行,因为整数也是不可变对象,所以你无法修改它。你把指针理解成一张上面写有数字的小纸条,纸条上面的数字告诉你该去对应哪一个编号的抽屉里面拿东西。
那么 Tuple 是这样的:
当你有一个 myTuple 的时候,你有一张名字叫 myTuple 的纸条,根据数字你可以找到一个抽屉,抽屉里面有一张或者多张另外的小纸条。并且这些纸条的数量是确定的,直到你把 myTuple 这张纸条扔掉了(或者说 myTuple 上的数字换了另外一个),才有可能会变化。
myTuple 从
(myInt,)
变成(myInt, myList)
的过程,实际上是 myTuple 上面的数字改写了,并且向新的数字对应的另外一个抽屉,放进去两张纸条:第一张是原先的 myInt 纸条,第二张是新加进去的 myList 纸条。你对 myList 添加或者删除任意元素,其实都是根据 myList 纸条去找对应的抽屉去做事,跟 myTuple 没有多大关系,只不过是 myTuple 里面恰巧有一张纸条的数字,跟 myTuple 纸条的数字是一样的。
就是常指针呗。
觉得可以用函数传递参数的情景解释,
1.可变对象传引用:可以在原对象上改变,函数内外都持有对该对象的指针,函数内外使用的是一个对象,如果在函数内部改变该对象,函数外部也会改变,类似于引用传递。
2.不可变对象传拷贝:不可变对象不能在原来的内存空间上变化,所以如果要改变其值,解释器会新建一个对象存放新的变量,这个时候函数内外使用的就不是一个对象了--函数外部还是用的以前的对象,而函数内部已经使用了一个全新的对象。
先确定myList是list对象,指向一个地址(或者说是引用,个人觉得像是一个指针),myTuple存有这个引用。因为是myList可变的,所以可以添加删除,但是它的地址都没变,看先后两次的id。如果将myList指向另一个对象,地址改变了。这时改变的myList不会影响myTuple了。因为Tuple保存的那个list对象没有变化,所以与myTuple无关。
在python中,你这里的myInt也是引用,指向某个整数实体,这跟c/c++不一样,在c/c++中myInt就是整数实体本身了;这样一来,Tuple里存的都是引用,区别只是Int是不可变的,而List是可变的;
所以你说的矛盾并不存在。