Python 使用对元素的现有引用修改列表?
(如果你想跳过凌晨2点的Python science,直接切入主题,我的问题在最后总结) 考虑以下几点:Python 使用对元素的现有引用修改列表?,python,list,Python,List,(如果你想跳过凌晨2点的Python science,直接切入主题,我的问题在最后总结) 考虑以下几点: 1: animals = ['cat', 'cow', 'donkey', 'horse'] # we start with a list 2: animals_reference = animals # make another reference and assign it to animals 3: cat = animals[0] # refer cat to first e
1: animals = ['cat', 'cow', 'donkey', 'horse'] # we start with a list
2: animals_reference = animals # make another reference and assign it to animals
3: cat = animals[0] # refer cat to first element of animals
4: assert cat is animals[0] # no copy occurred, still same object
5: animals[0] = animals[0].capitalize() # change first element of list
6: assert cat is not animals[0] # animals[0] now refers to another object
7: assert animals_reference is animals # animals still points to the same object as before
我的理解是Python列表的底层结构是一个C数组(有很多动态的东西在进行,但最终还是一个C数组)
让我困惑的是:我们将cat
设置为引用列表的第一个元素(3)。在C语言中,指的是数组第一个元素的地址
然后我们修改列表的第一个元素(5)
但这样做之后,cat不再引用该对象(6)。然而,列表引用也没有改变,因为在(7)中,我们看到它从一开始就指向同一个对象
这让我心神不宁,因为它表明cat现在指的是其他东西,尽管它从未被重新分配过任务
所以我做了以下实验:
cat = animals[0] # refer cat to first element of animals
assert cat is animals[0] # no copy occurred, still same object
print("id of cat: {}".format(hex(id(cat))))
print("id of animals[0]: {}".format(hex(id(animals[0]))))
print("id of animals[]: {}".format(hex(id(animals))))
print("capitalizing animals[0]...")
animals[0] = animals[0].capitalize()
print("-id of cat: {}".format(hex(id(cat))))
print("-id of animals[0]: {}".format(hex(id(animals[0]))))
print("-id of animals[]: {}".format(hex(id(animals))))
对于输出:
id of cat: 0xffdda580
id of animals[0]: 0xffdda580
id of animals[]: 0xffddc828
capitalizing animals[0]...
-id of cat: 0xffdda580 # stayed the same!
-id of animals[0]: 0xffe12d40 # changed!!
-id of animals[]: 0xffddc828
这让我相信Python列表不一定是内存中连续的元素,对元素的更改只会指向内存中的其他地方?我的意思是,数组的第一个元素的地址在内存中比数组本身的地址早
列表使用的底层结构究竟是什么来解释我所看到的?猫的id不会改变,因为它是对原始
动物列表的第一个元素的引用。当该元素更改(大写)时,动物[0]
的id会更改,但猫
不会更改,因为它是对原始动物[0]
的引用,是对包含字母猫
的字符串对象的引用,而不是当前的动物[0]
,它现在是对另一个字符串对象的引用,其中包含字母Cat
列表<代码>动物
仍然存在,并且已经修改到位,因此其id不会更改。由于列表是可变的,它们在最初创建时不能只是一个连续的内存区域,因为可以添加一个比预先分配的内存块大的对象
Python列表是一个动态对象,包含对其他对象的引用(如果为空,则不包含任何内容)。如果它只是一个静态的C数组,那么使用Python就没有意义了,你只需要使用C。Python列表的意义在于它们是可变的——动态的、可变的、可重排序的、可伸缩的、可折叠的、可排序的等等。在Python中,所有东西都是一个对象,这意味着所有东西都存储在堆中
当你定义
animals = ['cat', 'cow', 'donkey', 'horse']
每个字符串('cat'
,…)都存储在堆中。列表animals
包含对每个字符串的引用
分配cat=animals[0]
,使cat
保留对字符串“cat”的引用(与animals[0]
保留的引用相同)
将animates[0]=animates[0]。大写()
创建一个新字符串('Cat'
),并将animates[0]
持有的引用更改为新字符串。但是,Cat
仍持有对堆中原始对象的引用。以C为例-
假设A
是一个数组,该数组包含对象(不是基本体)
比方说,A按顺序存储在地址-1000、1008、1016等中,即第一个元素的引用存储在地址-1000中。比方说第一个元素本身存储在2000中
当你这样做的时候-
cat = A[0]
您得不到存储A的第一个元素的地址,而是获得了对第一个元素的引用。也就是说,您得不到1000
,而是2000
现在,如果您将[0]中存储的元素更改为地址3000中的对象,也就是说,您将地址1000
中的引用更改为3000
。您认为cat的引用会更改吗?这里有一种方法可以考虑:
1: animals = ['cat', 'cow', 'donkey', 'horse'] # we start with a list
2: animals_reference = animals # make another reference and assign it to animals
3: cat = animals[0] # refer cat to first element of animals
这并不意味着cat
指的是“动物的第一个元素”,至少不是你的意思。它让cat
指的是动物的第一个元素所指的任何东西。在这种情况下,就是字符串“cat”。换句话说,表达式anists[0]
本身就是一个对象的引用。该对象是字符串cat
。当您执行animals[0]
操作时,您将得到表达式animals[0]
所指的对象。当您执行cat=animals[0]
操作时,您将cat
设置为引用该对象
没有办法避免“取消引用”值anists[0]
。也就是说,没有办法说“给我anists[0]
的指向性,这样当anists[0]
开始指向其他对象时,我的新变量也将指向其他对象”。你只能得到anists[0]的内容
指的是,而不是指它本身
因此:
这里您更改了动物[0]
指向的对象。但是您将猫
设置为动物[0]
过去指向的对象。因此现在猫
和动物[0]
指向不同的对象。字符串猫
没有更改(这就是为什么is
测试仍然显示值相同的原因);这只是因为动物[0]
不再指向那个字符串,而是开始指向字符串“Cat”
。所以列表更像是指向对象的通用指针数组,而不是对象本身的容器(可能除了原语或其他东西)@andy没有Python中的“原语”——一切都是一个对象。我提到的引用有点像指针
4: assert cat is animals[0] # no copy occurred, still same object
5: animals[0] = animals[0].capitalize() # change first element of list