导图社区 Python基础
小白必看《Python基础》,包括计算机系统组成、认识Python、基础知识、数据埋点、mini-Web。注释内含大量解释,无他唯手熟尔!
编辑于2021-11-10 17:05:17Python基础
计算机系统组成
硬件系统
主机部分
中央处理器
运算器
控制器
内储存器
随机储存器
只读储存器
外设部分
输入设备
键盘
鼠标
摄像头等...
输出设备
显示器
音响
打印机等...
外储存器
软盘
硬盘
U盘等...
软件系统
系统软件
操作系统
驱动程序
语言处理程序
数据库管理系统等...
应用软件
浏览器
文本编辑器
音视频播放器
冯·诺依曼体系结构

认识Python
Python的作者是Guido van Rossum(龟叔)
是一种解释性语言(解释性语言每执行一次就需要逐行翻译一次)
基础知识
注释
#
单行注释
""" """
多行注释
变量
变量用于储存数据
num = 250
type()
查看类型
数据类型
数字类型
int(整型)
float(浮点型,小数)
bool(布尔型,用于判断Ture、False)
complex(复数型)
非数字型
str(字符串)
list(列表)
tuple(元组)
set(集合)
dict(字典)
标识符
由字母、下划线、数字组成(不能以数字开头、不能与关键字重合)
send_buf
区分大小写
andy != Andy
驼峰命名法
UserName
关键字
and as assert break class continue def del elif else except exec finally for from global if in import is lambda not or pass print raise return try while with yield
Python已经使用的了,所以不允许开发者自己定义和关键字相同的名字的标识符
查看关键字
# 1. 导入工具包 import keyword # 2. 打印关键字 print(keyword.kwlist)
输出
print('hello world')
格式化输出(看字符串)
输入
input
字符串变量 = input("提示信息:")
用户所输入的任何内容python都认为是一个字符串
类型转换
# int(x) 转换为整型 # float(x) 转换为浮点型 # str(x) 转换为字符串 a = 123 # 整型 # 将整型的a转换为字符串类型,再给a_str赋值 a_str = str(a) print(type(a_str)) # 输出结果:<class 'str'> print("a_str = %s" % a_str) # 输出结果:a_str = 123 b = "250" # 字符串类型 # 将字符串的b转换为整型,再给b_int赋值 b_int = int(b) print(type(b_int)) # 输出结果:<class 'int'> c = '3.14' # 字符串 c_float = float(c) print(type(c_float)) # 输出结果:<class 'float'>
int(x)
将 x 转换为一个整数
float(x)
将 x 转换到一个浮点数
str(x)
将 x 转换到一个字符串
运算符(见语句)
数据埋点
埋点是什么
针对特定业务场景进行数据采集和上报的技术方案
埋点形式
代码埋点
在网页/app中加上一些代码的,当用户触发相应行为时,进行数据上报 优点:可以详细的设置某一个事件自定义属性; 缺点:时间、人力成本大,数据传输的时效性。
可视化埋点
数据产品/数据分析师可以通过可视化界面(管理后台连接设备) 配置事件,可视化埋点仍需要先配置相关事件,再采集 优点:埋点只需业务人员接入,无需开发人员支持; 缺点:仅支持客户端(APP)行为。
无埋点(也叫全埋点)
指开发人员集成采集 SDK 后,SDK 便直接开始捕捉和监测用户在应用里的所有行为,并全部上报,不需要开发人员添加额外代码 优点:无需开发,业务人员埋点即可;支持先上报数据,后进行埋点。 缺点:数据存储量大
埋点方案
【借助第三方】如神策、友盟、腾讯移动分析、Talkingdata,GrowingIO等
首先要重视的,是用户唯一标识的建设。如果做不到对用户的唯一识别,那么基础的UV统计,都将是错误的
Web端数据采集
服务器日志
指Web服务器软件,例如Httpd、Nginx、Tomcat等自带的日志,例如Nginx的access.log日志等
URL解析
指访问服务器时,将URL信息及携带的参数进行解析后,上传服务器,例如在进行数据搜索时http://www.meiduo.site?search=iphone,我们可以获得本次用户搜索的内容search为“iphone”
JS回传
指在Web页面上添加的各类统计插件,通过在页面嵌入自定义的Javascript代码来获取用户的访问行为(比如鼠标悬停的位置,点击的事件等),然后通过Ajax请求到后台记录日志
埋点流程规范(五步骤)
1. 规划评审
【4W1H模型】 1. Who(谁)设备ID、用户ID、手机号、微信识别码等 2. When(在什么时间)记录日志的时间戳、日志上报的时间戳 3. Where(在什么位置)IP地址,GPS地址在哪 4. How(以什么方式)操作系统、设备型号、网络环境、APP版本、当前页面等信息 5. What (做了什么事情)如果是搜索行为,则记录关联词;如果是内容点击,则记录内容ID、内容类型、列表位置;如果是交易动作,记录交易的商品ID、类型、数量;如果是支付过程,记录付款的方式与付款金额
2. 技术开发
3. 埋点验证
实时功能验证
离线的日志验证
4. 发布上线
5. 监测评估
mini-Web
Web开发流程
logging日志 import logging
日志等级分5个
【日志等级】 DEBUG:程序调试bug时使用 INFO:程序正常运行时使用 WARNING:程序未按预期运行时使用,但并不是错误,如:用户登录密码错误 ERROR:程序出错误时使用,如:IO操作失败 CRITICAL:特别严重的问题,导致程序不能再继续运行时使用,如:磁盘空间为空,一般很少使用
默认的是WARNING等级,当在WARNING或WARNING之上等级的才记录日志信息。
日志等级从低到高的顺序是: DEBUG < INFO < WARNING < ERROR < CRITICAL
【打印日志】 logging.debug函数, 表示: 打印(记录)DEBUG级别的日志信息 logging.info函数, 表示: 打印(记录)INFO级别的日志信息 logging.warning函数, 表示: 打印(记录)WARNING级别的日志信息 logging.error函数, 表示: 打印(记录)ERROR级别的日志信息 logging.critical函数, 表示: 打印(记录)CRITICAL级别的日志信息
引用与深浅拷贝 import copy
【引用】两个变量指向同一个空间(地址一样)不能保证数据的独立性
【拷贝】两个变量指向不同的空间(地址不一样)保证数据独立性,开辟空间进行存储
浅拷贝
【语法】b1 = copy.copy(a1)
可变类型
import copy # 使用浅拷贝需要导入copy模块 # 不可变类型有: 数字、字符串、元组 a1 = 123123 b1 = copy.copy(a1) # 使用copy模块里的copy()函数就是浅拷贝了 # 查看内存地址 print(id(a1)) print(id(b1)) print("-" * 10) a2 = "abc" b2 = copy.copy(a2) # 查看内存地址 print(id(a2)) print(id(b2)) print("-" * 10) a3 = (1, 2, ["hello", "world"]) b3 = copy.copy(a3) # 查看内存地址 print(id(a3)) print(id(b3)) ######### end ######### 140459558944048 140459558944048 ---------- 140459558648776 140459558648776 ---------- 140459558073328 140459558073328
只对可变类型的第一层对象进行拷贝,对拷贝的对象开辟新的内存空间进行存储,不会拷贝对象内部的子对象
不可变类型
import copy # 使用浅拷贝需要导入copy模块 # 可变类型有: 列表、字典、集合 a1 = [1, 2] b1 = copy.copy(a1) # 使用copy模块里的copy()函数就是浅拷贝了 # 查看内存地址 print(id(a1)) print(id(b1)) print("-" * 10) a2 = {"name": "张三", "age": 20} b2 = copy.copy(a2) # 查看内存地址 print(id(a2)) print(id(b2)) print("-" * 10) a3 = {1, 2, "王五"} b3 = copy.copy(a3) # 查看内存地址 print(id(a3)) print(id(b3)) print("-" * 10) a4 = [1, 2, [4, 5]] # 注意:浅拷贝只会拷贝父对象,不会对子对象进行拷贝 b4 = copy.copy(a4) # 使用copy模块里的copy()函数就是浅拷贝了 # 查看内存地址 print(id(a4)) print(id(b4)) print("-" * 10) # 查看内存地址 print(id(a4[2])) print(id(b4[2])) # 修改数据 a4[2][0] = 6 # 子对象的数据会受影响 print(a4) print(b4) ########## end ######### 139882899585608 139882899585800 ---------- 139882919626432 139882919626504 ---------- 139882919321672 139882899616264 ---------- 139882899587016 139882899586952 ---------- 139882899693640 139882899693640 [1, 2, [6, 5]] [1, 2, [6, 5]]
不会给拷贝的对象开辟新的内存空间,而只是拷贝了这个对象的引用
深拷贝
【语法】b1 = copy.deepcopy(a1)
可变类型
import copy # 使用深拷贝需要导入copy模块 # 可变类型有: 列表、字典、集合 a1 = [1, 2] b1 = copy.deepcopy(a1) # 使用copy模块里的deepcopy()函数就是深拷贝了 # 查看内存地址 print(id(a1)) print(id(b1)) print("-" * 10) a2 = {"name": "张三"} b2 = copy.deepcopy(a2) # 查看内存地址 print(id(a2)) print(id(b2)) print("-" * 10) a3 = {1, 2} b3 = copy.deepcopy(a3) # 查看内存地址 print(id(a3)) print(id(b3)) print("-" * 10) a4 = [1, 2, ["李四", "王五"]] b4 = copy.deepcopy(a4) # 使用copy模块里的deepcopy()函数就是深拷贝了 # 查看内存地址 print(id(a4)) print(id(b4)) # 查看内存地址 print(id(a4[2])) print(id(b4[2])) a4[2][0] = "王五" # 因为列表的内存地址不同,所以数据不会收到影响 print(a4) print(b4) ######### end ######### 140348291721736 140348291721928 ---------- 140348311762624 140348311221592 ---------- 140348311457864 140348291752456 ---------- 140348291723080 140348291723144 140348291723208 140348291723016 [1, 2, ['王五', '王五']] [1, 2, ['李四', '王五']]
只要发现对象有可变类型就会对该对象到最后一个可变类型的每一层对象就行拷贝
不可变类型
import copy # 使用深拷贝需要导入copy模块 # 不可变类型有: 数字、字符串、元组 a1 = 1 b1 = copy.deepcopy(a1) # 使用copy模块里的deepcopy()函数就是深拷贝了 # 查看内存地址 print(id(a1)) print(id(b1)) print("-" * 10) a2 = "张三" b2 = copy.deepcopy(a2) # 查看内存地址 print(id(a2)) print(id(b2)) print("-" * 10) a3 = (1, 2) b3 = copy.deepcopy(a3) # 查看内存地址 print(id(a3)) print(id(b3)) print("-" * 10) # 注意: 元组里面要是有可变类型对象,发现对象有可变类型就会该对象到最后一个可变类型的每一层对象进行拷贝 a4 = (1, ["李四"]) b4 = copy.deepcopy(a4) # 查看内存地址 print(id(a4)) print(id(b4)) # 元组里面的可变类型子对象也会进行拷贝 print(id(a4[1])) print(id(b4[1])) ############# end ############## 9289120 9289120 ---------- 140115621848320 140115621848320 ---------- 140115621859592 140115621859592 ---------- 140115602480584 140115621834568 140115602328136 140115602436168
不可变类型进行深拷贝如果子对象没有可变类型则不会进行拷贝,而只是拷贝了这个对象的引用,否则会对该对象到最后一个可变类型的每一层对象就行拷贝
浅拷贝和深拷贝的区别
1. 浅拷贝最多拷贝对象的一层 2. 深拷贝可能拷贝对象的多层
with语句和上下文管理器
with语句
# 1、以写的方式打开文件 with open("1.txt", "w") as f: # 2、读取文件内容 f.write("hello world")
with 语句执行完成以后自动调用关闭文件操作,即使出现异常也会自动调用关闭文件操作
上下文管理器类
class File(object): # 初始化方法 def __init__(self, file_name, file_model): # 定义变量保存文件名和打开模式 self.file_name = file_name self.file_model = file_model # 上文方法 def __enter__(self): print("进入上文方法") # 返回文件资源 self.file = open(self.file_name,self.file_model) return self.file # 下文方法 def __exit__(self, exc_type, exc_val, exc_tb): print("进入下文方法") self.file.close() if __name__ == '__main__': # 使用with管理文件 with File("1.txt", "r") as file: file_data = file.read() print(file_data) ############### end ################# 进入上文方法 hello world 进入下文方法
必须实现两个方法
__enter__
__exit__
【执行顺序】 1. with语句开始时候,会自动调用上文函数`__enter__`, 获取上文函数返回资源,并取别名。 2. with语句结束的时候,会自动调用下文函数`__exit__`
正则表达式 import re
【概述】记录文本规则的代码
0\d{2}-\d{8} 是一个正则表达式,表达的意思是匹配的是座机号码
re模块
【使用步骤】 # 1.导入模块 import re # 2.使用match() 方法进行检测 result = re.match("正则表达式", "需要匹配的字符串") # 3.判断是否检测/匹配成功 if result: # 4.取出匹配的具体内容 print("匹配成功,结果是: ", result.group())
匹配单个字符
. 匹配任意1个字符(除了\n) [ ] 匹配[ ]中列举的字符 \d 匹配数字,即0-9 \D 匹配非数字,即不是数字 \s 匹配空白,即 空格,tab键 \S 匹配非空白 \w 匹配非特殊字符,即a-z、A-Z、0-9、_、汉字 \W 匹配特殊字符,即非字母、非数字、非汉字
匹配多个字符
* 匹配前一个字符出现0次或者无限次,即可有可无 + 匹配前一个字符出现1次或者无限次,即至少有1次 ? 匹配前一个字符出现1次或者0次,即要么有1次,要么没有 {m} 匹配前一个字符出现m次 {m,n} 匹配前一个字符出现从m到n次
匹配开头和结尾
^ 匹配字符串开头 $ 匹配字符串结尾
匹配分组
| 匹配左右任意一个表达式 (ab) 将括号中字符作为一个分组 \num 引用分组num匹配到的字符串 (?P<name>) 分组起别名 (?P=name) 引用别名为name分组匹配到的字符串
闭包和装饰器
闭包
【定义】在函数嵌套的前提下,内部函数使用了外部函数的变量,并且外部函数返回了内部函数,我们把这个使用外部函数变量的内部函数称为闭包。
【形成条件】 1. 在函数嵌套(函数里面再定义函数)的前提下 2. 内部函数使用了外部函数的变量(还包括外部函数的参数) 3. 外部函数返回了内部函数
# 外部函数 def out(fn): b = 10 # 内部函数 def inner(a): # 内部函数使用了外部函数的变量或者参数 print(a, b) # 返回内部函数, 这里返回的内部函数就是闭包实例 return inner # 创建闭包实例 f = out(1) #执行闭包 f(2) f(3) =========== end =========== 2 10 3 10
# 外部函数 def test1(a): b = 10 # 内部函数 def test2(): # 内部函数使用了外部函数的变量或者参数 print(a, b) # 返回内部函数, 这里返回的内部函数就是闭包实例 return test2
【作用】 1. 闭包可以保存外部函数内的变量,不会随着外部函数调用完而销毁 2. 闭包还可以提高代码的可重用性,不需要再手动定义额外的功能函数
装饰器
【定义】 1. 给已有函数增加额外功能的函数,它本质上就是一个特殊的闭包函数 2. 有且只有一个参数,必须是函数类型
【特点】 1. 不修改已有函数的源代码 2. 不修改已有函数的调用方式 3. 给已有函数增加额外的功能
# 添加一个登录验证的功能 def check(fn): def inner(): print("请先登录....") fn() return inner def comment(): print("发表评论") # 使用装饰器来装饰函数 comment = check(comment) comment() # 装饰器的基本雏形 # def decorator(fn): # fn:目标函数. # def inner(): # '''执行函数之前''' # fn() # 执行被装饰的函数 # '''执行函数之后''' # return inner ############## end ############### 请先登录.... 发表评论
语法糖
# 装饰器 # def decorator(fn): # fn:被装饰的目标函数. # def inner(): # '''执行函数之前''' # fn() # 执行被装饰的目标函数 # '''执行函数之后''' # return inner
# 添加一个登录验证的功能 def check(fn): print("装饰器函数执行了") def inner(): print("请先登录....") fn() return inner # 使用语法糖方式来装饰函数 @check def comment(): print("发表评论") comment() ########## end ########## 请先登录.... 发表评论
通用装饰器
(*args, **kwargs)
# 通用装饰器 def logging(fn): def inner(*args, **kwargs): print("--正在努力计算--") result = fn(*args, **kwargs) return result return inner
# 添加输出日志的功能 def logging(fn): def inner(*args, **kwargs): print("--正在努力计算--") result = fn(*args, **kwargs) return result return inner # 使用语法糖装饰函数 @logging def sum_num(*args, **kwargs): result = 0 for value in args: result += value for value in kwargs.values(): result += value return result @logging def subtraction(a, b): result = a - b print(result) result = sum_num(1, 2, a=10) print(result) subtraction(4, 2) ########## end ########## --正在努力计算-- 13 --正在努力计算-- 2
带有参数的装饰器
@装饰器(参数,...)
# 添加输出日志的功能 def logging(flag): def decorator(fn): def inner(num1, num2): if flag == "+": print("--正在努力加法计算--") elif flag == "-": print("--正在努力减法计算--") result = fn(num1, num2) return result return inner # 返回decorator装饰器 return decorator # 使用装饰器装饰函数 @logging("+") def add(a, b): result = a + b return result @logging("-") def sub(a, b): result = a - b return result result = add(1, 2) print(result) result = sub(1, 2) print(result)
HTTP协议和静态Web服务器
HTTP 协议
介绍: · HTTP协议是一个超文本传输协议(HyperText Transfer Protoco) · HTTP协议是一个基于TCP传输协议传输数据的 · HTTP协议规定了浏览器和 Web 服务器通信数据的格式

URL
URL就是网络资源的地址,简称网址,通过URL能够找到网络中对应的资源数据
URL组成部分: 1. 协议部分 https://、http://、ftp:// 2. 域名部分 news.163.com 3. 资源路径部分 /18/1122/10/E178J2O4000189FH.html --------------------------------- 4. 查询参数部分 (可选) ?id=2
HTTP 请求报文
GET 方式的请求报文
【GET 请求报文说明】 ---- 请求行 ---- GET / HTTP/1.1 # GET请求方式 请求资源路径 HTTP协议版本 ---- 请求头 ----- Host: www.itcast.cn # 服务器的主机地址和端口号,默认是80 Connection: keep-alive # 和服务端保持长连接 Upgrade-Insecure-Requests: 1 # 让浏览器升级不安全请求,使用https请求 User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36 # 用户代理,也就是客户端的名称 Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8 # 可接受的数据类型 Accept-Encoding: gzip, deflate # 可接受的压缩格式 Accept-Language: zh-CN,zh;q=0.9 #可接受的语言 Cookie: pgv_pvi=1246921728; # 登录用户的身份标识 ---- 空行 ---- 【GET 请求原始报文说明】 GET / HTTP/1.1\r\n Host: www.itcast.cn\r\n Connection: keep-alive\r\n Upgrade-Insecure-Requests: 1\r\n User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36\r\n Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8\r\n Accept-Encoding: gzip, deflate\r\n Accept-Language: zh-CN,zh;q=0.9\r\n Cookie: pgv_pvi=1246921728; \r\n \r\n (请求头信息后面还有一个单独的’\r\n’不能省略)
【每项数据之间使用:\r\n】 1. 请求行(请求方式 请求支援 HTTP协议版本) 2. 请求头 3. 空行
POST 方式的请求报文
【POST 请求报文说明】 ---- 请求行 ---- POST /xmweb?host=mail.itcast.cn&_t=1542884567319 HTTP/1.1 # POST请求方式 请求资源路径 HTTP协议版本 ---- 请求头 ---- Host: mail.itcast.cn # 服务器的主机地址和端口号,默认是80 Connection: keep-alive # 和服务端保持长连接 Content-Type: application/x-www-form-urlencoded # 告诉服务端请求的数据类型 User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36 # 客户端的名称 ---- 空行 ---- ---- 请求体 ---- username=hello&pass=hello # 请求参数 【POST 请求原始报文说明】 POST /xmweb?host=mail.itcast.cn&_t=1542884567319 HTTP/1.1\r\n Host: mail.itcast.cn\r\n Connection: keep-alive\r\n Content-Type: application/x-www-form-urlencoded\r\n User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36\r\n \r\n(请求头信息后面还有一个单独的’\r\n’不能省略) username=hello&pass=hello
【每项数据之间使用:\r\n】 1. 请求行(请求方式 请求支援 HTTP协议版本) 2. 请求头 3. 空行 4. 请求体
HTTP响应报文
【每项数据之间使用:\r\n】 1. 响应行(HTTP协议版本 状态码 状态描述) 2. 响应头 3. 空行 4. 响应体
HTTP 状态码
200 请求成功 307 重定向 400 错误的请求,请求地址或者参数有误 404 请求资源在服务器不存在 500 服务器内部源代码出现错误
搭建Python自带静态Web服务器
搭建Python自带的Web服务器终端命令:python3 –m http.server 9090 端口号不指定默认是8000
静态Web服务器-返回固定页面数据
1. 编写一个TCP服务端程序 2. 获取浏览器发送的http请求报文数据 3. 读取固定页面数据,把页面数据组装成HTTP响应报文数据发送给浏览器。 4. HTTP响应报文数据发送完成以后,关闭服务于客户端的套接字。
import socket if __name__ == '__main__': # 创建tcp服务端套接字 tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 设置端口号复用, 程序退出端口立即释放 tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True) # 绑定端口号 tcp_server_socket.bind(("", 9000)) # 设置监听 tcp_server_socket.listen(128) while True: # 等待接受客户端的连接请求 new_socket, ip_port = tcp_server_socket.accept() # 代码执行到此,说明连接建立成功 recv_client_data = new_socket.recv(4096) # 对二进制数据进行解码 recv_client_content = recv_client_data.decode("utf-8") print(recv_client_content) with open("static/index.html", "rb") as file: # 读取文件数据 file_data = file.read() # 响应行 response_line = "HTTP/1.1 200 OK\r\n" # 响应头 response_header = "Server: PWS1.0\r\n" # 响应体 response_body = file_data # 拼接响应报文 response_data = (response_line + response_header + "\r\n").encode("utf-8") + response_body # 发送数据 new_socket.send(response_data) # 关闭服务与客户端的套接字 new_socket.close()
静态Web服务器-返回指定页面数据
1. 获取用户请求资源的路径 2. 根据请求资源的路径,读取指定文件的数据 3. 组装指定文件数据的响应报文,发送给浏览器 4. 判断请求的文件在服务端不存在,组装404状态的响应报文,发送给浏览器
import socket def main(): # 创建tcp服务端套接字 tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 设置端口号复用, 程序退出端口立即释放 tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True) # 绑定端口号 tcp_server_socket.bind(("", 9000)) # 设置监听 tcp_server_socket.listen(128) while True: # 等待接受客户端的连接请求 new_socket, ip_port = tcp_server_socket.accept() # 代码执行到此,说明连接建立成功 recv_client_data = new_socket.recv(4096) if len(recv_client_data) == 0: print("关闭浏览器了") new_socket.close() return # 对二进制数据进行解码 recv_client_content = recv_client_data.decode("utf-8") print(recv_client_content) # 根据指定字符串进行分割, 最大分割次数指定2 request_list = recv_client_content.split(" ", maxsplit=2) # 获取请求资源路径 request_path = request_list[1] print(request_path) # 判断请求的是否是根目录,如果条件成立,指定首页数据返回 if request_path == "/": request_path = "/index.html" try: # 动态打开指定文件 with open("static" + request_path, "rb") as file: # 读取文件数据 file_data = file.read() except Exception as e: # 请求资源不存在,返回404数据 # 响应行 response_line = "HTTP/1.1 404 Not Found\r\n" # 响应头 response_header = "Server: PWS1.0\r\n" with open("static/error.html", "rb") as file: file_data = file.read() # 响应体 response_body = file_data # 拼接响应报文 response_data = (response_line + response_header + "\r\n").encode("utf-8") + response_body # 发送数据 new_socket.send(response_data) else: # 响应行 response_line = "HTTP/1.1 200 OK\r\n" # 响应头 response_header = "Server: PWS1.0\r\n" # 响应体 response_body = file_data # 拼接响应报文 response_data = (response_line + response_header + "\r\n").encode("utf-8") + response_body # 发送数据 new_socket.send(response_data) finally: # 关闭服务与客户端的套接字 new_socket.close() if __name__ == '__main__': main()
静态Web服务器-多任务版
1. 当客户端和服务端建立连接成功,创建子线程,使用子线程专门处理客户端的请求,防止主线程阻塞。 2. 把创建的子线程设置成为守护主线程,防止主线程无法退出。
import socket import threading # 处理客户端的请求 def handle_client_request(new_socket): # 代码执行到此,说明连接建立成功 recv_client_data = new_socket.recv(4096) if len(recv_client_data) == 0: print("关闭浏览器了") new_socket.close() return # 对二进制数据进行解码 recv_client_content = recv_client_data.decode("utf-8") print(recv_client_content) # 根据指定字符串进行分割, 最大分割次数指定2 request_list = recv_client_content.split(" ", maxsplit=2) # 获取请求资源路径 request_path = request_list[1] print(request_path) # 判断请求的是否是根目录,如果条件成立,指定首页数据返回 if request_path == "/": request_path = "/index.html" try: # 动态打开指定文件 with open("static" + request_path, "rb") as file: # 读取文件数据 file_data = file.read() except Exception as e: # 请求资源不存在,返回404数据 # 响应行 response_line = "HTTP/1.1 404 Not Found\r\n" # 响应头 response_header = "Server: PWS1.0\r\n" with open("static/error.html", "rb") as file: file_data = file.read() # 响应体 response_body = file_data # 拼接响应报文 response_data = (response_line + response_header + "\r\n").encode("utf-8") + response_body # 发送数据 new_socket.send(response_data) else: # 响应行 response_line = "HTTP/1.1 200 OK\r\n" # 响应头 response_header = "Server: PWS1.0\r\n" # 响应体 response_body = file_data # 拼接响应报文 response_data = (response_line + response_header + "\r\n").encode("utf-8") + response_body # 发送数据 new_socket.send(response_data) finally: # 关闭服务与客户端的套接字 new_socket.close() # 程序入口函数 def main(): # 创建tcp服务端套接字 tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 设置端口号复用, 程序退出端口立即释放 tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True) # 绑定端口号 tcp_server_socket.bind(("", 9000)) # 设置监听 tcp_server_socket.listen(128) while True: # 等待接受客户端的连接请求 new_socket, ip_port = tcp_server_socket.accept() print(ip_port) # 当客户端和服务器建立连接程,创建子线程 sub_thread = threading.Thread(target=handle_client_request, args=(new_socket,)) # 设置守护主线程 sub_thread.setDaemon(True) # 启动子线程执行对应的任务 sub_thread.start() if __name__ == '__main__': main()
静态Web服务器-面向对象开发
1. 把提供服务的Web服务器抽象成一个类(HTTPWebServer) 2. 提供Web服务器的初始化方法,在初始化方法里面创建socket对象 3. 提供一个开启Web服务器的方法,让Web服务器处理客户端请求操作。
import socket import threading # 定义web服务器类 class HttpWebServer(object): def __init__(self): # 创建tcp服务端套接字 tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 设置端口号复用, 程序退出端口立即释放 tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True) # 绑定端口号 tcp_server_socket.bind(("", 9000)) # 设置监听 tcp_server_socket.listen(128) # 保存创建成功的服务器套接字 self.tcp_server_socket = tcp_server_socket # 处理客户端的请求 @staticmethod def handle_client_request(new_socket): # 代码执行到此,说明连接建立成功 recv_client_data = new_socket.recv(4096) if len(recv_client_data) == 0: print("关闭浏览器了") new_socket.close() return # 对二进制数据进行解码 recv_client_content = recv_client_data.decode("utf-8") print(recv_client_content) # 根据指定字符串进行分割, 最大分割次数指定2 request_list = recv_client_content.split(" ", maxsplit=2) # 获取请求资源路径 request_path = request_list[1] print(request_path) # 判断请求的是否是根目录,如果条件成立,指定首页数据返回 if request_path == "/": request_path = "/index.html" try: # 动态打开指定文件 with open("static" + request_path, "rb") as file: # 读取文件数据 file_data = file.read() except Exception as e: # 请求资源不存在,返回404数据 # 响应行 response_line = "HTTP/1.1 404 Not Found\r\n" # 响应头 response_header = "Server: PWS1.0\r\n" with open("static/error.html", "rb") as file: file_data = file.read() # 响应体 response_body = file_data # 拼接响应报文 response_data = (response_line + response_header + "\r\n").encode("utf-8") + response_body # 发送数据 new_socket.send(response_data) else: # 响应行 response_line = "HTTP/1.1 200 OK\r\n" # 响应头 response_header = "Server: PWS1.0\r\n" # 响应体 response_body = file_data # 拼接响应报文 response_data = (response_line + response_header + "\r\n").encode("utf-8") + response_body # 发送数据 new_socket.send(response_data) finally: # 关闭服务与客户端的套接字 new_socket.close() # 启动web服务器进行工作 def start(self): while True: # 等待接受客户端的连接请求 new_socket, ip_port = self.tcp_server_socket.accept() # 当客户端和服务器建立连接程,创建子线程 sub_thread = threading.Thread(target=self.handle_client_request, args=(new_socket,)) # 设置守护主线程 sub_thread.setDaemon(True) # 启动子线程执行对应的任务 sub_thread.start() # 程序入口函数 def main(): # 创建web服务器对象 web_server = HttpWebServer() # 启动web服务器进行工作 web_server.start() if __name__ == '__main__': main()
静态Web服务器-命令行启动动态绑定端口号
1. 获取执行python程序的终端命令行参数 2. 判断参数的类型,设置端口号必须是整型 3. 给Web服务器类的初始化方法添加一个端口号参数,用于绑定端口号
import socket import threading import sys # 定义web服务器类 class HttpWebServer(object): def __init__(self, port): # 创建tcp服务端套接字 tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 设置端口号复用, 程序退出端口立即释放 tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True) # 绑定端口号 tcp_server_socket.bind(("", port)) # 设置监听 tcp_server_socket.listen(128) # 保存创建成功的服务器套接字 self.tcp_server_socket = tcp_server_socket # 处理客户端的请求 @staticmethod def handle_client_request(new_socket): # 代码执行到此,说明连接建立成功 recv_client_data = new_socket.recv(4096) if len(recv_client_data) == 0: print("关闭浏览器了") new_socket.close() return # 对二进制数据进行解码 recv_client_content = recv_client_data.decode("utf-8") print(recv_client_content) # 根据指定字符串进行分割, 最大分割次数指定2 request_list = recv_client_content.split(" ", maxsplit=2) # 获取请求资源路径 request_path = request_list[1] print(request_path) # 判断请求的是否是根目录,如果条件成立,指定首页数据返回 if request_path == "/": request_path = "/index.html" try: # 动态打开指定文件 with open("static" + request_path, "rb") as file: # 读取文件数据 file_data = file.read() except Exception as e: # 请求资源不存在,返回404数据 # 响应行 response_line = "HTTP/1.1 404 Not Found\r\n" # 响应头 response_header = "Server: PWS1.0\r\n" with open("static/error.html", "rb") as file: file_data = file.read() # 响应体 response_body = file_data # 拼接响应报文 response_data = (response_line + response_header + "\r\n").encode("utf-8") + response_body # 发送数据 new_socket.send(response_data) else: # 响应行 response_line = "HTTP/1.1 200 OK\r\n" # 响应头 response_header = "Server: PWS1.0\r\n" # 响应体 response_body = file_data # 拼接响应报文 response_data = (response_line + response_header + "\r\n").encode("utf-8") + response_body # 发送数据 new_socket.send(response_data) finally: # 关闭服务与客户端的套接字 new_socket.close() # 启动web服务器进行工作 def start(self): while True: # 等待接受客户端的连接请求 new_socket, ip_port = self.tcp_server_socket.accept() # 当客户端和服务器建立连接程,创建子线程 sub_thread = threading.Thread(target=self.handle_client_request, args=(new_socket,)) # 设置守护主线程 sub_thread.setDaemon(True) # 启动子线程执行对应的任务 sub_thread.start() # 程序入口函数 def main(): print(sys.argv) # 判断命令行参数是否等于2, if len(sys.argv) != 2: print("执行命令如下: python3 xxx.py 8000") return # 判断字符串是否都是数字组成 if not sys.argv[1].isdigit(): print("执行命令如下: python3 xxx.py 8000") return # 获取终端命令行参数 port = int(sys.argv[1]) # 创建web服务器对象 web_server = HttpWebServer(port) # 启动web服务器进行工作 web_server.start() if __name__ == '__main__': main()
网络编程
IP地址

IP 地址的作用是标识网络中唯一的一台设备的
IP 地址的表现形式
IPv4
点分十进制组成
IPv6
冒号十六进制组成
查看网卡信息:
ifconfig(Linux 和 mac OS)
ipconfig(windows)
检查网络
ping
端口

端口是传输数据的通道
端口号
每一个端口都会有一个对应的端口号
通过ip地址找到对应的设备,通过端口号找到对应的端口,然后通过端口把数据传输给应用程序
知名端口号:
0到1023
固定分配给一些服务
动态端口号:
1024到65535
运行一个程序默认会有一个端口号,当这个程序退出时,所占用的这个端口号就会被释放
TCP 协议
介绍
传输控制协议(Transmission Control Protocol)
面向连接的、可靠的、基于字节流的传输层通信协议
TCP特点
面向连接
通信双方必须先建立好连接才能进行数据的传输,数据传输完成后,双方必须断开此连接,以释放系统资源
可靠传输
1. TCP 采用发送应答机制 2. 超时重传 3. 错误校验 4. 流量控制和阻塞管理
socket 套接字
是进程间网络数据通信的工具

TCP 客户端程序开发

主动发起建立连接请求
【步骤】 1. 创建客户端套接字对象 2. 和服务端套接字建立连接(元组传参) 3. 发送数据 4. 接收数据 5. 关闭客户端套接字
import socket if __name__ == '__main__': # 创建tcp客户端套接字 # 1. AF_INET:表示ipv4 # 2. SOCK_STREAM: tcp传输协议 tcp_client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 和服务端应用程序建立连接 tcp_client_socket.connect(("192.168.131.62", 8080)) # 代码执行到此,说明连接建立成功 # 准备发送的数据 send_data = "你好服务端,我是客户端小黑!".encode("gbk") # 发送数据 tcp_client_socket.send(send_data) # 接收数据, 这次接收的数据最大字节数是1024 recv_data = tcp_client_socket.recv(1024) # 返回的直接是服务端程序发送的二进制数据 print(recv_data) # 对数据进行解码 recv_content = recv_data.decode("gbk") print("接收服务端的数据为:", recv_content) # 关闭套接字 tcp_client_socket.close() ############ 结果 ############# b'hello' 接收服务端的数据为: hello
1. 导入socket模块 2. 创建TCP套接字‘socket’对象:socket.socket(AddressFamily, Type) AddressFamily 表示IP地址类型, 分为IPv4和IPv6;Type 表示传输协议类型 参数1: ‘AF_INET’, 表示IPv4地址类型 参数2: ‘SOCK_STREAM’, 表示TCP传输协议类型 3. 发送数据‘send’ 参数1: 要发送的二进制数据, 注意: 字符串需要使用encode()方法进行编码 4. 接收数据‘recv’ 参数1: 表示每次接收数据的大小,单位是字节, 注意: 二进制需要使用decode()方法进行解码 5. 关闭套接字‘socket’表示通信完成
TCP 服务端程序开发

等待接受连接请求
【步骤】 1. 创建服务端端套接字对象 2. 绑定端口号 3. 设置监听 4. 等待接受客户端的连接请求(解包元组) 5. 接收数据 6. 发送数据 7. 关闭服务端新套接字
import socket if __name__ == '__main__': # 创建tcp服务端套接字 tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 设置端口号复用,让程序退出端口号立即释放 tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True) # 给程序绑定端口号 tcp_server_socket.bind(("", 8989)) # 设置监听 # 128:最大等待建立连接的个数, 提示: 目前是单任务的服务端,同一时刻只能服务与一个客户端,后续使用多任务能够让服务端同时服务与多个客户端, # 不需要让客户端进行等待建立连接 # listen后的这个套接字只负责接收客户端连接请求,不能收发消息,收发消息使用返回的这个新套接字来完成 tcp_server_socket.listen(128) # 等待客户端建立连接的请求, 只有客户端和服务端建立连接成功代码才会解阻塞,代码才能继续往下执行 # 1. 专门和客户端通信的套接字: service_client_socket # 2. 客户端的ip地址和端口号: ip_port service_client_socket, ip_port = tcp_server_socket.accept() # 代码执行到此说明连接建立成功 print("客户端的ip地址和端口号:", ip_port) # 接收客户端发送的数据, 这次接收数据的最大字节数是1024 recv_data = service_client_socket.recv(1024) # 获取数据的长度 recv_data_length = len(recv_data) print("接收数据的长度为:", recv_data_length) # 对二进制数据进行解码 recv_content = recv_data.decode("gbk") print("接收客户端的数据为:", recv_content) # 准备发送的数据 send_data = "ok, 问题正在处理中...".encode("gbk") # 发送数据给客户端 service_client_socket.send(send_data) # 关闭服务与客户端的套接字, 终止和客户端通信的服务 service_client_socket.close() # 关闭服务端的套接字, 终止和客户端提供建立连接请求的服务 tcp_server_socket.close() ################ 结果 ################# 客户端的ip地址和端口号: ('172.16.47.209', 52472) 接收数据的长度为: 5 接收客户端的数据为: hello
1. 导入socket模块 2. 创建TCP套接字‘socket’对象:socket.socket(AddressFamily, Type) AddressFamily 表示IP地址类型, 分为IPv4和IPv6;Type 表示传输协议类型 参数1: ‘AF_INET’, 表示IPv4地址类型 参数2: ‘SOCK_STREAM’, 表示TCP传输协议类型 3. 绑定端口号‘bind’ 参数: 元组, 比如:(ip地址, 端口号) 4. 设置监听‘listen’ 参数: 最大等待建立连接的个数 5. 等待接受客户端的连接请求‘accept’ 6. 发送数据‘send’ 参数: 要发送的二进制数据, 注意: 字符串需要使用encode()方法进行编码 7. 接收数据‘recv’ 参数: 表示每次接收数据的大小,单位是字节,注意: 解码成字符串使用decode()方法 8. 关闭套接字‘socket’表示通信完成
客户端和服务端建立连接后,服务端程序退出后端口号不会立即释放,需要等待大概1-2分钟。 解决办法有两种: 1. 更换服务端端口号 2. 设置端口号复用(推荐大家使用),也就是说让服务端程序退出后端口号立即释放。
# 参数1: 表示当前套接字 # 参数2: 设置端口号复用选项 # 参数3: 设置端口号复用选项对应的值 tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
TCP网络应用程序的注意点
1. 当 TCP 客户端程序想要和 TCP 服务端程序进行通信的时候必须要先建立连接 2. TCP 客户端程序一般不需要绑定端口号,因为客户端是主动发起建立连接的。 3. TCP 服务端程序必须绑定端口号,否则客户端找不到这个 TCP 服务端程序。 4. listen 后的套接字是被动套接字,只负责接收新的客户端的连接请求,不能收发消息。 5. 当 TCP 客户端程序和 TCP 服务端程序连接成功后, TCP 服务器端程序会产生一个新的套接字,收发客户端消息使用该套接字。 6. 关闭 accept 返回的套接字意味着和这个客户端已经通信完毕。 7. 关闭 listen 后的套接字意味着服务端的套接字关闭了,会导致新的客户端不能连接服务端,但是之前已经接成功的客户端还能正常通信。 8. 当客户端的套接字调用 close 后,服务器端的 recv 会解阻塞,返回的数据长度为0,服务端可以通过返回数据的长度来判断客户端是否已经下线,反之服务端关闭套接字,客户端的 recv 也会解阻塞,返回的数据长度也为0。
多任务版TCP服务端程序开发
1. 编写一个TCP服务端程序,循环等待接受客户端的连接请求 2. 当客户端和服务端建立连接成功,创建子线程,使用子线程专门处理客户端的请求,防止主线程阻塞 3. 把创建的子线程设置成为守护主线程,防止主线程无法退出。
import socket import threading # 处理客户端的请求操作 def handle_client_request(service_client_socket, ip_port): # 循环接收客户端发送的数据 while True: # 接收客户端发送的数据 recv_data = service_client_socket.recv(1024) # 容器类型判断是否有数据可以直接使用if语句进行判断,如果容器类型里面有数据表示条件成立,否则条件失败 # 容器类型: 列表、字典、元组、字符串、set、range、二进制数据 if recv_data: print(recv_data.decode("gbk"), ip_port) # 回复 service_client_socket.send("ok,问题正在处理中...".encode("gbk")) else: print("客户端下线了:", ip_port) break # 终止和客户端进行通信 service_client_socket.close() if __name__ == '__main__': # 创建tcp服务端套接字 tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 设置端口号复用,让程序退出端口号立即释放 tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True) # 绑定端口号 tcp_server_socket.bind(("", 9090)) # 设置监听, listen后的套接字是被动套接字,只负责接收客户端的连接请求 tcp_server_socket.listen(128) # 循环等待接收客户端的连接请求 while True: # 等待接收客户端的连接请求 service_client_socket, ip_port = tcp_server_socket.accept() print("客户端连接成功:", ip_port) # 当客户端和服务端建立连接成功以后,需要创建一个子线程,不同子线程负责接收不同客户端的消息 sub_thread = threading.Thread(target=handle_client_request, args=(service_client_socket, ip_port)) # 设置守护主线程 sub_thread.setDaemon(True) # 启动子线程 sub_thread.start() # tcp服务端套接字可以不需要关闭,因为服务端程序需要一直运行 # tcp_server_socket.close() ################# 结果 ################# 客户端连接成功: ('172.16.47.209', 51528) 客户端连接成功: ('172.16.47.209', 51714) hello1 ('172.16.47.209', 51528) hello2 ('172.16.47.209', 51714)
多任务编程-进程&线程
多任务
并发:
一段时间内 交替 执行多个任务
对于单核cpu处理多任务,操作系统轮流让各个软件交替执行
并行:
一段时间内 同时 执行多个任务
对于多核cpu处理多任务,操作系统会给cpu的每个内核安排一个执行的软件
进程
1. 操作系统进行资源分配的基本单位 2. 一个程序运行后至少有一个进程,一个进程默认有一个线程 3. 线程是依附在进程里面的,没有进程就没有线程
单进程

多进程

1. 导入进程包 import multiprocessing 2. 创建子进程并指定执行的任务 sub_process = multiprocessing.Process (target=函数名,args=(参数1,...),kwargs={'key':'value',...}) 3. 启动进程执行任务 sub_process.start()
import multiprocessing import time # 跳舞任务 def dance(): for i in range(5): print("跳舞中...") time.sleep(0.2) # 唱歌任务 def sing(): for i in range(5): print("唱歌中...") time.sleep(0.2) if __name__ == '__main__': # 创建跳舞的子进程 # group: 表示进程组,目前只能使用None # target: 表示执行的目标任务名(函数名、方法名) # name: 进程名称, 默认是Process-1, ..... dance_process = multiprocessing.Process(target=dance, name="myprocess1") sing_process = multiprocessing.Process(target=sing) # 启动子进程执行对应的任务 dance_process.start() sing_process.start() ############## 结果 ############## 唱歌中... 跳舞中... 唱歌中... 跳舞中... 唱歌中... 跳舞中... 唱歌中... 跳舞中... 唱歌中... 跳舞中...
Process
Process(group,target,name,args,kwargs)
group:指定进程组,目前只能使用None target:执行的目标任务名 name:进程名字 args:以元组方式给执行任务传参 kwargs: 以字典方式给执行任务传参
Process创建的实例对象的常用方法:
start():启动子进程实例(创建子进程) join():等待子进程执行结束 terminate():不管任务是否完成,立即终止子进程
Process创建的实例对象的常用属性:
name:当前进程的别名,默认为Process-N,N为从1开始递增的整数
获取当前进程对象
threading.current_Process()
获取进程编号
import multiprocessing import time import os # 跳舞任务 def dance(): # 获取当前进程的编号 print("dance:", os.getpid()) # 获取当前进程 print("dance:", multiprocessing.current_process()) # 获取父进程的编号 print("dance的父进程编号:", os.getppid()) for i in range(5): print("跳舞中...") time.sleep(0.2) # 扩展:根据进程编号杀死指定进程 os.kill(os.getpid(), 9) # 唱歌任务 def sing(): # 获取当前进程的编号 print("sing:", os.getpid()) # 获取当前进程 print("sing:", multiprocessing.current_process()) # 获取父进程的编号 print("sing的父进程编号:", os.getppid()) for i in range(5): print("唱歌中...") time.sleep(0.2) if __name__ == '__main__': # 获取当前进程的编号 print("main:", os.getpid()) # 获取当前进程 print("main:", multiprocessing.current_process()) # 创建跳舞的子进程 # group: 表示进程组,目前只能使用None # target: 表示执行的目标任务名(函数名、方法名) # name: 进程名称, 默认是Process-1, ..... dance_process = multiprocessing.Process(target=dance, name="myprocess1") sing_process = multiprocessing.Process(target=sing) # 启动子进程执行对应的任务 dance_process.start() sing_process.start() ################ 结果 ################# main: 70860 main: <_MainProcess(MainProcess, started)> dance: 70861 dance: <Process(myprocess1, started)> dance的父进程编号: 70860 跳舞中... sing: 70862 sing: <Process(Process-2, started)> sing的父进程编号: 70860 唱歌中... 唱歌中... 唱歌中... 唱歌中... 唱歌中...
获取当前进程编号
os.getpid()
获取当前父进程编号
os.getppid()
获取进程编号可以查看父子进程的关系
进程执行带有参数的任务
元组方式传参
args=()
import multiprocessing import time # 带有参数的任务 def task(count): for i in range(count): print("任务执行中..") time.sleep(0.2) else: print("任务执行完成") if __name__ == '__main__': # 创建子进程 # args: 以元组的方式给任务传入参数 sub_process = multiprocessing.Process(target=task, args=(5,)) sub_process.start() ################ 结果 ################## 任务执行中.. 任务执行中.. 任务执行中.. 任务执行中.. 任务执行中.. 任务执行完成
元组方式传参一定要和参数的顺序保持一致。
字典方式传参
kwargs={}
import multiprocessing import time # 带有参数的任务 def task(count): for i in range(count): print("任务执行中..") time.sleep(0.2) else: print("任务执行完成") if __name__ == '__main__': # 创建子进程 # kwargs: 表示以字典方式传入参数 sub_process = multiprocessing.Process(target=task, kwargs={"count": 3}) sub_process.start() ################# 结果 ################## 任务执行中.. 任务执行中.. 任务执行中.. 任务执行完成
字典方式传参字典中的key一定要和参数名保持一致。
进程的注意点
进程之间不共享全局变量
 import multiprocessing import time # 定义全局变量 g_list = list() # 添加数据的任务 def add_data(): for i in range(5): g_list.append(i) print("add:", i) time.sleep(0.2) # 代码执行到此,说明数据添加完成 print("add_data:", g_list) def read_data(): print("read_data", g_list) if __name__ == '__main__': # 创建添加数据的子进程 add_data_process = multiprocessing.Process(target=add_data) # 创建读取数据的子进程 read_data_process = multiprocessing.Process(target=read_data) # 启动子进程执行对应的任务 add_data_process.start() # 主进程等待添加数据的子进程执行完成以后程序再继续往下执行,读取数据 add_data_process.join() read_data_process.start() print("main:", g_list) # 总结: 多进程之间不共享全局变量 ############### 结果 ################## add: 0 add: 1 add: 2 add: 3 add: 4 add_data: [0, 1, 2, 3, 4] main: [] read_data []
创建子进程会对主进程资源进行拷贝,子进程其实就是主进程的一个副本
主进程会等待所有的子进程执行结束再结束
import multiprocessing import time # 定义进程所需要执行的任务 def task(): for i in range(10): print("任务执行中...") time.sleep(0.2) if __name__ == '__main__': # 创建子进程 sub_process = multiprocessing.Process(target=task) sub_process.start() # 主进程延时0.5秒钟 time.sleep(0.5) print("over") exit() # 总结: 主进程会等待所有的子进程执行完成以后程序再退出 ############### 结果 ################ 任务执行中... 任务执行中... 任务执行中... over 任务执行中... 任务执行中... 任务执行中... 任务执行中... 任务执行中... 任务执行中... 任务执行中...
守护主进程
子进程对象.daemon = True
import multiprocessing import time # 定义进程所需要执行的任务 def task(): for i in range(10): print("任务执行中...") time.sleep(0.2) if __name__ == '__main__': # 创建子进程 sub_process = multiprocessing.Process(target=task) # 设置守护主进程,主进程退出子进程直接销毁,子进程的生命周期依赖与主进程 sub_process.daemon = True sub_process.start() time.sleep(0.5) print("over") # 让子进程销毁 # sub_process.terminate() exit() # 总结: 主进程会等待所有的子进程执行完成以后程序再退出 # 如果想要主进程退出子进程销毁,可以设置守护主进程或者在主进程退出之前让子进程销毁
主进程退出,则子进程销毁
子进程销毁
子进程对象.terminate()
线程
1. 线程是cpu调度的基本单位 2. 每个进程至少都有一个线程,而这个线程就是我们通常说的主线程
多线程

1. 导入线程模块 import threading 2. 创建子线程并指定执行的任务 sub_thread = threading.Thread(target=任务名) 3. 启动线程执行任务 sub_thread.start()
import threading import time # 唱歌任务 def sing(): # 扩展: 获取当前线程 # print("sing当前执行的线程为:", threading.current_thread()) for i in range(3): print("正在唱歌...%d" % i) time.sleep(1) # 跳舞任务 def dance(): # 扩展: 获取当前线程 # print("dance当前执行的线程为:", threading.current_thread()) for i in range(3): print("正在跳舞...%d" % i) time.sleep(1) if __name__ == '__main__': # 扩展: 获取当前线程 # print("当前执行的线程为:", threading.current_thread()) # 创建唱歌的线程 # target: 线程执行的函数名 sing_thread = threading.Thread(target=sing) # 创建跳舞的线程 dance_thread = threading.Thread(target=dance) # 开启线程 sing_thread.start() dance_thread.start() ################ 结果 ################# 正在唱歌...0 正在跳舞...0 正在唱歌...1 正在跳舞...1 正在唱歌...2 正在跳舞...2
Thread
Thread(group,target,name,args,kwargs)
group: 线程组,目前只能使用None target: 执行的目标任务名 args: 以元组的方式给执行任务传参 kwargs: 以字典方式给执行任务传参 name: 线程名,一般不用设置
获取当前线程对象
threading.current_thread()
线程执行带有参数的任务
元组方式传参
args=()
import threading import time # 带有参数的任务 def task(count): for i in range(count): print("任务执行中..") time.sleep(0.2) else: print("任务执行完成") if __name__ == '__main__': # 创建子线程 # args: 以元组的方式给任务传入参数 sub_thread = threading.Thread(target=task, args=(5,)) sub_thread.start() ################ 结果 ################## 任务执行中.. 任务执行中.. 任务执行中.. 任务执行中.. 任务执行中.. 任务执行完成
元组方式传参一定要和参数的顺序保持一致。
字典方式传参
kwargs={}
import threading import time # 带有参数的任务 def task(count): for i in range(count): print("任务执行中..") time.sleep(0.2) else: print("任务执行完成") if __name__ == '__main__': # 创建子线程 # kwargs: 表示以字典方式传入参数 sub_thread = threading.Thread(target=task, kwargs={"count": 3}) sub_thread.start() ################# 结果 ################## 任务执行中.. 任务执行中.. 任务执行中.. 任务执行完成
字典方式传参字典中的key一定要和参数名保持一致。
线程的注意点
线程之间执行是无序的
import threading import time def task(): time.sleep(1) print("当前线程:", threading.current_thread().name) if __name__ == '__main__': for _ in range(5): sub_thread = threading.Thread(target=task) sub_thread.start() ############## 结果 ############## 当前线程: Thread-1 当前线程: Thread-2 当前线程: Thread-4 当前线程: Thread-5 当前线程: Thread-3
1. 线程之间执行是无序的,它是由cpu调度决定的 ,cpu调度哪个线程,哪个线程就先执行,没有调度的线程不能执行。 2. 进程之间执行也是无序的,它是由操作系统调度决定的,操作系统调度哪个进程,哪个进程就先执行,没有调度的进程不能执行。
主线程会等待所有的子线程执行结束再结束
import threading import time # 测试主线程是否会等待子线程执行完成以后程序再退出 def show_info(): for i in range(5): print("test:", i) time.sleep(0.5) if __name__ == '__main__': sub_thread = threading.Thread(target=show_info) sub_thread.start() # 主线程延时1秒 time.sleep(1) print("over") ################# 结果 ################### test: 0 test: 1 over test: 2 test: 3 test: 4
守护主线程
1. threading.Thread(target=show_info, daemon=True) 2. 线程对象.setDaemon(True)
import threading import time # 测试主线程是否会等待子线程执行完成以后程序再退出 def show_info(): for i in range(5): print("test:", i) time.sleep(0.5) if __name__ == '__main__': # 创建子线程守护主线程 # daemon=True 守护主线程 # 守护主线程方式1 sub_thread = threading.Thread(target=show_info, daemon=True) # 设置成为守护主线程,主线程退出后子线程直接销毁不再执行子线程的代码 # 守护主线程方式2 # sub_thread.setDaemon(True) sub_thread.start() # 主线程延时1秒 time.sleep(1) print("over") ################### 结果 ###################### test: 0 test: 1 over
线程之间共享全局变量
import threading import time # 定义全局变量 my_list = list() # 写入数据任务 def write_data(): for i in range(5): my_list.append(i) time.sleep(0.1) print("write_data:", my_list) # 读取数据任务 def read_data(): print("read_data:", my_list) if __name__ == '__main__': # 创建写入数据的线程 write_thread = threading.Thread(target=write_data) # 创建读取数据的线程 read_thread = threading.Thread(target=read_data) write_thread.start() # 延时 # time.sleep(1) # 主线程等待写入线程执行完成以后代码在继续往下执行 write_thread.join() print("开始读取数据啦") read_thread.start() ################ 结果 ################# write_data: [0, 1, 2, 3, 4] 开始读取数据啦 read_data: [0, 1, 2, 3, 4]
线程之间共享全局变量数据出现错误问题
1. 定义两个函数,实现循环100万次,每循环一次给全局变量加1 2. 创建两个子线程执行对应的两个函数,查看计算后的结果 import threading # 定义全局变量 g_num = 0 # 循环一次给全局变量加1 def sum_num1(): for i in range(1000000): global g_num g_num += 1 print("sum1:", g_num) # 循环一次给全局变量加1 def sum_num2(): for i in range(1000000): global g_num g_num += 1 print("sum2:", g_num) if __name__ == '__main__': # 创建两个线程 first_thread = threading.Thread(target=sum_num1) second_thread = threading.Thread(target=sum_num2) # 启动线程 first_thread.start() # 启动线程 second_thread.start() ################ 结果 ################# sum1: 1210949 sum2: 1496035
解决方法:线程同步
线程等待(join)
import threading # 定义全局变量 g_num = 0 # 循环1000000次每次给全局变量加1 def sum_num1(): for i in range(1000000): global g_num g_num += 1 print("sum1:", g_num) # 循环1000000次每次给全局变量加1 def sum_num2(): for i in range(1000000): global g_num g_num += 1 print("sum2:", g_num) if __name__ == '__main__': # 创建两个线程 first_thread = threading.Thread(target=sum_num1) second_thread = threading.Thread(target=sum_num2) # 启动线程 first_thread.start() # 主线程等待第一个线程执行完成以后代码再继续执行,让其执行第二个线程 # 线程同步: 一个任务执行完成以后另外一个任务才能执行,同一个时刻只有一个任务在执行 first_thread.join() # 启动线程 second_thread.start() ############### 结果 ################ sum1: 1000000 sum2: 2000000
互斥锁
#1. 创建锁 #2. 上锁 #3. 解锁 import threading import time g_num = 0 mutex = threading.Lock() def sum_num1(): global g_num for i in range(1000000): mutex.acquire() g_num += 1 mutex.release() print('num1',g_num) def sum_num2(): global g_num for i in range(1000000): mutex.acquire() g_num += 1 mutex.release() print('num2',g_num) if __name__ == '__main__': num1 = threading.Thread(target=sum_num1) num2 = threading.Thread(target=sum_num2) num1.start() num2.start() num1,num2.join() print('all',g_num) ############## 结果 ################ num1 1741270 num2 2000000 all 2000000
进程和线程对比
【区别对比】 1. 进程之间不共享全局变量 2. 线程之间共享全局变量,但是要注意资源竞争的问题,解决办法: 互斥锁或者线程同步 3. 创建进程的资源开销要比创建线程的资源开销要大 4. 进程是操作系统资源分配的基本单位,线程是CPU调度的基本单位 5. 线程不能够独立执行,必须依存在进程中 6. 多进程开发比单进程多线程开发稳定性要强
【进程优缺点】 优点:可以用多核 缺点:资源开销大 【线程优缺点】 优点:资源开销小 缺点:不能使用多核
异常、模块
异常
异常处理
【异常】程序运行后的非语法错误
【处理异常目的】防止程序退出,保证程序正常执行
捕获异常
try: 可能发生异常的代码 except: # 处理异常的代码 1. 如果try里面发生异常 2. 自动跳转到except里面
try: print('=' * 20) open('xxx.txt', 'r') # 发生了异常,自动跳转到except里面 print('=' * 20) except: print('try里面发生了异常') ######### 结果 ######### ==================== try里面发生了异常
except后面没有指定异常类型,可以捕获任意类型的异常
try: 可能发生异常的代码 except 异常类型: 处理异常的代码
try: print('=' * 20) open('xxx.txt', 'r') # 发生了异常,自动跳转到except里面 print('=' * 20) except FileNotFoundError: print('try里面发生了异常') ######### 结果 ########## ==================== try里面发生了异常
try: 可能发生异常的代码 except (异常类型1, 异常类型2): 处理异常的代码
try: print('=' * 20) # open('xxx.txt', 'r') print('=' * 20) print(num) print('=' * 20) except (FileNotFoundError, NameError): print('try里面发生了异常')
获取异常的信息描述
try: 可能发生异常的代码 except 异常类型 as 异常对象名: print(异常对象名) 即可获取异常的信息描述
ry: print('=' * 20) open('xxx.txt', 'r') print('=' * 20) except FileNotFoundError as e: print('异常信息为:', e) ########## 结果 ########### ==================== 异常信息为: [Errno 2] No such file or directory: 'xxx.txt'
异常中else
try: 可能发生异常的代码 except: 处理异常的代码 else: 没有发生异常,except不满足执行else
try: num = 100 print(num) except NameError as errorMsg: print('产生错误了:%s'%errorMsg) else: print('没有捕获到异常,真高兴') ############ 结果 ############# ==================== 666 ==================== 没有发生异常,很开心
finally
try: 可能发生异常的代码 except: 处理异常的代码 else: 没有发生异常,except不满足执行else finally: 不管有没有异常,最终都要执
try: print('=' * 20) # num = 666 print(num) print('=' * 20) except Exception as e: print('异常信息为:', e) else: print('没有发生异常,很开心') finally: print('不管有没有异常,最终都要执行') ############ 结果 ############### ==================== 异常信息为: name 'num' is not defined 不管有没有异常,最终都要执行
异常传递
try嵌套
try: f = open('yyy.txt', 'w') # 内部语句执行完,才向外部传递异常 try: # 前面只写方式打开文件,不能读文件,产生异常 # 内部没有捕获处理异常 ret = f.read() print(ret) finally: print('关闭文件') f.close() except Exception as e: print('外层捕获异常:', e) ############ 结果 ############## 关闭文件 外层捕获异常: not readable
如果内层try没有捕获处理该异常,就会向外层try进行传递
函数嵌套
# 定义1个函数,函数内部发生了异常 test01(),没有捕获处理 def test01(): print('开始执行test0111111') print(num) print('结束执行test0111111') # 定义另外一个函数 test02, 在函数内部调用test01 def test02(): print('开始执行test02222222') test01() print('结束执行test02222222') # 定义一个test03函数,函数内部调用test01,但是对test01做异常处理 def test03(): print('开始执行test0333333') try: test01() except Exception as e: print('外层函数捕获异常:', e) print('结束执行test0333333') # 调用test02() # test02() test03() ############## 结果 ############## 开始执行test0333333 开始执行test0111111 外层函数捕获异常: name 'num' is not defined 结束执行test0333333
如果内层函数没有捕获处理该异常,就会向外层函数进行传递
抛出自定义的异常
raise
""" 需求: 1. 自定义异常类,电话号码长度异常类 1.1 __init__,添加2个属性,用户电话的长度,要求的长度 1.2 __str__ 返回提示描述意思,如:用户电话长度为:xx位, 这边要求长度为:11位 2. 只要用户输入的手机号码不为11位,抛出自定义异常类 """ # 1. 自定义异常类,电话号码长度异常类 class NumberError(Exception): """自定义异常类,电话号码长度异常类""" # 添加2个属性,用户电话的长度,要求的长度 def __init__(self, _user_len, _match_len=11): super().__init__() # 调用父类的init self.user_len = _user_len # 用户电话的长度 self.match_len = _match_len # 要求号码的长度 def __str__(self): return f'用户电话长度为:{self.user_len} 位, 这边要求的长度为:{self.match_len} 位' # 2. 只要用户输入的手机号码不为11位,抛出自定义异常类 try: num_str = input('请输入你的号码:') if len(num_str) != 11: raise NumberError(len(num_str)) # 抛出自定义异常类 except NumberError as e: # e 为 NumberError(len(num_str))实例对象 的别名 print('异常信息为:', e) ################## 结果 ###################### 请输入你的号码:11232 异常信息为: 用户电话长度为:5 位, 这边要求的长度为:11 位
# 1. 自定义异常类 class 自定义异常类名字(Exception): 1.1 重新写__init__(self, 形参1, 形参2,……) # 建议调用父类的init,先做父类的初始化工作 super().__init__() 咱们自己写的代码 1.2 重新写__str__(),返回提示信息 # 2. 抛出异常类 raise 自定义异常类名字(实参1, 实参2,……)
模块
【介绍】模块是一个由Python代码组成的文件,就是一个以.py结尾的文件
模块导入
import
# 导入模块 import random # 模块名.函数 num = random.randint(1, 3) print(num) # 模块名.类名 # 创建对象 ran = random.Random() print(type(ran)) # 模块名.变量名 print(random.TWOPI)
导入模块,把整个模块都加载进来
导入格式: import 模块名 使用格式: 模块名.函数 模块名.类名 模块名.变量名
from…import
from random import randint, Random, TWOPI # 函数 num = randint(1, 3) print(num) # 类 ran = Random() print(type(ran)) # 变量 print(TWOPI)
只导入模块中需要使用的内容
导入格式: from 模块名 import 需使用的函数、类、变量 使用格式: 函数、类、变量 无需通过模块名引用
from…import *
from random import * # 函数 num = randint(1, 3) print(num) # 类 ran = Random() print(type(ran))
导入模块中大部分内容
导入格式: from 模块名 import * 使用格式: 函数、类、变量 无需通过模块名引用
import...as...
# 将模块random取别名为r import random as r # 模块别名.方法 num = r.randint(1, 3) print(num) # 模块工具randint取别名为ri from random import randint as ri num = ri(1, 3) print(num)
给导入的模块取别名
模块搜索路径
import sys print(sys.path) # 模块搜索路径存储在system模块的sys.path变量中
定义模块
每个Python文件都可以作为一个模块,模块的名字就是文件的名字
def my_add(a, b): """返回2个数相加结果""" return a+b def my_sub(a, b): """返回2个数相减结果""" return a-b
调用自己定义的模块
import module # 导入模块 # 调用模块中的函数 ret = module.my_add(1, 1) print(ret) ret = module.my_sub(10, 20) print(ret) 
测试模块
if __name__ == '__main__':
def my_add(a, b): """返回2个数相加结果""" return a+b def my_sub(a, b): """返回2个数相减结果""" return a-b if __name__ == '__main__': ret = my_add(2, 2) print('模块中测试代码:my_add(2, 2) = ', ret) ret = my_sub(10, 2) print('模块中测试代码:my_sub(10, 2) = ', ret)
被导入时,不执行测试代码块
__all__ = ['变量名', '类名', '函数名', ……]

1. 只对from xxx import *这种导入方式有效 2. 模块中__all__变量包含的元素,才能会被from xxx import *导入
面向对象
概述
面向对象
把构成问题事务分解成各个对象,适合开发大型项目
面向过程
把编程任务划分成一个一个的步骤,然后按照步骤分别去执行,适合开发中小型项目
类
描述 具有共同特征的事物的 抽象,称为 类 (class)。 类是对象的模板(不占内存空间)
类包含两个组成部分: 属性:比如姓名,年龄,身高,肤色等 方法:比如吃饭,睡觉,飞行,歌唱等
定义类
class 类名(object): def 方法名(self): pass
class Dog(object): # self自动添加的,后面有讲解 # 定义在类里面的函数,叫方法 def eat(self): print('吃骨头') def drink(self): print('喝水')
方法
实例方法
调用方法
对象变量名.方法名()
class Dog(object): # 定义在类里面的函数,叫方法 def eat(self): print('吃骨头') def drink(self): print('喝水') # 创建对象格式:实例对象变量名 = 类名() dog1 = Dog() # 对象变量.方法名字(), self不用处理 dog1.eat() dog1.drink()
注意:虽然定义方法时设置第一个参数 self, 但是 调用方法时不要传递对应self的参数,解释器自动处理
self
self是什么
哪个对象调用方法,方法中self就是这个对象
# 定义类 class Dog(object): def print_info(self): print('测试self', id(self)) # 创建对象1 dog1 = Dog() # 打印dog1的id print("调用方法前", id(dog1)) # dog1调用print_info, print_info的self就是dog1 # 底层调用:print_info(dog1), 解释器自动把dog1传给方法中的self dog1.print_info() print("调用方法后", id(dog1)) print('='*30) # 创建对象2 dog2 = Dog() print("调用方法前", id(dog2)) dog2.print_info() print("调用方法后", id(dog2)) ############## 结果 ################ 调用方法前 4400762352 测试self 4400762352 调用方法后 4400762352 ============================== 调用方法前 4400762544 测试self 4400762544 调用方法后 4400762544
self的作用
为了区分不同对象的属性和方法
# 定义类 class Dog(object): def print_info(self): print(self.type) # 创建对象,实例化对象 dog1 = Dog() # 添加属性 dog1.type = '大黄狗' # 直接调用方法 dog1.print_info() print('='*30) dog2 = Dog() dog2.type = '旺财' dog2.print_info() 
Magic Method 魔法方法
__init__()
特点:创建对象的时候,实例化对象,自动调用__init__方法
作用:添加属性
不带参数 和 带参数的用法
# 不带参数 class 类名(object): def __init__(self): pass # 实例化对象 对象名 = 类名() # 带参数 class 类名(object): def __init__(self, 形参1, 形参2 ……): pass # 实例化对象 对象名 = 类名(实参1, 实参2,……)
__str__()
""" __str__方法: 1. 返回值必须是字符串类型 2. print(对象变量名) 对象变量名的位置替换为__str__()方法返回值的内容 """ class Dog(object): # 添加属性,type, age def __init__(self, _type, _age): self.type = _type self.age = _age # 测试有定义 __str__ 方法,和没有定义,print(对象)的区别 def __str__(self): return "类型:%s, 年龄:%d" % (self.type, self.age) # 创建对象 dog1 = Dog('大白狗', 3) # 如果有__str__ 方法,dog1的位置替换为__str__()方法返回值的内容 print(dog1) print('对象描述信息为:', dog1)
__str__()方法作用:主要返回对象属性信息,print(对象变量名)输出对象时直接输出__str__()方法返回的描述信息
__str__()方法的返回值必须是 字符串类型
如果直接 print 打印对象,会看到创建出来的对象在内存中的地址
__del__()
class Dog(object): def __del__(self): print('我悄悄地离开了') # 设计一个函数,在函数内容创建对象 # 函数调用完毕,里面创建的对象,生命周期结束,自动调用__del__方法 def func(): dog1 = Dog() print('函数调用前') # 调用函数 func() print('函数调用后') ########## 结果 ########## 函数调用前 我悄悄地离开了 函数调用后
对象的生命周期结束(对象销毁)时, __del__()方法会自动被调用,做一些清理工作
私有方法
""" 私有方法: 1. __(2个下划线)开头的方法,就是私有方法 2. 只能在本类的内部访问,在类的外面无法直接访问 3. 在类的内部调用实例方法的语法格式:self.方法名() """ class Dog(object): def __init__(self): self.__baby_count = 0 # 私有属性,以__(2个下划线)开头的属性 self.age = 1 def print_info(self): print(self.__baby_count) self.__leave() # 定义一个私有方法 def __leave(self): print('休产假了') dog1 = Dog() dog1.print_info() # AttributeError: 'Dog' object has no attribute '__leave' # dog1.__leave() # err, 外部不能访问私有方法
【定义】私有方法开头有2个下划线 _ _,否则为公有方法
只能在本类的内部直接访问,不能在类外面直接访问
类方法
""" 类方法:为了方便处理类属性 1. 用装饰器 @classmethod 来标识其为类方法 2. 一般以 cls 作为第一个参数,代表当前这个类,这个参数不用人为传参,解释器会自动处理 3. 类方法调用: 3.1 类名.类方法() 推荐用法 3.2 实例对象名.类方法() """ class Dog(object): # 类属性 count = 0 # 定义类方法 @classmethod def print_num(cls): # 参数cls代表当前的类 # print('count = ', Dog.count) print('count = ', cls.count) # 调用类方法 Dog.print_num()
1. 类对象所拥有的方法,主要为了在没有创建实例对象前提下,处理类属性 2. 需要用装饰器@classmethod来标识其为类方法 3. 对于类方法,第一个参数必须是类对象(代表类),一般以cls作为第一个参数,这个参数不用人为传参,解释器会自动处理
静态方法
""" 静态方法: 1. 需要通过装饰器@staticmethod来进行修饰默认情况下 2. 既不传递类对象也不传递实例对象(形参没有self/cls) 3. 静态方法调用: 3.1 类名.静态方法() 推荐用法 3.2 实例对象名.静态方法() """ class Dog(object): # 定义静态方法 @staticmethod def normal_func(): print('一个和实例属性、类属性没有关系的普通方法') # 调用静态方法 Dog.normal_func()
1. 需要通过装饰器@staticmethod来进行修饰,静态方法默认情况下, 既不传递类对象也不传递实例对象(形参没有self/cls)。 2. 当方法中 既不需要使用实例对象,也不需要使用类对象时,定义静态方法 3. 取消不需要的参数传递,有利于 减少不必要的内存占用和性能消耗 4. 静态方法 也能够通过 实例对象 和 类对象(类名) 去访问。
实例方法、类方法、静态方法的区别
定义方法区别:
class 类名(object): # 实例方法定义 def 实例方法名(self): pass # 类方法 @classmethod def 类方法名(cls): pass # 静态方法 @staticmethod def 静态方法名(): pass
调用方法区别: 实例方法必须通过实例对象名调用:创建完实例对象后,通过实例对象调用 类方法、静态方法通过 实例对象 和 类对象(类名) 调用,推荐使用类名
实例对象
对象 是 类 的实例,是具体的实体(占内存空间)
创建对象
对象变量名 = 类名()
# 定义类型 class Dog(object): # 定义在类里面的函数,叫方法 def eat(self): print('吃骨头') def drink(self): print('喝水') # 创建对象格式:实例对象变量名 = 类名() dog1 = Dog()
创建多个对象
class Dog(object): # 定义在类里面的函数,叫方法 def eat(self): print('吃骨头') def drink(self): print('喝水') # 对象1 = 类名() # 对象2 = 类名() # 对象1 dog1 = Dog() dog1.eat() dog1.drink() # 对象2 dog2 = Dog() dog2.eat() dog2.drink()
类作为对象的模具,根据类可以创建多个对象
属性
定义/添加属性
对象变量名.属性名 = 数据
class Dog(object): # 定义在类里面的函数,叫方法 def eat(self): print('吃骨头') def drink(self): print('喝水') # 1. 创建对象变量 dog1 = Dog() # 2. 对象变量.属性 = 数值 dog1.age = 3 # 打印属性 print(dog1.age) # 修改属性 dog1.age = 2 print(dog1.age)
首次赋值时会定义属性,再次赋值改变属性
Python中 "万物皆对象"
类本身也是一个对象,执行class语句时会被创建,称为 类对象,为了和实例对象区分开来,我们习惯叫类
实例属性
# 定义类 class 类名(object): def __init__(self): self.实例属性变量1 = 数值1 self.实例属性变量2 = 数值3 # 创建实例对象 实例对象名 = 类名() # 添加属性 实例对象名.实例属性变量3 = 数值3
· 通过在__init__方法里面给实例对象添加的属性 · 在类的外面,直接通过实例对象添加的属性 · 实例属性必须通过实例对象才能访问
类属性
# 定义类 class 类名(object): 类属性变量 = 数值1 def __init__(self): pass
· 类属性就是 类对象 所拥有的属性,它被 该类的所有实例对象 所共有。 · 定义在类里面,类方法外面的变量就是类属性 · 类属性可以使用 类名 或 实例对象 访问,推荐使用类名访问
类属性和示例属性的区别
""" 1. 定义一个类属性count,用于记录实例对象初始化的次数 2. __init__添加实例属性name,每初始化1次,类属性count加1 """ class Dog(object): # 类属性 count = 0 def __init__(self, _name): # 实例属性 self.name = _name # 每初始化一次,类属性数量加1 Dog.count += 1 # 打印类属性的值 print(Dog.count) # 创建1个对象 d1 = Dog('旺财') # 打印:实例属性,类属性 print(d1.name, Dog.count) d2 = Dog('旺钱') print(d2.name, Dog.count) d3 = Dog('旺仔') print(d3.name, Dog.count) # 通过实例对象,访问类属性 print(d1.count, d2.count, d3.count) ############## 运行结果 ################ 
· 类属性就是 类对象 所拥有的属性,它被 该类的所有实例对象 所共有。 · 实例属性 要求 每个对象 为其 单独开辟一份内存空间 ,只属于某个实例对象的
私有属性
""" 私有属性: 1. __(2个下划线)开头的属性,就是私有属性 2. 只能在本类的内部访问,在类的外面无法直接访问 """ class Dog(object): # 添加属性 def __init__(self): self.__baby_count = 0 # 私有属性,以__(2个下划线)开头的属性 self.age = 1 # 公有属性 def print_info(self): print(self.__baby_count) # 类的外部 # 创建对象 dog1 = Dog() # print(dog1.__baby_count) # err, 私有属性,在类的外面无法直接访问 print(dog1.age) dog1.print_info()
【定义】私有属性开头有2个下划线 _ _,否则为公有属性
只能在本类的内部直接访问,不能在类外面直接访问
私有类属性
class Dog(object): # 类属性 __count = 0 print(Dog.__count) # 类的外面,不能直接访问私有类属性,err
类属性也可以设置为 私有,前边添加两个下划线__
面向对象的三大特征: (封装、继承、多态)
封装
面向对象的封装特性: 1. 将属性和方法放到一起封装成一个整体,然后通过实例化对象来处理 2. 对类的属性和方法增加访问权限控制
继承
继承:子类直接具备父类的能力(属性和方法)
作用:解决代码重用问题,提高开发效率
语法:
class 子类名(父类名): pass
# 定义一个父类 class Father(object): # 添加一个属性, money def __init__(self): self.money = 9999999 def print_info(self): print(self.money) # 定义一个子类,继承与Father class Son(Father): pass # 子类创建对象 s = Son() print(s.money) # 子类私用继承过来的属性 s.print_info() # 子类使用继承过来的方法
注意:
子类对象调用方法有一个就近原则 如果本类能找到方法,直接调用本类的方法 如果本类找不到,则调用父类继承过来的方法
子类重写父类同名方法:
# 定义一个父类, Animal class Animal(object): # 添加一个type属性 def __init__(self): print('Animal类中的__init__') self.type = '动物' # 设计一个方法,打印属性 def print_type(self): print('Animal类中的print_type = ', self.type) # 定义一个子类,继承与Animal class Dog(Animal): # __init__和父类的同名,重写父类同名方法 def __init__(self): print('Dog类中的__init__') self.type = '可爱的小狗' # print_type和父类的同名,重写父类同名方法 def print_type(self): print('Dog类中的print_type = ', self.type) # 定义一个子类对象 dog1 = Dog() # 调用子类的__init__ dog1.print_type() # 调用子类的print_type()
在子类中定义了一个和父类同名的方法(参数也一样)
单继承
# 定义一个父类, Animal class Animal(object): def eat(self): print('吃东西') # 定义一个子类,只有一个父类 class Dog(Animal): pass # 创建一个子类对象 dog1 = Dog() dog1.eat()
子类只继承一个父类
多层继承
# 定义一个爷爷类, Animal class Animal(object): def eat(self): print('吃东西') # 定义一个父亲类 class Dog(Animal): def drink(self): print('喝东西') # 定义一个儿子类 class SuperDog(Dog): pass # 创建对象 sd = SuperDog() sd.eat() sd.drink()
继承关系为多层传递,如生活中的爷爷、父亲、儿子
子类调用父类同名方法:
# 定义一个父类, Animal class Animal(object): # 添加一个type属性 def __init__(self): print('Animal类中的__init__') self.type = '动物' # 设计一个方法,打印属性 def print_type(self): print('Animal类中的print_type = ', self.type) # 定义一个子类,继承与Animal class Dog(Animal): # __init__和父类的同名,重写父类同名方法 def __init__(self): print('Dog类中的__init__') self.type = '可爱的小狗' # print_type和父类的同名,重写父类同名方法 def print_type(self): print('Dog类中的print_type = ', self.type) print('='*20) # 调用父类同名函数 # 方法1: 父类名.同名方法(self, 形参1, ……) Animal.__init__(self) Animal.print_type(self) print('=' * 20) # 方法2:super(子类名, self).同名方法(形参1, ……) super(Dog, self).__init__() super(Dog, self).print_type() print('=' * 20) # 方法3:super().同名方法(形参1, ……) # 是 4.2 方法的简写 # 推荐使用的方法 super().__init__() super().print_type() # 定义一个子类对象 dog1 = Dog() # 调用子类的__init__ dog1.print_type() # 调用子类的print_type() ############ 运行结果 ############### Dog类中的__init__ Dog类中的print_type = 可爱的小狗 ==================== Animal类中的__init__ Animal类中的print_type = 动物 ==================== Animal类中的__init__ Animal类中的print_type = 动物 ==================== Animal类中的__init__ Animal类中的print_type = 动物
super().同名方法(形参1, ……)
调用当前子类的父类的同名方法
父类名.同名方法(self, 形参1, ……)
调用指定父类的同名方法
super(子类名, self).同名方法(形参1, ……)
调用指定子类的父类同名方法
多继承
# 定义2个类,它们没有继承关系,是平级的 class SmallDog(object): def eat(self): print('吃小东西') # 再定义一个类 class BigDog(object): def drink(self): print('大口喝水') # 定义一个子类,多继承于上面2个父类 class SuperDog(SmallDog, BigDog): pass # 定义子类对象,调用方法 sd = SuperDog() sd.eat() sd.drink() ################ 运行结果 ################# 吃小东西 大口喝水
即子类有多个父类,并且具有它们的特征。
class 子类名(父类1, 父类2, ……): pass
子类调用父类同名方法:
# 定义2个类,它们没有继承关系,是平级的 class SmallDog(object): def eat(self): print('吃小东西') # 再定义一个类 class BigDog(object): def eat(self): print('啃大骨头') # 定义一个子类,多继承于上面2个父类 class SuperDog(SmallDog, BigDog): pass # 定义子类对象,调用方法 sd = SuperDog() sd.eat() # 默认先调用先继承的父类,即 SmallDog ############### 运行结果 ################ 吃小东西
如果继承过来的2个父类的方法同名,默认调用先继承父类的同名方法
子类调用父类同名方法:
# 定义2个类,它们没有继承关系,是平级的 class SmallDog(object): def eat(self): print('吃小东西') # 再定义一个类 class BigDog(object): def eat(self): print('啃大骨头') # 定义一个子类,多继承于上面2个父类 class SuperDog(SmallDog, BigDog): def eat(self): print('吃蟠桃') print('='*20) # 子类调用父类同名方法: # 1. 父类名.同名方法(self, 形参1, ……) SmallDog.eat(self) # 调用SmallDog的eat() print('=' * 20) # 2. super(类名, self).同名方法(形参1, ……):调用继承顺序中类名的下一个类的同名方法 # 继承顺序中,SmallDog的下一个类是BigDog,所以,调用BigDog的eat() super(SmallDog, self).eat() print('=' * 20) # 3. super().同名方法(形参1, ……) :调用先继承父类的同名方法 super().eat() # 定义子类对象,调用方法 sd = SuperDog() sd.eat() ################### 运行结果 #################### 吃蟠桃 ==================== 吃小东西 ==================== 啃大骨头 ==================== 吃小东西
super().同名方法(形参1, ……)
调用继承顺序中类名的下一个类的同名方法
父类名.同名方法(self, 形参1, ……)
调用指定父类的同名方法
super(子类名, self).同名方法(形参1, ……)
调用先继承父类的同名方法
查看类的继承顺序
类名.__mro__
# 定义2个类,它们没有继承关系,是平级的 class SmallDog(object): def eat(self): print('吃小东西') # 再定义一个类 class BigDog(object): def drink(self): print('大口喝水') # 定义一个子类,多继承于上面2个父类 class SuperDog(SmallDog, BigDog): pass # 查看类的继承顺序 print(SuperDog.__mro__) ############## 运行结果 ############### (<class '__main__.SuperDog'>, <class '__main__.SmallDog'>, <class '__main__.BigDog'>, <class 'object'>)
私有和继承
# 定义一个父类, Animal class Animal(object): # 添加一个type属性 def __init__(self): self.__type = '动物' # 私有 def __leave(self): # 私有 print('休产假3个月') # 通过公有方法,间接访问私有元素 def use_private(self): print(self.__type) self.__leave() # 定义一个子类 class Dog(Animal): def test(self): # print(self.__type) # err,私有不能直接继承使用 # self.__leave() # err,私有不能直接继承使用 pass # 创建子类对象 dog1 = Dog() dog1.use_private()
1. 父类中的私有方法、属性不能直接继承使用 2. 可以通过调用继承的父类的共有方法,间接的访问父类的私有方法、属性
修改类属性
# 类属性修改,只能通过类名修改,不能通过对象名修改 class Dog(object): # 类属性 count = 0 # 通过类名修改 Dog.count = 1 print(Dog.count) print('='*30) # 对象名.变量 = 数据 默认操作给实例对象添加实例属性,已经不能操作类属性 # 如果类属性名字和实例属性名字相同,实例对象名只能操作实例属性 d1 = Dog() d1.count = 250 print(Dog.count, d1.count) ############## 运行结果 ############### 1 ============================== 1 250 
类属性只能通过类对象修改,不能通过实例对象修改
类属性和实例属性同名
class Dog(object): # 类属性 count = 666 def __init__(self): self.count = 250 # 实例属性 # 创建对象 # 如果类属性和实例属性同名,实例对象名只能操作实例属性 d1 = Dog() print(Dog.count, d1.count) ############## 运行结果 ############### 666 250
如果类属性和实例属性同名,实例对象名只能操作实例属性 【结论】操作类属性建议使用类名,避免不必要的麻烦
多态
【释义】多种形态,调用同一个函数,不同表现
【实现步骤】 1. 实现继承关系 2. 子类重写父类方法 3. 通过对象调用该方法
""" 1. 多态:多种形态,调用同一个函数,不同表现 2. 实现多态的步骤: 1. 实现继承关系 2. 子类重写父类方法 3. 通过对象调用该方法 """ # 定义一个父类, Animal class Animal(object): def eat(self): print('吃东西') # 定义一个子类Dog,继承于Animal class Dog(Animal): def eat(self): """重写父类方法""" print('啃骨头') # 定义一个子类Cat,继承于Animal class Cat(Animal): def eat(self): """重写父类方法""" print('吃小鱼') # 定义一个函数,用于测试多态 def func(temp): temp.eat() # 创建子类对象 d = Dog() c = Cat() # 调用同一个函数,不同表现 # 传递d参数,调用Dog的eat() # 传递c参数,调用Cat的eat() func(d) # 啃骨头 func(c) # 吃小鱼
文件操作
文件的作用
持久化储存数据
文件操作流程
打开文件,或者新建立一个文件 读/写数据 关闭文件
打开文件
文件变量 = open(文件名字,访问模式)
f = open('xxx.txt', 'w')
关闭文件
文件变量.close()
# 'w': 只写方式打开文件,文件不存在创建,文件存在,清空文件内容 f = open('xxx.txt', 'w') # 关闭文件,为了释放资源 # 文件变量.close() f.close()
自动关闭文件
with open("123.txt", "w") as f:
写数据
文件变量.write(所需的内容)
# 1. 打开文件,只写方式打开 f = open('xxx.txt', 'w') # 2. 写文件 # 写文件格式:文件变量.write(所需的内容) f.write('hello abc') f.write(' hello python') # 3. 关闭文件 f.close()
读数据
read
内容变量 = 文件变量.read(n)
# 1. 打开文件,只读方式打开,'r' # 'r': 打开文件,必须存在,不存在,报错崩溃 f = open('xxx.txt', 'r') # 2. 读取文件内容 # 格式: 内容变量 = 文件变量.read(读取的长度) # 如果read的长度不指定,默认读取全部 ret = f.read(4) # 读取4个字符 print(ret) # 下次,继续接着上一次的位置 ret = f.read(7) # 接着读取7个字符 print(ret) ret = f.read() # 接着读取剩下所有个字符 print(ret) # 3. 关闭文件 f.close()
指:n 为读取的字符数,不设置则全部读取
readlines
内容列表变量 = 文件变量.readlines()
# 1. 打开文件,只读方式打开,'r' # 'r': 打开文件,必须存在,不存在,报错崩溃 f = open('xxx.txt', 'r') # 2. 读取文件内容 # readlines: 读取所有的行,按行作为分隔条件 # 格式:内容列表变量 = 文件变量.readlines() content_list = f.readlines() # '\n'换行符,代表换行 print(content_list) # 通过for取出列表的所有元素 for v in content_list: print(v) # 3. 关闭文件 f.close()
指:一次全部读出,读取所有的行,按行作为分隔条件,返回列表 (每行内容是一个元素)
readline
内容变量 = 文件变量.readline()
# 1. 打开文件,只读方式打开,'r' # 'r': 打开文件,必须存在,不存在,报错崩溃 f = open('xxx.txt', 'r') # 2. 读取文件内容 # readlines: 读取所有的行 # readline: 一次读取一行 # readline格式:内容变量 = 文件变量.readline() content = f.readline() print(content) content = f.readline() print(content) content = f.readline() print(content) # 3. 关闭文件 f.close()
指:每次读取一行数据
访问模式
r
# 'r',只读方式打开文件,文件不存存在,报错 f = open('abc.txt', 'r') f.close()
只用于读取, 默认模式。文件不存在,会报错。
w
# 'w',只写方式打开文件,文件不存在新建,文件存在清空文件内容 f = open('abc.txt', 'w') f.close()
只用于写入。文件存在则先清空内容, 文件不存在,创建新文件。
a
# 1. 'a',追加方式打开文件 f = open('abc.txt', 'a') # 2. 写数据 f.write('^_~') # 3. 关闭文件 f.close()
只用于写入。文件存在则追加内容, 文件不存在,创建新文件。
r+
用于读写。文件不存在,会报错。
w+
用于读写。文件存在则先清空内容, 文件不存在,创建新文件。
a+
用于读写。文件存在则追加内容, 文件不存在,创建新文件。
rb
二进制格式的只读操作。后续网络课程中具体讲解。
wb
二进制格式的只写操作。后续网络课程中具体讲解。
ab
二进制格式的追加操作。后续网络课程中具体讲解。
文件路径
相对路径
1.txt:等价于./1.txt,当前路径下的1.txt
../1.txt: 上一级路径下的1.txt
绝对路径(不建议)
文件相关操作
os模块
import os
导入os模块
os.rename(旧的文件名,新的文件名)
文件重命名
os.remove("毕业论文.txt")
删除文件
os.mkdir("张三")
创建文件夹
os.rmdir("张三")
删除空文件夹
print(work_path)
获取当前目录
os.chdir("../")
改变默认目录
temp_list = os.listdir('某路径')
获取目录列表
ret = os.path.exists('1.txt')
判断文件是否存在
函数
函数定义
""" def 函数名(): 函数内部的代码块 """ def say_hello(): print("hello 1")
def <函数名>(<参数(0个或多个)>): ...... return<返回值>
return
return 关键字只能使用在函数中
可以 使用变量 来 接收 函数的返回结果
在函数中使用 return 关键字可以中断函数,同时也返回一个结果
函数内部没有任何return语句,默认返回None,表示没有任何数据
return不设置返回值,默认也返回None
文档注释
"""我是文档注释"""
选中函数,按住ctrl后再点击,可以跳转到该函数
函数调用
<函数名>()
函数参数
形参(函数定义的参数叫形参)
实参(函数调用时的参数叫实参)
类型
· 无参数,无返回值 · 无参数,有返回值 · 有参数,无返回值 · 有参数,有返回值
位置参数
# 函数定义 def func(num1, num2): print('num1 = ', num1) print('num2 = ', num2) # 函数调用 func(1, 2)
按形参的位置,从左往右,一一匹配传递参数 · 位置参数必须一一对应,缺一不可
缺省参数
# 默认参数必须在普通参数的后边 def func(a, b=20, c=10): print(a, b, c) # 函数调用 func(1) # a=1,b=20,c=10 func(1, 2) # a=1,b=2,c=10 func(1, 3, 2) # a=1,b=3,c=2
形参设定默认值,称为缺省参数,也叫默认参数 · 调用函数时,如果没有传入默认参数对应的实参,则使用默认值 · 默认参数必须在普通参数的后边
关键字参数
# 函数定义 def func(num1, num2): print('num1 = ', num1) print('num2 = ', num2) # 函数调用 func(num2=1, num1=2)
关键字参数:通过 形参=值 方式为函数形参传值,无需和形参位置一一对应 · 关键字参数必须在位置参数的右边 · 对同一个形参不能重复传值
不定长参数
元组型不定长参数
# *args 不定长参数,可以接收0~多个实参 # 把实参的1,2,3, 包装成元组(1, 2, 3)再传递, 等价于args = (1, 2, 3) def func(*args): # 函数内部使用,无需加* print(args, type(args)) # 函数调用 func(1, 2, 3) # (1, 2, 3) <class 'tuple'>
形参变量名前面加上一个*,这个参数则为元组型不定长参数
字典型不定长参数
# 把实参包装成 {'city': 'sz', 'age': 18}给kwargs传递 # kwargs = {'city': 'sz', 'age': 18} def func(name, **kwargs): # 存在形参name, name不会被包装到字典中 print(name) print(kwargs) # 函数内部使用,无需加* # 实参的写法: 变量=数据,变量=数据 func(name='mike', city='sz', age=18)
定义参数时需要在形参名前添加**,则为字典型不定长参数
字典型可变形参必须在形参列表的最后边
函数嵌套
# 定义fun01函数 def fun01(): print('开始调用fun01') print('结束调用fun01') # 定义fun02函数,在代码块中间调用fun01 def fun02(): print('开始调用fun02') # 调用fun01 fun01() print('结束调用fun02') # 函数调用 fun02() # 参考图片 2021-09-11
一个函数里面 又调用 了 另外一个函数,这就是 函数嵌套调用
局部变量
# 函数定义 def func(temp): a = 10 print(a) # 函数调用 func(22) # NameError: name 'a' is not defined # print('函数的外部:', a) # err # print('函数的外部: ', temp) # err
局部变量是定义在函数内部的变量
局部变量只在定义所在的函数内部有效
局部变量的生命周期在函数代码块执行完后销毁
全局变量
# 定义全局变量 num = 10 def func(): print('func num = ', num) func() # 函数调用 print('函数的外面 num = ', num)
全局变量是定义在函数外部的变量
全局变量在所有函数内都有效
全局变量的生命周期在程序结束时销毁
函数内修改全局变量:先 global 声明全局变量,再修改
# 定义全局变量 num = 10 def func(): # global num 告诉解释器,函数内部使用的num就是外面的全局变量num global num num = 250 # 修改全局变量 print('func num = ', num) func() # 函数调用 print('函数的外面 num = ', num)
组包和拆包
组包
= 右边有多个数据时, 会自动包装为元组
result = 10, 20, 30
拆包
变量数量 = 容器长度, 容器中的元素会一一对应赋值给变量
a, b, c = (10, 20, 30)
【应用】函数可以同时返回多个数
# 函数可以同时返回多个数 def return_arg(): return 1, 2, 3 # 函数调用 # 变量名 = 函数() ret = return_arg() print(ret) # 返回值直接做拆包 r1, r2, r3 = return_arg() print(r1, r2, r3)
【应用】字典元素拆包
info_dict = {'name': 'mike', 'age': 34} # 遍历字典,取出每一个item for temp in info_dict.items(): print(temp) # 元组:(key, value) key, value = temp # 元组拆包 print(key, value)
引用
引用:是一个变量或值的另一个名字,又称别名
【案例】引用指向改变
# a 是 10 的引用,10的别名是a,操作a就是操作10 a = 10 print(id(a), id(10)) # 地址id一样,指向同一个内存空间 # b 是 a 的引用,b的别名是a,操作a就是操作b b = a print(id(b), id(a)) # 地址id一样,指向同一个内存空间 print(a, b) # 10 10 # b 是 20 的引用,20的别名是b,操作b就是操作20 # b已经和上面的a没有关系,现在是20的别名 b = 20 print(b, id(b)) # 和a的地址不一样了,说明b和a没有关系 
可以使用 id函数 查看变量的引用地址,引用地址相等,说明指向同一个内存空间
函数传参是引用传递
# 给函数传参是引用传递 # 带参数函数定义 def func(num): print('func = ', id(num)) # 给函数传参,变量传参 a = 10 print('func调用前 = ', id(a)) func(a) print('func调用后 = ', id(a)) ################运行结果################## func调用前 = 4404906944 func = 4404906944 func调用后 = 4404906944
可变类型与不可变类型
""" 可变类型: 列表、字典、集合 不可变类型: 数字类型(int, bool, float)、字符串、元组 可变类型:在地址不变的情况下,可以修改内容 不可变类型: 在地址不变的情况下,不可修改内容 """ # 验证列表是可变类型 my_list = ['a', 'b'] print('改之前的地址:', id(my_list)) my_list.append('c') # 追加元素 print('改之后的地址:', id(my_list)) # 地址和改之前一样 print(my_list) # ['a', 'b', 'c'] print('=' * 20) # 验证int是不可变类型 a = 10 print('改之前的地址:', id(a)) a = 20 # a是20的别名,重新指向新的空间,和前面的a没有关系,不是改前面的内容 print('改之后的地址:', id(a)) # 和改之前地址不一样了 print(a) # 20
【可变类型】在存储空间中可以直接修改的数据类型
列表 list
字典 dict
集合set
【不可变类型】在存储空间中不可以直接修改的数据类型
数值类型 int, bool, float
字符串 str
元组 tuple
range【创建整数列表】
# 1. 打印:0、1、2、3、4 # for i in range(5): # [0, 5) for i in range(0, 5): # [0, 5) print(i) # 2. 1~100的累加 # 2.1 定义辅助变量 _sum = 0 # 2.2 for 控制循环范围 for i in range(1, 101): # 2.3 累加 _sum += i # 2.4 在循环外面打印累加结果 print(_sum) # 3. 验证步长,打印:0、2、4 for i in range(0, 5, 2): # [0, 5) print(i)
for in range(开始位置,结束位置,步长)
可创建一个整数列表对象,一般用在 for 循环中
列表推导式
# 普通方法:遍历0~4范围的元素,这些元素添加到列表中 # 1. 空列表 new_list = [] # 2. range(5)遍历取数 for i in range(5): # 2.1 取出来的元素追加到列表 new_list.append(i) # 3. 循环外面,打印结果 print(new_list) print('='*30) # 通过列表推导式,实现上面的效果 [计算公式 for循环体] # 1. for i in range(5), 取出0,放在i变量中,i追加到列表 # 2. 循环下一步,取出2,放在i变量中,i追加到列表 # 重复,直到退出循环 new_list2 = [i for i in range(5)] print(new_list2) print('='*30) # 0~10之间数,偶数才添加到列表 # 普通方法实现 # 1. 空列表 new_list = [] # 2. range(11)遍历取数 for i in range(11): # 2.1 取出来的元素是偶数的话,追加到列表 # 2.2 i % 2 == 0, i 对 2求余,结果为0,就是偶数 if i % 2 == 0: new_list.append(i) # 3. 循环外面,打印结果 print(new_list) print('='*30) # 列表推导式实现 # [i for i in range(11) if i % 2 == 0] # 1. for i in range(11)取第一个元素 # 2. if i % 2 == 0 # 3. 上面满足条件的i, 条件到列表 new_list2 = [i for i in range(11) if i % 2 == 0] print(new_list2) ################ 运行结果 ################## [0, 1, 2, 3, 4] ============================== [0, 1, 2, 3, 4] ============================== [0, 2, 4, 6, 8, 10] ============================== [0, 2, 4, 6, 8, 10]
计算公式 for 循环 if 判断
【例子】new_list = [i for i in range(1, 101)]
匿名函数
# 无参有返回值匿名函数 # a) 匿名函数整体就是函数名字, 函数名字()就是函数调用 ret = (lambda: 1 + 1)() print(ret) # b) 给匿名函数起一个函数名字,函数名字()就是调用函数 func = lambda: 1 + 1 # 给匿名函数起一个函数名字叫func ret = func() # 返回值变量 = 函数名() print(ret) print('=' * 30) # 有参有返回值匿名函数 # a. 直接调用匿名函数 ret = (lambda a, b: a - b)(30, 10) print(ret) # b. 先给匿名函数起名,再调用 func = lambda a, b: a - b ret = func(30, 10) print(ret)
lambda [形参1], [形参2], ... : [单行表达式] 或 [函数调用]
通过匿名函数编写简单的函数
函数没有名字,这样的函数叫做匿名函数
· 匿名函数中不能使用 while 循环、for 循环,只能编写单行的表达式,或函数调用 · 匿名函数中返回结果不需要使用 return,表达式的运行结果就是返回结果 · 匿名函数中也可以不返回结果
递归函数
【通过递归函数实现阶乘】 # 阶乘的规律: 1! = 1 2! = 2 × 1 = 2 × 1! 3! = 3 × 2 × 1 = 3 × 2! 4! = 4 × 3 × 2 × 1 = 4 × 3! ... n! = n × (n-1)! 【示例代码】 # 1. 定义函数(参数) def func(n): # 2. 如果我是 1 ,直接返回 1 if n == 1: return 1 # 3. 否则,返回 n * 函数调用自己(n-1) else: ret = n * func(n-1) return ret # 函数调用 _ret = func(3) print(_ret) 【调用流程】 
· 函数在内部调用其本身 · 一定有出口,否则达到最大递归次数会报错
enumerate【获取元素索引位置】
【通过 for 配合 enumerate 遍历容器同时获取元素索引位置、元素】 user_list = [{'name': 'mike', 'age': 34, 'tel': '110'}, {'name': 'yoyo', 'age': 18, 'tel': '120'}] # 遍历列表,同时把索引位置能打印 # 普通方法实现 # 1. 定义索引位置变量 i = 0 # 2. for遍历列表,打印:索引、元素 for user_dict in user_list: print(i, user_dict) # 3. 索引位置+1 i += 1 print('=='*20) # 通过enumerate方法实现 # enumerate(容器变量):获取到:元素位置,元素 for i, user_dict in enumerate(user_list): print(i, user_dict) ############## 运行结果 ################ 0 {'name': 'mike', 'age': 34, 'tel': '110'} 1 {'name': 'yoyo', 'age': 18, 'tel': '120'} ======================================== 0 {'name': 'mike', 'age': 34, 'tel': '110'} 1 {'name': 'yoyo', 'age': 18, 'tel': '120'}
for i, user_dict in enumerate(user_list): print(i, user_dict)
del 列表[索引]【删除列表元素】
【通过del删除列表元素:del 列表[索引]】 user_list = [{'name': 'mike', 'age': 34, 'tel': '110'}, {'name': 'yoyo', 'age': 18, 'tel': '120'}] # 通过del删除列表元素 del 列表[索引位置] print(user_list) # [{'name': 'mike', 'age': 34, 'tel': '110'}, {'name': 'yoyo', 'age': 18, 'tel': '120'}] # 删除索引位置为0的元素 del user_list[0] print(user_list) # [{'name': 'yoyo', 'age': 18, 'tel': '120'}]
del user_list[0]
选择和循环语句
运算符
算数运算符
# 算术运算符 """ / 除 // 取整数商 % 取余数 ** 多次方 """ a = 5 / 2 print(a) a = 5 // 2 print(a) a = 5 % 2 print(a) a = 2 ** 3 print(a)
+
加
两个对象相加 a + b 输出结果 30
-
减
得到负数或是一个数减去另一个数 a - b 输出结果 -10
*
乘
两个数相乘或是返回一个被重复若干次的字符串 a * b 输出结果 200
/
除
b / a 输出结果 2
//
取整除
返回商的整数部分 9//2 输出结果 4 , 9.0//2.0 输出结果 4.0
%
取余
返回除法的余数 b % a 输出结果 0
**
指数
a**b 为10的20次方, 输出结果 100000000000000000000
赋值运算符
# 赋值运算符 a = 2 a = a + 3 print(a)
=
赋值运算符
把 = 号右边的结果 赋给 左边的变量,如 num = 1 + 2 * 3,结果num的值为7
复合赋值运算符
+=
加法赋值运算符
c += a 等效于 c = c + a
-=
减法赋值运算符
c -= a 等效于 c = c - a
*=
乘法赋值运算符
c *= a 等效于 c = c * a
/=
除法赋值运算符
c /= a 等效于 c = c / a
%=
取余赋值运算符
c %= a 等效于 c = c % a
**=
幂赋值运算符
c **= a 等效于 c = c ** a
//=
取整除赋值运算符
c //= a 等效于 c = c // a
比较运算符
==
检查两个操作数的值是否相等,如果是则条件变为真。
如a=3,b=3,则(a == b) 为 True
!=
检查两个操作数的值是否相等,如果值不相等,则条件变为真。
如a=1,b=3,则(a != b) 为 True
>
检查左操作数的值是否大于右操作数的值,如果是,则条件成立。
如a=7,b=3,则(a > b) 为 True
<
检查左操作数的值是否小于右操作数的值,如果是,则条件成立。
如a=7,b=3,则(a < b) 为 False
>=
检查左操作数的值是否大于或等于右操作数的值,如果是,则条件成立。
如a=3,b=3,则(a >= b) 为 True
<=
检查左操作数的值是否小于或等于右操作数的值,如果是,则条件成立。
如a=3,b=3,则(a <= b) 为 True
逻辑运算符
and
x and y
布尔"与":如果 x 为 False,x and y 返回 False,否则它返回 y 的值。
True and False, 返回 False。
or
x or y
布尔"或":如果 x 是 True,它返回 True,否则它返回 y 的值。
False or True, 返回 True。
not
not x
布尔"非":如果 x 为 True,返回 False 。如果 x 为 False,它返回 True。
not True 返回 False, not False 返回 True
语句
print()
打印内容
import
引用模块
import <模块>
from <模块> import <名称>
from <模块> import <名称>as <别名>
赋值
x[,y,z]=a[,b,c]
a,b=b,a
变量的值对调
a=b=c=1
多个变量赋给一个对象,地址相同
+=,-=,*=,/=
运算并把结果赋给原变量(如:a += 1,其实是a=a+1)
m is n
检查m,n是否指向同一个对象
if <>:
if/elif/else
if 条件1: 满足条件1,执行的事情1 elif 条件2: 满足条件2,执行的事情2 elif 条件3: 满足条件3,执行的事情3 else: 不满足上述条件,执行其它事情 # 说明 条件从上往下判断 只要满足一个条件,执行满足条件的代码块,其它条件不再判断执行
判断条件成立则运行下面的语句
a = y if x else z
三元操作符,x为真执行a=y,x为假执行a=z
【循环遍历】for
for...in
# 遍历,从头开始找,直到结束 name_list = ['mike', 'yoyo', 'rock', 'lily'] # 通过while实现遍历 # 1. 定义条件变量i = 0 i = 0 # 2. while i < 列表元素个数: while i < len(name_list): # 3. 取出某个元素,打印 name = name_list[i] print(name) # 4. 条件变量的修改 i += 1 print('==========华丽分割线=============') # for遍历循环,和上面的while效果等价 # 从头到尾 依次从 列表 中取出 每一个元素 for name in name_list: print(name)
从头到尾 依次从 列表 中取出 每一个元素
for name in name_list:
if…in
name_list = ['mike', 'yoyo', 'rock', 'lily'] # if…in:判断某个元素是否在列表中,如果在,if的条件为True name = 'yoyo' # name的内容'yoyo'是否在name_list列表中,如果在,name in name_list结果为True if name in name_list: print('%s 在列表中' % name) print('==========华丽分割线=============') # for…in:从头到尾 依次从 列表 中取出 每一个元素,这个元素给name赋值 for name in name_list: print(name) ####################################### 运行结果: yoyo 在列表中 ==========华丽分割线============= mike yoyo rock lily
判断某个元素是否在列表中,如果在,if的条件为True
if name in name_list:
for…else
name_list = ['mike', 'yoyo', 'rock', 'lily'] for name in name_list: print(name) # 测试有break,和没有break的else执行情况 # if name == 'yoyo': # break else: print('for循环里面没有遇到break语句,for执行完后,则会执行else的分支') 运行结果: mike yoyo rock lily for循环里面没有遇到break语句,for执行完后,则会执行else的分支
for循环里面没有遇到break语句,for执行完后,则会执行else的分支
while<>:
判断是True则循环,是False则结束
while....else
while嵌套
# 跑步 5 圈,每跑步1圈,做10个俯卧撑 i = 0 while i < 5: print('跑步第 %d 圈' % (i + 1)) j = 0 while j < 10: print('第%d俯卧撑' % (j + 1)) j += 1 i += 1
break
n = 5 while n > 0: n -= 1 if n == 2: break print(n) print('循环结束。') -----------------运行结果--------------------- 4 3 循环结束。
在循环过程中,如果 某一个条件满足后,不 希望 循环继续执行
continue
n = 5 while n > 0: n -= 1 if n == 2: continue print(n) print('循环结束。') ---------------------------------- # 运行结果: 4 3 1 0 循环结束。
从当前位置跳到循环体最后一行后面
range(start,stop[,step])
star:开始数值默认0,stop:结束数值必须写,step:步长,默认1.返回一个数字列表
zip(seq1[,seq2[...]])
返回将两个序列的元素一一对应的元组组成的列表,以短的为准
enumerate()
得到一个索引和元素对应元组组成的可迭代,必须list才能输出
y=[<x**2> for <x> in <range(1,10)>]
遍历后x的每个值经计算赋给列表
异常处理
try:
正常的操作
except(Exception1[, Exception2[,...ExceptionN]]]):
发生以上多个异常中的一个,执行这块代码
else:
如果没有异常执行这块代码
finally:
出没出错都会执行
raise [Exception [, args [, traceback]]]:
语句中 Exception 是异常的类型(例如,NameError)参数标准异常中任一种,args 是自已提供的异常参数。
最后一个参数是可选的(在实践中很少使用),如果存在,是跟踪异常对象
迭代
iter(o[,sentinel])...next()
同for
for ...in...
list(文件)
tuple(文件)
a,b,c,d,e=open('文件')
容器类型
公共语法
python内置函数
len(item)
计算容器中元素个数
max(item)
返回容器中元素最大值
如果是字典,只针对 key 比较
min(item)
返回容器中元素最小值
如果是字典,只针对 key 比较
索引和切片
索引从0开始,索引 就是元素在 列表 中的位置编号
 """ 列表定义的格式: 列表变量的名字 = [元素1, 元素2, ……] 使用格式: 列表变量[位置] 位置:也叫下标、索引 """ # 列表定义 name_list = ['mike', 'yoyo', 'rock', 'lily'] # 0 1 2 3 # -4 -3 -2 -1 # 最后一个元素取出来 print(name_list[3]) print(name_list[-1]) # 访问列表元素,不要超出范围,不要越界 # IndexError: list index out of range # print(name_list[4])
字符串切片
""" 字符串[开始索引:结束索引:步长] 0. 步长默认为1,步长理解为走几步,正数从左往右,负数从右往左 1. 字符串[开始索引:结束索引] 开始索引 ~(结束索引-1) 2. 字符串[开始索引: ] 开始索引 ~ 结束索引,末尾索引不写,默认能取到末尾那个索引 3. 字符串[ :结束索引] 0 ~(结束索引-1),开始索引不写,默认从第0个元素开始 """ temp_str = '12345678' # 01234567 # -2-1 # 站在用户角度,第1个元素才开始算第1个位置 # 截取从 2 ~ 5 位置 的字符串 print(temp_str[1:5]) # 截取从 2 ~ 末尾 的字符串 print(temp_str[1:]) # 没有指定结尾索引,默认取到末尾 # 截取从 开始 ~ 5 位置 的字符串 print(temp_str[:5]) # 没有指定开头索引,默认从0开始 # 从开始位置,每隔一个字符截取字符串,也就是说走2步 print(temp_str[::2]) # 没有指定开头、结尾索引,默认从头到尾 # 截取字符串末尾两个字符 print(temp_str[-2:]) # 没有指定结尾索引,默认取到末尾 # 字符串的逆序(面试题) print(temp_str[::-1])
【语法】 字符串[开始索引:结束索引:步长]
切片 方法适用于 字符串、列表、元组
步长默认为1,步长理解为走几步 字符串[开始位置:结束位置]: 开始位置 ~(结束位置-1) 字符串[开始位置: ]: 开始位置 ~ 结束位置,末尾位置不写,默认能取到末尾那个位置 字符串[ :结束位置]: 0 ~(结束位置-1),开始位置不写,默认从第0个元素开始
查找
字符串.find(目标字符串, 开始索引, 结束索引)
替换
字符串.replace(原内容, 新内容, 替换次数)
分割
字符串.split(分割符)
拼接
字符串 + 字符串
字符串.join(字符串列表)
区间为左闭右开
超出索引范围,程序会报错
[::]
a = [1, 2, 3, 4] print(a[::2]) 结果: [1, 3]
首相:末项:步长
[::][::]
可以对列表元素二次切片
[::-1]
反转
reversed()
list(reversed(...))
运算符
+
[1, 2] + [3, 4] 结果 [1, 2, 3, 4]
合并
字符串、列表、元组
*
[1, 2] * 2 结果 [1, 2, 1, 2]
重复
字符串、列表、元组
in
3 in (1, 2, 3) 结果 True
元素是否存在
字符串、列表、元组、字典
not in
4 not in (1, 2, 3) 结果 True
元素是否不存在
字符串、列表、元组、字典
> >= == < <=
(1, 2, 6) < (1, 3, 4) 结果 True
元素比较
字符串、列表、元组
组包和拆包
组包
= 右边有多个数据时, 会自动包装为元组
result = 10, 20, 30
拆包
如果 变量数量 = 容器长度, 容器中的元素会一一对应赋值给变量 拆包时要注意,需要拆的数据的个数要与变量的个数相同,否则程序会异常 除了对元组拆包之外,还可以对列表、字典等拆包
a, b, c = (10, 20, 30)
函数可以同时返回多个数
# 函数可以同时返回多个数 def return_arg(): return 1, 2, 3 # 函数调用 # 变量名 = 函数() ret = return_arg() print(ret) # 返回值直接做拆包 r1, r2, r3 = return_arg() print(r1, r2, r3)
字典元素拆包
info_dict = {'name': 'mike', 'age': 34} # 遍历字典,取出每一个item for temp in info_dict.items(): print(temp) # 元组:(key, value) key, value = temp # 元组拆包 print(key, value)
引用
引用
# a 是 10 的引用,10的别名是a,操作a就是操作10 a = 10 print(id(a), id(10)) # 地址id一样,指向同一个内存空间 
引用:是一个变量或值的另一个名字,又称别名 赋值本质:给右边的变量或值,起一个别名 可以使用 id函数 查看变量的引用地址,引用地址相等,说明指向同一个内存空间 每一次运行程序,每次地址都可能不一样
引用指向改变
# a 是 10 的引用,10的别名是a,操作a就是操作10 a = 10 print(id(a), id(10)) # 地址id一样,指向同一个内存空间 # b 是 a 的引用,b的别名是a,操作a就是操作b b = a print(id(b), id(a)) # 地址id一样,指向同一个内存空间 print(a, b) # 10 10 # b 是 20 的引用,20的别名是b,操作b就是操作20 # b已经和上面的a没有关系,现在是20的别名 b = 20 print(b, id(b)) # 和a的地址不一样了,说明b和a没有关系 
函数传参是引用传递
# 给函数传参是引用传递 # 带参数函数定义 def func(num): print('func = ', id(num)) # 给函数传参,变量传参 a = 10 print('func调用前 = ', id(a)) func(a) print('func调用后 = ', id(a)) #########结果 func调用前 = 4404906944 func = 4404906944 func调用后 = 4404906944
可变类型与不可变类型
""" 可变类型: 列表、字典、集合 不可变类型: 数字类型(int, bool, float)、字符串、元组 可变类型:在地址不变的情况下,可以修改内容 不可变类型: 在地址不变的情况下,不可修改内容 """ # 验证列表是可变类型 my_list = ['a', 'b'] print('改之前的地址:', id(my_list)) my_list.append('c') # 追加元素 print('改之后的地址:', id(my_list)) # 地址和改之前一样 print(my_list) # ['a', 'b', 'c'] print('=' * 20) # 验证int是不可变类型 a = 10 print('改之前的地址:', id(a)) a = 20 # a是20的别名,重新指向新的空间,和前面的a没有关系,不是改前面的内容 print('改之后的地址:', id(a)) # 和改之前地址不一样了 print(a) # 20
可变类型
列表 list 字典 dict 集合set
不可变类型
数值类型 int, bool, float 字符串 str 元组 tuple
列表 list()
列表的定义
列表变量的名字 = [元素1, 元素2, ……]
name_list = ['mike', 'yoyo', 'rock', 'lily']
专门用于存储 一串 数据,存储的数据 称为 元素
遍历列表
for循环
for name in name_list:
列表嵌套
school_list = [['北京大学', '清华大学'], ['中山大学', '华南理工大学'], ['哈工大', '哈工程']] # 0 1 2 # 取出第 2 个元素,也就是索引为1的元素 print(school_list[1]) # ['中山大学', '华南理工大学'] # 先取出索引为1的元素,再继续索引为1的元素 # ['中山大学', '华南理工大学'] # 0 1 print(school_list[1][1]) # '华南理工大学'
一个列表中的元素又是列表,那么这就是列表的嵌套
列表推导式
# 普通方法:遍历0~4范围的元素,这些元素添加到列表中 # 1. 空列表 new_list = [] # 2. range(5)遍历取数 for i in range(5): # 2.1 取出来的元素追加到列表 new_list.append(i) # 3. 循环外面,打印结果 print(new_list) print('='*30) # 通过列表推导式,实现上面的效果 [计算公式 for循环体] # 1. for i in range(5), 取出0,放在i变量中,i追加到列表 # 2. 循环下一步,取出2,放在i变量中,i追加到列表 # 重复,直到退出循环 new_list2 = [i for i in range(5)] print(new_list2) print('='*30) # 0~10之间数,偶数才添加到列表 # 普通方法实现 # 1. 空列表 new_list = [] # 2. range(11)遍历取数 for i in range(11): # 2.1 取出来的元素是偶数的话,追加到列表 # 2.2 i % 2 == 0, i 对 2求余,结果为0,就是偶数 if i % 2 == 0: new_list.append(i) # 3. 循环外面,打印结果 print(new_list) print('='*30) # 列表推导式实现 # [i for i in range(11) if i % 2 == 0] # 1. for i in range(11)取第一个元素 # 2. if i % 2 == 0 # 3. 上面满足条件的i, 条件到列表 new_list2 = [i for i in range(11) if i % 2 == 0] print(new_list2) ######运行结果###### [0, 1, 2, 3, 4] ============================== [0, 1, 2, 3, 4] ============================== [0, 2, 4, 6, 8, 10] ============================== [0, 2, 4, 6, 8, 10]
[计算公式 for 循环 if 判断]
快速生成列表元素的表达形式,通过for添加列表元素的简洁写法
方法
增
列表.append(值)
list1 = [13, 5] # 在列表结尾追加8 list1.append(8) print(list1) # [13, 5, 8]
在末尾追加值
列表.insert(索引, 值)
list1 = [13, 5, 8] # 在 索引1的位置 插入数据20 list1.insert(1, 20) print(list1) # [13, 20, 5, 8] # 在 索引10的位置 插入数据4(没有索引10, 追加4) list1.insert(10, 4) print(list1) # [13, 20, 5, 8, 4]
在指定位置插入值, 超过索引会追加值
列表.extend(可迭代对象)
list1 = [13, 5, 8] list2 = [20, 21] # 将list2中的元素追加到list1中 list1.extend(list2) print(list1) # [13, 5, 8, 20, 21]
将可迭代对象 中 的元素 追加到列表
删
列表.remove(值)
list1 = [13, 5, 8, 5] # 删除第一个匹配到的5 list1.remove(5) print(list1) # [13, 8, 5]
删除指定值的 第一个匹配项
del 列表[索引]
list1 = [13, 5, 8] # 删除 索引1 对应的值 del list1[1] print(list1) # [13, 8]
删除指定位置的值
列表.pop(索引)
list1 = [13, 5, 8] # 删除 索引1 对应的值, 并返回被删除的值 ret = list1.pop(1) print(list1) # [13, 8] print(ret) # 5
删除指定索引的值, 并返回被删除的值
列表.clear()
list1 = [11, 2, 8] # 清空list1中值 list1.clear() print(list1) # []
清空列表
改
列表[索引] = 值
list1 = [13, 5] # 修改 索引1 对应的值 list1[1] = 2 print(list1) # [13, 2] list1[10] = 3 # 索引不存在,程序报错
修改指定索引的值,索引不存在会报错
查
列表[索引]
list1 = [13, 5, 8] # 获取索引1 对应的值 print(list1[1]) # 5 print(list1[10]) # 索引不存在,程序报错
根据索引取值,索引不存在会报错
len(列表)
list1 = [13, 5, 8] # 获取列表中元素的个数 length = len(list1) print(length) # 3
列表长度(元素个数)
if 值 in 列表:
list1 = [13, 5, 8] # 判断列表中是否包含数据5 if 5 in list1: # 包含数据,返回True; 不包含,返回False print("列表中包含该数据") else: print("列表中不包含该数据") # 输出: 列表中包含该数据
判断列表中是否包含某个值
列表.index(值)
list1 = [13, 5, 8, 5] # 获取第一个5对应的索引 index = list1.index(5) print(index) # 1 index = list1.index(20) # 没有20对应的索引, 程序报错
根据值查询索引,返回 第一个匹配项 的索引,没有查到会报错
列表.count(值)
list1 = [13, 5, 8, 13] # 获取列表中 13 出现的次数 count = list1.count(13) print(count) # 2
值在列表中出现的次数
排序
列表.sort()
list1 = [13, 5, 8] # 升序排列 list1.sort() print(list1) # [5, 8, 13] # 降序排列 list1.sort(reverse=True) print(list1) # [13, 8, 5]
排序
列表.reverse()
list1 = [13, 5, 8] # 将列表中的数据反转排列 list1.reverse() print(list1) # [8, 5, 13]
逆序、反转
range()
# 1. 打印:0、1、2、3、4 # for i in range(5): # [0, 5) for i in range(0, 5): # [0, 5) print(i) # 2. 1~100的累加 # 2.1 定义辅助变量 _sum = 0 # 2.2 for 控制循环范围 for i in range(1, 101): # 2.3 累加 _sum += i # 2.4 在循环外面打印累加结果 print(_sum) # 3. 验证步长,打印:0、2、4 for i in range(0, 5, 2): # [0, 5) print(i)
range(开始位置, 结束位置,步长)
可创建一个整数列表对象,一般用在 for 循环中
元组 tuple()
元组的定义
元组变量 = (元素1, 元素2, ……)
my_tuple = ('mike', 'yoyo', 'lily')
元组不能修改
方法
查
元组[索引]
tuple1 = (13, 5, 8) # 获取索引1 对应的值 print(tuple1[1]) # 5 print(tuple1[10]) # 索引不存在,程序报错
根据索引取值,索引不存在会报错
len(元组)
tuple1 = (13, 5, 8) # 获取元组中元素的个数 length = len(tuple1) print(length) # 3
元组长度(元素个数)
if 值 in 元组:
tuple1 = (13, 5, 8) # 判断元组中是否包含数据5 if 5 in tuple1: # 包含数据,返回True; 不包含,返回False print("元组中包含该数据") else: print("元组中不包含该数据") # 输出: 元组中包含该数据
判断元组中是否包含某个值
元组.index(值)
tuple1 = (13, 5, 8, 5) # 获取第一个5对应的索引 index = tuple1.index(5) print(index) # 1 index = tuple1.index(20) # 没有20对应的索引, 程序报错
根据值查询索引,返回 第一个匹配项 的索引,没有查到会报错
元组.count(值)
tuple1 = (13, 5, 8, 13) # 获取元组中 13 出现的次数 count = tuple1.count(13) print(count) # 2
值在元组中出现的次数
字典 dict()
字典定义
字典变量 = {k1:v1, k2:v2,……}
info = {'name': 'mike', 'age': 34, 'city': 'sz'}
键key是索引,值value是数据,键必须是唯一的
取出元素的值
字典变量[键值]
遍历列表里的字典
for循环
for key,value in name_list:
遍历字典得出键
for循环
for key in dict1:
方法
增
字典[键] = 值
dict1 = {"name": "张三"} # 给字典添加键值对 dict1["age"] = 20 print(dict1) # {"name": "张三", "age": 20}
键不存在,会添加键值对
删
字典.pop(键)
dict1 = {"name": "张三", "age": 20} # 删除键值对 value = dict1.pop("age") print(dict1) # {"name": "张三"} print(value) # 20 # 键不存在, 会报错 dict1.pop("height")
根据键删除键值对,返回被删除的值
del 字典[键]
dict1 = {"name": "张三", "age": 20} # 删除键值对 del dict1["age"] print(dict1) # {"name": "张三"} # 键不存在, 会报错 del dict1["height"]
根据键删除键值对
字典.clear()
dict1 = {"name": "张三", "age": 20} # 清空键值对 dict1.clear() print(dict1) # {}
清空字典
改
字典[键] = 值
dict1 = {"name": "张三"} # 给字典添加键值对 dict1["name"] = "李四" print(dict1) # {"name": "李四"}
键存在,会修改键值对的值
字典.update(字典2)
dict1 = {"name": "张三", "age": 20} # 更新键值对 dict1.update({"age": 22, "height": 1.8}) print(dict1) # {"name": "张三", "age": 22, "height": 1.8}
取出字典2的键值对对字典1操作,键值对不存在,添加键值对;存在则修改值
字典.setdefault(键,数据)
dict1 = {"name": "张三", "age": 20} # 键不存在, 添加键值对 dict1.setdefault("height", 1.8) print(dict1) # {"name": "张三", "age": 20, "height": 1.8} # 键存在, 不做处理 dict1.setdefault("age", 22) print(dict1) # {"name": "张三", "age": 20, "height": 1.8}
键值对不存在,添加键值对;存在则不做处理
查
字典[键]
list1 = [13, 5] dict1 = {"name": "张三"} # 获取键值对的值 print(dict1["name"]) # "张三" # 键不存在, 会报错 print(dict1["height"])
根据键取值,键值对不存在会报错
字典.get(键)
dict1 = {"name": "张三"} # 获取键值对的值 print(dict1["name"]) # "张三" # 键不存在, 返回None print(dict1.get('height')) # None
根据键取值,键值对不存在返回None, 不会报错
for key in 字典
dict1 = {"name": "张三", "age": 20} # 遍历字典 for key in dict1: print(key) # 输出: name age
遍历字典, 获取所有的键
for key in 字典.keys()
dict1 = {"name": "张三", "age": 20} # 遍历字典 for key in dict1.keys(): print(key) # 输出: name age
遍历字典, 获取所有的键
for value in 字典.values()
dict1 = {"name": "张三", "age": 20} # 遍历字典所有的值 for value in dict1.values(): print(value) # 输出: "张三" 20
遍历字典, 获取所有的值
for item in 字典.items()
dict1 = {"name": "张三", "age": 20} # 遍历字典所有的键值对 for key, value in dict1.items(): print(key, value) # 输出: # "name", "张三" # "age", 20
遍历字典, 获取所有的键值对 (键, 值)
for key,value in 字典.items()
遍历字典, 获取所有的键值对 (键, 值)
字符串 str()
字符串的定义
字符串变量 = '字符串内容'
my_str = 'python'
字符串格式化

print('我叫%s, 年龄为%d, 性别为:%s' % (name, age, sex))
%s
字符串
%d
%06d 表示输出的整数显示位数,不足的地方使用 0 补全
%f
浮点数,%.2f 表示小数点后显示两位
print(f'我叫{name}, 年龄为{age}, 性别为:{sex}')
转义字符
\
在行尾时续行符
\\,\',\"
反斜杠符号,单引号,双引号
\a
响铃
\b
退格
\e
转义
\000
空
\n,\r,\f
换行,回车,换页
\v,\t
纵向制表符,横向制表符
\o...,\x...
八进制数,十六进制数
\other
其他字符以普通格式输出
方法
分割
字符串.split(分割符)
str1 = "hello python hello world" # 以空格分割字符串,每段一个元素,返回列表 str_list = str1.split(" ") print(str_list) # ["hello", "python", "hello", "world"]
以分割符拆分字符串, 返回列表
字符串.partition(分割符)
str1 = "zhangsan@163.com" # 以@分割字符串,分成三段式 str_tuple = str1.partition("@") print(str_tuple) # ("zhangsan", "@", "163.com")
返回元组,把字符串分成一个 3 元素的元组 (分割符前面, 分割符, 分割符后面)
拼接
字符串 + 字符串
str1 = "hello" str2 = "python" # 拼接字符串 str3 = str1 + str2 print(str3) # "hellopython"
拼接两个字符串
字符串.join(字符串列表)
str1 = "," name_list = ["beijing", "shenzhen", "shanghai", "guangzhou"] # 以字符串为连接符拼接字符串列表的元素,生成新的字符串 str2 = str1.join(name_list) print(str2) # "beijing,shenzhen,shanghai,guangzhou"
以字符串来连接字符串列表中每个元素,合并为一个新的字符串
替换
字符串.replace(原内容, 新内容, 替换次数)
str1 = "hello python" # 对内容进行替换, 返回新字符串 new_str = str1.replace("py", "Py") print(new_str) # hello Python
返回一个替换了原内容的新字符串,可以指定替换次数
查
字符串.find(目标字符串, 开始索引, 结束索引)
str1 = "hello python" # 查找"py"对应的索引(能找到的话, 返回"p"对应的索引) index = str1.find("py") print(index) # 6 # 查找"88"对应的索引, 不存在, 返回-1 index = str1.find("88") print(index) # -1
在指定范围内, 查询目标字符串的索引, 不存在返回-1
字符串.rfind(目标字符串, 开始索引, 结束索引)
str1 = "hello python" # 查找"o"对应的索引(能找到的话, 返回"o"对应的索引, 从结尾处开始查找) index = str1.rfind("o") print(index) # 10 # 查找"88"对应的索引, 不存在, 返回-1 index = str1.rfind("88") print(index) # -1
在指定范围内, 查询目标字符串的索引, 不存在返回-1, 从结尾处开始查找
字符串.index(目标字符串, 开始索引, 结束索引)
str1 = "hello python" # 查找"py"对应的索引(能找到的话, 返回"p"对应的索引) index = str1.index("py") print(index) # 6 # 查找"88"对应的索引, 不存在, 会报错 index = str1.index("88") print(index) # 报错
在指定范围内, 查询目标字符串的索引, 不存在会报错
判断
字符串.isalpha()
str1 = "hello python" # 判断字符串是否都为字母 if str1.isalpha(): print("都是字母") # 输出 else: print("不都是字母")
如果 string 至少有一个字符并且所有字符都是字母则返回 True
字符串.isdecimal()
str1 = "666" # 判断字符串是否都是数字 if str1.isdecimal(): print("都是数字") # 输出 else: print("不都是数字")
如果 string 只包含数字则返回 True
字符串.islower()
str1 = "hello" # 判断字符串中的字母是否都是小写 if str1.islower(): print("字母都是小写") # 输出 else: print("字母不都是小写")
如果 string 中包含至少一个区分大小写的字符,并且所有这些(区分大小写的)字符都是小写,则返回 True
字符串.isupper()
str1 = "HELLO" # 判断字符串中的字母是否都是大写 if str1.isupper(): print("字母都是大写") # 输出 else: print("字母不都是大写")
如果 string 中包含至少一个区分大小写的字符,并且所有这些(区分大小写的)字符都是大写,则返回 True
字符串.startswith(目标字符串)
str1 = "hello" # 判断字符串是否以目标字符串开头 if str1.startswith("he"): print("以he开头") # 输出 else: print("不以he开头")
检查字符串是否是以 目标字符串 开头,是则返回 True
字符串.endswith(目标字符串)
str1 = "hello" # 判断字符串是否以目标字符串结尾 if str1.endswith("lo"): print("以lo结尾") # 输出 else: print("不以lo结尾")
检查字符串是否是以 目标字符串 结尾,是则返回 True
大小写转换
字符串.lower()
str1 = "HELLO" # 转换大写字母为小写 str2 = str1.lower() print(str2) # hello
返回新字符串,转换 字符串 中所有大写字符为小写
字符串.upper()
str1 = "hello" # 转换小写字母为大写 str2 = str1.upper() print(str2) # HELLO
返回新字符串,转换 字符串 中所有大写字符为大写
文本对齐
字符串.center()
str1 = "hello" # 设置居中 str2 = str1.center(11, "-") print(str2) # ---hello---
按照指定宽度返回新字符串,并基于原字符串居中,可设置两端空白位置的填充字符
字符串.ljust()
str1 = "hello" # 设置左对齐 str2 = str1.ljust(11, "-") print(str2) # hello------
按照指定宽度返回新字符串,并基于原字符串左对齐,可设置两端空白位置的填充字符
字符串.rjust()
str1 = "hello" # 设置右对齐 str2 = str1.rjust(11, "-") print(str2) # ------hello
按照指定宽度返回新字符串,并基于原字符串右对齐,可设置两端空白位置的填充字符
去除两端字符
字符串.strip(目标字符串)
str1 = "--hello---" # 去除两端目标字符串 str2 = str1.strip("-") print(str2) # hello
返回新字符串,去除 字符串 左右两边的目标字符串, 不设置目标字符串则去除空格
字符串.lstrip(目标字符串)
str1 = "--hello---" # 去除左边目标字符串 str2 = str1.lstrip("-") print(str2) # hello---
返回新字符串,去除 字符串 左边的目标字符串, 不设置目标字符串则去除空格
字符串.rstrip(目标字符串)
str1 = "--hello---" # 去除右边目标字符串 str2 = str1.rstrip("-") print(str2) # --hello
返回新字符串,去除 字符串 右边的目标字符串, 不设置目标字符串则去除空格
集合 set()
集合的定义
元素不会重复,{元素1,元素2……}
my_set = {1, 2, 1, 2, 1, 2} print(my_set) # {1, 2}
通过集合完成对列表的去重
# 通过集合完成对列表去重功能 name_list = ['mike', 'yoyo', 'mike'] # set(name_list):给列表转换为set类型,即可完成去重功能 temp_set = set(name_list) print(temp_set) # {'mike', 'yoyo'}
frozenset()
返回一个不可修改的集合(可哈希)
操作
a in set
元素是否属于集合
a==b
两个集合是否完全一样
a!=b
两个集合是否不一样
a<b
a.issubset(b)
a是否是b的子集
a.issuperset(b)
a是否是b的超集
a|b
a.union(b)
a,b的并集,返回新的对象
a&b
a and b
a.intersection(b)
a,b的交集,共有的元素
a-b
a.difference(b)
a相对b的差,a有b没有
a.symmetric_difference(b)
a,b的对称差集,去除ab共有的余下所有