人生苦短 ,我用python ----压箱底文章

解释器

计算机不能直接理解任何除机器语言以外的语言,所以必须要把程序员所写的程序语言翻译成机器语言。计算机才能执行程序。将其他语言翻译成机器语言的工具,被称为编译器

编译器编译的方式有两种:一个是编译,另一个是解释。两种方式之间的区别在于翻译时间点的不同。

编译性语言:程序在执行之前需要一个专门的编译过程,把程序编译成为机器语言的文件,运行时不需要重新翻译,直接使用编译的结果就行了,程序执行效率高,依赖编译器,跨平台性差些。如C、C++

解释性语言:解释型语言编写的程序不进行预先编译,以文本方式存储程序代码,会将代码一句一句直接运行。在发布程序时,看起来省了道编译工程,但是在运行程序的时候,必须先解释再运行。如python
两者对比:

  • 速度—编译型语言比解释型语言执行速度快
  • 跨平台性—解释型语言比编译型语言跨平台性好

Python 的设计哲学

优雅
明确
简单

Python 特点

  • Python 是完全面向对象的语言
  • Python拥有一个强大的标准库
  • Python 社区提供了大量的第三方模块

Python 程序执行原理

在这里插入图片描述
程序就是用来处理数据的,而变量就是用来存储数据的。

定义变量(赋值)

在这里插入图片描述
在python中,定义变量时是不需要指定变量的类型的,在运行的时候,python解释器会根据赋值语句等号右侧的数据自动推导出变量中保存数据的准确类型。

在python中 变量和数据是分开存储的,数据保存在内存中的一个位置,变量中保存着数据在内存中的地址,变量中记录数据的地址,就叫做引用。使用id()函数可以查看变量中保存数据所在的 内存地址。
如果变量已经被定义,当给一个变量赋值的时候,本质上是 修改了数据的引用(变量的名字类似于 便签纸 贴在数据上)

调用函数时,本质上传递的是实参保存数据的引用,而不是实参保存的数据;函数返回的是数据的引用,而不是数据本身

局部变量
全局变量
在python中不允许在函数中直接修改全局变量,如果想要修改的话,使用global 变量名

常量

定义常量和定义变量的语法完全一样,都是使用赋值语句
常量 的 命名 应该 所有字母都使用大写,单词与单词之间使用下划线连接

数据类型

python中数据类型可以分为 数字型(整型、浮点型、布尔型、复数型)和非数字型(字符串、元祖 -》不可变 列表、字典 -》可变)
非数字型变量都支持以下特点:
1.都是一个序列(sequence),也可以理解为容器
2.取值[]
3.遍历 for in
4.计算长度、最大/最小值、比较、删除 (公共的内置函数有:len() del() max() min() 比较大小><)
5.链接 + 重复 * (字典除外) in not in(成员运算符,对于自定是判断key)
6.切片(字典除外)

列表(其他语言中叫数组

在这里插入图片描述
clear 方法可以清空列表,del 关键字本质上用来讲一个变量从内存中删除
‘’+ append 的区别是 + 号直接生成了一个新的列表,而extend直接在原来的列表上追加列表,append也是修改原来的列表,追加元素,而+= 本质上是在执行列表变量的extend 方法,不会修改变量的引用

元组

Tuple 与列表类似,不同之处在于元组的 元素不能修改
创建一个空元组:
info_tuple = ()
当元组中只包含一个元素时,需要在元素后面添加逗号,如:
info_tuple = (50,),如果为info_tuple = (50)就不是tuple类型而是int类型
tuple 只有两个方法 count index

交换两个数字的时候可以使用元组:a, b = b ,a 后面接收的是元组

元组的应用场景:
1.函数的参数和返回值,一个函数可以接收任意多个参数,或者一次返回多个数据
2.格式字符串 ,格式字符串后面的()本质上就是一个元组
3.让列表不可以被修改,以保护数据安全

字典

通常用于存储描述一个 物体 的相关信息,key值 是 不可变类型(数字、字符、元组),在python中,设置字典的 键值对 时,会首先对key进行 hash 已决定如何在内存中保存字典的数据
和列表的区别:列表是 有序 的对象集合,字典是 无序 的对象集合
增删改查
取值:通过key : student_dict[“name”]
增加: :student_dict[“age”] =18(这个key不存在)
修改: :student_dict[“name”] =“lisi”(这个key存在)
删除 :student_dict.pop(“name”)
方法:
len 统计键值对数量
update 合并字典(如果被合并的字典中包含已经存在的键值对,会覆盖原有的键值对)
clear 清空字典 student_dict.clear()
列表和字典 一起使用,将多个字典放在列表里面,同时执行相同的操作

元组和字典的拆包:

def demo(*args, **kwargs):
    print(args)
    print(kwargs)


# 需要将一个元组/字典变量传递给函数对应的参数
gl_nums = (1, 2, 3)
gl_dict = {"name": "wenmei", "age": 17}
# 会将gl_nums和gl_dict作为元组传递给args
demo(gl_nums,gl_dict)  # ((1, 2, 3), {'name': 'wenmei', 'age': 17})  {}
# 将gl_nums和gl_dict分别传给元组和字典
demo(*gl_nums, **gl_dict)  # (1, 2, 3)  {'name': 'wenmei', 'age': 17}

字符串

在字符串中经常会出现如下报错:
TypeError: 'str' object does not support item assignment,因为字符串是不可变类型
在python中可用使用一对双引号 “”或者一对单引号‘’定义一个字符串,大多数编程语言都是使用“”来定义字符串, 因此最好使用双引号
如: str1 = “hello world”
但是当在字符串中也是用双引号的时候,外层可以使用单引号,如:
str2 = ‘hello world “my dog”’
方法:
len 统计字符串长度
count 统计某一个子字符串出现的次数
index 判断某一个字符串出现的位置 ,find 同样可以查找指定的字符串在大字符串中的索引,区别是:index如果指定的字符串不存在,会报错;find如果指定的字符串不存在,会返回-1
在这里插入图片描述
在这里插入图片描述
其中,replace方法执行完成之后,会返回一个新的字符串,但是不会修改原有字符串的内容

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

str = "你只是来过并没有懂得 \t \n 你只是说着却没有爱过\r \t 你只是看到了我的微笑"
str_new = str.split()#把一个大的字符拆分成字符串列表,分割字符默认为 \r lt \n 空格
print(str_new) #['你只是来过并没有懂得', '你只是说着却没有爱过', '你只是看到了我的微笑']
#join 将字符串列表拼接成大的字符串  string.join()  以string作为分隔符,将seq中所有的元素(的字符串表示)合并为一个新的字符串
str_join = " ".join(str_new)#你只是来过并没有懂得 你只是说着却没有爱过 你只是看到了我的微笑
print(str_join)

字符串切片:字符串[开始索引:结束索引:步长]
逆序:num_str[::-1]

IPython 中的 I 代表交互 interactive ,是一个python的交互式shell,比默认的python shell好用的多

默认情况下,print函数输出内容之后,会自动在内容末尾增加换行,如果不希望增加换行,可以在print函数输出的内容的后面增加,end=“”
如:

print("*", end="")
print("hello world")

三大流程:顺序 分支 循环

判断 if elif else 判断嵌套
循环 while for 循环嵌套
(一般都是for in 但是也可以与else搭配,应用场景:在 迭代遍历 嵌套的数据类型时,例如一个列表包含了多个字典,需求:需要判断某一个字典中 是否存在指定的值,如果存在,提示并且退出循环,如果不存在,在循环整体结束后,希望得到一个统一的提示)
在这里插入图片描述

#打印星星
#第一种 简单
row = 1
while row <= 5:
    print("*" * row)
    row += 1
#第二种 循环嵌套
row = 1
while row <= 5:
    col = 1
    while col <= row:
        print("*", end="")
        col += 1
    print("") # 这行很重要,如果没有,星星会显示在一行
    row += 1
#打印九行九列的小星星  和  九九乘法表很像(列号*行号)用到了格式化输出
row = 1
while row <= 9:
    col = 1
    while col <= row:
        print("%d * %d = %d" %(col, row, col * row),end="\t")#注意end后面的\t是转义字符,使得在输出文本时,垂直方向 保持对齐
        col += 1
    print("")
    row += 1

模块

模块好比是工具包,要想使用这个工具包中的工具,就需要导入import这个模块
每一个以扩展名py结尾的python源代码文件都是一个模块
在模块中定义的 全局变量、函数、类 都是模块能够提供给外界直接使用的工具

from 模块名1 import 工具名 # 从某一个模块导入部分工具,不需要 通过 模块名.调用,直接使用
from 模块名1 import * # 从某一个模块导入全部工具,不需要 通过 模块名.调用,直接使用
如果两个模块,存在同名的函数,那么后导入模块的函数,会覆盖掉先导入的函数

python解释器 在导入模块时,会:
1.搜索 当前目录 指定模块名的文件,如果有就直接导入
2.如果没有,再搜索 系统目录
因此,在开发时,给文件起名,不要和系统的模块文件重名

python 中每一个模块都有一个内置属性__file__可以 查看模块 的 完整路径

一个独立的 python文件 就是一个 模块,在导入文件时,文件中 所有没有任何缩进的代码 都会被执行一遍!
name 属性使得测试模块的代码只在测试情况下被运行,而在被导入时不会被执行。name 一般是__main__

导入模块时,建议按照以下顺序导入:
1.官方标准模块导入
2.第三方模块导入
3.应用程序模块导入

包(Package)

包是一个 包含多个模块 的特殊目录
目录下有一个 特殊的文件 init.py,需要在此文件中指定 对外界提供的模块列表,如from . import send_message
保命的 命名方式 和变量名 一致,小写字母+_
好处:使用import包名可以一次性导入 包 中所有的模块

Pyc(compiled)

_ pycache _目录会有pyc文件,这个文件是由python解释器将 模块的源码 转换为 二进制字节码。python这样保存字节码是作为一种启动速度的优化
因此python在解释源程序时是分成两个步骤的:
1.首先处理源代码,编译生成了一个二进制字节码
2.再对字节码进行处理,才会生成CPU能够识别的机器码

函数

函数的参数,无论传递的参数是可变 还是 不可变 ,只要针对 参数使用赋值语句,会在函数内部 修改局部变量的引用,不会影响到 外部变量的引用;如果传递的参数是可变类型,在函数内部,使用 方法 修改了数据的内容,同样会影响到外部的数据。
函数的递归
特点:一个函数 内部 调用自己
代码特点:1.函数内部的代码是相同的,只是针对参数不同,处理的结果不同 2.当参数满足一个条件时,函数不再执行,通常被称为递归的出口,否则会出现死循环

面向对象(OOP)

object Oriented Programming 面向对象编程
面向过程 -------怎么做
1.把完成某一个需求的所有步骤 从头到尾 逐步实现
2.根据开发需求,将某些 功能独立 的代码封装成一个又一个 函数
3.最后完成的代码,就是顺序地调用不同的函数
特点:
1.注重 步骤和过程,不注重职责分工
2.如果需求复杂,代码会变得很复杂
3.开发复杂项目,没有固定的套路,开发难度很大
在这里插入图片描述
面向对象-------谁来做?
相比较函数,面向对象 是 更大的封装,根据职责 在 一个对象中封装多个方法
1.在完成某一个需求前,首先确定 职责—要做的事情(方法)
2.根据职责 确定不同的对象,在对象内部封装不同的方法(多个)
3.最后完成的代码,就是顺序地让不同的对象调用不同的方法
特点:
1.注重 对象和职责 ,不同的对象承担不同的职责
2.更加适合应对复杂的需求变化,是专门应对复杂项目开发,提供的固定套路
3.,需要在面向过程的基础上,再学习一些面向对象的语法
类和对象是面向对象编程的两个核心概念


是 对一群具有相同特征 或者行为的事物的一个统称,是抽象的,不能直接使用(类是一个模板【图纸】,是负责创建对象的)
特征 ------属性 ------- 在__init__方法内部使用self.属性名 =属性的初始值 就可以定义属性
行为------ 方法 ---------方法的定义格式和之前的函数几乎一样,区别在于第一个参数必须是self,self表示 当前调用方法的对象自己

设计 类名 需要满足 大驼峰命名法(每个单词的首字母大写,单词与单词之间没有下划线)

私有属性和私有方法
在定义属性或方法时,在属性名或者方法名前 增加 两个下划线,定义的就是私有 属性或方法
在对象的方法内部,是可以访问对象的私有属性的;在外界不能被直接访问。
伪私有属性和私有方法
python中,并没有 真正意义 的私有
1.在给属性、方法命名时,实际是对 名称 做了一些特殊处理,使得外界无法访问到
2.处理方式: 在名称前面加上 _类名=>_类名__名称
父类的 私有属性和私有方法
1.子类对象不能在自己的方法内部,直接访问父类的私有属性或私有方法
2.子类对象 可以 通过父类的公有方法 间接 访问到 私有属性 或 私有方法
在这里插入图片描述

对象
是 由类创建出来的一个具体存在,可以直接使用
由哪一个类创建出来的对象,就拥有在哪一个类中定义的:属性 和方法【对象就相当于用图纸制造的飞机】
(创建出来的对象 叫做 类的实例,创建对象的 动作 叫做 实例化,对象的属性叫做实例属性,对象调用的方法叫做实例方法。)

对象变量 = 类名()创建对象时,会自动执行以下操作
1.以对象 在内存中 分配空间 -----创建对象 (__ new__,是一个由object基类提供的内置的静态方法)
2.为对象的 属性 设置初始值 ---------初始化方法(__ init__,是对象的内置方法)
若希望在创建对象的同时,就设置对象的属性,可以在 __init__方法内部使用self. 属性 = 形参接收外部传递的参数,在创建对象时,使用 类名(属性1,属性2…)调用

每一个对象 都有自己 独立的内存空间,保存各自不同的属性;多个对象的方法,在内存中只有一份,在调用方法时,需要把对象的引用传递到方法内部

一个对象的 属性 可以是另外一个类创建的对象

类和对象的关系
类是模板,对象是根据类 这个模板 创建出来的
在程序开发中,应该先有类,再有对象
类只有一个,而对象可以有很多个,不同对象之间属性可能会各不相同

面向对象基础语法:
在python中 对象 几乎是无处不在的,我们之前学习的变量、数据、函数都是对象
使用内置函数dir传入标识符/数据,可以查看对象内的所有属性以及方法

内置方法:
del (对象被从内存中销毁前,会自动调用) 、
str返回对象的描述信息,print函数输出使用,必须要返回一个字符串)【在python中,使用print输出对象变量,默认情况下,会输出这个变量引用的对象是由哪一个类创建的对象,以及在内存中的地址 十六进制表示】

面向对象的三大特性:封装、继承、多态

封装
1.封装 是面向对象编程的一大特点
2.面向对象编程的第一步 ,是将属性和方法封装到一个抽象的类中
3.外界 使用 类创建对象,然后 让对象调用方法
4.对象方法的细节 都被 封装在类的内部
注: 在对象的方法内部,是可以直接访问对象的属性的

继承
继承 实现代码的重用,相同的代码不需要重复的编写
子类 继承自 父类,可以直接 享受 父类中已经封装好的方法,不需要再次开发
子类中应该根据职责,封装 子类特有的属性和方法
继承语法:

class 类名(父类名):
pass

专业术语:Dog 类 是 Animal 类的子类,Animal 类是Dog类的父类,Dog类从Animal 类继承==Dog类是Animal类的派生类,Animal 类是Dog类的基类,Dog类从Animal类派生
继承拥有传递性,C类从B类继承,B类又从A类继承,那么C类就具有B类和A类所有属性和方法。子类拥有父类以及父类的父类中封装的所有属性和方法

方法的重写
当父类的方法实现不能满足子类需求时,可以对方法进行重写
重写父类方法有两种情况:
1.覆盖父类的方法(在子类中定义了一个和父类同名的方法并且实现)
2.对父类方法进行扩展(1. 在子类中重写父类的方法 2.在需要的位置使用 super().父类方法 来调用父类方法的执行 3.代码其他的位置针对子类的需求,编写 子类特有的代码实现)

多继承
子类 可以拥有 多个父类,并且具有 所有父类 的属性和方法

语法
class 子类名(父类名,父类名…):
pass

多态
多态 不同的 子类对象 调用相同的 父类方法,产生不同的执行结果,增加代码的灵活度。
以 继承 和 重写父类方法为前提

身份运算符

身份运算符用于 比较 两个对象的 内存地址 是否一致 --------是否是对同一个对象的引用(is / is not)
在python中针对None 比较时,建议 使用 is 判断,如 x is y 类似于 id(x) == id(y)
is 与 ==区别
is 用于判断两个变量引用的对象是否为同一个
== 用于判断引用变量的值是否相等

类是一个特殊的对象(类对象)

在程序运行时,类对象 在内存中 只有一份,使用 一个类 可以创建出很多个对象实例

类属性:就是针对 类对象 定义的属性
1.使用 赋值语句 在class 关键字下方可以定义类属性
2.类属性用于记录与这个类相关的特征

类方法:就是针对类对象定义的方法(只需要访问类属性)
在 类方法 内部可以直接访问 类属性或者调用其他的类方法
语法如下:

@classmethod
def  类方法名(cls):
	pass

类方法需要用 修饰器 @classmethod 来标识,告诉解释器这是一个类方法
类方法的 第一个参数 应该是cls
通过类名. 调用 类方法

实例方法:方法内部需要访问实例属性或者类属性

静态方法:

在开发时,如果需要在类中封装一个方法,这个方法:
既 不需要 访问实例属性 或者 调用实例方法
也 不需要 访问类属性 或者 类方法
这个时候,可以把这个方法封装成一个静态方法
语法如下:

@staticmethod
def  静态方法名():
	pass

通过类名. 调用 静态方法

单例

在这里插入图片描述
单例----让 类 创建的对象,在系统中 只有 唯一的一个实例
1.定义一个类属性,初始值是None,用于记录 单例对象的引用
2.重写 __new__方法
3.如果类属性 is None,调用父类方法分配空间,并在类属性中记录结果
4.返回 类属性中记录的对象引用

class MusicPlayer:
    # 记录第一个被创建对象的引用
    instance = None
    def __new__(cls, *args, **kwargs):
        # 判断类属性是否是空对象
        if cls.instance is None:
            # 调用父类的方法,为第一个对象分配空间
            cls.instance = super().__new__(cls)
            # 返回类属性保存对象引用
        return cls.instance

player1 = MusicPlayer()
print(player1) # <__main__.MusicPlayer object at 0x0000019F3DB2FA20>
player2 = MusicPlayer()
print(player2) # <__main__.MusicPlayer object at 0x0000019F3DB2FA20>(每次都会得到第一次被创建对象的引用)

以上初始化 会执行两次,那初始化动作 怎么可以执行一次?
定义一个类属性init_flag 标记是否执行过初始化动作,初始值为False;在__init__方法中,判断init_flag,如果为False 就执行初始化动作;然后将init_flag设置为True;这样,再次自动调用__init__方法时,初始化动作就不会被再次执行

_ _ new _ _

主要作用:

  1. 在内存中为对象 分配空间
  2. 返回 对象的引用
    python解释器获得对象的引用后,将引用作为第一个参数,传递给__init__方法
    重写 __new__方法一定要return super().new(cls),否则Python 的解释器得不到分配空间的对象引用,就不会调用对象的初始化方法

异常

try# 尝试执行的代码
except 错误类型1# 针对错误类型1,对应的代码处理
except 错误类型2# 针对错误类型2,对应的代码处理
except Exception as result:
	print("未知错误 %s",% result)
else:
	#没有异常才会执行的代码
	pass
finally:
	# 无论是否有异常,都会执行的代码
	print("无论是否有异常,都会执行的代码")

捕获未知错误
在开发时,要预判到所有可能出现的错误,还是有一定难度的,如果希望程序无论出现任何错误,都不会因为python解释器 抛出异常而被终止,可以再增加一个except
except Exception as result:
print(“未知错误 %s”,% result)

异常的传递性
当函数/方法 执行出现异常,会 将异常传递 给 函数/方法 的调用一方
如果 传递到主程序,仍然没有异常处理,程序才会被终止

利用 异常的传递性,在主程序捕获异常,因为在主函数调用的其他函数,只要出现异常,都会传递到主函数的异常捕获中,这样就不需要在 代码中,增加大量的 异常捕获,能够保证代码的整洁。

主动抛出异常
1.创建 一个 Exception 的对象
2.使用raise 关键字 抛出 异常对象

文件

三部曲:
1.打开文件 open 打开文件,并且会返回文件操作对象
2.读、写 文件
读 将文件内容读入内存 read
写 将内存内容写入文件 write
3.关闭文件 close

open 函数默认以 只读方式 打开文件,并且返回文件对象
f = open(“文件名”,“访问方式”)
r 以只读 方式打开文件(默认)
w 只写(如果文件存在会被覆盖,如果文件不存在,创建新文件)
a 追加
r+ w+ a+ 以读写方式打开文件
read() 读取文件全部内容, readline()读取文件内容的一行

文本文件的编码格式

文本文件存储的内容是基于 字符编码 的文件。常见的编码有ASCII 编码,UNICODE 编码等
计算机中只有256个 ASCII 字符,一个ASCII在内存中占用1个字节的空间。8个0/1 的排列组合方式一共有 256种
计算机中使用1~6个字节来表示一个UTF-8字符,涵盖了地球上几乎所有地区的文字,大多数汉字会使用3个字节表示,UTF-8是UNICODE编码的一种编码格式

python 2.x 默认使用ASCII 编码(使用 # * - * coding:utf8 * - *识别中文)
python 3.x 默认使用UTF-8 编码
在这里插入图片描述

python可变与不可变

可变不可变,是指内存中的那块内容(value)是否可以被改变
可变类型(mutable),创建后可以继续修改对象的内容(值):字典,列表
不可变类型(unmutable),一旦创建就不可修改的对象(值):数字,字符串,元组
在这里插入图片描述这⾥的可变不可变,是指内存中的那块内容(value)是否可以被改变。如果是不可变类型,在对对象本身操作的时候,必须在内存中新申请⼀块区域(因为⽼区域#不可变#)。如果是可变类型,对对象操作的时候,不需要再在其他地⽅申请内存,只需要在此对象后⾯连续申请(+/-) 即可,也就是它的address会保持不变,但区域会变⻓或者变短。

python中深浅拷贝(python的copy模块)

基本概念
浅拷贝:
引用(地址)拷贝,并没有产生新的空间。如果拷贝的是对象,原对象和copy对象都指向同一个内存空间,只拷贝父对象,不会拷贝对象的内部的子对象。
用法:copy.copy(变量名)会进行拷贝

深拷贝:
会产生 新的空间。如果拷贝的是对象,原对象和copy对象都指向不同的内存空间,会拷贝对象及其子对象(会产生新的空间)
用法:copy.deepcopy(变量名)会进行拷贝

作用
1、减少内存的使⽤
2、以后在做数据的清洗、修改或者⼊库的时候,对原数据进⾏复制⼀份,以防数 据修改之后,找不到原数据。

简单可变类型的深浅拷贝问题

简单可变类型的浅拷贝,会产生新的空间,能够保持各自的独立性
简单可变类型的深拷贝,会产生新的空间,能够保持各自的独立性

===============简单可变类型的浅拷贝==============================
import copy
list1 = [1, 2, 3]
print("list1=", list1, id(list1))
# 浅拷贝
list2 = copy.copy(list1)
print("list2= ", list2, id(list2))
# 修改list2
list2.append(7)
print("list2= ", list2, id(list2))
print("list1=", list1, id(list1))
# 输出
# list1= [1, 2, 3] 2331125397192
# list2=  [1, 2, 3] 2331125322056
# list2=  [1, 2, 3, 7] 2331125322056
# list1= [1, 2, 3] 2331125397192

======================简单可变类型的深拷贝================
import copy
list1 =[1, 2, 3]
print("list1= ", list1, id(list1))

# 深拷贝
list2 = copy.deepcopy(list1)
print("list2= ", list2,id(list2))
# 修改list2
list2.append(7)
print("list2= ", list2,id(list2))
print("list1= ", list1, id(list1))
# 输出
# list1=  [1, 2, 3] 3070569854664
# list2=  [1, 2, 3] 3070569779464
# list2=  [1, 2, 3, 7] 3070569779464
# list1=  [1, 2, 3] 3070569854664

复杂可变类型的深浅拷贝问题

复杂可变类型的深浅拷贝问题:

  • 浅拷贝,拷贝的是顶层对象的值,子对象不会拷贝(只是引用子对象),无法保持独立性
  • 深拷贝,拷贝的是顶层对象和子对象,子对象会产生新的内存空间
======================复杂可变类型的浅拷贝================
import copy
A = [1, 2, 3]
B = [11, 22, 33]
C = [A, B] #[[1, 2, 3], [11, 22, 33]]

print("A = ", A, id(A))
print("B = ", B, id(B))
print("C = ", C,id(C))
print("C[0] = ", C[0], id(C[0])) # 是A的地址
# 进行浅拷贝
D = copy.copy(C)
print("D = ", D,id(D))
print("D[0]= ", D[0], id(D[0]))
# 修改A 的值
A[0] = 10
print("A = ", A, id(A))
print("D[0]= ", D[0], id(D[0]))

结果如下:()
在这里插入图片描述
在这里插入图片描述

那深拷贝呢?
在这里插入图片描述
在这里插入图片描述

  1. 赋值是将⼀个对象的地址赋值给⼀个变量,让变量指向该地址(旧瓶装旧酒)。
  2. 浅拷⻉是在另⼀块地址中创建⼀个新的变量或容器,但是容器内的元素的地址均是源对象的元素 的地址的拷⻉。也就是说新的容器中指向了旧的元素(新瓶装旧酒)。
  3. 深拷⻉是在另⼀块地址中创建⼀个新的变量或容器,同时容器内的元素的地址也是新开辟的,仅 仅是值相同⽽已,是完全的副本。也就是说(新瓶装新酒)。

简单不可变类型的深浅拷贝

不可变类型,不管浅拷贝还是深拷贝,都不会开辟新的空间,而是直接引用了被拷贝的数据的地址

======================不可变类型的浅拷贝(副本和源指向同一个空间)================
import copy
tuple1 = (1, 2, 3)
print("tuple1= ", tuple1, id(tuple))
# 浅拷贝
tuple2 = copy.copy(tuple1)
print("tuple2= ", tuple2, id(tuple))
# 输出
# tuple1=  (1, 2, 3) 140718215817744
# tuple2=  (1, 2, 3) 140718215817744

======================不可变类型的深拷贝(副本和源指向同一个空间)================
import copy
tuple1 = (1, 2, 3)
print("tuple1= ", tuple1, id(tuple))
#  深拷贝
tuple2 = copy.deepcopy(tuple1)
print("tuple2= ", tuple2, id(tuple))
# 输出
# tuple1=  (1, 2, 3) 140718215817744
# tuple2=  (1, 2, 3) 140718215817744

在这里插入图片描述

复杂不可变类型深浅拷贝问题

浅拷贝:直接引用
深拷贝:看数据具体是可变还是不可变的,如果数据是可变的,会产生新的空间,保持数据的独立性

======================复杂不可变类型浅拷贝================
# 只关⼼最外层的数据类型是什么,如果是不可变类型,直接引⽤,没有办法保证数据的独⽴性
import copy
A = [1, 2]
B = [3, 4]
# 定义元组(不可变)
C = (A, B)
print("C = ", C, id(C))
# 复杂不可变类型浅拷贝
D = copy.copy(C)
print("D = ",D,id(D))
print("D[0]",id(D[0]), "C[0]", id(C[0]), "A", id(A))
 D[0][0] = 10 # D[0]是一个列表,是可变的[0][0]修改值
 print("D = ",D,id(D),"C= ",C,id(C))
 # D[0] = [5, 6]  报错,因为修改的是元组的第一个元素 是不可变类型
# 输出
# C =  ([1, 2], [3, 4]) 2221000837064
# D =  ([1, 2], [3, 4]) 2221000837064
# D[0] 2221001123528 C[0] 2221001123528 A 2221001123528
# D =  ([10, 2], [3, 4]) 1762738849800 C=  ([10, 2], [3, 4]) 1762738849800

在这里插入图片描述

======================复杂不可变类型深拷贝================
import copy
A = [1, 2]
B = [3, 4]
# 定义元组(不可变)
C = (A, B)
print("C = ", C, id(C))

# 复杂不可变类型深拷贝
D = copy.deepcopy(C)
print("D = ", D, id(D))
print("D[0]", id(D[0]), "C[0]", id(C[0]), "A", id(A))
D[0][0] = 10
print("D = ", D, id(D))
print("C = ", C, id(C))
# 输出
# C =  ([1, 2], [3, 4]) 1883794065160
# D =  ([1, 2], [3, 4]) 1883792082248
# D[0] 1883794593096 C[0] 1883794483016 A 1883794483016
# D =  ([10, 2], [3, 4]) 1861224712328
# C =  ([1, 2], [3, 4]) 1861224580744

在这里插入图片描述

切片拷贝、字典拷贝

切片拷贝 [:] 是一种浅拷贝

A = [1, 2, 3]
B = [11, 22, 33]
C = (A, B)
print("C = ", C, id(C))
D = C[:]
print("D = ", D, id(D))
# 输出
# C =  ([1, 2, 3], [11, 22, 33]) 2464795097224
# D =  ([1, 2, 3], [11, 22, 33]) 2464795097224

字典拷贝,是一种浅拷贝

dict1 = {"age":[1, 2]}
print("dict1 = ", dict1, id(dict1))
# 字典拷⻉, 是通过对象⾃带的copy() ⽅法拷⻉的
dict2 = dict1.copy()
print("dict2 = ", dict2, id(dict2))
dict1['age'][0] = 100
print("dict1 = ", dict1, id(dict1))
print("dict2 = ", dict2, id(dict2))

在这里插入图片描述

import模块

import搜索路径(sys.path)

  • 存在的问题: 当我们把模块文件放到工程文件夹的外部的文件,发现无法正常引入模块

  • 原因: 外部的文件夹的路径,没有放到环境变量中

  • 查看环境变量:1.import sys 2.sys.path查看环境变量,返回值是列表
    在这里插入图片描述

  • 把自己写的模块的路径加入到环境变量中(运行的时候修改):

sys.path.append('/home/mnt/....')
sys.path.insert(0,'/home/mnt/....'# 放在第一个,可以确保优先搜索这个路径

import 重新导入模块(reload)

为什么需要重新导⼊模块?
import 导⼊模块后,如果模块被修改, import module 不能重新导⼊模块(import 自动防止重复包含)
解决方案:
重新导⼊ ⽤ reload 重新加载模块代码创建模块对象

from imp import reload
reload(要重新加载的模块)
执行模块中的方法

from…import的私有化问题

  • 私有化: 模块中的一些变量不希望被其他模块导入,可以使用私有化解决
  • 私有化使用的前提: 必须使用from xxx imoport *
  • 用法:在模块中,在变量前增加一个下划线 _变量名
  • 注意:如果使用其他方式导入模块,私有化将无效,如:
    from xxx import _私有变量
    print(_私有变量) 不会报错

import 和 from…import的区别

  • 写法:
import 模块名          使用: 模块名.变量/函数/from ....import *     使用: 变量名/函数/
  • 底层区别:
import 模块名          直接引用源模块的 变量名/函数/from ....import *     拷贝源模块的  变量名/函数/类 到当前自己类

在这里插入图片描述

可变参数的拆包问题

在这里插入图片描述

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值