模块

模块

#什么是模块
	模块就是一系列功能的结合体

#模块的三种来源
	1.内置模块	(python解释器自带的)
    2.第三方模块	(别人写的,requests)
    3.自定义模块	(自己写的)
    
#模块的四种表现形式
	1.使用python编写的py文件(一个py文件也可以成为一个模块)
    2.已被编译为共享库或DLL的C或C++扩展
    3.把一系列模块组织到一起的文件夹(文件夹下有一个__int__.py文件,该文件夹称之为包,包就是一系列py文件的集合体)
    4.使用C编写,并连接到python解释器的内置模块
    
#为什么用模块
	1.用别人写好的模块(内置的、第三方的),可以拿来就用,极大地提高开发效率
    2.使用自己写的模块(自定义的)
    	当程序比较大的时候,所有项目不可能只在一个py文件中
        当多个文件中都需要使用相同的方法的时候,可以将该公共的方法写到一个py文件中
        其他的文件以模块的形式导过去直接调用即可
        达到减少代码冗余的目的
    3.项目结构更加清晰

#怎么用模块
 	注意,一定要区分哪个是执行文件,哪个是被执行文件(导入文件)
    使用关键字import导入模块
    import md,模块名(文件名)叫md.py,但是导入的时候只需要写md,不要加.py后缀
    多次导入一个模块文件,只有第一次有效

#执行文件的流程
	鼠标右键运行a.py文件
    首先会创建一个a.py的名称空间
	如果import导入模块(md.py):
    	1.执行模块文件md.py,运行模块文件md.py中的代码
        2.将产生的名字与值存放到该模块文件的名称空间中
        3.在执行文件a.py中,定义一个变量名md,指向md.py的名称空间
        
#使用import导入模块
	#0.访问该模块名称空间中的变量名的统一句式为:模块名.变量名
import md
	#1.指名道姓的访问模块中的名字,永远不会与执行文件中的名字冲突
print(md.x)
	#2.只要能拿到函数名,无论在哪都可以通过函数名加括号来调用这个函数(调用函数,会回到函数定义阶段,依次执行代码)
	#3.函数在定义阶段,名字的查找顺序就已经固定死了,不会因为调用位置的改变而改变
print(md.read1)		#<function read1 at 0x000001EBC20AFF28>
md.change()
	
#可以使用import一次导入多个模块,但是不推荐使用(会导致代码的结构不清晰)
	#只有当几个模块有相同部分或者属于一个模块,可以一次导入
    #当几个模块没有联系的情况下,应该分多次导入
    #通常导入模块的句式会写在文件开头
import md,time,os
import md
import time
import os

#当模块名比较复杂的时候可以给模块起别名
import testtttttttttttttttt as test

#from ... import ...
	#多次导入,只有第一次有效
from md import x
from md import x
    #只会导入该模块的指定的一个变量名,不会导入别的变量名
    #访问模块中的名字,不需要加模块名前缀(小心覆盖当前名称空间中的变量名)
print(x)
	#from md import *一次性将md模块中的变量名全部加载(不推荐使用,占用内存)
    #__all__可以指定当前所在py文件被当做模块导入的时候,执行文件使用*,可以拿到的变量名(__all__ = ['x']),默认x为*
from md import *
print(x)
print(y)		#NameError: name 'y' is not defined

循环导入

#循环导入问题
	#如果程序出现循环导入问题,那么一定是程序设计的不合理
    #循环导入问题在程序设计阶段就应该避免

m1.py文件
from m2 import y		#第一次导入(导入、执行代码,但不执行函数)
print('正在导入m1')
x = 1

m2.py文件
from m1 import x		#第二次导入,此事x未定义
print('正在导入m2')
y = 2

a.py文件
import m1		#ImportError

#解决循环导入问题的方式:
	#1.方式1
    	将循环导入的句式写在文件最下方 
m1.py文件
print('正在导入m1')
x = 1
from m2 import y

m2.py文件
print('正在导入m2')
y = 2
from m1 import x
    
    #2.方式2
     	在函数内导入模块
m1.py文件
print('正在导入m1')
def f1():
    from m2 import y		#第一次导入
    print('m1,f1>>>y')
x = 1

m2.py文件
print('正在导入m2')
def f2():
    from m1 import x		#第二次导入,此时x已经定义
    print('m2,f2>>>x')
y = 2

import m1
m1.f1()

正在导入m1
正在导入m2
m1,f1>>>y
	#3.方式3
    	重新设计项目,拆分循环导入的代码

__name__可以区分py文件

#当文件被当做执行文件执行的时候,__name__打印的结果是__main__
#当文件被当做模块导入的时候,__name__打印的结果是被执行文件名(没有后缀)

def f1():
    print('f1')
def f2():
    print('f2')
f1()
f2()
print(__name__)

f1
f2
__main__/m1

#使用__name__,测试自定义模块功能
def f1():
    print('f1')
def f2():
    print('f2')
if __name__ == '__main__':		(快捷写法,main+TAB)
    f1()
    f2()

模块的查找顺序

# 模块的查找顺序
	1.先从内存中找
    2.再从内置模块中找
    3.再从sys.path中找(相当于当前文件的环境变量)
    
#模块sys
	#sys.path就是一个大列表,里面放了一堆文件路径,第一个路径永远是执行文件所在的文件夹
import sys
print(sys.path)		#['E:\\python_test', 'E:\\python_test', 'C:\\Program Files\\JetBrains\\PyCharm 2020.1.3\\plugins\\python\\helpers\\pycharm_display', 'C:\\Users\\17575\\AppData\\Local\\Programs\\Python\\Python36\\python36.zip', 'C:\\Users\\17575\\AppData\\Local\\Programs\\Python\\Python36\\DLLs', 'C:\\Users\\17575\\AppData\\Local\\Programs\\Python\\Python36\\lib', 'C:\\Users\\17575\\AppData\\Local\\Programs\\Python\\Python36', 'C:\\Users\\17575\\AppData\\Roaming\\Python\\Python36\\site-packages', 'C:\\Users\\17575\\AppData\\Local\\Programs\\Python\\Python36\\lib\\site-packages', 'C:\\Users\\17575\\AppData\\Local\\Programs\\Python\\Python36\\lib\\site-packages\\pip-20.2.3-py3.6.egg', 'C:\\Program Files\\JetBrains\\PyCharm 2020.1.3\\plugins\\python\\helpers\\pycharm_matplotlib_backend']

#验证查找顺序  -- 先从内存中找
	1.鼠标右键运行
    2.删除f1()
import time
import m1
time.sleep(10)
m1.f1()

#验证查找顺序  -- 从内置模块中找
	1.鼠标右键运行
    2.可以看到,导入了内置模块
import time
print(time.time())		#变量名应该避免与模块名冲突

#验证查找顺序  -- 从sys.path中找(执行文件所在文件夹)
	当执行文件所在文件夹下,存在该文件时,直接导入
	当执行文件所在文件夹下,不存在该文件时(#不会查找目录)
    	可以使用from dir import xx,from dir.dir1.dir2 import xx导入指定文件
        把被执行文件夹添加到环境变量,再导入(#项目文件夹)
        import sys
        sys.path.append(r'E:\python_test\m1.py')
        import m1

#注意
	1.文件名不应该与模块名(内置、第三方)冲突

相对导入(相对路径)

#相对导入不能在被执行文件中使用,可以在被导入文件中使用 (模块文件)    
	#相对导入不需要考虑绝对路径,只需要知道与被导入文件的两个路径之间的关系即可
    
#点
	.		当前路径
    ..		上一级路径
    ...		上上一级路径

绝对导入(绝对路径)

#绝对路径无论在执行文件还是被执行文件中都适用
	#绝对导入必须依据执行文件所在的文件夹路径为基准   
    #一般情况下,一个项目只会有一个执行文件,其他文件都是模块,所以相对导入的的应用是很广泛的

软件开发目录规范

#优点
	1.项目结构更加清晰
    2.便于管理
    3.扩展性高

#start.py(启动文件,该文件可以放到项目根目录下)
import os
import sys
BASE_dir = os.path.dirname(os.path.dirname(__file__))
sys.path.append(BASE_dir)
from core import src
if __name__ == '__main__':
    src.run()
    
#settings.py(配置文件)
	#把一个目录放到环境变量里面,那么,该目录下的文件可以使用相对路径导入
    #如果项目目录就是一级目录(顶级目录),那么不需要把该目录添加到环境变量(pycharm自动添加)
    #不要使用%s拼接路径,要使用os模块,以适用于把不同的用户
    #添加环境变量的操作尽量保留,以适用于不同的系统
import os
BASE_dir = os.path.dirname(os.path.dirname(__file__))
LOG_path = os.path.join(BASE_dir,'log')

#src.py(核心文件)
from lib import comman
db_username = 'syy'
db_password = 123
user_dic = {
    'is_login':None
}
@comman.login_auth
def register():
    print('reigster')
def login():
    while True:
        username = input('请输入用户名>>>: ').strip()
        password = int(input('请输入用户密码>>>: ').strip())
        if username == db_username and password == db_password:
            user_dic['is_login'] = True
            run()
        else:
                print('输入错误,请重新输入')
def shopping():
    print('shopping')
def transfer():
    print('transfer')
@comman.login_auth
def pay():
    print('pay')
def withdraw():
    print('withdraw')
@comman.login_auth
def pay_bak():
    print('pay_bak')
def check_record():
    print('check_record')

my_dict = {
    '1':register,
    '2':login,
    '3':shopping,
    '4':transfer,
    '5':pay,
    '6':withdraw,
    '7':pay_bak,
    '8':check_record
}
def run():
    while True:
        print("""
        1  register(注册)
        2  login(登录)
        3  shopping(购物)
        4  transfer(转账)
        5  pay(支付)
        6  withdraw(提现)
        7  pay_bak(还款)
        8  check_record(查看流水)
        """)
        choice = input('please choice func to operate>>>: ').strip()
        if choice not in my_dict:
            print('相关功能还在开发中')
        my_dict.get(choice)()
        break
        
#comman.py(公共的功能)
from core import src
def login_auth(func):
    def inner(*args,**kwargs):
        if src.user_dic.get('is_login'):
            res = func(*args,**kwargs)
            return res
        else:
            print('请先登录')
            src.login()
    return inner

#Readme.py
对这款软件的介绍
    本软件是一个结合了ATM+购物的一个多功能的购物应用程序
    用户可以通过bin目录下的启动程序启动程序
    
#db文件夹下面的userinfo.py(数据库数据)
#log文件夹下面的文件(日志)

正则

正则表达式匹配手机号

正则表达式匹配身份证号

#正则
	在编写处理字符串的程序或网页时,经常有查找符合某些复杂规则的字符串的需要。正则表达式就是用于描述这些规则的工具。换句话说,正则表达式就是记录文本规则的代码。
	正则就是筛选字符串中的特定的内容的规则的代码
    接触到的,以reg开头的单词,通常与正则有关(正则测试:http://tool.chinaz.com/regex)
    正则表达式默认都是贪婪匹配,可以通过在量词的后面加上一个?,就可以将贪婪匹配变成非贪婪匹配(惰性匹配)

#正则的应用场景(推荐: 《正则指引》)
	爬虫
    数据分析
    
#普通的python脚本
while True:
    phone_number = input('please input your phone number>>>: ').strip()
    if len(phone_number) == 11 \
        and phone_number.isdigit() \
        and (phone_number.startswith('13') \
            or phone_number.startswith('14')
            or phone_number.startswith('15')
            or phone_number.startswith('16')
            or phone_number.startswith('17')
            or phone_number.startswith('18')
            ):
        print('手机号合法')
    else:
        print('输入的手机号不合法')
        
#使用re模块写的python脚本
import re
while True:
    phone_number = input('please input your phone number>>>: ').strip()
    if re.match('^(13|14|15|16|17|18)[0-9]{9}$',phone_number):
        print('手机号合法')
    else:
        print('输入的手机号不合法')
        
import re
while True:
    phone_number = input('please input your phone number>>>: ').strip()
    if re.match('^1(?:3\d|4[4-9]|5[0-35-9]|6[67]|7[013-8]|8\d|9\d)\d{8}$',phone_number):		#?:将贪婪匹配变成非贪婪匹配
        print('手机号合法')
    else:
        print('输入的手机号不合法')   
        
#身份证号码
	身份证号码是一个长度为15或者18个字符的字符串
    	如果是15位,则全部由数字组成,并且首位不能为0 
        如果是18位,则前17位全部都是数字,末位可能是数字,也可能是X
        
import re
while True:
    phone_number = input('please input your phone number>>>: ').strip()
    if re.match('\^[1-9]\d{14}(\d{2}[0-9x])?$\',phone_number):
        print('身份证号码合法')
    else:
        print('身份证号码不合法') 
                
import re
while True:
    phone_number = input('please input your phone number>>>: ').strip()
    if re.match('^[1-9]\d{16}[0-9x]|^[1-9]\d{14}$',phone_number):
        print('身份证号码合法')
    else:
        print('身份证号码不合法')       

常用元字符

代码 说明
. 匹配除换行符以外的任意一个字符
\w 匹配字母或数字或下划线、汉字(w是word的缩写)
\s 匹配任意的空白符(space)(空格或TAB键,不包括换行符)
\d 匹配数字(digit)
N\b 匹配单词的结束
^N 匹配字符串的开始(类似于startswith)
N$ 匹配字符串的结束(类似于endswith)
\n 匹配一个换行符
\t 匹配一个制表符
a|b 匹配字符a,或者字符b (注意ab|abc匹配abc是ab的作用,abc|ab是abc的作用)
() 匹配括号内的表达式,也表示一个组,使用.group()访问组匹配

分组:当多个正则符号需要重复多次的时候,或者当做一个整体进行其他操作,那么就要使用分组,分组使用一对括号() 表示

常用限定符 (量词)

代码/语法 说明
* 当前字符重复零次或更多次(a*b)
N+ 左边字符重复一次或更多次(贪婪匹配),N+? (惰性匹配)
N? 左边字符重复零次或一次
N 重复n次
N 重复n次或更多次
N 重复n到m次(贪婪匹配),N{n,m}? (惰性匹配)

量词中的多:贪婪匹配,尽量匹配满足条件的最多的字符,可以通过在量词的后面加上一个?,就可以将贪婪匹配变成非贪婪匹配(惰性匹配)

量词中的少:量词只作用于左边的第一个字符,也就是说只能跟在字符的后面

常用反义词

代码/语法 说明
\W 匹配任意不是字母,数字,下划线,汉字的字符
\S 匹配任意不是空白符的字符
\D 匹配任意非数字的字符
\B 匹配不是单词结束的单词
[^x] 匹配除了x以外的任意字符
[^aeiou] 匹配除了aeiou这几个字母以外的任意字符

转义符

正则 待匹配字符 解释
'\n' 匹配一个换行符
'\\n' \n 转义符转移后面的\
'\\\\n' \\n 转义符转移后面的\
r'\\n' \n r,让整个字符串不转义

非贪婪匹配 (惰性匹配)

正则 解释
*?、.**? 重复任意次,但是尽可能的少重复
N+? 重复一次或更多次,但是尽可能的少重复
?? 重复0次或一次,但是尽可能的少重复
N{n,m}? 重复n到m此,但是尽可能的少重复
N{n,}? 重复0次以上,但是尽可能的少重复

re模块

#re模块与正则表达式之间的关系
	正则表达式不是python独有的,他是一门独立的技术
    所有的编程语言都可以使用正则
    如果想在python中使用正则,就必须依赖于re模块,来达到书写正则表达式目的
    
#re.findall('正则表达式','字符串')
    找到字符串中符合正则表达式的全部内容,返回一个列表,列表中就是正则匹配到的内容
    
import re
res = re.findall('s','syyttsry')
print(res)		#['s', 's']

import re
res = re.findall('k','syyttsry')	
print(res)	#[],找不到的话,返回空列表

import re
res = re.findall('[a-z]','syyttsry')
print(res)		#['s', 'y', 'y', 't', 't', 's', 'r', 'y']
    
import re
res = re.findall('[a-z]+','syyttsry')
print(res)		#['syyttsry']
    
import re
res = re.findall('[a-z]+','syyttsry yys')
print(res)		#['syyttsry', 'yys']
    
#re.search('正则表达式','字符串')
	search会匹配字符串开头、中间、结尾的部分,只返回找到的第一个单词
    search不会给你直接返回匹配到的结果,而是给你返回一个对象
    使用.group(),才能看到匹配的结果
    注意,search只会根据正则查找目标一次,找到即停,如果找不到的话,返回None
    
import re
res = re.search('s','syytrihys si op')
print(res)				#<_sre.SRE_Match object; span=(0, 1), match='s'>
print(res.group())		#s

import re
res = re.search('syyi','syytrihys syyi opa')
print(res)				#<_sre.SRE_Match object; span=(10, 14), match='syyi'>
print(res.group())		#syyi

import re
res = re.search('k','syytrihys si op')
print(res)				#None
print(res.group())		#AttributeError,不存在的话(None)直接报错

import re
res = re.search('k','syytrihys si op')
print(res)
if res:
    print(res.group())
    
#re.match('正则表达式','字符串')
	match只会匹配字符串的开头,只返回一个
	match不会给你直接返回匹配到的结果,而是给你返回一个对象
    使用.group(),才能看到匹配的结果
    注意,match只会根据正则查找目标一次,找到即停,如果找不到的话,返回None
    
import re
res = re.match('syy','syytrihys si op')
print(res)				#<_sre.SRE_Match object; span=(0, 3), match='syy'>
print(res.group())		#syy

import re
res = re.match('k','syytrihys si op')
print(res)				#None
print(res.group())		#AttributeError

import re
res = re.match('k','syytrihys syyi op')
print(res)
if res:
    print(res.group())		#防止报错
    
#re.split('正则表达式','字符串')
	切割字符串
    
import re
res = re.split('[ab]','1a2bcd')
print(res)			#['1', '2', 'cd']
import re
res = re.split('(\d)','1a2bcd')
print(res)			#['', '1', 'a', '2', 'bcd'],保留切割的数字

#re.sub('正则表达式','新的内容','字符串',N)
	替换字符

import re
res = re.sub('\d','H','1a2b3c4d',2)
print(res)			#HaHb3c4d

#re.subn('正则表达式','新的内容','字符串',N)
	替换字符
    返回的是一个元组,内容是替换后的字符串和替换的个数
    
import re
res = re.subn('\d','H','1a2b3c4d',2)
print(res)			#('HaHb3c4d', 2)

#re.compile('正则表达式')
	编译正则表达式
    
import re
obj = re.compile('\d{3}')		#将正则表达式编译成一个正则表达式对象
res = obj.search('abc123eee111ttt')		#正则表达式对象调用search,参数为待匹配的字符串
print(res.group())				#123
res = obj.findall('abc123eee111ttt')
print(res)						#['123', '111']

#re.finditer('正则表达式')  
	与findall类似,不同点在于结果是一个迭代器
import re
res = re.finditer('\d','1n2j3kL')
print(res)			#<callable_iterator object at 0x000002CF41AFF898>
print(next(res))	#<_sre.SRE_Match object; span=(0, 1), match='1'>
print(next(res))	#<_sre.SRE_Match object; span=(2, 3), match='2'>
print(next(res))	#<_sre.SRE_Match object; span=(4, 5), match='3'>
print(next(res))	#StopIteration

#re模块中的分组
	findall会优先把匹配结果组里面的结果返回,如果想要匹配结果,使用?:取消权限即可
import re
res = re.findall('^[1-9]\d{14}(\d{2}[0-9x])?$','94596847739994375x')
print(res)
res = re.findall('^[1-9]\d{14}(?:\d{2}[0-9x])?$','94596847739994375x')
print(res)	
res = re.search('^[1-9]\d{14}(\d{2}[0-9x])?$','94596847739994375x')
print(res)
print(res.group())
print(res.group(1))
res = re.match('^[1-9]\d{14}(\d{2}[0-9x])?$','94596847739994375x')
print(res)
print(res.group())
print(res.group(1))

['75x']
['94596847739994375x']
<_sre.SRE_Match object; span=(0, 18), match='94596847739994375x'>
94596847739994375x
75x
<_sre.SRE_Match object; span=(0, 18), match='94596847739994375x'>
94596847739994375x
75x

#re模块中的分组与别名(?P<别名>)
import re
res = re.findall('^[1-9]\d{14}(\d{2}[0-9x])?$','94596847739994375x')
print(res)
res = re.search('^[1-9]\d{14}(?P<password>\d{2}[0-9x])?$','94596847739994375x')
print(res)
print(res.group())
print(res.group(1))
print(res.group('password'))
res = re.match('^[1-9]\d{14}(?P<password>\d{2}[0-9x])?$','94596847739994375x')
print(res)
print(res.group())
print(res.group(1))
print(res.group('password'))

['75x']
<_sre.SRE_Match object; span=(0, 18), match='94596847739994375x'>
94596847739994375x
75x
75x
<_sre.SRE_Match object; span=(0, 18), match='94596847739994375x'>
94596847739994375x
75x
75x

正则在爬虫中的应用

#爬虫就是爬取网页的html代码(就是一堆字符串)
	1.研究网站是否有反爬措施
    2.研究该网站页面URL的规律
    3.想要什么内容,写出对应的正则表达式,爬取代码
    4.从这一堆字符串中筛选出你想要的的内容

typing模块

参考网站

#typing模块
	输入提示,只会提示
	
from typing import List ,Tuple
def test1(
        name: str,
        age :int,
        balance :float,
        user_info :List
        pawass: tuple
) -> Tuple[int,float,str,list,dict,tuple]:
    return (name ,age ,balance ,user_info)
res = test1('111' ,'2' ,3.3 ,[4,4,4])
print(res)

collection模块

#python的基本数据类型有整形、浮点型、字符串、列表、元组、字典、布尔值、集合
#collection模块提供别的数据类型
    namedtuple 	:具名元组,生成使用名字来访问元素内容的tuple
    deque 		:双端队列,可以快速的从另外一侧追加和推出对象
    counter 	:计数器,主要用来计数
    ordereddict :有序字典
    defaultdict	:带有默认值的字典
    
#具名元组,namedtuple('注释','['']')
from collections import namedtuple
point = namedtuple('坐标',['x','y'])		#使用列表(可迭代对象)表示坐标
p = point(1,2)	#注意元素数量要保持一致
print(p)		#坐标(x=1, y=2)
print(p.x)		#1
print(p.y)		#2
    
from collections import namedtuple		
point = namedtuple('坐标','x,y')			#使用字符串表示坐标(以空格或逗号分隔)
p = point(1,2)
print(p)
print(p.x)
print(p.y) 

	#打印扑克
from collections import namedtuple
card = namedtuple('扑克牌','color number')
A = card('♠','A')
print(A)
print(A.color)
print(A.number)
    
扑克牌(color='♠', number='A')
♠
A
	#表示圆
from collections import namedtuple
card = namedtuple('圆','x y r')

#双端队列,deque()
	队列,先进先出(FIFO)
    堆栈,先进后出
import queue
q = queue.Queue()
q.put('first')		#先进
q.put('second')
q.put('third')
print(q)			#<queue.Queue object at 0x0000020F9FAFA908>
print(q.get())		#first
print(q.get())		#second
print(q.get())		#third
print(q.get())		#如果队列中的值取完了,程序会在原地等待,知道从队列中拿到值才停止

	双端队列
from collections import deque
q = deque(['a','b',1,2])
q.append(3)
q.appendleft('A')
print(q.pop())		#3
print(q.popleft())	#A

	#双端队列,可以根据索引,在任意位置插入值,而队列不支持在任意位置插值,只能在首尾插值(不能插队)
from collections import deque
q = deque(['a','b',1,2])
q.insert(3,'哈哈哈')			
print(q.pop())		#2
print(q.pop())		#哈哈哈
print(q.pop())		#1
print(q.pop())		#b
print(q.pop())		#a

#有序字典,ordereddict()
normal_d = dict([('a',1),('b',2),('c',3)])
print(normal_d)

from collections import OrderedDict
order_d = OrderedDict([('a',1),('b',2),('c',3)])
print(order_d)
order_d = OrderedDict([])
order_d['x'] = 1
order_d['y'] = 2
order_d['z'] = 3
print(order_d)

{'a': 1, 'b': 2, 'c': 3}						#无顺序
OrderedDict([('a', 1), ('b', 2), ('c', 3)])		#有顺序
OrderedDict([('x', 1), ('y', 2), ('z', 3)])		#有顺序

#默认值字典,defaultdict()
	普通字典
values = [11,22,33,44,55,66,77,88,99]
my_dict = {}
for value in values:
    if value > 66:
        if 'k1' in my_dict.keys():
            my_dict['k1'].append(value)
        else:
            my_dict['k1'] = [value]
    else:
        if 'k2' in my_dict.keys():
            my_dict['k2'].append(value)
        else:
            my_dict['k2'] = [value]
print(my_dict)		#{'k2': [11, 22, 33, 44, 55, 66], 'k1': [77, 88, 99]}

	默认值字典
from collections import defaultdict
values = [11,22,33,44,55,66,77,88,99]
my_dict = defaultdict(list)	#在该字典中新建key,对应的value默认值就是列表
# print(my_dict)			#defaultdict(<class 'list'>, {})
for value in values:
    if value > 66:
        my_dict['k1'].append(value)
    else:
        my_dict['k2'].append(value)
print(my_dict)		#{'k2': [11, 22, 33, 44, 55, 66], 'k1': [77, 88, 99]}

	普通字典
s = 'sfregersggggggrvsdesdfrrrrrr'
d = {}
for i in s:
    if i in d.keys():
        d[i]+= 1
    else:
        d[i] = 1
print(d)

from collections import Counter
s = 'sfregersggggggrvsdesdfrrrrrr'
res = Counter(s)
print(res)			#Counter({'r': 9, 'g': 7, 's': 4, 'e': 3, 'f': 2, 'd': 2, 'v': 1})

常用模块 OS

#py文件名应该避免跟已有的模块名相同,防止导入冲突
#OS模块是跟操作系统打交道的模块,使用该模块可以使APP跨平台使用

#目录列表
import os
print(os.listdir(r'E:\python_test\ATM'))		#['bin', 'conf', 'core', 'db', 'lib', 'log', 'Readme']

import os
#当前文件所在当前目录
BASE_DIR = os.path.dirname(__file__)
#根据获取的当前目录,拼接处指定目录
MOVE_DIR = os.path.join(BASE_DIR,'老师们的作品')
#获取指定目录下的文件名,结果是列表
movie_list = os.listdir(MOVE_DIR)
#计算列表内的元素的个数,也就是指定目录下的文件数
file_num = len(movie_list)
while True:
    #打印开头用户可以参考的信息
    for i,j in enumerate(movie_list,1):
        print(i,j)
    choise = input('请输入想看的资源序号>>>: ').strip()
    #判断用户输入是否为纯数字
    if choise.isdigit():
        choise = int(choise)
        #判断用户输入数字是否有意义
        if choise in range(1,file_num+1):
            #列表索引取值,得到文件名
            target_file = movie_list[choise-1]
            target_path = os.path.join(MOVE_DIR,target_file)
            print(target_path)
            with open(target_path,mode='r',encoding='utf-8') as f:	#不能使用r''
                print(f.read())
                break
        else:
            print('输入数字无效,请重新输入,数字范围为>>>: 1-%s'%file_num)
    else:
        print('输入无效,请输入指定的数字序号')
        
#mkdir(),创建单级目录
import os
os.mkdir('test目录')		#与当前的python文件同级

#makedirs(),创建多级目录
import os
os.makedirs('T1/T2')	 #与当前的python文件同级

#rmdir(),removedirs(),删除单级空目录
import os
os.rmdir(r'E:\python_test\test')
os.removedirs(r'E:\python_test\test目录\testdir')

#删除多级目录,要删除的目录下不能有文件(目录不为空就报错)
import os
os.removedirs(r'E:\python_test\T1\T2')

#exists(),判断文件或目录是否存在
import os
print(os.path.exists(r'E:\python_test\test目录'))		#True
print(os.path.exists(r'E:\python_test\a.py'))		#True

#isfile(),判断文件是否存在
import os
print(os.path.isfile(r'E:\python_test\老师们的作品'))		#False,目录只返回True
print(os.path.isfile(r'E:\python_test\a.py'))		#True

#remove(),删除一个文件
import os
os.remove(r'E:\python_test\m2.py')

#重命名目录/文件
import os
os.rename(r'E:\python_test\a.py',r'E:\python_test\aaa.py')		#重命名文件
os.rename(r'E:\python_test\老师们的作品',r'E:\python_test\老师们的作品哈') #重命名目录

#stat(),获取文件/目录的信息
import os
res = os.stat((r'E:\python_test\aaa.py'))		#获取文件的信息
print(res)		#os.stat_result(st_mode=33206, st_ino=19140298416324717, st_dev=2122398715, st_nlink=1, st_uid=0, st_gid=0, st_size=1406, st_atime=1605492292, st_mtime=1605492292, st_ctime=1604305169)
res = os.stat((r'E:\python_test\老师们的作品哈'))	#获取目录的信息
print(res)		#os.stat_result(st_mode=16895, st_ino=119345390125318349, st_dev=2122398715, st_nlink=1, st_uid=0, st_gid=0, st_size=0, st_atime=1605492215, st_mtime=1605433594, st_ctime=1605255301)

#getcwd(),查看当前文件的工作目录
import os
print(os.getcwd())		#E:\python_test

#chdir(),改变当前文件的工作目录,相当于shell下的cd
import os
print(os.getcwd())		#E:\python_test
os.chdir(r'E:\python_test\老师们的作品哈')
print(os.getcwd())		#E:\python_test\老师们的作品哈

#system(),运行shell命令
import os
os.system('mkdir TT')

#popen(),运行shell命令,获取执行结果
import os
os.popen('mkdir T').read()

#getsize(),获取文件大小
import os
res = os.path.getsize(r'E:\python_test\老师们的作品哈\小泽老师')
print(res)					#7,字节
with open(r'E:\python_test\老师们的作品哈\小泽老师',mode='r',encoding='utf-8') as f:
    print(len(f.read()))	#5,字符

sys模块

#sys模块,是跟python解释器打交道的模块

#append(),将某个目录添加到系统的环境变量中
import sys
sys.path.append(r'E:\python_test\老师们的作品哈')

#platform,查看当前的操作系统
import sys
print(sys.platform)		#win32

#version,查看python解释器的版本
import sys
print(sys.version)		

#argv[n],获取终端的命令行的参数
import sys
username = sys.argv[1]
password = sys.argv[2]
if username == 'syy' and password == '123':
    print('当前代码')
else:
    print('用户名或密码错误')
E:\python_test>python E:\python_test\aaa.py syy 123

#实例
import sys
if len(sys.argv) != 3:
    print('请输入用户名和密码')
else:
    username = sys.argv[1]
    password = sys.argv[2]
    if username == 'syy' and password == '123':
        print('当前代码')
    else:
        print('用户名或密码错误')

time模块

#time的三种表现形式
	1.时间戳
    2.格式化时间(给人看的)
    3.结构化时间
    
	#时间戳
import time
print(time.time())							#1605249911.3957567

	#格式化时间
print(time.strftime('%Y-%m-%d'))			#2020-11-13
print(time.strftime('%Y-%m-%d %H:%M:%S'))	#2020-11-13 14:47:11
print(time.strftime('%Y-%m-%d %X'))			#等价,2020-11-13 14:48:58
print(time.strftime('%m/%d %H:%M:%S')) 		#拼接,11/13 14:48:05
    
    #结构化时间
print(time.localtime())		#time.struct_time(tm_year=2020, tm_mon=11, tm_mday=13, tm_hour=14, tm_min=50, tm_sec=50, tm_wday=4, tm_yday=318, tm_isdst=0)

#三种时间的转化
	#时间戳转结构化时间
import time
print(time.localtime(1605249911.3957567))	#time.struct_time(tm_year=2020, tm_mon=11, tm_mday=13, tm_hour=6, tm_min=45, tm_sec=11, tm_wday=4, tm_yday=318, tm_isdst=0)
import time
print(time.gmtime(1605249911.3957567))
    #结构化时间转时间戳
import time
res = time.localtime(time.time())
print(time.mktime(res))		#1605250876.0
print(time.time())			#1605250876.6250167

	#格式化时间转结构化时间
import time
res = time.strftime('%Y-%m-%d %H:%M:%S')
print(time.strptime(res,'%Y-%m-%d %H:%M:%S'))
    #结构化时间转格式化时间
import time
print(time.strftime('%Y-%m-%d %H:%M:%S',time.localtime()))	#2020-11-13 15:10:12
    
#time.sleep()
import time
print('倒计时: 3...')
print(time.sleep(1))
print('倒计时: 2..')
print(time.sleep(1))
print('倒计时: 1.')
print(time.sleep(1))
print('倒计时: 0')
    
倒计时: 3...
None
倒计时: 2..
None
倒计时: 1.
None
倒计时: 0    

datatime模块

#datatime模块
import datetime
print(datetime.date.today())		#2020-11-13(年月日)
print(datetime.datetime.today())	#2020-11-13 15:18:02.062366(年月日时分秒)
print(res1.year)					#2020
print(res1.month)					#11
print(res1.day)						#13
print(res1.weekday())				#4(用0-6表示)
print(res1.isocalendar())			#(2020, 46, 5)(星期用1-7表示,7表示周日)

#时间可以相加减
	#日期对象 = 日期对象 +/- timedelta对象
import datetime
corrent_time = datetime.date.today()
timetel_t = datetime.timedelta(days=3)
print(corrent_time+timetel_t)		#2020-11-16
    #timedelta对象 = 日期对象 +/- 日期对象
    
#UTC时间
import datetime
dt_today = datetime.datetime.today()
dt_now = datetime.datetime.now()
dt_utcnow = datetime.datetime.utcnow()
print(dt_today,dt_now,dt_utcnow)
2020-11-13 15:34:25.711797 
        2020-11-13 15:34:25.711797 
                2020-11-13 07:34:25.711797

json模块

#序列化
	序列:		字符串
    序列化:	其他数据类型转换成字符串的过程
    反序列化:	字符串转换成其他数据类型
    
#为什么要使用序列化
数据有指定的数据类型,转换成字符串(序列化),才能str.encoding转化为二进制数据(基于网络传输的数据必须是二进制数据),进而写入文件

#json模块的优点
	所有编程语言都可以使用json格式,也就是说,json代码可以跨语言、跨平台使用
    
#json模块的缺点:
	支持的数据类型少(json只可以转字符串、列表、字典,不能转对象、索引、函数等)

#json能够支持的python的数据类型,python中其他的数据类型json都不支持
import json
json.JSONEncoder
    +-------------------+---------------+
    | Python            | JSON          |
    +===================+===============+
    | dict              | object        |对象(字符串)
    +-------------------+---------------+
    | list, tuple       | array         |数组(字符串)
    +-------------------+---------------+
    | str               | string        |字符串
    +-------------------+---------------+
    | int, float        | number        |数值
    +-------------------+---------------+
    | True              | true          |
    +-------------------+---------------+
    | False             | false         |
    +-------------------+---------------+
    | None              | null          |
    +-------------------+---------------+
    
  
#dumps()序列化,将其他的数据类型转换成json格式的字符串
#loads()反序列化,将json格式的字符串转换成其他的数据类型
import json
d = {"name":"syy"}
print(d)				#{'name': 'syy'},只有json格式的数据打印出来是双引号
res = json.dumps(d)
print(res)				#{"name": "syy"},等效于'{"name": "syy"}'
print(type(res))		#<class 'str'>
res = json.loads(res)
print(res)				#{'name': 'syy'}
print(type(res))		#<class 'dict'>

#dump(),load()
import json
d = {"name":"syy"}
with open(r'userinfo','w',encoding='utf-8') as f:
    json.dump(d,f)		#转换成字符串,并且自动写入
with open(r'userinfo','r',encoding='utf-8') as f:
    res = json.load(f)
    print(res,type(res))	#{'name': 'syy'} <class 'dict'> 

#反序列化中的问题,以及解决方法
import json
d = {"name":"syy"}
with open(r'userinfo','w',encoding='utf-8') as f:
    json.dump(d,f)
    json.dump(d,f)
with open(r'userinfo','r',encoding='utf-8') as f:
    res = json.load(f)		#不能多次反序列化
    print(res,type(res))	#json.decoder.JSONDecodeError:

	#解决方法
import json
d = {"name":"syy"}
with open(r'userinfo','w',encoding='utf-8') as f:
    json_str = json.dumps(d)
    json_str2 = json.dumps(d)
    f.write('%s\n'%json_str)
    f.write('%s\n'%json_str2)
with open(r'userinfo','r',encoding='utf-8') as f:
    for line in f:
        res = json.loads(line)
        print(res,type(res)) 

#将元组转换成数组
import json
t = (1,2,3)
res = json.dumps(t)
print(res,type(res))   		#[1, 2, 3] <class 'str'>
       
#中文转json的时候的转码问题
import json
d = {'name':'猪坚强'}
print(json.dumps((d)))						#{"name": "\u732a\u575a\u5f3a"}
print(json.dumps(d,ensure_ascii=False))		#{"name": "猪坚强"}

pickle模块

#pickle模块
    python所有的数据类型都支持pickle模块(包括对象、索引、函数)
    
#pickle模块
	只能自己跟自己玩,不支持跨语言传输
    只有python才能使用pickle模块

#dumps(),loads()
import pickle
d = {'name':'syy'}
res = pickle.dumps(d)
print(res)		#b'\x80\x03}q\x00X\x04\x00\x00‘,将对象直接转换成二进制
res = pickle.loads(res)
print(res,type(res))		#{'name': 'syy'} <class 'dict'>

#dump(),load()
	#使用pickle操作文件的时候,文件的打开模式必须是b模式
import pickle
d = {'name':'syy'}
with open(r'userinfo','wb') as f:
    pickle.dump(d,f)
with open(r'userinfo','rb') as f:	
    res = pickle.load(f)
    print(res,type(res))		#{'name': 'syy'} <class 'dict'>

random模块

#随机数
import random
print(random.randint(1,6))	#1,包括首尾

import random
print(random.random())		#0.5979006446755478,取0-1之间的小数

import random
print(random.choice([1,2,3,4,5,6]))		#2,从列表中随机取值

import random
res = [1,2,3,4,5,6]
random.shuffle(res)
print(res)			#[2, 1, 3, 6, 5, 4],打乱顺序

#生成随机验证码
	验证码由大写字母、小写字母、数字组成
import random
def get_code(n):
    code = ''
    for i in range(n):
        upper_str = chr(random.randint(65,90))
        lower_str = chr(random.randint(97,122))
        random_int = str(random.randint(0,9))
        code +=random.choice([upper_str,lower_str,random_int])
    return code
res = get_code(5)
print(res)

#验证码忽略大小写
	使用upper()、lower()统一转换成大写或者小写
s = 'rigllllllllllllf'
res = s.upper()
print(res)
res = s.lower()
print(res)

logging模块

#日志
	日志本质上是就是一个文本文件,我们可以直接写入,但是太麻烦,所以使用logging模块封装了日志的编辑

#logging模块也叫日志模块
	记录程序的输入与输出
    
#日志分为5个等级
import logging
file_handler = logging.FileHandler(filename='x1.log', mode='a', encoding='utf-8',)
logging.basicConfig(
    format='%(asctime)s - %(name)s - %(levelname)s -%(module)s:  %(message)s',
    datefmt='%Y-%m-%d %H:%M:%S %p',
    handlers=[file_handler,],
    level=logging.ERROR
)
logging.error('你好')
logging.debug('debug日志')		#10
logging.info('info日志')			#20
logging.warning('warning日志')	#30
logging.error('error日志')		#40
logging.critical('critical日志')	#50

2020-11-17 15:37:47 PM - root - ERROR -aaa:  你好
2020-11-17 15:37:47 PM - root - DEBUG -aaa:  debug日志
2020-11-17 15:37:47 PM - root - INFO -aaa:  info日志
2020-11-17 15:37:47 PM - root - WARNING -aaa:  warning日志
2020-11-17 15:37:47 PM - root - ERROR -aaa:  error日志
2020-11-17 15:37:47 PM - root - CRITICAL -aaa:  critical日志

#问题
	1.乱码
    2.日志格式
    3.如何既打印到终端,又写入到文件
    
#学习logging模块需要了解的几个核心的对象(角色)
	1.logger对象:负责产生日志
    2.filter对象:过滤日志
    3.handler对象:控制日志输出的位置(文件/终端)
    4.formmater对象:规定日志内容的格式

#参数
logging.basicConfig()函数中可通过具体参数来更改logging模块默认行为,可用参数有:
filename:用指定的文件名创建FiledHandler,这样日志会被存储在指定的文件中。
filemode:文件打开方式,在指定了filename时使用这个参数,默认值为“a”还可指定为“w”。
format:指定handler使用的日志显示格式。
datefmt:指定日期时间格式。
level:设置rootlogger(后边会讲解具体概念)的日志级别
stream:用指定的stream创建StreamHandler。可以指定输出到sys.stderr,sys.stdout或者文件(f=open(‘test.log’,’w’)),默认为sys.stderr。若同时列出了filename和stream两个参数,则stream参数会被忽略。
format参数中可能用到的格式化串:
%(name)s Logger的名字
%(levelno)s 数字形式的日志级别
%(levelname)s 文本形式的日志级别
%(pathname)s 调用日志输出函数的模块的完整路径名,可能没有
%(filename)s 调用日志输出函数的模块的文件名
%(module)s 调用日志输出函数的模块名
%(funcName)s 调用日志输出函数的函数名
%(lineno)d 调用日志输出函数的语句所在的代码行
%(created)f 当前时间,用UNIX标准的表示时间的浮 点数表示
%(relativeCreated)d 输出日志信息时的,自Logger创建以 来的毫秒数
%(asctime)s 字符串形式的当前时间。默认格式是 “2003-07-08 16:49:45,896”。逗号后面的是毫秒
%(thread)d 线程ID。可能没有
%(threadName)s 线程名。可能没有
%(process)d 进程ID。可能没有
%(message)s用户输出的消息

#logger对象配置
import logging
logger = logging.getLogger()
# 创建一个handler,用于写入日志文件
fh = logging.FileHandler('test.log',encoding='utf-8') 
# 再创建一个handler,用于输出到控制台 
ch = logging.StreamHandler() formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
fh.setLevel(logging.DEBUG)fh.setFormatter(formatter) ch.setFormatter(formatter) 
logger.addHandler(fh) 
#logger对象可以添加多个fh和ch对象 
logger.addHandler(ch) logger.debug('logger debug message') logger.info('logger info message') logger.warning('logger warning message') logger.error('logger error message') logger.critical('logger critical message')

#日志切割
import time
import logging
from logging import handlers
sh = logging.StreamHandler()
rh = handlers.RotatingFileHandler('myapp.log', maxBytes=1024,backupCount=5)
fh = handlers.TimedRotatingFileHandler(filename='x2.log', when='s', interval=5, encoding='utf-8')
logging.basicConfig(
    format='%(asctime)s - %(name)s - %(levelname)s -%(module)s:  %(message)s',
    datefmt='%Y-%m-%d %H:%M:%S %p',
    handlers=[fh,sh,rh],
    level=logging.ERROR
)
for i in range(1,100000):
    time.sleep(1)
    logging.error('KeyboardInterrupt error %s'%str(i))

subprocess模块

#sub		子
#process	进程

#
import subprocess
while True:
    cmd = input('please input your command>>>: ').strip()
    obj = subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
    print('stdout',obj.stdout.read().decode('gbk'))		#正确命令的返回结果
    print('stderr',obj.stderr.read().decode('gbk'))		#错误命令返回的提示信息

#过程分析
	1.用户通过网络,连接电脑
    2.用户输入相应的命令,基于网络发送给这台电脑上的某个程序
    3.获取用户输入的命令,python解释器通过subprocess模块执行用户的命令
    4.将执行结果再基于网络发送给用户,这样就实现了用户的远程操作

#模块的三种来源
	1.内置的
    2.第三方的
    3.自定义的
    
#模块的四种表现形式
	1.py文件
    2.共享库
    3.文件夹/包(一系列模块的结合体)
    4.C++编译连接到python内置的 
   
#使用import导入模块
	1.产生一个执行文件的名称空间
    2.创建模块文件的名称空间
    3.执行模块文件中的代码,将产生的名字放入模块文件的名称空间中
    4.在执行文件的名称空间中,创建一个指向模块文件名称空间的名字

#模块与包
	#可以站在两个角度来分析不同的问题
    1.模块的开发者
    2.模块的使用者
    
#包
	包是一系列模块文件的结合体,他的表现形式就是一个文件夹
    该文件夹内部通常会有一个__init__.py文件
    包的本质还是一个模块
    
#导入包
	1.产生一个执行文件的名称空间
    2.创建包下面的__init__.py文件的名称空间
    3.执行包下面的__init__.py文件中的代码,将产生的名字放入包下面的__init__.py文件的名称空间中
    4.在执行文件中,创建一个指向包下面的__init__.py文件的名称空间的名字
    
#导入包的时候,.号的左边永远是包(文件夹)
#当作为包的设计者来说
	1.当模块的功能特别多的情况下,应该分文件管理,也就是做成包
    2.每个模块之间为了避免后期模块改名的问题,可以使用相对导入,因为包里面的文件都应该是被导入的模块
# import m1
# import m2
# import m3
# import m1.f1				#不支持,import只能导入模块

# import bao.m1
# import bao.m2
# import bao.m3
# import bao.bao2.m4		#支持

# from bao import m1
# from bao import m2
# from bao import m3
# from bao.bao2 import m4	#支持
# from bao.m1 import f1		#支持

# import m4                 #不报错
import bao2.m4              #报错
# from bao.bao2 import m4   #不报错
# import .m4                #报错
# from .m4 import m4        #报错

#站在包的开发者来说,如果使用绝对路径来管理自己的模块,那么他只需要以包为基准,依次导入模块
#站在包的使用者来说,必须将包所在的那个文件夹路径添加到system.path中

#python2中,如果要导入包,包下面必须要有__init__.py文件
#python3中,如果要导入包,包下面没有__init__.py文件也不会报错
	#当删除代码中不必要的文件的时候,千万不要随意删除__init__.py文件

configparse模块


hashlib模块

#算法模块、加密模块	
	该模块只能加密,不能解密
    相同的加密数据,得到的密文一定相同,密文相同,加密数据一定相同
    不同的加密方法,加密过程相同
    密文的长度越长,对应的算法越复杂,但是时间消耗越长,占用空间越大,通常使用md5加密算法(32位)
    要加密的数据可以分多次传入
 
#md5()
import hashlib
md = hashlib.md5()                      #生成一个造密文的对象
md.update('hello'.encode('utf-8'))      #向对象中传明文数据
# md.update(b'hello')                   #等效
print(md.hexdigest())					#5d41402abc4b2a76b9719d911017c592

#sha3_256()      
import hashlib
md = hashlib.sha3_256()                    
md.update('hello'.encode('utf-8'))     
# md.update(b'hello')                   
print(md.hexdigest())	#3338be694f50c5f338814986cdf0686453a888b84f424d792a

#加密对象的多次传入
import hashlib
md = hashlib.md5()
md.update('he'.encode('utf-8'))
md.update('ll'.encode('utf-8'))
md.update('o'.encode('utf-8'))
print(md.hexdigest())		#5d41402abc4b2a76b9719d911017c592

#hashlib模块的应用场景
	1.密码的密文传输
	2.校验文件的内容是否一致
    
#加盐处理、动态加盐
import hashlib
md = hashlib.md5()
md.update('加盐'.encode('utf-8'))
md.update('hello'.encode('utf-8'))
print(md.hexdigest())		#7e17f9d744b7f40b93c22fa9da785d40

import hashlib
md = hashlib.md5()
md.update('加盐hello'.encode('utf-8'))
print(md.hexdigest())		#7e17f9d744b7f40b93c22fa9da785d40

#实例
import hashlib
def get_md5(data):
    md = hashlib.md5()
    md.update('加盐'.encode('utf-8'))
    md.update(data.encode('utf-8'))
    return md.hexdigest()
password = input('请输入你的密码>>>: ').strip()
res = get_md5(password)
print('密码密文为:加盐%s',%res)

openpyxl模块

#操作Excel表格的模块
	Excel文件在03版本之前,后缀名叫xls
    Excel文件在03版本之后,后缀名叫xlsx

#xlwd模块
	写Excel
#xlrt模块
	读Excel
    
#xlwd模块、xlrt模块,既支持03版本之前的Excel文件,也支持03版本之后的文件
#openyxl模块,只支持03版本之后的文件

#openpyxl模块
from openpyxl import Workbook
wb = Workbook()         #先生成一个工作蒲
wb1 = wb.create_sheet('index0',0)        #指定sheet名,初始化sheet位置
wb2 = wb.create_sheet('index1',1)
wb3 = wb.create_sheet('index2',2)

wb1.append(['username','age','hobby'])      #添加表头
wb1.append(['syy',18,'study'])              #添加数据
wb1['A3'] = 666         #在指定的sheet中写入内容
wb1['A4'] = 777
wb2['G6'] = 888
wb3.cell(row=6,column=3,value=999)
wb1['A5'] = '=sum(A3:A4)'       #求和

wb1.title = 'login'     #修改sheet名
wb.save('test.xlsx')    #指定文件名

copy模块

#浅拷贝
	#浅拷贝中不可变数据类型指向的是同一个
    #浅拷贝中可变数据类型拷贝指向的还是原来的拷贝对象,而深拷贝指向的是新的
    
#深拷贝
	#深拷贝中,所有的id指向的都是对应的同一个值
    #深浅拷贝的本质在于新的变量名指向的值,的指向是原来的内存地址还是原来的值

#链式变量
l = [1,2,[1,2]]
l1 = l
print(id(l),id(l1))		#2422474796744 2422474796744
l[0] = 222
print(l,l1)				#[222, 2, [1, 2]] [222, 2, [1, 2]]

#copy模块
import copy
l = [1,2,[1,2]]
l1 = copy.copy(l)
print(id(l),id(l1))		#1742143678728 1742143677768
l[0] = 222				#这部分是深拷贝
print(l,l1)				#[222, 2, [1, 2]] [1, 2, [1, 2]]
l[2].append(666)		#这部分是浅拷贝
print(l,l1)				#[1, 2, [1, 2, 666]] [1, 2, [1, 2, 666]]

#深拷贝
import copy
l = [1,2,[1,2]]
l1 = copy.deepcopy(l)
print(id(l),id(l1))
l[2].append(666)
print(l,l1)

2493117098248 2493117097288
[1, 2, [1, 2, 666]] [1, 2, [1, 2]]
posted @ 2020-11-30 09:08  看萝卜在飘  阅读(214)  评论(0编辑  收藏  举报