【Python学习】——语言风格(变量赋值、深浅拷贝、for循环陷阱)
目录
1、赋值
2、赋值的分类——引用赋值、值赋值
1) 不可变对象引用赋值——字符串、数值、元组等
2)可变对象引用赋值——列表、集合、字典
3)可变与不可变对象的引用赋值内部分析
4)在py文件中,和作用域有关,如在同一个函数中的相同值的变量是相等的,即值相等,地址也相等
3、深拷贝与浅拷贝
4、循环——序列和非序列的循环中进行元素的修改
1、赋值
# 赋值包含多种赋值方式,一般赋值、元组赋值、序列赋值、解包赋值
a = "long"
b,c = "1",2
d,e,f,g = "long"
h,*i = "long"
print(a)
print(b)
print(c)
print(d)
print(e)
print(f)
print(g)
print(h)
print(i)
long
1
2
l
o
n
g
l
['o', 'n', 'g']
当使用一个
*
前缀变量的时候,表示将序列中对应的元素全部收集到一个列表中(注意,总是一个列表),这个列表名为*
开头的那个变量名。*
号可以出现在任意位置处,只要赋值的时候能前后对应位置关系即可。注意其中的几个关键字:序列、对应的元素、列表
- 序列意味着可以是列表、元组、字符串等等
- 列表意味着只要收集不报错,赋值给解包变量的一定是一个列表
- 对应的元素意味着可能收集到0或任意个元素到列表。
不管如何,收集的结果总是列表,只不过可能是空列表或者只有一个元素的列表。
两个注意事项:
- 因为序列解包是根据元素位置来进行赋值的,所以不能出现多个解包变量
- 如果将序列直接赋值给单个解包变量时(即没有普通变量),这个解包变量必须放在列表或元组中
a,*b,c,*d = L # 错误 *a = L # 错误 [*a] = L # 正确 (*a) = L # 正确
2、赋值的分类——引用赋值、值赋值
引用赋值——指的是将内存地址赋值给变量来实现赋值
1) 不可变对象引用赋值——字符串、数值、元组等
a = 1000
b = a
a = 2000
前两行b=a是将1000的地址赋值给b,即a和b都是指向值1000的内存地址。第三行a=2000是对a重新进行赋值,因为数值是不可改变的对象,因此会先开辟一个内存地址用于存储2000,然后将a指向2000
不可变对象变量之间不会互相影响,即如果一开始两个变量指向同一个内存地址,当其中一个变量的值发生了改变的时候,另一个变量不会受到影响
对于不可变对象,修改变量的值意味着在内存中要新创建一个数据对象
a = 10000
b = a
a = 20000>>> a,b
(20000, 10000)
2)可变对象引用赋值——列表、集合、字典
对于可变对象,比如列表,它是在"原处修改"数据对象的(注意加了双引号)。比如修改列表中的某个元素,列表的地址不会变,还是原来的那个内存对象,所以称之为"原处修改"。例如:
L1 = [111,222,333]
L2 = L1
L1[1] = 2222>>> L1,L2
([111, 2222, 333], [111, 2222, 333])
L2是通过引用赋值得到的值,值为可变对象列表,当对L1改变一个列表元素时,其列表的地址不会发生改变,因此其L2的值也会发生相应的改变
在L1[1]
赋值的前后,数据对象[111,222,333]
的地址一直都没有改变,但是这个列表的第二个元素的值已经改变了。因为L1和L2都指向这个列表,所以L1修改第二个元素后,L2的值也相应地到影响。也就是说,L1和L2仍然是同一个列表对象[111,2222,333]
。
结论是:对于可变对象,变量之间是相互影响的。
3)可变与不可变对象的引用赋值内部分析
可变对象和不可变对象的赋值形式虽然一样,但是修改数据时的过程不一样。
对于不可变对象,修改数据是直接在堆内存中新创建一个数据对象。如图:
对于可变对象,修改这个可变对象中的元素时,这个可变对象的地址不会改变,所以是"原处修改"的。但需要注意的是,这个被修改的元素可能是不可变对象,可能是可变对象,如果被修改的元素是不可变对象,就会创建一个新数据对象,并引用这个新数据对象,而原始的那个元素将等待垃圾回收器回收。
>>> L=[333,444,555]
>>> id(L),id(L[1])
(56583832, 55771984)
>>> L[1]=4444
>>> id(L),id(L[1])
(56583832, 55771952)
如图所示:
4)在py文件中,和作用域有关,如在同一个函数中的相同值的变量是相等的,即值相等,地址也相等
3、深拷贝与浅拷贝
1)深拷贝
完全创建一个新的数据对象,不会受到其他变量的元素值变化的影响
2)浅拷贝,只是拷贝了第一层的元素,若第一层的元素是可变对象,则引用的是可变对象的地址,因此还是会受到其他变量的影响
# 引用赋值——只是得到了地址
print("赋值----------------------")
L = [1,2,[3,4,5]]
L2 = L
print("修改元素前-----------")
print(L)
print(L2)
print("修改元素后-----------")
L[0] = 0
print(L)
print(L2)
赋值----------------------
修改元素前-----------
[1, 2, [3, 4, 5]]
[1, 2, [3, 4, 5]]
修改元素后-----------
[0, 2, [3, 4, 5]]
[0, 2, [3, 4, 5]]
print("浅拷贝------------------------")print("浅拷贝1------------------------")
# 浅拷贝——只是拷贝了第一层的元素,若为不可变元素,则会重新为元素创建一个新的数据对象,若为可变数据对象,则只是拷贝了地址
L = [1,2,[3,4,5],6]
L1 = L.copy()
print("修改不可变对象的元素前-----------")
print(L)
print(L1)
print("修改不可变对象的元素后-----------")
L[0] = 0
print(L)
print(L1)print("浅拷贝1------------------------")
L = [1,2,[3,4,5],6]
L1 = L.copy()
print("修改可变对象的元素前-----------")
print(L)
print(L1)
print("修改可变对象的元素后-----------")
L[2][0] = 0
print(L)
print(L1)print("浅拷贝2------------------------")
L = [1,2,[3,4,5]]
L3 = L[:]
print("修改元素前-----------")
print(L)
print(L3)
print("修改元素后-----------")
L[0] = 0
L[2][0] = 0
print(L)
print(L3)
浅拷贝------------------------
浅拷贝1------------------------
修改不可变对象的元素前-----------
[1, 2, [3, 4, 5], 6]
[1, 2, [3, 4, 5], 6]
修改不可变对象的元素后-----------
[0, 2, [3, 4, 5], 6]
[1, 2, [3, 4, 5], 6]
浅拷贝1------------------------
修改可变对象的元素前-----------
[1, 2, [3, 4, 5], 6]
[1, 2, [3, 4, 5], 6]
修改可变对象的元素后-----------
[1, 2, [0, 4, 5], 6]
[1, 2, [0, 4, 5], 6]
浅拷贝2------------------------
修改元素前-----------
[1, 2, [3, 4, 5]]
[1, 2, [3, 4, 5]]
修改元素后-----------
[0, 2, [0, 4, 5]]
[1, 2, [0, 4, 5]]
from copy import deepcopy
print("深拷贝------------------------")
L = [1,2,[3,4,5]]
L1 = deepcopy(L)
L[0] = 0
L[2][0] = 0
print(L)
print(L1)
深拷贝------------------------
[0, 2, [0, 4, 5]]
[1, 2, [3, 4, 5]]
一般我们使用到的都是浅拷贝
4、循环——序列和非序列的循环中进行元素的修改
https://www.cnblogs.com/f-ck-need-u/p/10129317.html
1)列表进行原地修改时(L+=[val1,val2]),进行后面的迭代时,进行迭代的是修改后的列表,因为for是一个迭代器,使用的是next,即通过索引进行的,因此列表原地修改会导致元素出现奇怪的现象
为了避免这种情况,我们对列表进行修改时,建议生成一个新的列表对象来进行存放
L = ['a','b','c','d','e']## 原处修改列表,新元素f、g也会被迭代
for i in L:if i in "de":L += ["f", "g"]print(i)## 创建新列表,新元素f、g不会被迭代
for i in L:if i in "de":L = L + ["f", "g"]print(i)
这个for迭代器在迭代刚开始的时候,先找到L所指向的迭代对象,即内存中的[1,2,3,4]
。如果迭代过程中如果L变成了一个集合,或另一个列表对象,for的迭代并不会收到影响。但如果是在原处修改这个列表,那么迭代将会收到影响,例如新增元素也会被迭代到。
这里通过列表索引来进行元素的遍历和修改即可避免上面的情况
2)迭代一个列表,迭代过程中删除一个列表元素。
L = ['a','b','c','d','e']
for i in L:if i in "bc":L.remove(i)print(i)print(L)
输出的结果将是:
b
['a', 'c', 'd', 'e']
这个for循环的本意是想删除b、c元素,但结果却只删除了b。通过结果可以发现,c根本就没有被for迭代。之所以会这样,是因为迭代到b的时候,满足if条件,然后删除了列表中的b元素。正因为删除操作,使得列表中b后面的元素整体前移一个位置,也就是c元素的索引位置变成了index=1,而index=1的元素已经被for迭代过(即元素b),使得c幸运地逃过了for的迭代。
3)迭代的是字典或者集合时,虽然两者都是可变序列,但是时无序的,因此在迭代的过程中,是不允许字典或者集合发生改变的,否则会报错
D = {'a':1,'b':2,'c':3,'d':4,'e':5}for i in D:if i in "bc":del D[i]print(i)print(D)
报错:
b
Traceback (most recent call last):File "g:/pycode/lists.py", line 12, in <module>for i in D:
RuntimeError: dictionary changed size during iteration
S = {'a','b','c','d','e'}for i in S:if i in "bc":S.remove(i)print(i)print(S)
报错:
b
Traceback (most recent call last):File "g:/pycode/lists.py", line 4, in <module>for i in L:
RuntimeError: Set changed size during iteration
若想修改字典的话,我们可以复制一份数据对象作为副本,然后将副本进行迭代,将原来的字典或集合作为修改对象
D = {'a':1,'b':2,'c':3,'d':4,'e':5}for i in D.copy():if i in "bc":D.pop(i)print(i)
print(D)S = {'a','b','c','d','e'}for i in S.copy():if i in "bc":S.remove(i)print(i)
print(S)
注意:在进行可变对象数据对象的迭代与修改时,我们只需要将迭代对象和修改对象分开就不会出现上述的错误。
【Python学习】——语言风格(变量赋值、深浅拷贝、for循环陷阱)相关推荐
- 21天学通python电子版-小数据池,深浅拷贝,集合+菜中菜
小数据池,不同代码块的缓存机制,也称为小整数缓存机制,或者称为驻留机制等等,博主认为,只要你在网上查到的这些名字其实说的都是一个意思,叫什么因人而异. 那么到底什么是小数据池?他有什么作用呢? 前提条 ...
- R语言进阶 | 变量赋值背后的机制与R语言内存优化
为什么要了解变量赋值? 变量赋值牵涉到对象和变量名,理解对象和变量名之间的区别和联系将对你有如下帮助: (1)帮助你更精准预测代码的行为和内存的使用情况:(2)避免代码运行过程中不必要的对象复制,从而 ...
- Python学习笔记之变量
1.变量: 首先,新建一个文件为hello.py,添加一个变量并打印出来,具体代码如下: #python变量学习 sex='男' print(sex)sex="女" print(s ...
- Python全栈开发【基础-09】深浅拷贝+while循环
专栏介绍: 本专栏为Python全栈开发系列文章,技术包括Python基础.函数.文件.面向对象.网络编程.并发编程.MySQL数据库.HTML.JavaScript.CSS.JQuery.boots ...
- python学习之一(变量的基本使用)
变量的基本使用(学习记录使用) 程序就是用来处理数据的,而变量就是用来存储数据的 目标 变量定义 变量的类型 变量的命名 01. 变量定义 在 Python 中,每个变量 在使用前都必须赋值,变量 赋 ...
- python怎么输入给变量赋值_python中如何给input变量赋值
python中如何给input变量赋值 发布时间:2020-11-13 10:07:40 来源:亿速云 阅读:94 作者:小新 了解python中如何给input变量赋值?这个问题可能是我们日常学习或 ...
- Python学习笔记之常用操作符,条件分支和循环用法示例
本文实例讲述了Python常用操作符,条件分支和循环用法.分享给大家供大家参考,具体如下: #Pyhon常用操作符 ''' 想要学习Python?Python学习交流群:973783996满足你的需求 ...
- c语言中变量赋值使用什么运算符,C语言基础学习运算符-赋值运算符
简单赋值 在C语言里,运算符=并不表示相等,而是一个赋值运算符.这意味着,符号=的左边该是一个可修改的变量名,而右边是赋给该变量的值. 如下程序语句: i = i+1; 在数学上,该等式无法成立.而作 ...
- python可变类型和不可变深浅拷贝类型_python3笔记十四:python可变与不可变数据类型+深浅拷贝...
一:学习内容 python3中六种数据类型 python赋值 python浅拷贝 python深拷贝 二:python3六种数据类型 1.六种数据类型 Number(数字) string(字符串) L ...
- python可变类型和不可变深浅拷贝类型_理解python可变类型vs不可变类型,深拷贝vs浅拷贝...
核心提示: 可变类型 Vs 不可变类型 可变类型(mutable):列表,字典 不可变类型(unmutable):数字,字符串,元组 这里的可变不可变,是指内存中的那块内容(value)是否可以被改变 ...
最新文章
- java程序运行结果题_(Java程序设计)试题
- [置顶]IA32 architecture 学习笔记 (一)
- Ansible第二篇:ansible-playbook
- Golang教程:常量
- ubuntu安装 rust nightly_一起学Rust编程「1」:开发环境
- idea 跳转到行数,Intellij IDEA 一些不为人知的技巧
- java+selenium报异常org.openqa.selenium.StaleElementReferenceException的解决方案
- HDU2159 研发费用背包
- Python vs Cpython
- 用 Node.js 把玩一番 Alfred Workflow
- 2022最新分布式面试题合集,轻松应对Java面试
- numpy和pandas官方文档中文版分享
- java随机点名器_基于JavaScript实现随机点名器
- (模拟)HDU - 5857 Median
- PPT画图-颜色搭配
- 标识符(含义、组成、定义规则、命名规范)
- HAL - RTC分析
- 目前最新《Thinkphp 5.0 仿百度糯米开发多商家电商平台》
- 使用Git Bash实现Git代码上传加密
- 梦幻西游代理途径有哪些?需要注意什么