Python比较操作符、变量赋值、对象拷贝

1. 比较操作符 == 和 is

1.1 区别
  • == 操作符比较对象之间的值是否相等
  • is 操作符比较的是对象的身份标识是否相等,即是否是同一个对象,是否指向同一个内存地址
  • is 操作符的速度效率通常要优于==,因为is操作符不能被重载,执行is操作只是简单的获取对象的ID,并进行比较,而等于操作符则会递归地遍历对象所有值,并逐一比较
  • 当比较一个变量与一个单例时,通常使用is
1.2 实例
# 比较两个对象
def compare(A, B):
if A == B:
print(f"{A} == {B}:{True}")
else:
print(f"{A} == {B}:{False}")
if A is B:
print(f"{A} is {B}:{True}")
else:
print(f"{A} is {B}:{False}")
print(id(A), id(B))
A = -7
B = -7
compare(A, B)
C = 4
D = 4
compare(C, D)
"""
python内部对**-5到256的整型**维持一个数组,起到一个缓存的作用,使得性能优化,因此,在-5到256之间的整型数字比较,都相等。上述代码是在jupyter notebook中运行的,如果在pycharm中运行,则都是True,pycharm中做了优化。
"""
-7 == -7:True
-7 is -7:False
139667038587504 139667038587152
4 == 4:True
4 is 4:True
# 比较一个变量
if a is None:
... if a is not None:
...

2. 变量及其赋值

2.1 概念和逻辑关系
  • 变量的赋值,只是表示让变量指向了某个对象,并不表示拷贝对象给变量;而一个对象,可以被多个变量所指向。

  • 可变对象(列表,字典,集合等等)的改变,会影响所有指向该对象的变量。

  • 对于不可变对象(字符串、整型、元组等等),所有指向该对象的变量的值总是一样的,也不会改变。但是通过某些操作(+= 等等)更新不可变对象的值时,会返回一个新的对象

  • 变量可以被删除,但是对象无法被删除,需要通过python垃圾回收机制回收。

2.2 Python函数的参数传递

​ 是赋值传递,python里所有的数据类型都是对象,所以参数传递时,只是让让新变量与原变量指向相同的对象而已,并不存在值传递或是引用传递(c++等语言中)一说。

2.3 思考题

2.3.1 变量指向的是同一个对象吗?——查id

# l1,l2,分别分配了内存空间,不是指向同一个对象
# l2,l3,是指向
l1 = [1, 2, 3] # 创建了新对象
l2 = [1, 2, 3] # 创建了新对象
l3 = l2 # 指向同一个对象
print(id(l1), id(l2), id(l3))
# 1479611736648 1479611736712 1479611736712

2.3.2 变量被修改了吗?

# 字典是可变对象,对象改变,会影响所有指向该对象的变量
def func(d):
d['a'] = 10
d['b'] = 20
d = {'a': 1, 'b': 2}
func(d)
print(d) # {'a': 10, 'b': 20}

3. 浅拷贝和深度拷贝

3.1 浅拷贝概念

浅拷贝是指重新分配一块内存,创建一个新的对象,里面的元素是原对象内第一层对象的引用,因此,如果原对象中的元素是可变的,改变其也会影响拷贝后的对象,存在一定的副作用。

3.2 浅拷贝方法——可变对象
  • 类型工厂函数:

    • 是指不通过类而是通过函数来创建对象,list(),set(),int(),dict(),tuple(),str()等数据类型本身的构造器
    • 浅拷贝中适用的是可变对象,因此,list(),set(),dict()适用,其余不适用
  • 切片操作:列表
  • copy模块中copy方法:copy.copy()
3.3 深度拷贝概念

深度拷贝是指重新分配一块内存,创建一个新的对象,并且将元对象中的元素,以递归的方式,通过创建新的子对象拷贝到新对象中,因此,新对象和原对象没有任何关联。另外,深度拷贝中会维护一个字典,记录已经拷贝的对象及其ID,来提高效率并放置无限递归的发生。

3.4 深度拷贝方法
  • copy模块中的deepcopy方法:copy.deepcopy()
3.5 实例
3.5.1 浅拷贝和赋值的区别——是否会创建一个新对象
l1 = [1, 2, 3]
l2 = list(l1) # 使用工厂函数实现浅拷贝
compare(l1, l2)
a = [1, 2, 3]
b = a # 变量赋值
compare(a, b)
[1, 2, 3] == [1, 2, 3]:True
[1, 2, 3] is [1, 2, 3]:False
140603550652928 140603550660720
[1, 2, 3] == [1, 2, 3]:True
[1, 2, 3] is [1, 2, 3]:True
3.5.2 字符串、数字不能实现拷贝
import copy
t1 = 4
t2 = copy.deepcopy(t1) # 对一个元组实现深度拷贝
compare(t1, t2)
sr1 = "adc"
sr2 = str(sr1) # 字符串不能创建浅拷贝
compare(sr1, sr2)
4 == 4:True
4 is 4:True
adc == adc:True
adc is adc:True
3.5.3 元组的浅拷贝和深度拷贝
import copy
s1 = (1, 2, 3,[1,2])
s2 = copy.copy(s1) # 元组不能实现浅拷贝
compare(s1, s2)python
(1, 2, 3, [1, 2]) == (1, 2, 3, [1, 2]):True
(1, 2, 3, [1, 2]) is (1, 2, 3, [1, 2]):True
import copy
t1 = (1, 2, 3)
t2 = copy.deepcopy(t1) # 只包含不可变对象的元组不能实现深拷贝
compare(t1, t2)
(1, 2, 3) == (1, 2, 3):True
(1, 2, 3) is (1, 2, 3):True
import copy
s1 = (1, 2, 3,[1,2])
s2 = copy.deepcopy(s1) # 对一个包含可变对象的元组可以实现深度拷贝
compare(s1, s2)
(1, 2, 3, [1, 2]) == (1, 2, 3, [1, 2]):True
(1, 2, 3, [1, 2]) is (1, 2, 3, [1, 2]):False
139823086708208 139823086707728
3.5.4 浅拷贝和深度拷贝的影响
# 浅拷贝:原对象的改变可能会影响新对象
import copy
l1 = [[1, 2], (30, 40)]
print(f"原对象:{l1}")
l2 = copy.copy(l1)
l1.append(100)
l1[0].append(3)
print(f"原对象修改:{l1}")
print(f"浅拷贝后的新对象:{l2}")
原对象:[[1, 2], (30, 40)]
原对象修改:[[1, 2, 3], (30, 40), 100]
浅拷贝后的新对象:[[1, 2, 3], (30, 40)]
# 深度拷贝:新对象和原对象没有任何关联
import copy
l1 = [[1, 2], (30, 40)]
print(f"原对象:{l1}")
l2 = copy.deepcopy(l1)
l1.append(100)
l1[0].append(3)
print(f"原对象修改:{l1}")
print(f"深度拷贝后的新对象:{l2}")
原对象:[[1, 2], (30, 40)]
原对象修改:[[1, 2, 3], (30, 40), 100]
深度拷贝后的新对象:[[1, 2], (30, 40)]
3.5.6 用一个深度拷贝,拷贝一个无限嵌套的列表,是否相等
import copy
x = [1]
x.append(x)
y = copy.deepcopy(x)
print(len(y)) # 输出为2
if x == y:
print(True)
else:
print(False)
# 运行报错:RecursionError: maximum recursion depth exceeded in comparison

总结

一、赋值:

在 Python 中,对象的赋值就是简单的对象引用,这点和 C++不同

二、浅拷贝(shallow copy):

浅拷贝会创建新对象,重新分配内存,其内容非原对象本身的引用,而是原对象内第一层对象的引用。浅拷贝有三种形式:切片操作工厂函数copy 模块中的 copy 函数

三、深拷贝(deep copy):

深拷贝只有一种形式,copy 模块中的 deepcopy()函数。深拷贝和浅拷贝对应,深拷贝拷贝了对象的所有元素,包括多层嵌套的元素。因此,它的时间和空间开销要高。

四、拷贝的注意点:

1、对于非容器类型,如数字、字符,以及其他的“原子”类型,没有拷贝一说,产生的都是原对象的引用

2、如果元组变量值包含原子类型对象,即使采用了深拷贝,也只能得到浅拷贝。

写在最后:

吐血整理,不过好在是终于理清了,文章中所有的例子可以在jupyter nootbook上运行,转载请注明出处,谢谢!!

Python比较操作符、变量赋值、对象拷贝的更多相关文章

  1. Python中的变量引用对象需注意的几点

    Python中的变量引用对象需注意的几点 分类:Python (55)  (0) 普通引用: Python中,变量的作用仅仅是一个标识,只有赋值后才被创建,它可以引用任何类型的对象,而且在引用之前必须 ...

  2. 第2章 Python编程基础知识 第2.1节 简单的Python数据类型、变量赋值及输入输出

    第三节 简单的Python数据类型.变量赋值及输入输出 Python是一门解释性语言,它的执行依赖于Python提供的执行环境,前面一章介绍了Python环境安装.WINDOWS系列Python编辑和 ...

  3. python中的变量与对象

    一. 什么是变量 变量就是以前学习的数学中常见的等式x = 3(x是变量,3是变量值),在编程中,变量不仅可以是数学,还可以是任意数据类型 二. 变量的命名规则 变量名必须是英文大小写.数字和_的组合 ...

  4. Python基础-注释-变量赋值

    一.注释 # 注释 \n 行分隔符 \ 继续上一行 '''   *** ''' 多行注释 二.基本规则 : 分开代码块(组)   头$尾 缩进块  语句代码块  用缩进深度区分 空行     用于分割 ...

  5. Python中的变量、对象

    由于没时间系统学习下Python 只能见一个问题探究一个问题了 一.初级 - 对象 关于Python中的数据类型,今天重新认识了下.[参考] 首先,Python中,对象才有类型, 变量是没有类型的,它 ...

  6. Python学习四|变量、对象、引用的介绍

    变量 变量创建:一个变量也就是变量名,就像a,当代码第一次赋值时就创建了它.之后的赋值将会改变已创建的变量名的值,从技术上讲,Python在代码运行之前先检测变量名,可以当成是最初的赋值创建了变量. ...

  7. 【python】字符串变量赋值时字符串可用单或双引号

    >>> name='萧峰' >>> print(name) 萧峰 >>> name="独孤求败" >>> p ...

  8. python多个变量赋值

    a, b = 3, 4 c, d = 3, 4 a, b = b, a + b c = d d = c + d print(a, b, c, d) 输出: 4 7 4 8 因为a, b和b, a + ...

  9. python中的变量和字符串

    一.变量 1.python变量 *变量用于存储某个或某些特定的值,它与一个特定标识符相关联,该标识符称为变量名称.变量名指向存储在内存中的值.在创建变量时会在内存中开辟一个空间.基于变量的数据类型,解 ...

随机推荐

  1. jquery 手写一个简单浮窗的反面教材

    前言 初学jquery写的代码,陈年往事回忆一下. 正文 介绍一下大体思路 思路: 1.需要控制一块区域,这块区域一开始是隐藏的. 2.这个区域需要关闭按钮,同时我需要写绑定事件,关闭的时候让这块区域 ...

  2. Cypress系列(18)- 可操作类型的命令 之 点击命令

    如果想从头学起Cypress,可以看下面的系列文章哦 https://www.cnblogs.com/poloyy/category/1768839.html 前言 啥是可操作类型?就是可以和 DOM ...

  3. sql 获取当前时间的前一天,不加时分秒

    select convert(datetime,convert(char(20),dateadd(day,-1,getdate()),102)) -1 为减去天数 getdate 为 获取当前时间

  4. centos7上安装redis以及PHP安装redis扩展(二)

    PHP 使用 Redis 安装 开始在 PHP 中使用 Redis 前, 我们需要确保已经安装了 redis 服务及 PHP redis 驱动,且你的机器上能正常使用 PHP. 接下来让我们安装 PH ...

  5. JVM内存结构详解

    从java编程语言说起... 1. Java编程语言简介 1.1 编程语言概述 系统级和应用级 系统级:C,C++,go,erlang 应用级:C#,Java,Python,Perl,Ruby,php ...

  6. Vue —— 精讲 VueRouter(1)

    最近被Boos调去给新人做培训去了,目前把自己整理的一些东西分享出来,希望对大家有所帮助 本章节为VueRouter前端 路由的章节部分 大纲 一.基本概念 路由就是通过网络把讯息从源地址传输到目的地 ...

  7. SpringColud Eureka的服务注册与发现

    一.Eureka简介 本文中所有代码都会上传到git上,请放心浏览 项目git地址:https://github.com/839022478/Spring-Cloud 在传统应用中,组件之间的调用,通 ...

  8. 如何从二进制文件中读取int型序列

    使用的主要函数是int.from_bytes 代码如下: f = open('./T26.dat', 'rb') for i in range(20): A = f.read(2) A = int.f ...

  9. tarjan算法求scc & 缩点

    前置知识 图的遍历(dfs) 强连通&强连通分量 对于有向图G中的任意两个顶点u和v存在u->v的一条路径,同时也存在v->u的路径,我们则称这两个顶点强连通.以此类推,强连通分量 ...

  10. MySQL-数据库和表的基本操作

    数据库和表的基本操作 数据库基础知识 创建数据库 CREATE DATABASE 数据库名称 ; 查看数据库(显示数据库名列表) SHOW DATABASES ; 查看某数据库信息(显示创建的信息) ...