文档章节

可变对象与不可变对象

crossin
 crossin
发布于 07/13 01:45
字数 1342
阅读 12
收藏 0
点赞 0
评论 0

前阵子我们聊了下函数的参数传递以及变量赋值的一些内容:关于函数参数传递,80%人都错了

简单回顾下要点:

1. Python 中的变量不是装有对象的“容器”,而是贴在对象上的“标签”。

2. 参数传递相当于一次赋值:多贴了一个标签。

3. 至于在函数内部对参数的修改是否会影响到外部变量的值,取决于你怎样修改:如果是重新赋值就不会,如果是修改对象自身内容则会。

讲到这里就有个常被提及的概念:
可变对象和不可变对象

在 Python 中,
可变对象包括 list、dict、set、自定义类型等;
不可变对象包括 int、float、bool、str、tuple 等。

不可变对象不允许对自身内容进行修改。如果我们对一个不可变对象进行赋值,实际上是生成一个新对象,再让变量指向这个对象。哪怕这个对象简单到只是数字 0 和 1:

a = 0
print('a', id(a))
a = 1
print('a', id(a))

输出:

a 4463151440
a 4463151472

因为对象不可变,所以为了提高效率,Python 会使用一些公用的对象:

a = 1
print('a', id(a))
b = 1
print('b', id(b))
print(a == b)
print(a is b)
c = 'hello world'
print('c', id(c))
d = 'hello world'
print('d', id(d))
print(c == d)
print(c is d)

输出:

a 4423761776
b 4423761776
True
True
c 4430180912
d 4430180912
True
True

这里顺便提一下 is 这个操作符。它和 == 的区别在于:== 只判断“值”是不是相等,而 is 则判断是否为同一个对象,也就是地址一致。比如:

a = 2
b = 2.0
print(a == b)
print(a is b)

输出:

True
False

而可变对象则可以对自身内容进行修改,如:

m = [1, 2, 3]
print('m', m, id(m))
m[1] = 4
print('m', m, id(m))
m.append(5)
print('m', m, id(m))

输出:

m [1, 2, 3] 4536815752
m [1, 4, 3] 4536815752
m [1, 4, 3, 5] 4536815752

可以看到,虽然 m 的值发生了变化,但是地址没变,还是原来那个 m。

上次我也说到,很多的教程都在用可变和不可变来谈论赋值和参数传递,我觉得这很不好。因为他们说到不可变对象时用的是赋值,而说到可变对象又用了 list 的索引、apeend 等方法,这根本是两码事。如果大家都是赋值,那么无论是否可变,效果都是一样的:

m = [1, 2, 3]
print('m', m, id(m))
m = [4, 5, 6]
print('m', m, id(m))

输出

m [1, 2, 3] 4329894024
m [4, 5, 6] 4329910856

所以理解了 Python 的赋值原理,就明白这与是否可变无关。而可变对象于不可变对象本身的不同仅在于一个可以修改变量的值,而另一个不允许。

基于这一设定,两者在功能上的最大区别就是:不可变对象可以作为字典 dict 的键 key,而可变对象不行。比如 list 不能作为字典的键,但 tuple 可以。

另外,明白了可变与不可变的区别,一些方法的效果也就自然理解了:

s = 'abc'
s2 = s.replace('b', 'd')
print('s', s)
print('s2', s2)
m = [1, 2, 3]
m2 = m.reverse()
print('m', m)
print('m2', m2)

输出:

s abc
s2 adc
m [3, 2, 1]
m2 None

因为 str 是不可变对象,所以它的方法如 replacestripupper 都不可能修改原对象,只会返回一个新对象,比如重新赋值才可以。而 list 是可变对象,它的方法如 reversesortappend,都是在原有对象上直接修改,无返回值。

不过,有个特殊情况需要注意:

m = [1, 2, 3]
print('m', m, id(m))
m += [4]
print('m', m, id(m))
m = m + [5]
print('m', m, id(m))

输出

m [1, 2, 3] 4494164104
m [1, 2, 3, 4] 4494164104
m [1, 2, 3, 4, 5] 4494181128

m = m +m += 虽然是一样的结果,但 m 指向的对象却发生了变化。原因在于,前者是做了赋值操作,而后者其实是调用的 __iadd__ 方法。

如果我们就是需要产生一个 list 对象的副本,可以通过 [:]

m = [1, 2, 3]
print('m', m, id(m))
n = m[:]
print('n', n, id(n))
n[1] = 4
print('m', m)
print('n', n)

这样对 n 的修改便不再会影响到 m,因为它们已不是同一个对象。

那么如果是这样呢:

m = [1, 2, [3]]
n = m[:]
n[1] = 4
n[2][0] = 5
print(m)

猜一猜 m 的结果是什么?

  1. [1, 2, [3]]
  2. [1, 4, [3]]
  3. [1, 2, [5]]
  4. [1, 4, [5]]
  5. 其它结果

再去 Python 里执行下看看输出,是不是和预期一样,想想为什么?这个牵涉到浅拷贝、深拷贝的概念,我们下次再聊。


════
其他文章及回答:
如何自学Python | 新手引导 | 精选Python问答 | Python单词表 | 区块链 | 人工智能 | 双11 | 嘻哈 | 爬虫 | 排序算法 | 我用Python | 高考 | 世界杯 | 竞猜 | requests
欢迎搜索及关注:Crossin的编程教室

© 著作权归作者所有

共有 人打赏支持
crossin
粉丝 3
博文 81
码字总数 125658
作品 0
闵行
为什么copy不可以修饰可变数组

总结: 1:对于不可变对象,copy都是浅复制,即指针复制。mutableCopy 都是Alloc一个新对象返回。 2:对于可变对象,copy和mutableCopy都是Alloc新对象返回。 3:不论是可变还是不可变对象,...

Ethan-GOGO
2016/10/19
8
0
关于iOS开发中copy的使用

在iOS开发中我们在使用@property定义属性的时候经常会使用copy属性来个对一个NSString或者NSArray、NSDictionary来进行修饰,然而许多同学只是在别人的代码上看到,学习这样使用。 也有一部分...

我是何先生
05/23
0
0
copy与mutableCopy区别,strong和copy的使用

本篇文章主要讲两个知识点:1.深拷贝与浅拷贝 2.NSArray和NSMutaleArray应该用copy还是stong修饰。 一、我们先来分析深拷贝(返回一个对象,一个新的指针指向一个新的内容)与浅拷贝(返回一个...

Jesse1949
2017/09/01
0
0
python面试题之函数参数传递

所有的变量都可以理解是内存中一个对象的“引用”,在python中,对象分为可变对象和不可变对象。可变对象为列表、字典类型;不可变对象为数字、字符串、元组类型。 如果,当一个引用传递给一...

一颗成长树
2017/04/06
0
0
Effective-java 3 中文翻译系列 (Item 17)

看了Effective-java2的中文翻译版之后,发现比较难理解,而且有很多内容也已经过时,后来找到有第3版的原文版和一些热心人士的中文翻译,发现翻译还是感觉不太好理解,就打算自己翻译一下。而...

薛银亮
06/06
0
0
Effective Java 第三版——17. 最小化可变性

Tips 《Effective Java, Third Edition》一书英文版已经出版,这本书的第二版想必很多人都读过,号称Java四大名著之一,不过第二版2009年出版,到现在已经将近8年的时间,但随着Java 6,7,8...

M104
01/08
0
0
Java并发教程-6不可变对象

一个对象如果在创建后不能被修改,那么就称为不可变对象。在并发编程中,一种被普遍认可的原则就是:尽可能的使用不可变对象来创建简单、可靠的代码。 在并发编程中,不可变对象特别有用。由...

noday
2014/04/25
0
0
Java 可变对象和不可变对象

一、简单定义 不可变对象(Immutable Objects)即对象一旦被创建它的状态(对象的数据,也即对象属性值)就不能改变,反之即为可变对象(Mutable Objects)。 不可变对象的类即为不可变类(Immuta...

JackieYeah
2014/03/04
0
0
copy与mutableCopy

一、深拷贝和浅拷贝 深拷贝:对象拷贝 - 直接拷贝内容。 浅拷贝:指针拷贝 - 将指针中的地址值拷贝一份。 二、对于 Copy 与 mutableCopy 的实践 思路:我用四个方案来验证 Copy 与 mutableC...

JlongTian
2016/01/05
24
0
OC语言自学《十二》---- OC(Foundation框架部分知识总结)

NSRange 表示一个范围 主要用来操作字符串 CGPoint、NSPoint 通过设置x坐标和y坐标来设置某个点 IOS设备的x和y从左上角开始,y值往下面增加,x值往右面增加 CGSize、NSSize 表示一个矩形尺寸...

罗眯眯
2014/04/09
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

FOMO游戏代码解析

源代码在此处

怎当她临去时秋波那一转
11分钟前
0
0
EOS智能合约与DApp开发入门

EOS的是Block.One主导研发的一个区块链底层公链系统,它专门为支撑商业去中心化 应用(Decentralized Application)而设计,其代码开源。 比特币被称为区块链1.0,因为它开辟了数字加密货币的...

笔阁
24分钟前
1
0
编译cjson到dll

https://blog.csdn.net/mengzhisuoliu/article/details/52203724 编译完成后 是纯lua实现的json decode 的10倍以上...

梦想游戏人
33分钟前
0
0
JS基础- Date 对象

Date 对象 Date 对象用于处理日期和时间。 创建 Date 对象的语法: var myDate=new Date() 注释:Date 对象会自动把当前日期和时间保存为其初始值。 Date 对象属性 属性 描述 constructor 返...

ZHAO_JH
36分钟前
0
0
Python数据分析numpy(1)

Python开源的科学计算基础库 1.表示N维数组对象ndarray 2.线性代数、傅里叶变换、随机数生成 3.广播函数,整合c++、c 一.数据的维度 1.数据 2.数据维度 3.一维数据 (1)特点 (2)Python中的...

十年磨一剑3344
38分钟前
0
0
csv导入Hive脚本

from pyspark.sql import HiveContexthivec = HiveContext(sc) # 创建一个hivecontext对象用于写执行SQL,sc为sparkcontext# 拼接一个字段类型字符串str_s = 'label String,'...

gulf
41分钟前
0
0
TensorFlow 隐含层 拟合 异或运算

a⊕b = (¬a ∧ b) ∨ (a ∧¬b) 数据 X = [[0, 0], [0, 1], [1, 0], [1, 1]]Y = [[0], [1], [1], [0]] 单层网络只能拟合线性问题,由于异或是非线性问题,需要使用多层网络 输入和输出 [[...

阿豪boy
55分钟前
0
0
SVN 教程

http://www.runoob.com/svn/svn-tutorial.html 记一次 svn 管理的项目迁移工作 之前A服务器上的项目版本管理工具是用的SVN,后来,之前管理linux服务器的员工离职了,这管理服务器的工作也就...

yeahlife
55分钟前
0
0
在Python中,不用while和for循环遍历列表

a = [1, 2, 3, 8, 9]def printlist(l, index): if index == len(l): return else: print(l[index]) printlist(l, index + 1)printlist(a,......

丁典
57分钟前
0
0
Kubernetes使用中发现的错误及解决

运行 kubectl dashboard 时报错: Error validating service: Error getting service kubernetes-dashboard: services "kubernetes-dashboard" not found 排查 kubectl get po --all-names......

哎码
今天
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部