python 将列表值赋予函数_听说你会玩 Python 系列 3 考了 99 分的潘石屹肯定不懂的知识点...

6f9c972e3599c87e9f7481704258ff08.png

本文含  2734  字, 12  图表截屏 建议阅读  15  分钟

本文是听说你会玩 Python 系列的第三篇

  • 1 - 六酷技巧

  • 2 - 99% 的人会做错的题

  • 3 - 深挖变量

引言

在 Python 中,当创建变量时,不用像 C 语言那样在前面加入变量类型,如下图所示:

90b86e21cb403e1238bf44936c20aea1.png

对比发现在 Python 中定义变量时,不需要声明其数据类型,因此 Python 属于动态类型(dynamic typed)语言。读者可能会认为 Python 不够严谨,怎么定义变量都不带变量类型呢?

原因是 Python 中的变量只是一个名字而已,就像下图的 x 存在变量名一样,它的作用仅仅是“指向”引用对象(PyObject)。PyObject 是计算机分配的一块内存,其下有类型大小引用计数等属性。引用计数是说多少个变量名“指向”该对象,当引用计数为零时,意味着没有任何变量名引用,因此可以被回收。

02b43042e02d46ca686915ed9452dfd4.png

为什么 x 能“轻易地”指向不同变量类型?这要深挖 Python 内部机制是如何运行下面四条语句的。

  • 定义整数 x 并赋值 1031

  • 给 x 赋予一个新值 1032

  • 创建一个新变量 y 并等于 x

  • 将 y 值增加 1

不可修改的整数

定义整数 x 并赋值 1031

  1. 表面上是敲入 x = 1031,实际发生的是:

  2. 创建一个新对象 PyObject

  3. 将该 PyObject 的类型属性设为 int

  4. 将该 PyObject 的值属性设为 1031

  5. 创建一个变量名,叫做 x

  6. 将 x 指向新对象 PyObject

  7. 将 PyObject 里的引用计数加 1

11b0a9102f5be73610a575a6bdb58045.png


x 赋予一个新值1032

表面上是敲入 x =1032,实际发生的是:

  1. 创建一个新对象 PyObject

  2. 将该 PyObject 的类型属性设为 int

  3. 将该 PyObject 的值属性设为 1032

  4. 将 x 指向新对象 PyObject

  5. 将新对象 PyObject 里的引用计数加 1

  6. 将旧对象 PyObject 里的引用计数减 1

旧对象“颜色变灰退出舞台”,代表着它随时会被清理。

b2ed597d24072db854faac185f0e97a3.png


创建一个新变量 y 并等于 x

表面上是敲入 y = x 时,实际发生的是:

  1. 将 y 和 x 指向同样的对象 PyObject

  2. 将该对象 PyObject 里的引用计数加 1

注意:在上面过程中没有创建任何新对象 PyObject

d902a9945004e49ff47dc9e079d4fcf3.png


y 值增加 1

表面上是敲入 y += 1,实际发生的是:

  1. 创建一个新对象 PyObject

  2. 将该 PyObject 的引用计数设为 int

  3. 将该 PyObject 的值属性设为 1033

  4. 将 y 指向新对象 PyObject

  5. 将新对象 PyObject (即 y 指向的对象) 里的引用计数加 1

将旧对象 PyObject (即 x 指向的对象) 里的引用计数减 1

c4d05d3654d9ee1eae77da4ddfb71c7d.png


由上图可知,在 Python 中,即便对于一个简单的整数,它不单单包含其值,还包含其类型、大小和引用计数,封装成 PyObject。根据不同的变量值会生成不同的 PyObject,而变量名可以随意指向 PyObject。

让人迷惑是第三步,当 x 和 y 同时指向值为 1032 的 PyObject,但在第四步将 y 加 1,x 却保持不变。虽然迷惑但是合理,要不然改变 y 也改 x 会造成很多麻烦。但为什么改变 y 而不是改变 x 呢?原因在于改变 y 时新建了一个值为1033 的 PyObject,并将 y 指向它,而 x 还是指向原来值为 1032 的 PyObject。

从上面描述可以侧面推出整数是不可修改(immutable)的,因为更改变量值不是在原来的 PyObject 里改,而是新创建一个 PyObject。

判断变量 x 是否可修改,用 id(x) 函数,该函数打印出变量 x 的地址。

  • 如果 x 可修改,那么更新其值前后的地址一样

  • 如果 x 不可修改,那么更新其值前后的地址不一样

创建 x 并打印出地址
x = 1031id(x) 
2479057898512

更新 x 的值,地址变了,因此 x 不可修改

x = 1032id(x) 
2479067931376
将 x 赋予 y,两个指向相同对象,地址相同
y = xprint( id(x) )print( id(y) )
2479067931376
2479067931376
更新 y 的值,y 的地址变了,因此 y 不可修改
y += 1print( id(x) )print( id(y) )
2479067931376
2479067931440

结论:整型变量是不可修改的。

再回到上面动态类型的例子,当变量 x 定义为整数 1、字符串 'one' 和布尔值 True 时,实际上变量名 x 轮流指向三个 PyObject,因此它们的内存地址也不一样。

d9e974061002ad3bf0bd65b9b8d09ab4.png

配着上面的解释再回顾一下引言里的图,现在都明白了吧。

02b43042e02d46ca686915ed9452dfd4.png

可修改的列表

Python 中的整数变量是不可修改的,而列表是可修改的。虽然还没介绍列表,可把它当成一个存储元素的容器,创建一个存储 1, 10.31 和'Python' 的列表,起名为 l,它在内存中的示意图如下:

acc7407352b91b8949eb7e4ec904f655.png

和上面整数变量一样,表面上是敲入l = [1, 10.31, 'Python'],实际发生的是:

  1. 创建一个新对象 PyObject(列表的)

    1. 将该 PyObject的类型属性设为 list

    2. 将该 PyObject的值属性指向三个地址

  2. 创建三个新对象 PyObjects(列表元素的)

    1. 将它们的类型属性设为 int, float, str

    2. 将它们的值属性设为 1, 10.31, 'Python'

  3. 将三个地址分别指向 PyObjects

  4. 创建一个变量名,叫做 l

  5. 将 l 指向新对象 PyObject

  6. 将 PyObject 里的引用计数加 1

根据上述流程,当更改列表中的元素,只是新创建其元素的 PyObject,而没有新创建列表本身的 PyObject。因此列表是可修改的,可用 id() 函数来验证更改列表前后的地址是一样的。

创建 l 并打印出地址

l = [1, 2, 3]id(l) 
2233189737736

更新 l 第一个元素值,地址没变,因此 l 可修改

l[0] = 10000id(l) 
2233189737736
不可修改的元组

和列表不同,元组是不可修改的。创建一个元组 t,注意里面还包含一个列表 [1, 2]。

t = (1, [1, 2], 'Python')

它在内存中的示意图如下(注意第二个列表元素又指向两个整型 PyObject):

1c89337ffd004c340dd724358f7735ba.png

由于元组不可修改,直接给元组元素赋值会报错。

t[1] = [1, 2, 3]
TypeError: 'tuple' object does not support item assignment

但只要元组中的元素可修改,比如列表,那么可以更改它,注意这跟赋值其元素不同。

t[1].append(3)t
(1, [1, 2, 3], 'Python')

这也好理解,由于列表可修改,因此在[1, 2] 后面加个 3 不会改变列表的内存地址 0x210640,因此元组的内存地址也没有改变。但如果将整个列表重新赋值,那么要新创建一个列表赋给元组第二个元素,列表的地址肯定改变了,那么元组的内存地址也改变了,这样就违背了元组不可修改的特性,所以会报错。

总结

记住整数和元组不可修改、列表可修改一点也不难。

知道用 id() 函数来验证一个变量是否可修改也不难。

难的是要知道为什么,知其然还要知其所以然!

Stay Tuned!

9ffc330d946e1099a10b2096c47774f3.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值