1 开发基础

1 计算机扫盲

1-1 计算机基础

1、什么是编程语言?什么语言?为什么要有编程语言?

​ 编程语言的本质就是一门语言

​ 语言就是一种事物与另外一种事物沟通的表达方式/工具

2、什么编程?为什么要编程?

​ 编程就是人把自己想计算机做的事,也就是自己的思维逻辑,用编程语言表达出来

​ 编程的目的就是让计算机按照人类的思维逻辑去工作,从而解放人力

3、什么是计算机

​ 计算机俗称“电脑" ,包含人对计算机的终极期望,能够真的像人脑一样去工作

4、为何要有计算机

​ 为了执行人类的程序,从而把人类解放出来

​ 大前提:计算机所有的组成都是模仿人的某一功能或器官

5、计算机的组成

  • 控制器:

    作用:是计算机的指挥系统,负责控制计算机所有其他组件如何工作的
    类比:控制器=》人类的大脑

  • 运算器:

    作用:运算包括数学运算与逻辑运算

    类比:运算=》人类的大脑

    控制器+运算器=》cpu===》 人类的大脑

  • 存储器/IO设备

    作用:是计算机的记忆功能,负责数据的存取

    分类:

    内存(基于电工作) :存取数据都快,断电数据丢失,只能临时存取数据

    外存(硬盘,基于磁工作) :存取速度都慢,断电数据也不丢,可以永久保存数据

  • 输入设备input

    如键盘、鼠标

  • 输出设备output

    如显示器、打印机

6、三大核心硬件的关系

程序最先是存放于硬盘之上

程序的运行一定事先把程序的代码加载到内存

然后cpu从内存中读取指令执行


1-2 操作系统

1、引入

​ 应用程序(暴风影音)

​ 操作系统:控制程序

​ 计算机硬件

2、操作系统概念

​ 操作系统是一个协调、管理、控制计算机硬件资源与应用软件资源的一个控制程序

​ 作用:

  • 控制计算机硬件的基本运行

  • 把使用硬件的复杂操作封装成简单的功能,给,上层的应用程序使用

3、程序的区分

​ 计算机硬件是死的,计算机硬件的运行都受软件控制,所以说,软件相当于计算机的灵 云魂

​ 具体来说软件分为两种:

  • 应用软件:应用程序相关逻辑

  • 系统软件:控制底层硬件的

4、计算机系统三层结构

​ 应用程序

​ 操作系统

​ 计算机硬件

5、平台

​ 计算机硬件+操作系统=》平台

​ 软件的跨平台性指的是: 一款软件可以任意平台上运行,是衡量软件质量高低的一个非 常重要的指标


1-3 知识详解

1、cpu详解

​ cpu的分类与指令集

​ x86- -64 cpu具有向下兼容性(*****)

​ 64位的cpu既能运行32位的程序也能运行64位的程序

  • 内核态与用户态(*****)

    代表cpu的两种工作状态

    *内核态:运行的程序是操作系统,可以操作计算机硬件

    *用户态:运行的程序是应用程序,不能操作计算机硬件

  • 内核态与用户态的转换

    应用程序的运行必然涉及到计算机硬件的操作,那就必须有用户态切换到

    内核态下才能实现,所以计算机工作时在频繁发生内核态与用户态的转换

  • 多线程与多核芯片

    2核4线程:

    2核代表有两个cpu, 4线程指的是每个cpu都有两个线程=》假4核

    4核8线程

    4核代表有4个cpu, 8线程指的是每个cpu都有两个线程=》假8核

2、存储器

  • RAM:内存

  • ROM:“只读内存”

    存放计算机厂商写死计算机上的一段核心程序=》BIOS

  • CMOS:存取速度慢,断电数据丢失,耗电量极低

  • 硬盘:

    机械硬盘:磁盘

    磁道:

    圈数据,对应着一 串二进制(1bit代表一一个二进制位)

    8bit比特位= 1Bytes字节
    
    1024Bytes=1KB
    
    1 02 4KB= 1MB
    
    1 02 4MB=1GB
    
    1024GB=1TB 
    
    1024TB=1PB 
    

    扇区:

    一个扇区通过为512Bytes

    站在硬盘的解读,一次性读写数据的最小单为为扇区

    操作系统一次性读写的单位是一个block块=》8扇区的大小=4096Bytes

  • I0延迟(*****)

    7200转/min120转/s

    1/120转/s=转一圈需要花费8ms

    平均寻道时间:机械手臂转到数据所在磁道需要花费的时间,受限于物理工艺水平,目前机械硬能到的速度是5ms

    平均延迟时间:转半圈需要花费4ms,受限于硬盘的转速

    IO延迟=平均寻道时间+平均延迟时间

    优化程序运行效率的一个核心法则:能从内存取数据,就不要从硬盘取

3、操作系统的启动流程(*****)

  • BIOS介绍:

    ​ BIOS: Basic Input output SystemBIOS被写入ROM设备

  • 裸机:

    ​ cpu

    ​ ROM:充当内存,存放BIOS系统CMOS:充当硬盘

  • 启动流程

    1.计算机加电

    2.BIOS开始运行,检测硬件:cpu、内存、硬盘等

    3.BIOS读取CMOS存储器中的参数,选择启动设备

    4.从启动设备上读取第一个扇区的内容(MBR主引导记录512字节,前446为引导信息,后64为分区信息,最后两个为标志位)

    5.根据分区信息读入bootloader启动装载模块,启动操作系统

    6.然后操作系统询问BIOS,以获得配置信息。对于每种设备,系统会检查其设备驱动程序是否存在,如果没有,系统则会要求用户按照设备驱动程序。一旦有了全部的设备驱动程序操作系统就将它们调入内核。然后初始有关的表格(如进程表),穿件需要的进程,并在每个终端上启动登录程序或GUI


1-4 语言介绍

1、编程语言介绍

  • 分类:

    机器语言

    汇编语言

    高级语言(编译型、解释型号)

2、总结

  • 执行效率:机器语言>汇编语言>高级语言(编译型>解释型)
  • 开发效率:机器语言<汇编语言<高级语言(编译型<解释型)
  • 跨平台性:解释型具有极强的跨平台型

3、运行python的两种方式

  • 交互式

    即时得 到程序的运行结果,多用于调试

  • 脚本的方式

    把程序写到文件里(约定俗称文件名后缀为. py)

    然后用python解释器解释执行其中的内容

    python3.8 py thon程序文件的路径

4、一个python应用程序的运行的三个步骤(******)

python3.8 C: \a\b\c. py

执行python程序经历三个步骤:

  • 先启动python3.8解释器,此时相当于启动了一个文本编辑器

  • 解释器会发送系统调用,把c. py的内容从硬盘读入内存,此时c. py中的内容

    全部为普通字符,没有任何语法意义

  • 解释器开始解释执行刚刚读入内存的C.py的代码,开始识别python语法

对比文本编辑器读取C:\a\b\c.py文件内容也经历了三个步骤

  • 先启动文本编辑器

  • 文本编辑器会发送系统调用,把c. py的内容从硬盘读入内存

  • 文本编辑会将刚刚读入内存的内容控制输出到屏幕上,让用户看到结果

总结:

二者在前两个阶段做的事情完全一致

唯一不同的就是第三个阶段对读入内存的python代码的处理方式不同


2 变量与数据类型

2-1 变量

1、什么是变量

​ 变量就是可以变化的量,量指的是事物的状态,比如人的年龄、性别,

​ 游戏角色的等级金钱等等。

2、为什么要有变量

​ 为了让计算机能够像人一样去记忆事物的某种状态,并且状态是可以发生变化的。

3、变量的命名规范

  • 如果我们要存储的数据18代表的是一个人的年龄,那么变量名推荐命名为age

    age = 18        
    
  • 如果我们要存储的数据18代表的是一个人的等级,那么变量名推荐命名为level

    level = 18
    

① 变量名只能是 字母、数字或下划线的任意组合

② 变量名的第一个字符不能是数字

③ 关键字不能声明为变量名,常用关键字如下

['and', 'as', 'assert', 'break', 'class', 'continue', 'def', 'del', 'elif', 'else', 'except', 'exec', 'finally', 'for', 'from','global', 'if', 'import', 'in', 'is', 'lambda', 'not', 'or', 'pass', 'print', 'raise', 'return', 'try', 'while', 'with', 'yield']

4、内存管理:垃圾回收机制

垃圾:当一个变量值被绑定的变量名的个数为0时,该变量值无法被访问到,称之为垃圾

  • 引用计数增加

    ​ x=10 # 10的引用计数为1

    ​ y=X # 10的引用计数为2

    ​ z=X # 10的引用计数为3

  • 引用计数减少

    del X # 解除变量名x与值10的绑定关系,10的引用计数变为2

    del y # 10的引用计数变为1

    z = 12345 # 10的引用计数变为0

5、变量有三大组成部分

  • 变量名:是指向等号右侧值的内存地址的,用来访问等号右侧的值

  • 赋值符号:将变量值的内存地址绑定给变量名

  • 变量值:代表记录的事物的状态

6、变量值三个重要的特征

name= 'egon'
# id:反映的是变量值的内存地址,不是真的内存地址而是一种映射;内存地址不同id则不同
print( id(name) )

# type:不同类型的值用来表示记录不同的状态
print(type(name) )

# value:值本身
print ( name )

id不同的情况下,值有可能相同,即两块不同的内存空间里可以存相同的值
id相同的情况下,值一定相同,X is y成立,X == y也必然成立 

7、is 与 ==

  • is:比较左右两个值身份id是否相等

    Id不同的情况下,值有可能相同(不同的内存空间可以存在相同的值)

    id相同的情况下,内存地址相同,值一定相同

  • ==:比较左右两个值他们的值是否相等

8、常量:不变的量

​ 注意: python语法中没有常量的概念,但是在程序的开发过程中会涉及到常量的概念

​ AGE_ OF_ ALEX=73 #小写字母全为大写代表常量,这只是一种约定、规范

9、了解:小整数池

​ python解释器启动的那一刻,就会在内存中事先申请好一系类列内存空间存放常用的整 数;范围(-5~256)

​ 而pycharm会扩大该范围。


2-2 int整形

1、作用:记录年龄,身份证号等跟数字有关的

2、定义:

age = 18

3、应用:数值运算(加减乘除)

​ int和float可以相加减乘除;比较运算

level = 1
level = level + 1

4、类型转换

  • 纯数字的字符串转成int

    res = int('1011')
    print(res, type(res))
    
  • 十进制转其他进制

    十转二:除2取余法,余数倒着写;

    二转十:1011:1x2**3+0x2**2+1x2**1+1x2**0=8+2+1=11

    八转十:13:1x8**1+3x8**0=11

    16转十:7b:7x16**1+11x16**0=123

    bin(3)   # 十进制转二进制
    '0b11'    # 0b代表二进制
    oct(9)   # 十进制转八进制
    '0o11'    # 0o代表八进制
    hex(17)  # 十进制转十六进制
    '0x11'    # 0x代表十六进制
    
  • 其他进制转十进制

    int('0b11',2)  # 二进制转十进制
    3
    int('0o11',8)  # 八进制转十进制
    9
    int('0x11',16) # 十六进制转十进制
    17
    

5、内置方法

int没有需要掌握的内置方法,它的使用就是数学运算+比较运算


2-3 float浮点型

1、作用:记录身高,体重,薪资等有关小数点的类型

2、定义:

height = 1.78

3、应用:数值运算(加减乘除)

​ int和float可以相加减乘除;比较运算

level = 1.3
level = level + 1

4、类型转换

  • 字符串类型的小数转成浮点型

    res = float('1.23')
    print(res, type(res))
    

5、内置方法

Float没有需要掌握的内置方法,它的使用就是数学运算+比较运算


2-4 str字符串类型

1、作用:记录描述形式的状态,如:名字,性别

2、定义:用引号包含的一串字符(' ', " ", """ """, ''' ''')

info = "我是个大帅比"

3、应用:双引号时,内外区分

info = "my name is 'lxx'"
info = "my name is \'lxx\'"  # 转义

# 字符串之间可以相加,但只限于str与str之间。代表字符串的拼接,缺点是效率极低。
y = 'hello'
x = 'world'
print(x + y)  

# 还可以相乘
print('=' * 10)  # 打印出10个=

4、类型转换

# str()可以将任意数据类型转换成字符串类型,例如 
type(str([1,2,3])) # list->str
<class 'str'>
type(str({"name":"jason","age":18})) # dict->str
<class 'str'>
type(str((1,2,3)))  # tuple->str
<class 'str'>
type(str({1,2,3,4})) # set->str
<class 'str'>

5、内置方法

  • 优先掌握

    str1 = 'hello python!'
    # 1.按索引取值(正向取,反向取):
    # 1.1 正向取(从左往右)
    >>> str1[6]
    p
    # 1.2 反向取(负号表示从右往左)
    >>> str1[-4]
    h
    # 1.3 对于str来说,只能按照索引取值,不能改
    >>> str1[0]='H' # 报错TypeError
    
    # 2.切片(顾头不顾尾,步长)
    # 2.1 顾头不顾尾:取出索引为0到8的所有字符
    >>> str1[0:9]  
    hello pyt
    # 2.2 步长:0:9:2,第三个参数2代表步长,会从0开始,每次累加一个2即可,所以会取出索引0、2、4、6、8的字符
    >>> str1[0:9:2]  
    hlopt 
    # 2.3 反向切片
    >>> str1[::-1]  # -1表示从右往左依次取值
    !nohtyp olleh
    
    # 3.长度len
    # 3.1 获取字符串的长度,即字符的个数,但凡存在于引号内的都算作字符)
    >>> len(str1) # 空格也算字符
    13
    
    # 4.成员运算 in 和 not in    
    # 4.1 int:判断hello 是否在 str1里面
    >>> 'hello' in str1  
    True
    # 4.2 not in:判断tony 是否不在 str1里面
    >>> 'tony' not in str1 
    True
    
    # 5.strip移除字符串首尾指定的字符(默认移除空格)
    # 5.1 括号内不指定字符,默认移除首尾空白字符(空格、\n、\t)
    >>> str1 = '  life is short!  '
    >>> str1.strip()  
    life is short!
    
    # 5.2 括号内指定字符,移除首尾指定的字符
    >>> str2 = '**tony**'  
    >>> str2.strip('*')  
    tony
    
    msg = '**/-=()**lxx&*_+_'
    print(msg.strip('*/-=()&_+'))
    lxx
    
    # 6.切分split
    # 6.1 括号内不指定字符,默认以空格作为切分符号
    >>> str3='hello world'
    >>> str3.split()
    ['hello', 'world']
    # 6.2 括号内指定分隔字符,则按照括号内指定的字符切割字符串
    >>> str4 = '127.0.0.1'
    >>> str4.split('.')  
    ['127', '0', '0', '1']  # 注意:split切割得到的结果是列表数据类型
    
    # 6.3 括号内指定分隔字符和分割次数,则按照括号内指定的字符切割字符串
    info = 'egon:12:male'
    print(info.split(':', 1))  
    
    # 7.循环
    >>> str5 = '今天你好吗?'
    >>> for line in str5:  # 依次取出字符串中每一个字符
    ...     print(line)
    ...
    今
    天
    你
    好
    吗
    ?
    
  • 需要掌握的操作

    1.strip, lstrip, rstrip

    >>> str1 = '**tony***'
    
    >>> str1.strip('*')  # 移除左右两边的指定字符
    'tony'
    >>> str1.lstrip('*')  # 只移除左边的指定字符
    tony***
    >>> str1.rstrip('*')  # 只移除右边的指定字符
    **tony
    

    2.lower(),upper()

    >>> str2 = 'My nAme is tonY!'
    
    >>> str2.lower()  # 将英文字符串全部变小写
    my name is tony!
    >>> str2.upper()  # 将英文字符串全部变大写
    MY NAME IS TONY!
    

    3.startswith,endswith

    >>> str3 = 'tony jam'
    
    # startswith()判断字符串是否以括号内指定的字符开头,结果为布尔值True或False
    >>> str3.startswith('t') 
    True
    >>> str3.startswith('j')
    False
    # endswith()判断字符串是否以括号内指定的字符结尾,结果为布尔值True或False
    >>> str3.endswith('jam')
    True
    >>> str3.endswith('tony')  
    False
    

    4.格式化输出之format

    之前我们使用%s来做字符串的格式化输出操作,在传值时,必须严格按照位置与%s一一对应,而字符串的内置方法format则提供了一种不依赖位置的传值方式

    案例:

    # format括号内在传参数时完全可以打乱顺序,但仍然能指名道姓地为指定的参数传值,name=‘tony’就是传给{name}
    >>> str4 = 'my name is {name}, my age is {age}!'.format(age=18,name='tony')
    >>> str4  
    'my name is tony, my age is 18!'
    
    >>> str4 = 'my name is {name}{name}{name}, my age is {name}!'.format(name='tony', age=18)
    >>> str4  
    'my name is tonytonytony, my age is tony!'
    

    format的其他使用方式(了解)

    # 类似于%s的用法,传入的值会按照位置与{}一一对应
    >>> str4 = 'my name is {}, my age is {}!'.format('tony', 18)
    >>> str4 
    my name is tony, my age is 18!
    # 把format传入的多个值当作一个列表,然后用{索引}取值
    >>> str4 = 'my name is {0}, my age is {1}!'.format('tony', 18)
    >>> str4
    my name is tony, my age is 18!
    
    >>> str4 = 'my name is {1}, my age is {0}!'.format('tony', 18)
    >>> str4  
    my name is 18, my age is tony!
    
    >>> str4 = 'my name is {1}, my age is {1}!'.format('tony', 18)
    >>> str4  
    my name is 18, my age is 18!
    

    5.split,rsplit

    # split会按照从左到右的顺序对字符串进行切分,可以指定切割次数
    >>> str5='C:/a/b/c/d.txt'
    >>> str5.split('/',1)
    ['C:', 'a/b/c/d.txt']  
    
    # rsplit刚好与split相反,从右往左切割,可以指定切割次数
    >>> str5='a|b|c'
    >>> str5.rsplit('|',1)
    ['a|b', 'c']
    

    6.join

    # 从可迭代对象中取出多个字符串,然后按照指定的分隔符进行拼接,拼接的结果为字符串
    >>> '%'.join('hello') # 从字符串'hello'中取出多个字符串,然后按照%作为分隔符号进行拼接
    'h%e%l%l%o'
    >>> '|'.join(['tony','18','read'])  # 从列表中取出多个字符串,然后按照|作为分隔符号进行拼接
    'tony|18|read'
    

    7.replace

    # 用新的字符替换字符串中旧的字符
    >>> str7 = 'my name is tony, my age is 18!'  # 将tony的年龄由18岁改成73岁
    >>> str7 = str7.replace('18', '73')  # 语法:replace('旧内容', '新内容')
    >>> str7
    my name is tony, my age is 73!
    
    # 可以指定修改的个数,默认修改全部
    >>> str7 = 'my name is tony, my age is 18!'
    >>> str7 = str7.replace('my', 'MY',1) # 只把一个my改为MY
    >>> str7
    'MY name is tony, my age is 18!'
    

    8.isdigit

    # 判断字符串是否是纯数字组成,返回结果为True或False
    >>> str8 = '5201314'
    >>> str8.isdigit()
    True
    
    >>> str8 = '123g123'
    >>> str8.isdigit()
    False
    
  • 了解掌握

    # 1.find,rfind,index,rindex,count
    # 1.1 find:从指定范围内查找子字符串的起始索引,找得到则返回数字1,找不到则返回-1
    >>> msg='tony say hello'
    >>> msg.find('o',1,3)  # 在索引为1和2(顾头不顾尾)的字符中查找字符o的索引
    1  
    # 1.2 index:同find,但在找不到时会报错
    >>> msg.index('e',2,4) # 报错ValueError
    # 1.3 rfind与rindex:略
    # 1.4 count:统计字符串在大字符串中出现的次数
    >>> msg = "hello everyone"
    >>> msg.count('e')  # 统计字符串e出现的次数
    4
    >>> msg.count('e',1,6)  # 字符串e在索引1~5范围内出现的次数
    1
    
    # 2.center,ljust,rjust,zfill
    >>> name='tony'
    >>> name.center(30,'-')  # 总宽度为30,字符串居中显示,不够用-填充
    -------------tony-------------
    >>> name.ljust(30,'*')  # 总宽度为30,字符串左对齐显示,不够用*填充
    tony**************************
    >>> name.rjust(30,'*')  # 总宽度为30,字符串右对齐显示,不够用*填充
    **************************tony
    >>> name.zfill(50)  # 总宽度为50,字符串右对齐显示,不够用0填充
    0000000000000000000000000000000000000000000000tony
    
    # 3.expandtabs
    >>> name = 'tony\thello'  # \t表示制表符(tab键)
    >>> name
    tony    hello
    >>> name.expandtabs(1)  # 修改\t制表符代表的空格数为1
    tony hello
    
    # 4.captalize,swapcase,title
    # 4.1 captalize:首字母大写
    >>> message = 'hello everyone nice to meet you!'
    >>> message.capitalize()
    Hello everyone nice to meet you!  
    # 4.2 swapcase:大小写翻转
    >>> message1 = 'Hi girl, I want make friends with you!'
    >>> message1.swapcase()  
    hI GIRL, i WANT MAKE FRIENDS WITH YOU!  
    #4.3 title:每个单词的首字母大写
    >>> msg = 'dear my friend i miss you very much'
    >>> msg.title()
    Dear My Friend I Miss You Very Much 
    
    # 5.is数字系列
    #在python3中
    num1 = b'4' #bytes
    num2 = u'4' #unicode,python3中无需加u就是unicode
    num3 = '四' #中文数字
    num4 = 'Ⅳ' #罗马数字
    
    #isdigt:bytes,unicode
    >>> num1.isdigit()
    True
    >>> num2.isdigit()
    True
    >>> num3.isdigit()
    False
    >>> num4.isdigit() 
    False
    
    #isdecimal:uncicode(bytes类型无isdecimal方法)
    >>> num2.isdecimal() 
    True
    >>> num3.isdecimal() 
    False
    >>> num4.isdecimal() 
    False
    
    #isnumberic:unicode,中文数字,罗马数字(bytes类型无isnumberic方法)
    >>> num2.isnumeric() 
    True
    >>> num3.isnumeric() 
    True
    >>> num4.isnumeric() 
    True
    
    # 三者不能判断浮点数
    >>> num5 = '4.3'
    >>> num5.isdigit()
    False
    >>> num5.isdecimal()
    False
    >>> num5.isnumeric()
    False
    
    '''
    总结:
        最常用的是isdigit,可以判断bytes和unicode类型,这也是最常见的数字应用场景
        如果要判断中文数字或罗马数字,则需要用到isnumeric。
    '''
    
    # 6.is其他
    >>> name = 'tony123'
    >>> name.isalnum() #字符串中既可以包含数字也可以包含字母
    True
    >>> name.isalpha() #字符串中只包含字母
    False
    >>> name.isidentifier() # 判断标识符是否合法(内置名字都是合法的)
    True
    >>> name.islower()  # 字符串是否是纯小写
    True
    >>> name.isupper()  # 字符串是否是纯大写
    False
    >>> name.isspace()  # 字符串是否全是空格
    False
    >>> name.istitle()  # 字符串中的单词首字母是否都是大写
    False
    

2-5 list列表

1、作用:按位置记录多个值,索引从0开始,按照索引取值

2、定义:在[ ]内,逗号分开多个任意类型的值,一个值称为一个元素

love = ['小姐姐', 18, 'mimi', 1.78, [11,22]]
print(love)  # 取出所有的值
print(love[0])  # 取出列表中第一个值
print(love[-1])  # 取出列表中最后一个值
print(love[-1][1])  # 取出列表中最后一个值中索引为1的值

3、应用:

love = [['小姐姐', 18, '1.68'], ['大姐姐', 18, '1.78']]
# 取出大姐姐的身高
print(love[1][2])

4、类型转换

# 但凡能被for循环遍历的数据类型都可以传给list()转换成列表类型,list()会跟for循环一样遍历出数据类型中包含的每一个元素然后放到列表中
>>> list('wdad') # 结果:['w', 'd', 'a', 'd'] 
>>> list([1,2,3]) # 结果:[1, 2, 3]
>>> list({"name":"jason","age":18}) #结果:['name', 'age']
>>> list((1,2,3)) # 结果:[1, 2, 3] 
>>> list({1,2,3,4}) # 结果:[1, 2, 3, 4]

5、内置方法

  • 优先掌握方法

    # 1.按索引存取值(正向存取+反向存取):即可存也可以取  
    # 1.1 正向取(从左往右)
    >>> my_friends=['tony','jason','tom',4,5]
    >>> my_friends[0]  
    tony
    # 1.2 反向取(负号表示从右往左)
    >>> my_friends[-1]  
    5
    # 1.3 对于list来说,既可以按照索引取值,又可以按照索引修改指定位置的值,但如果索引不存在则报错
    >>> my_friends = ['tony','jack','jason',4,5]
    >>> my_friends[1] = 'martthow'
    >>> my_friends
    ['tony', 'martthow', 'jason', 4, 5]
    
    # 2.切片(顾头不顾尾,步长)
    # 2.1 顾头不顾尾:取出索引为0到3的元素
    >>> my_friends[0:4] 
    ['tony', 'jason', 'tom', 4]
    # 2.2 步长:0:4:2,第三个参数2代表步长,会从0开始,每次累加一个2即可,所以会取出索引0、2的元素
    >>> my_friends[0:4:2]  
    ['tony', 'tom']
    >>> my_friends[:] # 重头切到尾(相当于浅拷贝)
    >>> my_friends[::-1] # 从尾切到头
    
    >>> l=[1,2,3,4,5,6]
    >>> l[0:3:1] 
    [1, 2, 3]  # 正向步长
    >>> l[2::-1] 
    [3, 2, 1]  # 反向步长
    
    # 通过索引取值实现列表翻转
    >>> l[::-1]
    [6, 5, 4, 3, 2, 1]
    
    # 3.长度
    >>> len(my_friends)
    5
    
    # 4.成员运算in和not in
    >>> 'tony' in my_friends
    True
    >>> 'xxx' not in my_friends
    True
    
    # 5.添加
    # 5.1 append()列表尾部追加元素
    >>> l1 = ['a','b','c']
    >>> l1.append('d')
    >>> l1
    ['a', 'b', 'c', 'd']
    
    # 5.2 extend()一次性在列表尾部添加多个元素
    >>> l1.extend(['a','b','c'])
    >>> l1
    ['a', 'b', 'c', 'd', 'a', 'b', 'c']
    
    # 5.3 insert()在指定位置插入元素
    >>> l1.insert(0,"first")  # 0表示按索引位置插值
    >>> l1
    ['first', 'a', 'b', 'c', 'alisa', 'a', 'b', 'c']
    
    # 6.删除
    # 6.1 del 单纯删除,没有返回值
    >>> l = [11,22,33,44]
    >>> del l[2]  # 删除索引为2的元素
    >>> l
    [11,22,44]
    
    # 6.2 pop()默认删除列表最后一个元素,并将删除的值返回,括号内可以通过加索引值来指定删除元素
    >>> l = [11,22,33,22,44]
    >>> res=l.pop()
    >>> res
    44
    >>> res=l.pop(1)
    >>> res
    22
    
    # 6.3 remove()括号内指名道姓表示要删除哪个元素,没有返回值
    >>> l = [11,22,33,22,44]
    >>> res=l.remove(22) # 从左往右查找第一个括号内需要删除的元素
    >>> print(res)
    None
    
    # 7.reverse()颠倒列表内元素顺序
    >>> l = [11,22,33,44]
    >>> l.reverse() 
    >>> l
    [44,33,22,11]
    
    # 8.sort()给列表内所有元素排序
    # 8.1 排序时列表元素之间必须是相同数据类型,不可混搭,否则报错
    >>> l = [11,22,3,42,7,55]
    >>> l.sort()
    >>> l 
    [3, 7, 11, 22, 42, 55]  # 默认从小到大排序
    >>> l = [11,22,3,42,7,55]
    >>> l.sort(reverse=True)  # reverse用来指定是否跌倒排序,默认为False
    >>> l 
    [55, 42, 22, 11, 7, 3]
    # 8.2 了解知识:
    # 我们常用的数字类型直接比较大小,但其实,字符串、列表等都可以比较大小,原理相同:都是依次比较对应位置的元素的大小,如果分出大小,则无需比较下一个元素,比如
    >>> l1=[1,2,3]
    >>> l2=[2,]
    >>> l2 > l1
    True
    # 字符之间的大小取决于它们在ASCII表中的先后顺序,越往后越大
    >>> s1='abc'
    >>> s2='az'
    >>> s2 > s1 # s1与s2的第一个字符没有分出胜负,但第二个字符'z'>'b',所以s2>s1成立
    True
    # 所以我们也可以对下面这个列表排序
    >>> l = ['A','z','adjk','hello','hea']
    >>> l.sort()
    >>> l
    ['A', 'adjk', 'hea', 'hello','z']
    
    # 9.循环
    # 循环遍历my_friends列表里面的值
    for line in my_friends:
        print(line) 
    'tony'
    'jack'
    'jason'
    4
    5
    
    # 10.count统计出现的次数
    l = [1,'aaa','bbb','aaa','ccc']
    l.count('aaa')  # 2
    
    # 11.index查找元素的索引
    l = [1,'aaa','bbb','aaa','ccc']
    l.index('aaa')  # 1 从左到右查找‘aaa'的索引,没有元素报错
    
    # 12.clear 清空列表元素
    l = [1,'aaa','bbb','aaa','ccc']
    l.clear()  # []
    
  • 队列:FIFO

    # ---------------先进先出------------------
    # 入队操作
    l = []
    l.append('app01')
    l.append('app02')
    l.append('app03')
    
    # 出队操作
    l.pop(0)
    l.pop(0)
    l.pop(0)
    
  • 堆栈:LIFO

    # --------------后进先出------------------
    # 入队操作
    l = []
    l.append('app01')
    l.append('app02')
    l.append('app03')
    # 出队操作
    l.pop()
    l.pop()
    l.pop()
    

2-6 dict字典

1、作用:用来存多个值,每个值都有一个key对应,key对应值其中key通常为字符串型, 对值可以有描述性的作用

2、定义:在{ }内用逗号分开多个key:value,字典默认是无序的;value可以使任意 类型,但是key必须是不可变类型。

dic = {'a': 1, 'b': 'lxx'}
print(dic['a']) 
d = {}  # 造出一个空字典
d = dict(x=1, y=2,z=3)  # 造出一个字典d

3、应用:

students=[
{'name':'tony','age':38,'hobbies':['play','sleep']},
{'name':'jack','age':18,'hobbies':['read','sleep']}]
print(students[1]['hobbies'][1])  # #取第1个学生的第1个爱好

4、类型转换

# 转换1: 
>>> info=dict([['name','tony'],('age',18)])
>>> info
{'age': 18, 'name': 'tony'}

# 转换2:fromkeys会从元组中取出每个值当做key,然后与None组成key:value放到字典中
>>> {}.fromkeys(('name','age','sex'),None)  
{'age': None, 'sex': None, 'name': None}

5、内置方法

# 1、按key存取值:可存可取
# 1.1 取
>>> dic = {
...     'name': 'xxx',
...     'age': 18,
...     'hobbies': ['play game', 'basketball']
... }
>>> dic['name']
'xxx'
>>> dic['hobbies'][1]
'basketball'
# 1.2 对于赋值操作,如果key原先不存在于字典,则会新增key:value
>>> dic['gender'] = 'male'  
>>> dic
{'name': 'tony', 'age': 18, 'hobbies': ['play game', 'basketball'],'gender':'male'}
# 1.3 对于赋值操作,如果key原先存在于字典,则会修改对应value的值
>>> dic['name'] = 'tony'
>>> dic
{'name': 'tony', 'age': 18, 'hobbies': ['play game', 'basketball']}
# 2、长度len
>>> len(dic) 
3
# 3、成员运算in和not in
>>> 'name' in dic  # 判断某个值是否是字典的key
True
# 4、删除
>>> del dic['name']  # 单纯删除么有返回值
>>> dic.popitem()  # 随机删除一个key,返回删除的(key,value)
>>> dic.pop('name')  # 通过指定字典的key来删除键值对,返回删除的value
>>> dic
{'age': 18, 'hobbies': ['play game', 'basketball']}
# 5、键keys(),值values(),键值对items()
>>> dic = {'age': 18, 'hobbies': ['play game', 'basketball'], 'name': 'xxx'}
# 获取字典所有的key
>>> dic.keys()  
dict_keys(['name', 'age', 'hobbies'])
# 获取字典所有的value
>>> dic.values()
dict_values(['xxx', 18, ['play game', 'basketball']])
# 获取字典所有的键值对
>>> dic.items()
dict_items([('name', 'xxx'), ('age', 18), ('hobbies', ['play game', 'basketball'])])
# 6、循环
# 6.1 默认遍历的是字典的key
>>> for key in dic:
...     print(key)
... 
age
hobbies
name
# 6.2 只遍历key
>>> for key in dic.keys():
...     print(key)
... 
age
hobbies
name
# 6.3 只遍历value
>>> for key in dic.values():
...     print(key)
... 
18
['play game', 'basketball']
xxx
# 6.4 遍历key与value
>>> for key in dic.items():
...     print(key)
... 
('age', 18)
('hobbies', ['play game', 'basketball'])
('name', 'xxx')

1.get()

>>> dic= {'k1':'jason','k2':'Tony','k3':'JY'}
>>> dic.get('k1')
'jason'  # key存在,则获取key对应的value值
>>> res=dic.get('xxx') # key不存在,不会报错而是默认返回None
>>> print(res)
None  
>>> res=dic.get('xxx',666) # key不存在时,可以设置默认返回的值
>>> print(res)
666 
# ps:字典取值建议使用get方法

2.pop()

>>> dic= {'k1':'jason','k2':'Tony','k3':'JY'}
>>> v = dic.pop('k2')  # 删除指定的key对应的键值对,并返回值
>>> dic
{'k1': 'jason', 'kk2': 'JY'}
>>> v
'Tony'

3.popitem()

>>> dic= {'k1':'jason','k2':'Tony','k3':'JY'}
>>> item = dic.popitem()  # 随机删除一组键值对,并将删除的键值放到元组内返回
>>> dic
{'k3': 'JY', 'k2': 'Tony'}
>>> item
('k1', 'jason')

4.update()

# 用新字典更新旧字典,有则修改,无则添加
>>> dic= {'k1':'jason','k2':'Tony','k3':'JY'}
>>> dic.update({'k1':'JN','k4':'xxx'})
>>> dic
{'k1': 'JN', 'k3': 'JY', 'k2': 'Tony', 'k4': 'xxx'}

5.fromkeys()

>>> dic = dict.fromkeys(['k1','k2','k3'],[])
>>> dic
{'k1': [], 'k2': [], 'k3': []}

6.setdefault()

# key不存在则新增键值对,并将新增的value返回
>>> dic={'k1':111,'k2':222}
>>> res=dic.setdefault('k3',333)
>>> res
333
>>> dic # 字典中新增了键值对
{'k1': 111, 'k3': 333, 'k2': 222}

# key存在则不做任何修改,并返回已存在key对应的value值
>>> dic={'k1':111,'k2':222}
>>> res=dic.setdefault('k1',666)
>>> res
111
>>> dic # 字典不变
{'k1': 111, 'k2': 222}

2-7 tuple元组

1、作用:按照索引/位置存放多个值,只用于读不用于改写

2、定义:()内逗号分隔开多个任意类型的元素

t = (1,1.3,'aa')  # t = tuple((1,1.3,'aa'))

# 强调:如果元组内只有一个值,则必须加一个逗号,否则()就只是包含的意思而非定义元组
t = (10,)

3、类型转换

# 但凡能被for循环的遍历的数据类型都可以传给tuple()转换成元组类型
>>> tuple('wdad') # 结果:('w', 'd', 'a', 'd') 
>>> tuple([1,2,3]) # 结果:(1, 2, 3)
>>> tuple({"name":"jason","age":18}) # 结果:('name', 'age')
>>> tuple((1,2,3)) # 结果:(1, 2, 3)
>>> tuple({1,2,3,4}) # 结果:(1, 2, 3, 4)
# tuple()会跟for循环一样遍历出数据类型中包含的每一个元素然后放到元组中

4、内置方法

>>> tuple1 = (1, 'hhaha', 15000.00, 11, 22, 33) 
# 1、按索引取值(正向取+反向取):只能取,不能改否则报错!  
>>> tuple1[0]
1
>>> tuple1[-2]
22
>>> tuple1[0] = 'hehe'  # 报错:TypeError:
# 2、切片(顾头不顾尾,步长)
>>> tuple1[0:6:2] 
(1, 15000.0, 22)
# 3、长度
>>> len(tuple1)  
6
# 4、成员运算 in 和 not in
>>> 'hhaha' in tuple1 
True
>>> 'hhaha' not in tuple1  
False 
# 5、循环
>>> for line in tuple1:
...     print(line)
1
hhaha
15000.0
11
22
33
# 6、index查看元素的索引
tuple1 = (1, 'hhaha', 15000.00, 11, 22, 33) 
tuple1.index('hhaha')  # 1 
tuple1.index('xxx')  # 报错
# 7、count统计出现的次数
tuple1 = (1, 'hhaha', 15000.00, 11, 22, 33) 
tuple1.count('hhaha')  # 1 

2-8 set集合

1、作用:集合、list、tuple、dict一样都可以存放多个值,但是集合主要用于:去重、关系运算

2、定义:

"""
定义:在{}内用逗号分隔开多个元素,集合具备以下三个特点:
     1:每个元素必须是不可变类型
     2:集合内没有重复的元素
     3:集合内元素无序
"""
s = {1,2,3,4}  # 本质 s = set({1,2,3,4})

# 注意1:列表类型是索引对应值,字典是key对应值,均可以取得单个指定的值,而集合类型既没有索引也没有key与值对应,所以无法取得单个的值,而且对于集合来说,主要用于去重与关系元素,根本没有取出单个指定值这种需求。

# 注意2:{}既可以用于定义dict,也可以用于定义集合,但是字典内的元素必须是key:value的格式,现在我们想定义一个空字典和空集合,该如何准确去定义两者?
d = {} # 默认是空字典 
s = set() # 这才是定义空集合

3、类型转换

# 但凡能被for循环的遍历的数据类型(强调:遍历出的每一个值都必须为不可变类型)都可以传给set()转换成集合类型
>>> s = set([1,2,3,4])
>>> s1 = set((1,2,3,4))
>>> s2 = set({'name':'jason',})
>>> s3 = set('egon')
>>> s,s1,s2,s3
{1, 2, 3, 4} {1, 2, 3, 4} {'name'} {'e', 'o', 'g', 'n'}

4、关系运算

我们定义两个集合friends与friends2来分别存放两个人的好友名字,然后以这两个集合为例讲解集合的关系运算

>>> friends1 = {"zero","kevin","jason","egon"} # 用户1的好友们 
>>> friends2 = {"Jy","ricky","jason","egon"}   # 用户2的好友们
# 1.合集/并集(|):求两个用户所有的好友(重复好友只留一个)
>>> friends1 | friends2
{'kevin', 'ricky', 'zero', 'jason', 'Jy', 'egon'}

# 2.交集(&):求两个用户的共同好友
>>> friends1 & friends2
{'jason', 'egon'}

# 3.差集(-):
>>> friends1 - friends2 # 求用户1独有的好友
{'kevin', 'zero'}
>>> friends2 - friends1 # 求用户2独有的好友
{'ricky', 'Jy'}

# 4.对称差集(^) # 求两个用户独有的好友们(即去掉共有的好友)
>>> friends1 ^ friends2
{'kevin', 'zero', 'ricky', 'Jy'}

# 5.值是否相等(==)
>>> friends1 == friends2
False

# 6.父集:一个集合是否包含另外一个集合
# 6.1 包含则返回True
>>> {1,2,3} > {1,2}
True
>>> {1,2,3} >= {1,2}
True
# 6.2 不存在包含关系,则返回False
>>> {1,2,3} > {1,3,4,5}
False
>>> {1,2,3} >= {1,3,4,5}
False

# 7.子集
>>> {1,2} < {1,2,3}
True
>>> {1,2} <= {1,2,3}
True

5、去重

集合去重复有局限性

# 1. 只能针对不可变类型
# 2. 集合本身是无序的,去重之后无法保留原来的顺序

示例如下

>>> l=['a','b',1,'a','a']
>>> s=set(l)
>>> s # 将列表转成了集合
{'b', 'a', 1}
>>> l_new=list(s) # 再将集合转回列表
>>> l_new
['b', 'a', 1] # 去除了重复,但是打乱了顺序

# 针对不可变类型,并且保证顺序则需要我们自己写代码实现,例如
l=[
    {'name':'lili','age':18,'sex':'male'},
    {'name':'jack','age':73,'sex':'male'},
    {'name':'tom','age':20,'sex':'female'},
    {'name':'lili','age':18,'sex':'male'},
    {'name':'lili','age':18,'sex':'male'},
]

new_l=[]

for dic in l:
    if dic not in new_l:
        new_l.append(dic)

print(new_l)
# 结果:既去除了重复,又保证了顺序,而且是针对不可变类型的去重
[
    {'age': 18, 'sex': 'male', 'name': 'lili'}, 
    {'age': 73, 'sex': 'male', 'name': 'jack'}, 
    {'age': 20, 'sex': 'female', 'name': 'tom'}
]

其他操作

# 1.长度
>>> s={'a','b','c'}
>>> len(s)
3

# 2.成员运算
>>> 'c' in s
True

# 3.循环
>>> for item in s:
...     print(item)
... 
c
a
b

# 4.删除元素
s = {1,2,3}
s.discard(4)  # 删除一个不存在元素不会报错
s.remove(4)  # 删除一个不存在元素会报错

# 5.更新元素
s.update({2,3,4})

# 6.添加一个元素
s.add(10)

# 7.isdisjoint判断两个集合是否独立,没有共同部分,返回true
res = s.isdisjoint({4,5,6})

# 8.pop随机删除一个元素
s.pop()

2-9 bool类型

1、作用:用来记录真假这两种状态;

2、定义:

is_ok = True 
is_ok = False

3、应用:通常用来当作判断的条件,在if中经常用到

4、隐式bool值:

​ 0,None,空(空字符串、列表、字典)的bool值为假,其余都为真


2-10 bytes类型

bytes类型,在底层就是一堆二进制数,python友好的给你转成了十六进制。以后碰到bytes类型,就当是二进制数


3 内存管理

1、什么是垃圾回收机制?

​ 回收机制(简称GC)是Python解释器自带一种机制,专门用来回收不可用的变量

​ 值所占用的内存空间。

2、为什么要用垃圾回收机制?

程序运行过程中会申请大量的内存空间,而对于一些无用的内存空间如果不及时清理的 话会导致内存使用殆尽(内存溢出),导致程序崩溃,因此管理内存是一件重要 且繁 杂的事情,而python解释器自带的垃圾回收机制把程序员从繁杂的内存管理中解放来。


3-1 引用计数

  • 直接引用:指的是从栈区出发直接引用到的内存地址
  • 间接引用:指的是从栈区出发引用到堆区后,再通过进一步引用才能到达的内存地址
x = 10  # 值10被变量名x直接引用
l1 = [x, 20]  # 列表本身被变量名l1直接引用,包含的元素被列表间接引用
l2 = [30, 40]  # 列表本身被变量名l2直接引用,包含的元素被列表间接引用

image


3-2 标记清除

引用计数机制存在着一个致命的弱点,即循环引用(也称交叉引用)

# 如下我们定义了两个列表,简称列表1与列表2,变量名l1指向列表1,变量名l2指向列表2
l1=['xxx']  # 列表1被引用一次,列表1的引用计数变为1   
l2=['yyy']  # 列表2被引用一次,列表2的引用计数变为1   
l1.append(l2)    # 把列表2追加到l1中作为第二个元素,列表2的引用计数变为2
l2.append(l1)    # 把列表1追加到l2中作为第二个元素,列表1的引用计数变为2

# l1与l2之间有相互引用
# l1 = ['xxx'的内存地址,列表2的内存地址]
# l2 = ['yyy'的内存地址,列表1的内存地址]
print(l1)
['xxx', ['yyy', [...]]]
print(l2)
['yyy', ['xxx', [...]]]
print(l1[1][1][0])
'xxx'

image

解决方案:标记-清除

标记:标记的过程其实就是,遍历所有的GC Roots对象(栈区中的所有内容或者线程都可以作为GC Roots对象),然后将所有GC Roots的对象可以直接或间接访问到的对象标记为存活的对象,其余的均为非存活对象,应该被清除。

清除:清除的过程将遍历堆中所有的对象,将没有标记的对象全部清除掉。直接引用或者间接引用的不用清除,如果只剩下彼此之间的间接引用(交叉引用)会被清除掉。

基于上例的循环引用,当我们同时删除l1与l2时,会清理到栈区中l1与l2的内容以及直接引用关系

image

这样在启用标记清除算法时,从栈区出发,没有任何一条直接或间接引用可以访达l1与l2,即l1与l2成了“无根之人”,于是l1与l2都没有被标记为存活,二者会被清理掉,这样就解决了循环引用带来的内存泄漏问题。


3-3 分代回收

引用计数还有一个效率问题

基于引用计数的回收机制,每次回收内存,都需要把所有对象的引用计数都遍历一遍,这是非常消耗时间的,于是引入了分代回收来提高回收效率,分代回收采用的是用“空间换时间”的策略。

解决方案:分代回收:

分代回收的核心思想是:在历经多次扫描的情况下,都没有被回收的变量,gc机制就会认为,该变量是常用变量,gc对其扫描的频率会降低


4 用户交互

1、什么是与用户交互

​ 用户交互就是人往计算机中input/输入数据,计算机print/输出结果

2、input:输入

# 在python3中input功能会等待用户的输入,用户输入任何内容,都存成字符串类型,然后赋值给等号左边的变量名
username=input('请输入您的用户名:')  
请输入您的用户名:jack  # username = "jack"

password=input('请输入您的密码:')  
请输入您的密码:123  # password = "123"*

3、print:输出

print('hello world')  # 只输出一个值
hello world
print('first','second','third')  # 一次性输出多个值,值用逗号隔开
first second third
# 默认print功能有一个end参数,该参数的默认值为"\n"(代表换行),可以将end参数的值改成任意其它字符
print("aaaa",end='')
print("bbbb",end='&')
print("cccc",end='@')
#整体输出结果为:aaaabbbb&cccc@

5 格式化输出

1、%号

​ %s:是代表字符串; %d:整数; %f:浮点数

%s占位符:可以接收任意类型的值,如:整数,列表,字典	
%d占位符:只能接收数字
%f占位符:只能接收浮点数
  • 按位置传值

    print('亲爱的%s你好!你%s月的话费是%d,余额是%d' %('tony',12,103,11))
    # 亲爱的tony你好!你12月的话费是103,余额是11
    
  • key传值

    print('my name is %(name)s and age %(age)s' % {'name': 'tony', 'age': '19'})
    # my name is tony and age 19
    

2、str:format 可以使用花括号,兼容性好

  • 按位置传值

    print('亲爱的{}你好!你{}月的话费是{},余额是{}'.format('tony',12,103,11))
    # 亲爱的tony你好!你12月的话费是103,余额是11
    
    print('亲爱的{0}你好!你{1}月的话费是{1}'.format('tony',12))
    # 亲爱的tony你好!你12月的话费是12
    
  • key传值

    print("我的名字是{name} 我的年龄是{age}". format(age=18, name= ' egon'))
    # 我的名字是 egon 我的年龄是18
    

3、f python3.5以后才推出,格式化速度最快,但python2中不支持该语法

X= input( ' your name :' )
y=input( 'your age:' )
res=f'我的名字是{x}我的年龄是{y} '
print( res)

6 基本运算符

6-1 算数运算符

python支持的算数运算符与数学上计算的符号使用是一致的,我们以x=9,y=2为例来依次介绍它们
image


6-2 比较运算符

python语法中除了有=号这种简单的赋值运算外,还支持增量赋值、链式赋值、交叉赋值、解压赋值,这些赋值运算符存在的意义都是为了让我们的代码看起来更加精简。我们以x=9,y=2为例先来介绍一下增量赋值
image


6-3 赋值运算符

1、增量赋值
image

2、链式赋值

链式赋值指的是可以用一行代码搞定这件事

x=y=z=10
print(x,y,z) 
# (10, 10, 10)

3、交叉赋值

# 我们定义两个变量m与n
m = 10
n = 20
# 如果我们想将m与n的值交换过来,可以这么做
m,n=n,m  # 交叉赋值(20, 10)

4、解压赋值

nums=[11,22,33,44,55]
# 如果我们想把列表中的多个值取出来依次赋值给多个变量名,可以这么做
a,b,c,d,e=nums

​ 注意,上述解压赋值,等号左边的变量名个数必须与右面包含值的个数相同,否则会报错

​ 但如果我们只想取头尾的几个值,可以用*_匹配

# 取前两个值
a,b,*_=nums
print(a,b)  # 11 22; *会将没有对应关系的值存成列表形式
a,b,*C=nums
print(C)  # [33,44,55]

# 取后两个值
*_,a,b=nums
print(a,b)  # 44 55

# 取两边的值,但无法取中间值
a,*_,b=nums
print(a,b)  # 11 55

​ ps:字符串、字典、元组、集合类型都支持解压赋值


6-4 逻辑运算符

也称短路运算

逻辑运算符用于连接多个条件,进行关联判断,会返回布尔值True或False
image

1、连续多个and

​ 可以用and连接多个条件,会按照从左到右的顺序依次判断,一旦某一个条件False, 则无需再往右判断,可以立即判定最终结果就为False,只有在所有条件的结果都True 的情况下,最终结果才为True。

2 > 1 and 1 != 1 and True and 3 > 2
# 判断完第二个条件,就立即结束,得的最终结果为False

2、连续多个or

​ 可以用or连接多个条件,会按照从左到右的顺序依次判断,一旦某一个条件为True,则 无需再往右判断,可以立即判定最终结果就为True,只有在所有条件的结果都为False 的情况下,最终结果才为False

2 > 1 or 1 != 1 or True or 3 > 2
# 判断完第一个条件,就立即结束,得的最终结果为True

3、优先级not>and>or

三者的优先级关系:not>and>or,同一优先级默认从左往右计算。

3>4 and not 4>3 or 1==3 and 'x' == 'x' or 3 >3
# False

最好使用括号来区别优先级,其实意义与上面的一样

(3>4 and (not 4>3)) or (1==3 and 'x' == 'x') or 3 >3

6-5 成员运算符

image

'lili' not in ['jack','tom','robin']
# True

6-6 身份运算符

image

强调:==双等号比较的是value是否相等,而is比较的是id是否相等

#1. id相同,内存地址必定相同,意味着type和value必定相同
#2. value相同type肯定相同,但id可能不同,如下
x='Info Tony:18'
y='Info Tony:18'
id(x),id(y) # x与y的id不同,但是二者的值相同
(4327422640, 4327422256)

x == y # 等号比较的是value
True
type(x),type(y) # 值相同type肯定相同
(<class 'str'>, <class 'str'>)
x is y # is比较的是id,x与y的值相等但id可以不同
False

7 可变不可变类型

1、可变类型:

​ 值改变,ID不变,证明改的是原值,原值是可以改变的。不可哈希的。

​ (值发生改变时,内存地址不变,即id不变,证明在改变原值)

list、dict、set

2、不可变类型:

​ 值改变,ID也变,证明产生了新的值,没有改变原值,证明原值不可修改。

​ (值发生改变时,内存地址也发生改变,即id也变,证明是没有在改变原值,是产生 了新的值)

int、float、str、bool、元组、都被设计成了不可分割的整体,不能够被改变

8 if判断

用if关键字来实现分支结构,完整语法如下

if 条件1:   # 如果条件1的结果为True,就依次执行:代码1、代码2,......
   代码1
    代码2
    ......
elif 条件2: # 如果条件2的结果为True,就依次执行:代码3、代码4,......
   代码3
    代码4
    ......
elif 条件3: # 如果条件3的结果为True,就依次执行:代码5、代码6,......
   代码5
    代码6
    ......
else:     # 其它情况,就依次执行:代码7、代码8,......
    代码7
    代码8
    ......
# 注意:
# 1、python用相同缩进(4个空格表示一个缩进)来标识一组代码块,同一组代码会自上而下依次运行
# 2、条件可以是任意表达式,但执行结果必须为布尔类型
     # 在if判断中所有的数据类型也都会自动转换成布尔类型
       # 2.1、None,0,空(空字符串,空列表,空字典等)三种情况下转换成的布尔值为False
       # 2.2、其余均为True

练习: 登陆功能

name=input('请输入用户名字:').strip()
password=input('请输入密码:').strip()
if name == 'tony' and password == '123':
    print('tony login success')
else:
    print('用户名或密码错误')

9 深浅copy

需求:

拷贝一下原列表产生一个新的列表;

让两个列表完全独立开,并且针对的是改操作的独立而不是读操作

1、浅copy:是把原列表第一层内存地址不加区分的完全copy一份给新地址

list1 = ['alex', 'ds', [1, 2]]
list2 = list1.copy()  #拷贝 

拷贝的列表内存地址不一样,但是列表中值的内存地址是相同的。(如果是不可变类型,那么浅copy是没有问题的。但如果是可变类型,那么list1list2中的可变类型就会粘在一起)

简而言之:浅拷贝,不可变类型内存地址相同,可变类型内存地址也相同。

2、深copy:要想copy得到的新列表与原列表的改操作完全独立开必须有一种可以区分开 可变类 型与不可变类型的copy机制,这就是深copy

import copy
list1 = ['alex', 'ds', [1, 2]]
list2 = copy.deepcopy(list1)

拷贝的列表内存地址不一样,列表中不可变值内存地址是相同,但可变类型内存地址是不相同的。

简而言之:深拷贝,不可变类型内存地址相同,可变类型内存地址不同。


10 while循环

1、while循环称之为条件循环,语法如下:

while 条件:
     代码1     
     代码2     
     代码3
        
while的运行步骤:
步骤1:如果条件为真,那么依次执行:代码1、代码2、代码3、......
步骤2:执行完毕后再次判断条件,如果条件为True则再次执行:代码1、代码2、代码3、......,如果条件为False,则循环终止

2、案例一:while循环的基本使用

用户认证程序

username = "jason"
password = "123"
# 记录错误验证的次数
count = 0
while count < 3:
    inp_name = input("请输入用户名:")
    inp_pwd = input("请输入密码:")
    if inp_name == username and inp_pwd == password:
        print("登陆成功")
    else:
        print("输入的用户名或密码错误!")
        count += 1

3、while+break的使用

纯计算无io的死循环会导致致命的效率问题

使用了while循环后,代码确实精简多了,但问题是用户输入正确的用户名密码以后无法结束循环,那如何结束掉一个循环呢?这就需要用到break了!

username = "jason"
password = "123"
count = 0
while count < 3:  # 第一层循环
    inp_name = input("请输入用户名:").strip()
    inp_pwd = input("请输入密码:").strip()
    if inp_name == username and inp_pwd == password:
        print("登陆成功")
        while True:  # 第二层循环
            cmd = input('>>: ')
            if cmd == 'quit':
                break  # 用于结束本层循环,即第二层循环
            print('run <%s>' % cmd)
        break  # 用于结束本层循环,即第一层循环
    else:
        print("输入的用户名或密码错误!")
        count += 1

4、while循环嵌套+tag的使用

针对嵌套多层的while循环,如果我们的目的很明确就是要在某一层直接退出所有层的循环,其实有一个窍门,就让所有while循环的条件都用同一个变量,该变量的初始值为True,一旦在某一层将该变量的值改成False,则所有层的循环都结束

username = "jason"
password = "123"
count = 0

tag = True
while tag: 
    inp_name = input("请输入用户名:")
    inp_pwd = input("请输入密码:")
    if inp_name == username and inp_pwd == password:
        print("登陆成功")
        while tag:  
            cmd = input('>>: ')
            if cmd == 'quit':
                tag = False  # tag变为False, 所有while循环的条件都变为False 
                break
            print('run <%s>' % cmd)
        break  # 用于结束本层循环,即第一层循环
    else:
        print("输入的用户名或密码错误!")
        count += 1

5、while+continue的使用

break代表结束本层循环,而continue则用于结束本次循环,直接进入下一次循环

# 打印1到10之间,除7以外的所有数字
number=11
while number>1:
    number -= 1
    if number==7:
        continue # 结束掉本次循环,即本次循环continue之后的代码都不会运行了,而是直接进入下一次循环
    print(number)

6、while+else的使用

在while循环的后面,我们可以跟else语句,当while 循环正常执行完并且中间没有被break 中止的话,就会执行else后面的语句,所以我们可以用else来验证,循环是否正常结束

count = 0
while count <= 5 :
    count += 1
    print("Loop",count)
    """
    # 由于循环被break打断了,所以不执行else后的输出语句
    if count == 3:
    	break
    """
else:
    print("循环正常执行完啦")
print("-----out of while loop ------")

11 for循环

循环结构的第二种实现方式是for循环,for循环可以做的事情while循环都可以实现,之所以用for循环是因为在循环取值(即遍历值)时for循环比while循环的使用更为简洁,

1、for循环语法如下

for 变量名 in 可迭代对象: # 此时只需知道可迭代对象可以是字符串\列表\字典,我们之后会专门讲解可迭代对象 可迭代对象可以是: 列表、字典、字符串、元组、集合
    代码一
    代码二
    ...

#例1
for item in ['a','b','c']:
    print(item)
# 运行结果
a
b
c

# 参照例1来介绍for循环的运行步骤
# 步骤1:从列表['a','b','c']中读出第一个值赋值给item(item=‘a’),然后执行循环体代码
# 步骤2:从列表['a','b','c']中读出第二个值赋值给item(item=‘b’),然后执行循环体代码
# 步骤3: 重复以上过程直到列表中的值读尽

2、for循环案例

案例一:打印数字0-5

# 简单版:for循环的实现方式
for count in range(6):  # range(6)会产生从0-5这6个数
    print(count)

# 复杂版:while循环的实现方式
count = 0
while count < 6:
    print(count)
    count += 1

案例二:遍历字典

# 简单版:for循环的实现方式
for k in {'name':'jason','age':18,'gender':'male'}:  # for 循环默认取的是字典的key赋值给变量名k
    print(k)

# 复杂版:while循环确实可以遍历字典,后续将会迭代器部分详细介绍

注意:break 与 continue也可以用于for循环,使用语法同while循环

总结:for循环和while循环的异同

​ 相同之处: 都是循环,for循环可以干的事,while循环也可以干

​ 不同之处: while循环称之为条件循环,循环次数取决于条件何时变为假for循环称之为"取值循环",循环次数取决in后包含的值的个数


11-1 for+range

for循环控制循环次数:range()

range(1,10,1) # 开头1,结束10,步长1。顾头不顾尾

for+break : 同while循环一 样

for+else : 同while循环一 样

lit=['aaa', 'bbb' , 'ccc'] # len(lit)

for i in range(len(lit)): # len取长度。
print(i, lit[i] )

案例:for循环嵌套

# 外层循环循环一次,内层循环需要完整的循环完毕
# 请用for循环嵌套的方式打印如下图形:
*****
*****
*****

for i in range(3):
    for j in range(5):
        print("*",end='')
    print()  # print()表示换行
    
# 补充:终止for循环只有break一种方法

11-2 for+break+else

for+break:同while循环一样

for+else:同while循环一样

username = 'lxx'
password = '123'

for i in range(3):  # 012345
    inp_name = input('>>:')
    inp_pwd = input('>>:')
    if inp_name == username and inp_pwd == password:
        print('登录成功...')
        break
else:
    print('输入错误次数太多...')

11-3 for+continue

for+continue:同while循环一样

for i in range(6):#012345
    if i == 4:continue
    print( i)

11-4 for+enumrate

请使用enumrate输出列表元素和序号(序号从100开始)

for index, val in enumerate(li, 100):    
    print(index, val)

12 字符编码

字符串类型、文本文件的内容都是由字符组成的,但凡涉及到字符的存取,都需要考虑字符编码的问题。

12-1 知识储备

1 核心硬件

所有软件都是运行硬件之上的,与运行软件相关的三大核心硬件为cpu、内存、硬盘,我们需要明确三点

#1、软件运行前,软件的代码及其相关数据都是存放于硬盘中的

#2、任何软件的启动都是将数据从硬盘中读入内存,然后cpu从内存中取出指令并执行

#3、软件运行过程中产生的数据最先都是存放于内存中的,若想永久保存软件产生的数据,则需要将数据由内存写入硬盘

2 文件读取流程

#阶段1、启动一个文件编辑器(文本编辑器如nodepad++,pycharm,word)

#阶段2、文件编辑器会将文件内容从硬盘读入内存

#阶段3、文本编辑器会将刚刚读入内存中的内容显示到屏幕上

3 程序执行流程

以python test.py为例,执行流程如下

#阶段1、启动python解释器,此时就相当于启动了一个文本编辑器

#阶段2、python解释器相当于文本编辑器,从硬盘上将test.py的内容读入到内存中

#阶段3、python解释器解释执行刚刚读入的内存的内容,开始识别python语法

4 总结

python解释器与文件本编辑的异同如下

#1、相同点:前两个阶段二者完全一致,都是将硬盘中文件的内容读入内存,详解如下
python解释器是解释执行文件内容的,因而python解释器具备读py文件的功能,这一点与文本编辑器一样

#2、不同点:在阶段3时,针对内存中读入的内容处理方式不同,详解如下
文本编辑器将文件内容读入内存后,是为了显示或者编辑,根本不去理会python的语法,而python解释器将文件内容读入内存后,可不是为了给你瞅一眼python代码写的啥,而是为了执行python代码、会识别python语法)

12-2 编码介绍

人类在与计算机交互时,用的都是人类能读懂的字符,如中文字符、英文字符、日文字符等

而计算机只能识别二进制数,详解如下

#二进制数即由0和1组成的数字,例如010010101010。计算机是基于电工作的,电的特性即高低电平,人类从逻辑层面将高电平对应为数字1,低电平对应为数字0,这直接决定了计算机可以识别的是由0和1组成的数字

毫无疑问,由人类的字符到计算机中的数字,必须经历一个过程,如下

image

翻译的过程必须参照一个特定的标准,该标准称之为字符编码表,该表上存放的就是字符与数字一一对应的关系。

字符编码中的编码指的是翻译或者转换的意思,即将人能理解的字符翻译成计算机能识别的数字

12-3 编码发展史

  • 阶段一

现代计算机起源于美国,所以最先考虑仅仅是让计算机识别英文字符,于是诞生了ASCII表

# ASCII表的特点:
    1、只有英文字符与数字的一一对应关系
    2、一个英文字符对应1Bytes,1Bytes=8bit,8bit最多包含256个数字,可以对应256个字符,足够表示所有英文字符

image

  • 阶段二:

为了让计算机能够识别中文和英文,中国人定制了GBK

# GBK表的特点:
    1、只有中文字符、英文字符与数字的一一对应关系
    2、一个英文字符对应1Bytes
       一个中文字符对应2Bytes   
       补充说明:
       1Bytes=8bit,8bit最多包含256个数字,可以对应256个字符,足够表示所有英文字符
       2Bytes=16bit,16bit最多包含65536个数字,可以对应65536个字符,足够表示所有中文字符

每个国家都各自的字符,为让计算机能够识别自己国家的字符外加英文字符,各个国家都制定了自己的字符编码表

# Shift_JIS表的特点:
    1、只有日文字符、英文字符与数字的一一对应关系

# Euc-kr表的特点:
    1、只有韩文字符、英文字符与数字的一一对应关系

此时,美国人用的计算机里使用字符编码标准是ASCII、中国人用的计算机里使用字符编码标准是GBK、日本人用的计算机里使用字符编码标准是Shift_JIS

文本文件内容全都为字符,无论存取都是涉及到字符编码问题
#1、存文本文件
人类通过文本编辑器输入的字符会被转化成ASCII格式的二进制存放于内存中,如果需要永久保存,则直接将内存中的ASCII格式的二进制写入硬盘

#2、读文本文件
直接将硬盘中的ASCII格式的二进制读入内存,然后通过ASCII表反解成英文字符
  • 阶段三:

unicode于1990年开始研发,1994年正式公布,具备两大特点:

#1. 存在所有语言中的所有字符与数字的一一对应关系,即兼容万国字符

#2. 与传统的字符编码的二进制数都有对应关系,详解如下

很多地方或老的系统、应用软件仍会采用各种各样传统的编码,这是历史遗留问题。此处需要强调:软件是存放于硬盘的,而运行软件是要将软件加载到内存的,面对硬盘中存放的各种传统编码的软件,想让我们的计算机能够将它们全都正常运行而不出现乱码,内存中必须有一种兼容万国的编码,并且该编码需要与其他编码有相对应的映射/转换关系,这就是unicode的第二大特点产生的缘由

image

文本编辑器输入任何字符都是最新存在于内存中,是unicode编码的,存放于硬盘中,则可以转换成任意其他编码,只要该编码可以支持相应的字符,但是不能老编码之间互转

# 英文字符可以被ASCII识别
英文字符--->unciode格式的数字--->ASCII格式的数字

# 中文字符、英文字符可以被GBK识别
中文字符、英文字符--->unicode格式的数字--->gbk格式的数字

# 日文字符、英文字符可以被shift-JIS识别
日文字符、英文字符--->unicode格式的数字--->shift-JIS格式的数字

12-4 编码与解码

由字符转换成内存中的unicode,以及由unicode转换成其他编码的过程,都称为编码encode

image

由内存中的unicode转换成字符,以及由其他编码转换成unicode的过程,都称为解码decode

image

在诸多文件类型中,只有文本文件的内存是由字符组成的,因而文本文件的存取也涉及到字符编码的问题


12-5 utf-8

注意:如果保存到硬盘的是GBK格式二进制,当初用户输入的字符只能是中文或英文,同理如果保存到硬盘的是Shift_JIS格式二进制,当初用户输入的字符只能是日文或英文……如果我们输入的字符中包含多国字符,那么该如何处理?

#多国字符—√—》内存(unicode格式的二进制)——X—》硬盘(GBK格式的二进制)

#多国字符—√—》内存(unicode格式的二进制)——X—》硬盘(Shift_JIS格式的二进制)

#多国字符—√—》内存(unicode格式的二进制)——√—》硬盘(???格式的二进制)

理论上是可以将内存中unicode格式的二进制直接存放于硬盘中的,但由于unicode固定使用两个字节来存储一个字符,如果多国字符中包含大量的英文字符时,使用unicode格式存放会额外占用一倍空间(英文字符其实只需要用一个字节存放即可),然而空间占用并不是最致命的问题,最致命地是当我们由内存写入硬盘时会额外耗费一倍的时间,所以将内存中的unicode二进制写入硬盘或者基于网络传输时必须将其转换成一种精简的格式,这种格式即utf-8(全称Unicode Transformation Format,即unicode的转换格式)

# 多国字符—√—》内存(unicode格式的二进制)——√—》硬盘(utf-8格式的二进制)

image

那为何在内存中不直接使用utf-8呢?

utf-8是针对Unicode的可变长度字符编码:一个英文字符占1Bytes,一个中文字符占3Bytes,生僻字用更多的Bytes存储

unicode更像是一个过渡版本,我们新开发的软件或文件存入硬盘都采用utf-8格式,等过去几十年,所有老编码的文件都淘汰掉之后,会出现一个令人开心的场景,即硬盘里放的都是utf-8格式,此时unicode便可以退出历史舞台,内存里也改用utf-8,天下重新归于统一

12-6 编码应用

我们学习字符编码就是为了存取字符时不发生乱码问题:

#1、内存中固定使用unicode无论输入任何字符都不会发生乱码

#2、我们能够修改的是存/取硬盘的编码方式,如果编码设置不正确将会出现乱码问题。乱码问题分为两种:存乱了,读乱了

#2.1 存乱了:如果用户输入的内容中包含中文和日文字符,如果单纯以shift_JIS存,日文可以正常写入硬盘,而由于中文字符在shift_jis中没有找到对应关系而导致存乱了

#2.2 读乱了:如果硬盘中的数据是shift_JIS格式存储的,采GBK格式读入内存就读乱了

总结:

#1. 保证存的时候不乱:在由内存写入硬盘时,必须将编码格式设置为支持所输入字符的编码格式
#2. 保证存的时候不乱:在由硬盘读入内存时,必须采用与写入硬盘时相同的编码格式

12-7 三个阶段

  • 在文件首行写入包含#号在内的以下内容
# coding: 当初文件写入硬盘时采用的编码格式
  • 文本编辑器读取文件内容的流程

#阶段1、启动一个文件编辑器(文本编辑器如nodepad++,pycharm,word)

#阶段2、文件编辑器会将文件内容从硬盘读入内存

#阶段3、文本编辑器会将刚刚读入内存中的内容显示到屏幕上

  • python解释器执行文件的流程

以python test.py为例,执行流程如下

#阶段1、启动python解释器,此时就相当于启动了一个文本编辑器

#阶段2、python解释器相当于文本编辑器,从硬盘上将test.py的内容读入到内存中

#阶段3、python解释器解释执行刚刚读入的内存的内容,开始识别python语法

  • python解释器与文件本编辑的异同如下

#1、相同点:前两个阶段二者完全一致,都是将硬盘中文件的内容读入内存,详解如下

python解释器是解释执行文件内容的,因而python解释器具备读py文件的功能,这一点与文本编辑器一样

#2、不同点:在阶段3时,针对内存中读入的内容处理方式不同,详解如下

文本编辑器将文件内容读入内存后,是为了显示或者编辑,根本不去理会python的语法,而python解释器将文件内容读入内存后,可不是为了给你瞅一眼python代码写的啥,而是为了执行python代码、会识别python语法)


12-8 编解使用

# 1、unicode格式------编码encode-------->其它编码格式
>>> x='上' # 在python3在'上'被存成unicode
>>> res=x.encode('utf-8')
>>> res,type(res) # unicode编码成了utf-8格式,而编码的结果为bytes类型,可以当作直接当作二进制去使用
(b'\xe4\xb8\x8a', <class 'bytes'>)

# 2、其它编码格式------解码decode-------->unicode格式
>>> res.decode('utf-8') 
'上'

# 保证python2的str类型不乱码: x = u"上"

针对python3用gbk写的程序;解决方法:文件开头coding:gbk

存文件用gbk,并不让python3;解决方法:用python2,文件开头coding:gbk,字符串开头用u


13 文件操作

  • 什么是文件

    应用程序运行过程中产生的数据最先都是存放于内存中的,若想永久保存下来,必须要保存于硬盘中。应用程序若想操作硬件必须通过操作系统,而文件就是操作系统提供给应用程序来操作硬盘的虚拟概念,用户或应用程序对文件的操作,就是向操作系统发起调用,然后由操作系统完成对硬盘的具体操作。

13-1 操作模式

  • 文件读写内容的模式:tb

    大前提: tb模式均不能单独使用,必须与r/w/a之一结合使用

    t(默认的):文本模式
        1. 读写文件都是以字符串(unicode)为单位的
        2. 只能针对文本文件
        3. 必须指定encoding="utf-8"参数
        
    b:二进制模式:
       1.读写文件都是以bytes/二进制为单位的
       2. 可以针对所有文件
       3. 一定不能指定encoding参数
    
  • 文件读写操作的模式

    r(默认的):只读
    w:只写
    a:只追加写
    

13-2 操作文件

# 1. 打开文件,由应用程序向操作系统发起系统调用open(...),操作系统打开该文件,对应一块硬盘空间,并返回一个文件对象赋值给一个变量f
f=open('a.txt',mode='rt',encoding='utf-8') #默认打开模式就为rt

# 2. 调用文件对象下的读/写方法,会被操作系统转换为读/写硬盘的操作
data=f.read()

# 3. 向操作系统发起关闭文件的请求,回收系统资源
f.close()  # 回收操作系统资源
# del f  # 回收应用程序资源(python可自动回收)
ulimit -n # 查看可以打开的文件数,linux系统

13-3 with管理

python提供了with关键字来帮我们管理上下文

# 1、在执行完子代码块后,with 会自动执行f.close()
with open('a.txt','w') as f:
    pass 

# 2、可用用with同时打开多个文件,用逗号分隔开即可
with open('a.txt','r') as read_f,open('b.txt','w') as write_f:  
    data = read_f.read()
    write_f.write(data)
    
# 3、没有指定encoding参数,操作系统会使用默认的编码
# linux系统默认utf-8
# windows系统默认gbk
with open('c.txt', 'rt', encoding='utf-8') as f:
	res = f.read() # t模式会将f.read()读出的结果解码成unicode
	print(res, type(res))
    # 内存:utf-8格式的二进制---->解码------> unicode
    # 硬盘:c.txt内容:utf-8格式的二进制

13-4 r模式

# r只读模式: 在文件不存在时则报错,文件存在文件内指针直接跳到文件开头
with open('a.txt', mode='r', encoding='utf-8') as f:
    res = f.read()  # 会将文件的内容由硬盘全部读入内存,赋值给res

小案例:实现用户认证功能

inp_name = input('请输入你的名字: ').strip()
inp_pwd = input('请输入你的密码: ').strip()
with open(r'db.txt', mode='r', encoding='utf-8') as f:
    for line in f:
        # 把用户输入的名字与密码与读出内容做比对
        u, p = line.strip('\n').split(':')
        if inp_name == u and inp_pwd == p:
            print('登录成功')
            break
    else:
        print('账号名或者密码错误')

13-5 w模式

# w只写模式: 在文件不存在时会创建空文档,文件存在会清空文件,文件指针跑到文件开头
with open('b.txt', mode='w', encoding='utf-8') as f:
    f.write('你好\n')
    f.write('我好\n')
    f.write('大家好\n')
    f.write('111\n222\n333\n')
# 强调:
# 1 在文件不关闭的情况下,连续的写入,后写的内容一定跟在前写内容的后面
# 2 如果重新以w模式打开文件,则会清空文件内容

小案例:文件的copy工具,用在创建全新的文件

src_file = input('源文件路径:').strip()
dst_file = input('目标文件路径:').strip()
with open('{}.txt'.format(src_file), 'rt', encoding='utf-8') as f1, \
        open('{}.txt'.format(dst_file), 'wt', encoding='utf-8') as f2:
    res = f1.read()
    f2.write(res)

13-6 a模式

# a只追加写模式: 在文件不存在时会创建空文档,文件存在会将文件指针直接移动到文件末尾
 with open('c.txt',mode='a',encoding='utf-8') as f:
     f.write('44444\n')
     f.write('55555\n')
#强调 w 模式与 a 模式的异同:
# 1 相同点:在打开的文件不关闭的情况下,连续的写入,新写的内容总会跟在前写的内容之后
# 2 不同点:以 a 模式重新打开文件,不会清空原文件内容,会将文件指针直接移动到文件末尾,新写的内容永远写在最后

小案例:实现注册功能:

name=input('username>>>: ').strip()
pwd=input('password>>>: ').strip()
with open('db1.txt',mode='a',encoding='utf-8') as f:
    info='%s:%s\n' %(name,pwd)
    f.write(info)

13-7 +模式

# r+ w+ a+ :可读可写
# r+ 文件不存在直接报错,光标跳至开头,写内容会覆盖老内容
# w+ 文件不存在则创建,文件存在则清空
# a+ 文件不存在则创建,文件存在则光标跳至末尾
#在平时工作中,我们只单纯使用r/w/a,要么只读,要么只写,一般不用可读可写的模式

13-8 x模式

x:只写模式不可读;文件不存在则创建,存在则报错

with open('db.txt', 'xt', encoding='utf-8') as f:
    f.write('只能写...')

13-9 b模式

  • 案例一:t 模式的使用

    # t 模式:如果我们指定的文件打开模式为r/w/a,其实默认就是rt/wt/at
     with open('a.txt',mode='rt',encoding='utf-8') as f:
         res=f.read() 
         print(type(res)) # 输出结果为:<class 'str'>
    
     with open('a.txt',mode='wt',encoding='utf-8') as f:
         s='abc'
         f.write(s) # 写入的也必须是字符串类型
    
     #强调:t 模式只能用于操作文本文件,无论读写,都应该以字符串为单位,而存取硬盘本质都是二进制的形式,当指定 t 模式时,内部帮我们做了编码与解码
    
  • 案例二: b 模式的使用

    # b: 读写都是以二进制位单位
     with open('1.mp4',mode='rb') as f:
         data=f.read()
         print(type(data)) # 输出结果为:<class 'bytes'>
    
     with open('a.txt',mode='wb') as f:
         msg="你好"
         res=msg.encode('utf-8') # res为bytes类型
         f.write(res) # 在b模式下写入文件的只能是bytes类型
    
    #强调:b模式对比t模式
    1、在操作纯文本文件方面t模式帮我们省去了编码与解码的环节,b模式则需要手动编码与解码,所以此时t模式更为方便
    2、针对非文本文件(如图片、视频、音频等)只能使用b模式
    
    # 小练习: 编写拷贝工具
    src_file=input('源文件路径: ').strip()
    dst_file=input('目标文件路径: ').strip()
    with open(r'%s' %src_file,mode='rb') as read_f,open(r'%s' %dst_file,mode='wb') as write_f:
        for line in read_f:
            # print(line)
            write_f.write(line)
    

13-10 操作文件方法

1、重点

# 读操作
f.read()  # 读取所有内容,执行完该操作后,文件指针会移动到文件末尾
f.readline()  # 读取一行内容,光标移动到第二行首部
f.readlines()  # 读取每一行内容,存放于列表中

# 强调:
# f.read()与f.readlines()都是将内容一次性读入内容,如果内容过大会导致内存溢出,若还想将内容全读入内存,则必须分多次读入,有两种实现方式:
# 方式一
with open('a.txt',mode='rt',encoding='utf-8') as f:
    for line in f:
        print(line) # 同一时刻只读入一行内容到内存中

# 方式二
with open('1.mp4',mode='rb') as f:
    while True:
        data=f.read(1024) # 同一时刻只读入1024个Bytes到内存中
        if len(data) == 0:
            break
        print(data)

# 写操作
f.write('1111\n222\n')  # 针对文本模式的写,需要自己写换行符
f.write('1111\n222\n'.encode('utf-8'))  # 针对b模式的写,需要自己写换行符
f.writelines(['333\n','444\n'])  # 文件模式
f.writelines([bytes('333\n'.encoding='utf-8'),'444\n'.encode('utf-8'), b'hello']) #b模式

2、了解

f.readable()  # 文件是否可读
f.writable()  # 文件是否可写
f.closed  # 文件是否关闭
f.encoding  # 如果文件打开模式为b,则没有该属性
f.flush()  # 立刻将文件内容从内存刷到硬盘
f.name  # 文件名

13-11 文件指针操作

#大前提:文件内指针的移动都是Bytes为单位的,唯一例外的是t模式下的read(n),n以字符为单位
with open('a.txt',mode='rt',encoding='utf-8') as f:
     data=f.read(3) # 读取3个字符


with open('a.txt',mode='rb') as f:
     data=f.read(3) # 读取3个Bytes


# 之前文件内指针的移动都是由读/写操作而被动触发的,若想读取文件某一特定位置的数据,则则需要用f.seek方法主动控制文件内指针的移动,详细用法如下:
# f.seek(指针移动的字节数, 模式控制): 
# 模式控制:
# 0: 默认的模式,该模式代表指针移动的字节数是以文件开头为参照的
# 1: 该模式代表指针移动的字节数是以当前所在的位置为参照的
# 2: 该模式代表指针移动的字节数是以文件末尾的位置为参照的
# 强调:其中0模式可以在t或者b模式使用,而1跟2模式只能在b模式下用

# f.tell()  获取指针当前的位置
  • 案例1:0模式详解
# a.txt用utf-8编码,内容如下(abc各占1个字节,中文“你好”各占3个字节)
abc你好

# 0模式的使用
with open('a.txt',mode='rt',encoding='utf-8') as f:
    f.seek(3,0)     # 参照文件开头移动了3个字节
    print(f.tell()) # 查看当前文件指针距离文件开头的位置,输出结果为3
    print(f.read()) # 从第3个字节的位置读到文件末尾,输出结果为:你好
    # 注意:由于在t模式下,会将读取的内容自动解码,所以必须保证读取的内容是一个完整中文数据,否则解码失败

with open('a.txt',mode='rb') as f:
    f.seek(6,0)
    print(f.read().decode('utf-8')) #输出结果为: 好
  • 案例2:1模式详解
# 1模式的使用
with open('a.txt',mode='rb') as f:
    f.seek(3,1) # 从当前位置往后移动3个字节,而此时的当前位置就是文件开头
    print(f.tell()) # 输出结果为:3
    f.seek(4,1)     # 从当前位置往后移动4个字节,而此时的当前位置为3
    print(f.tell()) # 输出结果为:7
  • 案例3:2模式详解
# a.txt用utf-8编码,内容如下(abc各占1个字节,中文“你好”各占3个字节)
abc你好

# 2模式的使用
with open('a.txt',mode='rb') as f:
    f.seek(0,2)     # 参照文件末尾移动0个字节, 即直接跳到文件末尾
    print(f.tell()) # 输出结果为:9
    f.seek(-3,2)     # 参照文件末尾往前移动了3个字节
    print(f.read().decode('utf-8')) # 输出结果为:好

# 小练习:实现动态查看最新一条日志的效果
import time
with open('access.log',mode='rb') as f:
    f.seek(0,2)
    while True:
        line=f.readline()
        if len(line) == 0:
            # 没有内容
            time.sleep(0.5)
        else:
            print(line.decode('utf-8'),end='')

13-12 动态检测文件

# 生成日志数据
with open('b.txt', 'at', encoding='utf-8') as f:
    f.write('2013-12-21 11:11:11 xxx转账给小红10W\n')

# 检测日志数据变化
import time
with open('b.txt', 'rb') as f:
    f.seek(0, 2)
    while 1:
        line = f.readline()
        if len(line) == 0:
            time.sleep(0.3)
        else:
            print(line.decode('utf-8'))

13-13 文件修改方式

1、文件修改方式一:

# 实现思路:将文件内容发一次性全部读入内存,然后在内存中修改完毕后再覆盖写回原文件
# 优点: 在文件修改过程中同一份数据只有一份
# 缺点: 会过多地占用内存
with open('db.txt',mode='rt',encoding='utf-8') as f:
    data=f.read()

with open('db.txt',mode='wt',encoding='utf-8') as f:
    f.write(data.replace('kevin','SB'))

2、文件修改方式二:

# 实现思路:以读的方式打开原文件,以写的方式打开一个临时文件,一行行读取原文件内容,修改完后写入临时文件...,删掉原文件,将临时文件重命名原文件名
# .开头的文件称为隐藏文件
# 优点: 不会占用过多的内存
# 缺点: 在文件修改过程中同一份数据存了两份
import os

with open('db.txt',mode='rt',encoding='utf-8') as read_f,\
        open('.db.txt.swap',mode='wt',encoding='utf-8') as wrife_f:
    for line in read_f:
        wrife_f.write(line.replace('SB','kevin'))

os.remove('db.txt')
os.rename('.db.txt.swap','db.txt')

2022-09-22 14:34:46 星期四

posted @ 2022-09-19 17:35  角角边  Views(66)  Comments(0)    收藏  举报