Python3基础知识
学习路线主要参照:菜鸟教程 和 Python教程2020版
一.Python简介
1. What is Python?
Python 是一个高层次的结合了解释性、编译性、互动性和面向对象的脚本语言。
Python 的设计具有很强的可读性,相比其他语言经常使用英文关键字,其他语言的一些标点符号,它具有比其他语言更有特色语法结构。
- Python 是一种解释型语言: 这意味着开发过程中没有了编译这个环节。类似于PHP和Perl语言。
- Python 是交互式语言: 这意味着,您可以在一个 Python 提示符 >>> 后直接执行代码。
- Python 是面向对象语言: 这意味着Python支持面向对象的风格或代码封装在对象的编程技术。
- Python 是初学者的语言:Python 对初级程序员而言,是一种伟大的语言,它支持广泛的应用程序开发,从简单的文字处理到 WWW 浏览器再到游戏。
2. Python 发展历史
- Python 是由 Guido van Rossum 在八十年代末和九十年代初,在荷兰国家数学和计算机科学研究所设计出来的。
- Python 本身也是由诸多其他语言发展而来的,这包括 ABC、Modula-3、C、C++、Algol-68、SmallTalk、Unix shell 和其他的脚本语言等等。像 Perl 语言一样,Python 源代码同样遵循 GPL(GNU General Public License)协议。
- 现在 Python 是由一个核心开发团队在维护,Guido van Rossum 仍然占据着至关重要的作用,指导其进展。
- Python 2.0 于 2000 年 10 月 16 日发布,增加了实现完整的垃圾回收,并且支持 Unicode。
- Python 3.0 于 2008 年 12 月 3 日发布,此版不完全兼容之前的 Python 源代码。不过,很多新特性后来也被移植到旧的Python 2.6/2.7版本。
- Python 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。
- Python 2.7 被确定为最后一个 Python 2.x 版本,它除了支持 Python 2.x 语法外,还支持部分 Python 3.1 语法。
3. Python 特点
-
易于学习:Python有相对较少的关键字,结构简单,和一个明确定义的语法,学习起来更加简单。
-
易于阅读:Python代码定义的更清晰。
-
易于维护:Python的成功在于它的源代码是相当容易维护的。
-
一个广泛的标准库:Python的最大的优势之一是丰富的库,跨平台的,在UNIX,Windows和Macintosh兼容很好。
-
互动模式:互动模式的支持,您可以从终端输入执行代码并获得结果的语言,互动的测试和调试代码片断。
-
可移植:基于其开放源代码的特性,Python已经被移植(也就是使其工作)到许多平台。
-
可扩展:如果你需要一段运行很快的关键代码,或者是想要编写一些不愿开放的算法,你可以使用C或C++完成那部分程序,然后从你的Python程序中调用。
-
数据库:Python提供所有主要的商业数据库的接口。
-
GUI编程:Python支持GUI可以创建和移植到许多系统调用。
-
可嵌入: 你可以将Python嵌入到C/C++程序,让你的程序的用户获得"脚本化"的能力。
4. Python的优缺点
优点:
-
简单
Python 是一种代表简单主义思想的语言。阅读一个良好的 Python 程序就感觉像是在读英语一样,尽管这个英语的要求非常严格!Python 的这种伪代码本质是它最大的优点之一。它使你能够专注于解决问题而不是去搞明白语言本身。
-
易学
就如同你即将看到的一样,Python 极其容易上手。前面已经提到了,Python 有极其简单的语法。
-
免费、开源
Python 是 FLOSS(自由/开放源码软件)之一。简单地说,你可以自由地发布这个软件的拷贝、阅读它的源代码、对它做改动、把它的一部分用于新的自由软件中。FLOSS 是基于一个团体分享知识的概念。这是为什么 Python 如此优秀的原因之一——它是由一群希望看到一个更加优秀的 Python 的人创造并经常改进着的。
-
高层语言
当你用 Python 语言编写程序的时候,你无需考虑诸如如何管理你的程序使用的内存一类的底层细节。
-
可移植性
-
由于它的开源本质,Python 已经被移植在许多平台上(经过改动使它能够工作在不同平台上)。如果你小心地避免使用依赖于系统的特性,那么你的所有 Python 程序无需修改就可以在下述任何平台上面运行。这些平台包括 Linux、Windows、FreeBSD、Macintosh、Solaris、OS/2、Amiga、AROS、AS/400、BeOS、OS/390、z/OS、Palm OS、QNX、VMS、Psion、Acom RISC OS、VxWorks、PlayStation、Sharp Zaurus、Windows CE 甚至还有 PocketPC、Symbian 以及 Google 基于 Linux 开发的 Android 平台!
-
解释性
这一点需要一些解释。一个用编译性语言比如 C 或 C++ 写的程序可以从源文件(即 C 或 C++ 语言)转换到一个你的计算机使用的语言(二进制代码,即0和1)。这个过程通过编译器和不同的标记、选项完成。当你运行你的程序的时候,连接/转载器软件把你的程序从硬盘复制到内存中并且运行。而 Python 语言写的程序不需要编译成二进制代码。你可以直接从源代码运行程序。在计算机内部,Python 解释器把源代码转换成称为字节码的中间形式,然后再把它翻译成计算机使用的机器语言并运行。事实上,由于你不再需要担心如何编译程序,如何确保连接转载正确的库等等,所有这一切使得使用 Python 更加简单。由于你只需要把你的 Python 程序拷贝到另外一台计算机上,它就可以工作了,这也使得你的 Python 程序更加易于移植。
-
面向对象
Python 既支持面向过程的编程也支持面向对象的编程。在“面向过程”的语言中,程序是由过程或仅仅是可重用代码的函数构建起来的。在“面向对象”的语言中,程序是由数据和功能组合而成的对象构建起来的。与其他主要的语言如 C++ 和 Java 相比,Python 以一种非常强大又简单的方式实现面向对象编程。
-
可扩展性
如果你需要你的一段关键代码运行得更快或者希望某些算法不公开,你可以把你的部分程序用 C 或 C++ 编写,然后在你的 Python 程序中使用它们。
-
丰富的库
Python 标准库确实很庞大。它可以帮助你处理各种工作,包括正则表达式、文档生成、单元测试、线程、数据库、网页浏览器、CGI、FTP、电子邮件、XML、XML-RPC、HTML、WAV 文件、密码系统、GUI(图形用户界面)、Tk 和其他与系统有关的操作。记住,只要安装了 Python,所有这些功能都是可用的。这被称作 Python 的“功能齐全”理念。除了标准库以外,还有许多其他高质量的库,如 wxPython、Twisted 和 Python 图像库等等。
-
规范的代码
Python 采用强制缩进的方式使得代码具有极佳的可读性。
缺点:
- 运行速度,有速度要求的话,用 C++ 改写关键部分吧。
- 国内市场较小(国内以 Python 来做主要开发的,目前只有一些 web2.0 公司)。但时间推移,目前很多国内软件公司,尤其是游戏公司,也开始规模使用他。
- 中文资料匮乏(好的 Python 中文资料屈指可数,现在应该变多了)。托社区的福,有几本优秀的教材已经被翻译了,但入门级教材多,高级内容还是只能看英语版。
- 构架选择太多(没有像 C# 这样的官方 .net 构架,也没有像 ruby 由于历史较短,构架开发的相对集中。Ruby on Rails 构架开发中小型web程序天下无敌)。不过这也从另一个侧面说明,python比较优秀,吸引的人才多,项目也多。
5. Python应用
Python 的主要运用领域有:
- 云计算:云计算最热的语言,典型的应用OpenStack
- WEB开发:许多优秀的WEB框架,许多大型网站是Python开发、YouTube、Dropbox、Douban……典型的Web框架包括Django
- 科学计算和人工智能:典型的图书馆NumPy、SciPy、Matplotlib、Enided图书馆、熊猫
- 系统操作和维护:操作和维护人员的基本语言
- 金融:定量交易、金融分析,在金融工程领域,Python不仅使用最多,而且使用最多,其重要性逐年增加。
- 图形 GUI:PyQT,WXPython,TkInter
Python 在一些公司的运用有:
- 谷歌:谷歌应用程序引擎,代码。Google.com、 Google 爬虫、Google 广告和其他项目正在广泛使用 Python。
- CIA:美国中情局网站是用 Python 开发的。
- NASA:美国航天局广泛使用 Python 进行数据分析和计算。
- YouTube:世界上最大的视频网站 YouTube 是用 Python 开发的。
- Dropbox:美国最大的在线云存储网站,全部用 Python 实现,每天处理 10 亿的文件上传和下载。
- Instagram:美国最大的照片共享社交网站,每天有 3000 多万张照片被共享,所有这些都是用 Python 开发的。
- Facebook:大量的基本库是通过 Python 实现的
- Red Hat/Centos:世界上最流行的 Linux 发行版中的 Yum 包管理工具是用 Python 开发的
- Douban:几乎所有公司的业务都是通过 Python 开发的。
- 知乎:中国最大的 Q&A 社区,通过 Python 开发(国外 Quora)
除此之外,还有搜狐、金山、腾讯、盛大、网易、百度、阿里、淘宝、土豆、新浪、果壳等公司正在使用 Python 来完成各种任务。
二.Python3 开发环境搭建
1.Windows部署Python3
https://www.runoob.com/python3/python3-install.html
2.Centos7部署python3
https://blog.csdn.net/Yanxu_Jin/article/details/109204959
三.编程基础
1. 常量
程序在运行的过程中,值永远不会发生改变的量称之为常量,python没有专门的常量类型,一般约定使用大写表示常量。
# 圆周率
PI = 3.1415926
# 我的生日
MY_BIRTHDAY = '2008/2/29'
2. 变量
在程序中,我们一般以变量表示数据,所谓变量:
- 变量是用来保存数据的
- 在程序中,变量的值是可以改变的,所以叫变量,不能改变的叫常量
- Python中变量就是变量,它没有类型,所以创建变量时不需要声明数据类型。每个变量在使用前都必须赋值,变量赋值以后该变量才会被创建,我们所说的"类型"是变量所指的内存中对象的类型。
2.1 变量的定义
等号=
用来给变量赋值。等号=
运算符左边是一个变量名,等号(=)运算符右边是存储在变量中的值。例如:
counter = 100 # 整型变量
miles = 1000.0 # 浮点型变量
name = "runoob" # 字符串
print (counter)
print (miles)
print (name)
''' Python允许你同时为多个变量赋值。例如:'''
a = b = c = 1
''' 也可以为多个对象指定多个变量。例如:'''
a, b, c = 1, 2, "runoob"
2.2 变量的命名规范
变量名也可称之为标识符(变量名、函数名、类名、包名等统称为标识符),其命名要符合python的语法要求:
- 由数字、字母、下划线组成,不能以数字开头
- 严格区分大小写
- 不能是python的关键字(保留字)
# Python 的标准库提供了一个 keyword 模块,可以输出当前版本的所有关键字:
>>> import keyword
>>> keyword.kwlist
['False', 'None', 'True', 'and', 'as', 'assert', 'break', 'class', 'continue', 'def', 'del', 'elif', 'else', 'except', 'finally', 'for', 'from', 'global', 'if', 'import', 'in', 'is', 'lambda', 'nonlocal', 'not', 'or', 'pass', 'raise', 'return', 'try', 'while', 'with', 'yield']
变量命名风格:
- 见名知意,有自描述性
- 不建议使用中文命名变量
- 一般变量可以用小驼峰规则:变量名由多个单词组成,第一个单词首字母小写,其它单词首字母大写
- 也可以全部变量名都小写,单词之间用下划线分隔
小驼峰命名:
myBook yourMoney
下划线分隔:
my_book your_money
2.3 变量输入和输出
执行下面的程序在按回车键后就会等待用户输入:
a = input("\n\n按下 enter 键后退出。")
print("获取到a的值为:" + a)
print 默认输出是换行的,如果要实现不换行需要在变量末尾加上 end="":
x="a"
y="b"
# 换行输出
print( x )
print( y )
print('---------')
# 不换行输出
print( x, end=" " )
print( y, end=" " )
print()
2.4 变量删除
删除后变量就不存在了,不能够在通过变量名进行存取了,语法:
del 变量名
2.5 变量和内存
python中一切都是对象,python中变量保存了对象的引用,变量的好比是一个容器,容器中保存的变量所指对象的引用(地址);变量本身是没有类型的,变量的类型是指其所指对象的类型,比如说变量是一个瓶子,盛了醋就是醋瓶,盛了酱油就是酱油瓶
- python中变量的所指对象的地址可以用id函数获取
- 获取变量的类型可以使用type函数
num1 = 10
print(id(num1)) # 查看变量所指对象的地址
print(type(num1)) # 查看变量的类型
2.6 变量的作用域
全局变量:声明在函数外层是全局的,所有函数都可以访问。
局部变量:函数内部声明的变量,局部变量仅限于在函数内部使用。
如果全局变量是可变的,在函数中修改的时候就不需要添加global。
3. 注释
# 这是一个单行注释
print("Hello, World!")
'''
这是多行注释,用三个单引号
'''
print("Hello, World!")
"""
这是多行注释,用三个双引号
"""
print("Hello, World!")
# 以下实例我们可以输出函数的注释:
def a():
'''这是文档字符串'''
pass
print(a.__doc__)
# 三个双引号赋值给字符串变量时,表示一种字符串的特殊写法。
str="""
create table user(
id int ,
name varchar(20)
) COMMENT '注册用户表'"""
print(str)
4. 编码规范
4.1 行与缩进
python最具特色的就是使用缩进来表示代码块,不需要使用大括号 {} 。
缩进的空格数是可变的,但是同一个代码块的语句必须包含相同的缩进空格数。实例如下:
if True:
print ("Answer")
print ("True")
else:
print ("Answer")
print ("False") # 缩进不一致,会导致运行错误
以上程序由于缩进不一致,执行后会报错:
IndentationError: unindent does not match any outer indentation level
。
4.2 多行语句
Python 通常是一行写完一条语句,但如果语句很长,我们可以使用反斜杠来实现多行语句,例如:
total = item_one + \
item_two + \
item_three
在 [], {}, 或 () 中的多行语句,不需要使用反斜杠(),例如:
total = ['item_one', 'item_two', 'item_three',
'item_four', 'item_five']
4.3 同一行写多条语句
Python可以在同一行中使用多条语句,语句之间使用分号;
分割。
import sys; x = 'runoob'; sys.stdout.write(x + '\n')
4. 命令行参数
很多程序可以执行一些操作来查看一些基本信息,Python可以使用-h参数查看各参数帮助信息:
python -h
usage: python [option] ... [-c cmd | -m mod | file | -] [arg] ...
Options and arguments (and corresponding environment variables):
-c cmd : program passed in as string (terminates option list)
-d : debug output from parser (also PYTHONDEBUG=x)
-E : ignore environment variables (such as PYTHONPATH)
-h : print this help message and exit
四.数据类型
1. 数据类型介绍
python中数据类型可以分为:
- 内置基本数据类型:
- Number:数字类型(整型int,浮点型float,布尔型bool,复数complex)
- str:字符串
- list:列表
- tuple:元组
- dict:字典
- set:集合
- None:空值,表示变量没有确定的值
- 自定义类型
- class :类
Python3 的六个标准数据类型中:
- 不可变数据(3 个):Number(数字)、String(字符串)、Tuple(元组);
- 可变数据(3 个):List(列表)、Dictionary(字典)、Set(集合)。
序列:
在python中,有这样一些类型,它们的成员是有序排列的,并且可以通过下标访问成员,这些类型称之为序列,包括:列表、range、元组和字符串;其中列表的成员可修改,属于可变序列,字符串和元组,成员不可修改,属于不可变序列。
2. 数据类型判断
'''
内置的 type() 函数可以用来查询变量所指的对象类型,type()不会认为子类是一种父类类型。
还可以用 isinstance 来判断变量是否是某个数据类型,isinstance()会认为子类是一种父类类型。
'''
a, b, c, d = 20, 5.5, True, 4+3j
print(type(a), type(b), type(c), type(d))
a = 111
print(isinstance(a, int))
class A:
pass
class B(A):
pass
objA = A()
objB = B()
# 输出否
if type(objB) is A:
print('是')
else:
print('否')
print(isinstance(objB, A)) # True
# 可以使用del语句删除一些对象引用。
del a
# print(a) # 会报错NameError: name 'a' is not defined
注意:在 Python2 中是没有布尔型的,它用数字 0 表示 False,用 1 表示 True。到 Python3 中,把 True 和 False 定义成关键字了,但它们的值还是 1 和 0,它们可以和数字相加。
3. 数据类型转换
python是一种强类型语言:要求运算符两边的操作数必须是同一个类型的,否则必须转换。
函数名 | 函数值 |
---|---|
int(x,[基数]) | 将数字或字符串转换为整数,如果x为浮点数,则自动截断小数部分 |
float(x) | 将x转换成浮点型 |
str(x) | 将x转换成字符串,适合人阅读 |
bool(x) | 转换成bool类型 的True或 False |
repr(x) | 返回一个对象的String格式,适合机器执行 |
eval(str) | 执行一个字符串表达式,返回计算的结果 |
tuple(seq) | 参数可以是元组、列表或者字典,为字典时,返回字典的key组成的集合 |
list(s) | 将序列转变成一个列表,参数可为元组、字典、列表,为字典时,返回字典的key组成的集合 |
set(s) | 将一个可以迭代对象转变为可变集合,并且去重复,返回结果可以用来计算差集x - y、并集x | y、交集x & y |
chr(x) | 输入一个ASCII码(0-255),返回一个对应的字符。返回值是当前整数对应的ascii字符。 |
ord(x) | 返回一个字符所对应的码值 |
4. Number(数字)
Python3 支持 int、float、bool、complex(复数)。在Python 3里,只有一种整数类型 int,表示为长整型,没有 python2 中的 long。
布尔值:在python中,能够解释为假的值有:None、0、0.0、False、所有的空容器(空列表、空元组、空字典、空集合、空字符串),其它是真。
5. String(字符串)
Python中的字符串用单引号'
或双引号"
括起来,使用三引号('''
或"""
)可以指定一个多行字符串。常见注意事项
- Python 没有单独的字符类型,一个字符就是长度为1的字符串
- 按字面意义级联字符串,如
"this " "is " "string"
会被自动转换为"this is string"
。 - 换行符:
\n
,转义字符:反斜杠\
,如果你不想让反斜杠发生转义,可以在字符串前面添加一个r
,表示原始字符串 - Python中的字符串是不可变的数据类型。
- 字符串有两种索引方式,从左往右以0开始,从右往左以-1开始
- 字符串的连接符
+
- 星号
*
表示复制当前字符串,与之结合的数字为复制的次数 - 字符串的截取的语法格式如下:变量[头下标:尾下标:步长]
print('字符串')
print("这是一个句子。")
para_str = """这是一个多行字符串的实例
多行字符串可以使用制表符
TAB ( \t )。
可以使用回车键和换行符 [ \n ]实现换行效果。
"""
print(para_str)
print(r"create table \n user")
a = '0123456789'
print(a[1:5:2])
print("this " "is " "string")
print("Hel" + "lo")
print('word' * 2) # 输出字符串两次,也可以写成 print (2 * 'word')
5.1 字符串转义
在需要在字符中使用特殊字符时,python用反斜杠()转义字符。如下表:
转义字符 | 描述 |
---|---|
(在行尾时) | 续行符 |
\ | 反斜杠符号 |
’ | 单引号 |
" | 双引号 |
\a | 响铃 |
\b | 退格(Backspace) |
\000 | 空 |
\n | 换行 |
\v | 纵向制表符 |
\t | 横向制表符 |
\r | 回车 |
\f | 换页 |
\oyy | 八进制数,yy 代表的字符,例如:\o12 代表换行,其中 o 是字母,不是数字 0。 |
\xyy | 十六进制数,yy代表的字符,例如:\x0a代表换行 |
\other | 其它的字符以普通格式输出 |
5.2 字符串运算符
下表实例变量 a 值为字符串 “Hello”,b 变量值为 “Python”:
操作符 | 描述 | 实例 |
---|---|---|
+ | 字符串连接 | a + b 输出结果: HelloPython |
* | 重复输出字符串 | a*2 输出结果:HelloHello |
[] | 通过索引获取字符串中字符 | a[1] 输出结果:e |
[ : ] | 截取字符串中的一部分,遵循左闭右开原则,str[0:2] 是不包含第 3 个字符的 | a[1:4] 输出结果:ell |
in | 成员运算符 - 如果字符串中包含给定的字符返回 True | 'H' in a 输出结果:True |
not in | 成员运算符 - 如果字符串中不包含给定的字符返回 True | 'M' not in a 输出结果:True |
r/R | 反转义,转义字符\ 不会生效 | print( r'\n' ) print( R'\n' ) |
% | 格式字符串 | 见下一节内容 |
5.3 字符串格式化
Python的字符串格式化有两种方式:%格式符方式,format方式。
print('{}今年{}岁'.format('小明', 18))
print("我叫 %s %s,今年%d岁!" % ('小', '明明', 10))
print("姓名:%(name)s,年龄:%(age)d " % {'name': 'xx', 'age': 20})
# name占10位,+,右对齐,age占10位,-,左对齐
print("%(name)+10s————————%(age)-10d————————"%{'name':'xx','age':20})
# 0,用0填充空白处
print("------%(year) d******%(age)010d "%{'year':2016,'age':-20})
# .precision 可选,小数点后保留的位数
print('--------%(p).2f'%{'p':1.23456})
print('--------%(p)f'%{'p':1.23456})
# https://www.cnblogs.com/songdanlee/p/11105807.html
拓展:f-string
f-string 是 python3.6 之后版本添加的,称之为字面量格式化字符串,是新的格式化字符串的语法。之前我们习惯用百分号%
,用了这种方式明显更简单了,不用再去判断使用 %s,还是 %d。
name = 'Runoob'
# 替换变量
print(f'Hello {name}') # 替换变量
# 使用表达式
print(f'{1+2}')
w = {'name': 'Runoob', 'url': 'www.runoob.com'}
print(f'{w["name"]}: {w["url"]}')
5.4 字节与字符串
在python3中最重要的特性是对文本和二进制数据做了更加清晰的区分,默认情况下,Python 3 源码文件以 UTF-8 编码,所有字符串(文本)都是 unicode 字符串,而二进制数据则由byte类型表示,python3不会以任意隐式方式混用字节型和字符型,也因此在python3中不能拼接字符串和字节包(python2中可以,会自动进行转换),也不能在字节包中搜索字符串,也不能将字符串传入参数为字节包的函数。
Bytes 对象是由单个字节作为基本元素(8位,取值范围 0-255)组成的序列,为不可变对象。 bytes对象只负责以二进制字节序列的形式记录所需记录的对象,至于该对象到底表示什么(比如到底是什么字符)则由相应的编码格式解码所决定。Python3中,bytes通常用于网络数据传输、二进制图片和文件的保存等等。可以通过调用bytes()生成bytes实例,其值形式为 b'xxxxx'
,其中 ‘xxxxx’ 为一至多个转义的十六进制字符串(单个 x 的形式为:\x12,其中\x为小写的十六进制转义字符,12为二位十六进制数)组成的序列,每个十六进制数代表一个字节(八位二进制数,取值范围0-255),对于同一个字符串如果采用不同的编码方式生成bytes对象,就会形成不同的值.
创建字节
# 创建字节
b1 = b'hello'
b2 = b"ello"
b3 = b'''hello'''
b4 = bytes('中文', 'utf-8')
print(type(b1), type(b2), type(b3), type(b4))
设置python文件编码格式
# -*- coding: utf-8 -*-
字符串和字节的转换
# 字符串转字节
s1 = "中文"
s2 = s1.encode('utf-8') # str.encode()
print(type(s2)) # <class 'bytes'>
print(s2) # b'\xe4\xb8\xad\xe6\x96\x87'
# 字节转字符串
s3 = s2.decode('utf-8') # bytes.decode()
print(type(s3)) # <class 'str'>
print(s3) # 中文
5.5 字符串函数
要掌握的函数
# todo 返回对象(字符、列表、元组等)长度或项目个数:len(s)
str = "runoob"
print(len(str))
l = [1, 2, 3, 4, 5]
print(len(l))
# 字符串截取
a = '0123456789'
print(a[1:5:2])
# todo 检测字符串中是否包含子字符串 str,返回的是索引值在字符串中的索引值,如果不包含索引值,返回-1:find(str, beg=0, end=len(string))
str = "Runoob example....woow!!!"
print(str.find("exam")) # todo rfind() 查最后一次出现的位置
print(str.find("exam", 10, str.__len__()))
# todo 检测字符串中是否包含子字符串 str,与find()方法一样,只不过如果str不在 string中会报一个异常:index(str, beg=0, end=len(string))
print(str.index("oo")) # todo rindex() 查最后一次出现的位置
# print(str1.index(str2, 10,str1.__len__())) # 报错内容:ValueError: substring not found
# todo 检测字符串是否只由数字组成:isdigit()
print("123456".isdigit())
# todo 方法把字符串中的 old(旧字符串) 替换成 new(新字符串),如果指定第三个参数max,则替换不超过 max 次:replace(old, new [, max])
print("www.w3cschool.cc".replace("w3cschool.cc", "runoob.com"))
str = "this is string example....wow!!!"
print(str.replace("is", "was", 1))
# todo 通过指定分隔符对字符串进行切割:split()
str = "this is string example....wow!!!"
print(str.split()) # 以空格为分隔符
print(str.split('i', 1)) # 以 i 为分隔符,仅分隔一次
print(str.split('w')) # 以 w 为分隔符
# todo 按照行('\r', '\r\n', \n')分隔,返回一个包含各行作为元素的列表:splitlines()
print('ab c\n\nde fg\rkl\r\n'.splitlines())
print('ab c\n\nde fg\rkl\r\n'.splitlines(True)) # 参数 keepends 默认为 False,不包含换行符,如果为 True,则保留换行符
# todo 移除字符串头尾指定的字符(默认为空格)或字符序列:strip([chars])
# todo 相似的还有 lstrip([chars]) 、 rstrip([chars])
print(' a aa '.strip()) # 指定字符串 *
str = "*****this is **string** example....wow!!!*****"
print(str.strip('*')) # 指定字符串 *
# todo 转换字符串中所有大写字符为小写:lower()
print("Runoob EXAMPLE....WOW!!!".lower())
# todo 转换字符串中所有小写字符为大写:upper()
print("fakhfakfha".upper())
# todo 判断字符串是否以指定前缀开始:startswith(suffix, beg=0, end=len(string))
Str = 'Runoob example....wow!!!'
print(Str.startswith('Ru'))
print(Str.startswith('no', 2))
# todo 判断字符串是否以指定后缀结尾:endswith(suffix, beg=0, end=len(string))
print(Str.endswith('!!'))
print(Str.endswith('w!', 0, 22))
# todo 统计字符串里某个字符出现的次数:count(str, beg= 0,end=len(string))
str = "www.runoob.com"
sub = 'o'
print("str.count('o') : ", str.count(sub))
print("str.count('run', 0, 10) : ", str.count(sub, 0, 10)) # 可选参数限制字符串的开始与结束位置
# todo 检测字符串是否由小写字母组成:islower()
print("Aabch".islower())
# todo 检测字符串是否由大写字母组成:isupper()
print("ADHSJ".isupper())
# todo 检测字符串是否只由空白字符组成:isspace()
str = " "
print(str.isspace())
# todo 以指定的编码格式解码 bytes 对象。默认编码为 'utf-8':bytes.decode(encoding="utf-8", errors="strict")
str = "菜鸟教程";
str_utf8 = str.encode("UTF-8")
str_gbk = str.encode("GBK")
print(str)
print("UTF-8 编码:", str_utf8)
print("GBK 编码:", str_gbk)
print("UTF-8 解码:", str_utf8.decode('UTF-8', 'strict'))
print("GBK 解码:", str_gbk.decode('GBK', 'strict'))
要了解的函数
# 将序列中的元素以指定的字符连接生成一个新的字符串:join()
s1 = "--"
s2 = ""
seq = ("r", "u", "n", "o", "o", "b") # 字符串序列
print(s1.join(seq))
print(s2.join(seq))
# 检测字符串是否由字母和数字组成:isalnum()
str = "runoob2016" # 字符串没有空格
print(str.isalnum())
str = "www.runoob.com"
print(str.isalnum())
# 检测字符串是否只由字母或文字组成:isalpha()
print("runoob菜鸟教程".isalpha()) # 字母和中文文字
print("Runoob example....wow!!!".isalpha()) # false
# 对字符串的大小写字母进行相互转换:swapcase()
print("aaaBBBcccDDD".swapcase())
# 将字符串的第一个字母变成大写,其他字母变小写:capitalize()
str = "this is string example from runoob....wow!!!"
print("str.capitalize() : ", str.capitalize())
# 返回一个指定的宽度 width 居中的字符串,fillchar 为填充的字符,默认为空格:center(width, fillchar)
str = "[runoob]"
print("str.center(40, '*') : ", str.center(40, '*'))
# 返回一个原字符串左对齐,并使用空格填充至指定长度的新字符串。如果指定的长度小于原字符串的长度则返回原字符串:ljust()
str = "Runoob example....wow!!!"
print(str.ljust(50, '*')) # ljust()是右对齐
# 返回"标题化"的字符串,就是说所有单词的首个字母转化为大写,其余字母均为小写:title()
str = "this is string example from runoob....wow!!!"
print(str.title())
# 把字符串中的 tab 符号 \t 转为空格,tab 符号 \t 默认的空格数是8:expandtabs(tabsize=8)
# 在第 0、8、16...等处给出制表符位置,如果当前位置到开始位置或上一个制表符位置的字符数不足 8 的倍数则以空格代替
str = "runoob\t12345\tabc"
print('原始字符串:', str)
print('替换 \\t 符号:', str.expandtabs())
print('使用 2 个空格替换 \\t 符号:', str.expandtabs(2))
6. 列表
引出:存储一个数据可以采用变量
问题:需要同时存储多个数据,该怎么做?
#需求:有5个人的年龄,求平均年龄
age1 = 10
age2 = 32
age3 = 43
age4 = 18
age5 = 90
average = (age1 + age2 + age3 + age4 + age5) / 5
解决:容器【Python提供了一种数据结构list,可以同时存储多个数据】
本质:一种有序的集合
List(列表) 是 Python 中使用最频繁的数据类型,列表的特点:
- 列表中的元素是可以改变的。
- 列表可以完成大多数集合类的数据结构实现:列表中元素的类型可以不相同,它支持数字,字符串甚至可以包含列表(二维列表)。
- 列表是写在方括号
[]
之间、用逗号分隔开的元素列表。 - 列表可以被索引:由左往右索引
0,1,...,len()-1
从0开始;也可从左往右索引-len(),...,-2,-1
。 - 列表被截取后返回一个包含所需元素的新列表。
- 加号
+
是列表连接运算符,星号*
是重复操作。 - 可以用[列表推导式](#6.1 列表推导式)生成列表。
# 创建列表
myList = ['abcd', 786, 2.23, 'runoob', 70.2, "aaa"]
print(myList) # 输出完整列表
print(len(myList)) # 输出列表长度
# 访问列表中的元素
print(myList[0]) # 输出列表第一个元素
print(myList[-1]) # 输出列表最后一个元素
# 向列表中添加元素
myList.append("ccc")
# 将指定对象插入列表的指定位置
myList.insert(3, "eee")
print(myList)
# 修改列表中的元素
myList[0] = 'bbbb'
print(myList)
# 删除列表中的元素
secList = [111, 222, 333, 444, 555]
del secList[1]
print(secList)
# 移除列表中的一个元素(默认最后一个元素),并且返回该元素的值
print(secList.pop(3))
print(secList)
# 移除列表中某个值的第一个匹配项
print(secList.remove(444)) # 无返回值
print(secList)
# 判断元素是否在列表内
print("aaa" in myList)
# 统计某个元素在列表中出现的次数
print('字符串aaa在mylist列表中出现的次数为:', myList.count('aaa'))
# 从列表中找出某个值第一次出现的索引位置,可以从指定的索引出查找
print(myList.index("ccc"))
# 遍历列表
i = 0
for x in myList:
print('myList[%d]的值为:' % (i), x)
i += 1
# 截取列表
print(myList[1:3]) # 从第二个开始输出到第三个元素
print(myList[2:]) # 输出从第三个元素开始的所有元素
# 反转列表
print(myList.reverse()) # 无返回值
print(myList)
# 列表排序
sortList = [6, 5, 2, 4, 1, 2, 7, 3]
sortList.sort() # 等价于:sortList.sort(key=None, reverse=False)key参数指明用哪个函数进行排序,默认值是None,用<进行比较 可选参数reserse参数默认值是False,从小到大排序,如果设置为True,则从大到小排序
print(sortList)
# 拼接列表
tinylist = [123, 456, 1263]
print(tinylist * 2) # 输出两次列表
print(myList + tinylist) # 连接列表,返回值是一个新列表
# 返回列表元素最大值
print('tinylist列表中的最大值为:%(maxV)d' % {'maxV': max(tinylist)})
# 返回列表元素最小值
print(f'tinylist列表中的最小值为:{min(tinylist)}')
# 系统提供内建函数range(start,end,[step=1]),生成一个等差序列[start, end),注意序列属于不可变序列,不支持元素修改,不支持+和*操作。range一般用于for-in循环遍历
a = range(0,9,3) # 左闭右开 0,3,6
print(type(a)) # <class 'range'>
for elem in a:
print(elem)
import random
# 随机返回一个序列(列表、元组,字符串)中的一个元素
list5 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
print(random.choice(list5))
# 将序列元素随机排列(打乱顺序)
random.shuffle(list5)
print(list5)
# zip函数:
a = [1, 2, 3, 4]
b = [2, 3, 4]
res = zip(a, b)
print(type(res), list(res)) # [(1, 2), (2, 3), (3, 4)]
# 可以使用for-in 遍历
for x, y in zip(a, b):
print(x, y)
# 将元祖转换为列表
aTuple = (123, 'Google', 'Runoob', 'Taobao')
list1 = list(aTuple)
print("列表元素 : ", list1)
# 将字符串转为字符数组
str = "Hello World"
list2 = list(str)
print("列表元素 : ", list2)
# 清空列表
list2.clear()
print(list2) # 结果: []
# 在列表末尾一次性追加另一个序列中的多个值(用新列表扩展原来的列表)
list3 = ['Google', 'Runoob', 'Taobao']
list4 = list(range(5)) # 创建 0-4 的列表
list3.extend(list4) # 扩展列表
print("扩展后的列表:", list3)
# 语言列表
language = ['French', 'English', 'German']
# 元组
language_tuple = ('Spanish', 'Portuguese')
# 集合
language_set = {'Chinese', 'Japanese'}
# 添加元组元素到列表末尾
language.extend(language_tuple)
print('新列表: ', language)
# 添加集合元素到列表末尾
language.extend(language_set)
print('新列表: ', language)
7. 元组
Python的元组与列表类似,不同之处在于元组的元素不能修改、删除。
元组使用小括号,列表使用方括号。
元组创建很简单,只需要在括号中添加元素,并使用逗号隔开即可。
# 创建元组
tup1 = ('Google', 'Runoob', 1997, 2000)
print(type(tup1), '\t', tup1)
print(tup1)
# 元组中只包含一个元素时,需要在元素后面添加逗号,否则括号会被当作运算符使用:
tup2 = (50)
print(type(tup2)) # 不加逗号,类型为整型 <class 'int'>
tup3 = (50,)
print(type(tup3)) # 加上逗号,类型为元组 <class 'tuple'>
# 拼接元组
tup4 = tup1 + tup3
print(tup4)
# 产出元组
tup5 = ('Google', 'Runoob', 1997, 2000)
print(tup5)
del tup5
print(tup5) # del会删除整个元组,此时打印报错:NameError: name 'tup5' is not defined
元组的一些常用操作符和函数与列表相似。
8. 字典
字典属于无序序列,元素存放无序,是通过哈希方式进行数据存取的,字典是一个可变容器。
字典的每个键值 key=>value 对用冒号 :
分割,每个元素之间用逗号,
分割,整个字典包括在花括号 {}
中 。
键必须是唯一的、不可变的,所以键可以用数字,字符串或元组充当,而用列表就不行。
值可以取任何数据类型,如字符串,数字。
# 创建字典
d1 = {} # 空字典
d1 = dict() # 空字典
d2 = {'name': '麻辣龙虾', 'taste': '美味'}
d3 = dict(a=1, b=2)
d4 = dict([('a', 1), ('b', 2)])
d5 = dict({'b': 2,'a': 1})
print(d1, d2, d3, d4, d5)
# 创建字典
tup01 = (1, 3, 5, 7)
list01 = [2, 4, 6, 8]
dict1 = {'Name': 'Runoob', 1: 'First', 'Age': 7, tup01: list01}
# 访问字典里的值
print("dict['Name']: ", dict1['Name'], '\t', "dict[1]: ", dict1[1])
# print("dict['Sex']: ", dict1['Sex']) # 访问的key不存在会报错:KeyError: 'Sex'
# 修改字典
dict1['Age'] = 18
# 添加字典中的元素
dict1['sex'] = '男'
# 删除字典中的元素
del [dict1[1]]
print(dict1)
# 删除字典给定键 key 所对应的值,返回值为被删除的值。key值必须给出。 否则,返回default值。
print(dict1.pop('Age'))
# print(dict1.pop('Hobby')) # 如果key不存在,而又没有指定默认值,会报错:KeyError: 'Hobby'
print(dict1.pop('Hobby', "足球"))
print(dict1)
# 返回并删除字典中的最后一对键和值
site = {'name': '菜鸟教程', 'alexa': 10000, 'url': 'www.runoob.com'}
print(site.popitem(), site)
# 清空字典
dict1.clear()
print(dict1) # 结果: {}
# 删除字典【直接在内存中删除了,不能继续使用了】
del dict1
# print(dict1)
# todo 字典内置函数&方法
dict2 = {1: "hadoop", 2: "hive", 3: "spark"}
# 计算字典元素个数,即键的总数。
print(len(dict2))
# 输出字典,以可打印的字符串表示。
a = str(dict2)
print(type(a)) # <class 'str'>
# 复制字典
dict3 = dict2.copy()
print(id(dict2), '\t', id(dict3)) # id不一致
# 以序列 seq 中元素做字典的键,value 为字典所有键对应的初始值,创建一个新字典,
seq = ('name', 'age', 'sex')
dict4 = dict.fromkeys(seq) # dict类当中的静态方法
print("新的字典为 : %s" % str(dict4)) # 新的字典为 : {'name': None, 'age': None, 'sex': None}
dict5 = dict.fromkeys(seq, 10)
print("新的字典为 : %s" % str(dict5)) # 新的字典为 : {'name': 10, 'age': 10, 'sex': 10}
# dict.get(key, default=None)返回指定键的值,如果键不在字典中返回默认值None或者指定的默认值。
dict6 = {'Name': 'Runoob', 'Age': 27}
print("Age 值为 : %s" % dict6.get('Age'))
print("Sex 值为 : %s" % dict6.get('Sex', "NA"))
print(dict6) # 结果:{'Name': 'Runoob', 'Age': 27}
# setdefault() 方法和 get()方法 类似, 如果键不已经存在于字典中,将会添加键并将值设为默认值
print("Age 值为 : %s" % dict6.setdefault('Age', 20)) # Age 值为 : 27
print("Sex 值为 : %s" % dict6.setdefault('Sex')) # Sex 值为 : None
print("Hobby 值为 : %s" % dict6.setdefault('Hobby', "篮球")) # Hobby 值为 : 篮球
print(dict6) # {'Name': 'Runoob', 'Age': 27, 'Sex': None, 'Hobby': '篮球'}
# 操作符 in 用于判断键是否存在于字典中,如果键在字典 dict 里返回 true,否则返回 false。
print('Name' in dict6)
print('Sex' in dict6)
# 以列表返回可遍历的(键, 值) 元组数组
list02 = dict6.items()
print("%s \t %s" % (str(list02), type(list02)))
# 遍历
for key, value in list02:
print(key, value)
# 返回一个可迭代对象,可以使用 list() 来转换为列表
dickeys = dict6.keys()
print(type(dickeys), dickeys, list(dickeys))
# 返回一个迭代器,可以使用 list() 来转换为列表,列表为字典中的所有值
dicvalues = dict6.values()
print(type(dicvalues), dicvalues, list(dicvalues))
# 把参数中的字典里的k:v对更新到字典里
dict7 = {'Name': 'Runoob', 'Age': 7}
dict8 = {'Age': 19, 'Sex': 'female'}
dict7.update(dict8)
print("更新字典 dict7 : ", dict7) # 更新字典 dict7 : {'Name': 'Runoob', 'Age': 19, 'Sex': 'female'}
9. 集合
集合(set)是一个无序的不重复元素序列。
可以使用大括号 { } 或者 set() 函数创建集合,注意:创建一个空集合必须用 set() 而不是 { },因为 { } 是用来创建一个空字典。
# 创建集合
set1 = {'apple', 'apple', 'orange', 'pear', 'orange', 'banana'}
print(type(set1), set1)
set2 = set()
print(type(set2), set2)
set3 = set('aabcdefg')
print(type(set3), set3)
# 添加元素
set4 = set(("Google", "Runoob", "Taobao", "Sun")) # 根据元组创建集合
print(set4.add("Facebook")) # 无返回值,等价于:set4.update("Facebook")
# 移除一个指定的元素
print(set4.remove("Taobao")) # 无返回值是,结果为:None
print(set4.discard("Sun")) # 无返回值是,结果为:None
# set4.remove("ahfkaffah") # 如果被移除的元素在集合中不存在会报错:KeyError: 'ahfkaffah'
set4.discard("hfaklsdfha") # discard则不会报错
print(set4)
# 随机移除一个元素并返回此元素的值
print(set4.pop())
print(set4)
# 集合的操作符
a = {'a', 'c', 'r', 'd', 'b'}
b = {'a', 'c', 'z', 'm', 'l'}
# 集合中包含而集合b中不包含的元素
print(a - b) # 等价于:a.difference(b),结果:{'r', 'd', 'b'}
# a.difference_update(b),
# print(a.difference_update(b)) # 没有返回值,直接在原来的集合中移除元素
print(a)
# 集合a和b的并集
print(a | b) # 等价于:a.union(b),结果:{'z', 'a', 'c', 'm', 'l', 'b', 'd', 'r'}
# 集合a和b的交集
print(a & b) # 等价于:a.intersection(b)【也有intersection_update这个方法】,结果:{'a', 'c'}
# 集合a和b中不重复的元素
print(a ^ b) # 等价于:a.symmetric_difference(b)【也有symmetric_difference_update这个方法】,结果:{'z', 'd', 'm', 'r', 'l', 'b'}
"""
isdisjoint() 方法用于判断两个集合是否包含相同的元素,如果没有返回 True,否则返回 False
issubset()判断指定集合是否为该方法参数集合的子集。
issuperset()判断该方法的参数集合是否为指定集合的子集
"""
c = {"a", "b", "c", "d"}
print(c.isdisjoint({"e", "f"})) # 结果:True
print(c.issubset({"a", "b", "c", "d", "e", "f", "g"})) # 结果:False
print(c.issuperset({"a", "b"})) # 结果:False
五.运算符
1. 算术运算符
运算符 | 说明 | 示例 |
---|---|---|
- | 负号,取原数的相反数 | a = 10 print(-a) #-10 |
+ - * / | 加减乘除,同数学上一样 | a + b = 30 a - b = 10 a * b = 200 a / b = 2 |
% | 模运算,即求 a整除以b的余数 | a % 10 = 0 |
// | 整除 | a // b = 2 |
** | 对运算符进行指数(幂)计算 | a ** b 表示20的10次方 |
示例:
# 算术运算符MathOperators
a = 21
b = 10
c = 0
c = a + b
print("1 - c 的值为:", c)
c = a - b
print("2 - c 的值为:", c)
c = a * b
print("3 - c 的值为:", c)
c = a / b
print("4 - c 的值为:", c)
c = a % b
print("5 - c 的值为:", c)
# 修改变量 a 、b 、c
a = 2
b = 3
c = a ** b
print("6 - c 的值为:", c)
a = 10
b = 5
c = a // b
print("7 - c 的值为:", c)
2. 比较运算符
比较运算就是关系运算,如果表达式成立,返回True,否则返回False。关系运算的结果是布尔值。
运算符 | 示例 | 说明 |
---|---|---|
== | a == b | a和b值相等,结果是True,a和b值不相等结果为False |
!= | a != b | a不等于b 结果为True,否则结果为True |
> | a > b | a大于b结果为True,否则为False |
>= | a >= b | a大于等于b结果为True,否则为False |
< | a < b | a小于b结果为True,否则为False |
<= | a <= b | a小于等于b结果为True,否则为False |
注意:
- 优先级: 比较运算符优先级相同
- 从左向右算
- 可以这样算:
1 < a < 3
等价于 a > 1 and a < 3
# 比较运算符CompareOperate
a = 21
b = 10
c = 0
if (a == b):
print("1 - a 等于 b")
else:
print("1 - a 不等于 b")
# Pyhton3 已不支持 <> 运算符,可以使用 != 代替
if (a != b):
print("2 - a 不等于 b")
else:
print("2 - a 等于 b")
if (a < b):
print("3 - a 小于 b")
else:
print("3 - a 大于等于 b")
if (a > b):
print("4 - a 大于 b")
else:
print("4 - a 小于等于 b")
# 修改变量 a 和 b 的值
a = 5
b = 20
if (a <= b):
print("5 - a 小于等于 b")
else:
print("5 - a 大于 b")
if (b >= a):
print("6 - b 大于等于 a")
else:
print("6 - b 小于 a")
3. 赋值运算符
运算符 | 说明 | 示例 |
---|---|---|
= | 简单赋值, 将b+c的结果赋给a | a = b +c #a=30 |
+= | a += b等价于 a = a +b | a = 15 |
-= | a -= b等价于 a = a - b | a = -5 |
*= | a *= b等价于 a = a * b | a = 50 |
/= | a /= b 等价于a = a / b | a = 0.5 |
%= | a %= b等价于a = a % b | a = 5 |
//= | a //= b等价于 a = a // b | a = 0 |
**= | a **= b等价于a = a ** b |
# 赋值运算符
a = 21
b = 10
c = 0
c = a + b
print("1 - c 的值为:", c)
c += a
print("2 - c 的值为:", c)
c *= a
print("3 - c 的值为:", c)
c /= a
print("4 - c 的值为:", c)
c = 2
c %= a
print("5 - c 的值为:", c)
c **= a
print("6 - c 的值为:", c)
# 取整除赋值运算符
c = 42
c //= a
print("7 - c 的值为:", c)
4. 位运算符
# Python位运算符
a = 60 # 60 = 0011 1100
b = 13 # 13 = 0000 1101
'''
& 按位与运算符:参与运算的两个值,如果两个相应位都为1,则该位的结果为1,否则为0
(a & b) 输出结果 12 ,二进制解释: 0000 1100
| 按位或运算符:只要对应的二个二进位有一个为1时,结果位就为1
(a | b) 输出结果 61 ,二进制解释: 0011 1101
^ 按位异或运算符:当两对应的二进位相异时,结果为1
(a ^ b) 输出结果 49 ,二进制解释: 0011 0001
~ 按位取反运算符:对数据的每个二进制位取反,即把1变为0,把0变为1。~x 类似于 -x-1
(~a ) 输出结果 -61 ,二进制解释: 1100 0011, 在一个有符号二进制数的补码形式。
<< 左移动运算符:运算数的各二进位全部左移若干位,由"<<"右边的数指定移动的位数,高位丢弃,低位补0。
a << 2 输出结果 240 ,二进制解释: 1111 0000
>> 右移动运算符:把">>"左边的运算数的各二进位全部右移若干位,">>"右边的数指定移动的位数
a >> 2 输出结果 15 ,二进制解释: 0000 1111
'''
c = 0
c = a & b # 12 = 0000 1100
print("1 - c 的值为:", c)
c = a | b # 61 = 0011 1101
print("2 - c 的值为:", c)
c = a ^ b # 49 = 0011 0001
print("3 - c 的值为:", c)
c = ~a # -61 = 1100 0011
print("4 - c 的值为:", c)
c = a << 2 # 240 = 1111 0000
print("5 - c 的值为:", c)
c = a >> 2 # 15 = 0000 1111
print("6 - c 的值为:", c)
5. 逻辑运算符
以a = 10 b = 20
为例
运算符 | 逻辑表达式 | 描述 | 实例 |
---|---|---|---|
and | x and y | 布尔"与" - 如果 x 为 False,x and y 返回 False,否则它返回 y 的计算值 | (a and b) 返回 20 |
or | x or y | 布尔"或" - 如果 x 是 True,它返回 x 的值,否则它返回 y 的计算值 | (a or b) 返回 10 |
not | not x | 布尔"非" - 如果 x 为 True,返回 False 。如果 x 为 False,它返回 True | not(a and b) 返回 False |
# Python逻辑运算符
a = 10
b = 20
c = a and b
print('a and b 的返回值为:',c)
if (a and b):
print("a and b 的结果为 True\n")
else:
print("a and b 的结果为 False\n")
c = a or b
print('a or b 的返回值为:',c)
if (a or b):
print("a or b 的结果为 True\n")
else:
print("a or b 的结果为 False\n")
c = not (a and b)
print('not (a and b) 的返回值为:',c)
if not (a and b):
print("not (a and b) 的结果为 True")
else:
print("not (a and b) 的结果为 False")
print( '\n=========================\n')
# 修改变量 a 的值
a = 0
if (a and b):
print("3 - 变量 a 和 b 都为 True")
else:
print("3 - 变量 a 和 b 至少有一个变量为 False")
if (a or b):
print("4 - 变量 a 和 b 至少有一个变量为 True")
else:
print("4 - 变量 a 和 b 都为 False")
6. 三目运算符
# 格式:表达式 ? 真 : 假
result = (8>10)?'真':'假'
print(result)
7. 成员运算符
# Python成员运算符
a = 10
b = 20
list = [1, 2, 3, 4, 5]
'''
in 如果在指定的序列中找到值返回 True,否则返回 False。
not in 如果在指定的序列中没有找到值返回 True,否则返回 False。
'''
if (a in list):
print("1 - 变量 a 在给定的列表中 list 中")
else:
print("1 - 变量 a 不在给定的列表中 list 中")
if (b not in list):
print("2 - 变量 b 不在给定的列表中 list 中")
else:
print("2 - 变量 b 在给定的列表中 list 中")
# 修改变量 a 的值
a = 2
if (a in list):
print("3 - 变量 a 在给定的列表中 list 中")
else:
print("3 - 变量 a 不在给定的列表中 list 中")
8. 身份运算符
# Python身份运算符
a = 20
b = 20
'''
is 判断两个标识符是不是引用自一个对象 x is y, 类似 id(x) == id(y) , 如果引用的是同一个对象则返回 True,否则返回 False
is not 判断两个标识符是不是引用自不同对象 x is not y , 类似 id(a) != id(b)。如果引用的不是同一个对象则返回结果 True,否则返回 False。
'''
if (a is b):
print("1 - a 和 b 有相同的标识")
else:
print("1 - a 和 b 没有相同的标识")
if (id(a) == id(b)):
print("2 - a 和 b 有相同的标识")
else:
print("2 - a 和 b 没有相同的标识")
# 修改变量 b 的值
b = 30
if (a is b):
print("3 - a 和 b 有相同的标识")
else:
print("3 - a 和 b 没有相同的标识")
if (a is not b):
print("4 - a 和 b 没有相同的标识")
else:
print("4 - a 和 b 有相同的标识")
六.表达式(条件控制与循环)
1. if 语法
- 每个条件后面要使用冒号 :,表示接下来是满足条件后要执行的语句块。
- 使用缩进来划分语句块,相同缩进数的语句在一起组成一个语句块。
- 在Python中没有switch – case模式匹配语句。
1.1 单一分支
if 条件表达式:
【代码块】
【后续代码】
执行流程:如果条件表达式为真,则执行【代码块】。否则不执行,直接执行if语句后面的【后续代码】
1.2 双向分支
if 条件表达式:
【语句块A】
else:
【语句块B】
【后续代码】
1.3 多向条件分支
if 条件1:
【代码段1】
elif 条件2:
【代码段2】
............
elif 条件n:
【代码段n】
else:
【else语句块】
【后续代码】
示例:输入一个正整数与100比较大小
varStr = input("请输入一个正整数:")
# 判断输入的字符串是否只由数字组成
if (varStr.isdigit()):
varInt = int(varStr)
print(type(varInt))
if (varInt == 100):
print("您输入的值等于100")
elif varInt > 100:
print(varStr, "大于100")
else:
print(varStr, "小于100")
else:
print("您输入的不是一个正整数!!!")
2. while 语法
2.1 while
while 表达式:
循环体
示例:
flag = True
varStr = input("请输入一个正整数:")
while flag:
# 判断输入的字符串是否只由数字组成
if (varStr.isdigit()):
varInt = int(varStr)
if (varInt % 2 == 0):
print(varStr, "是偶数")
else:
print(varStr, "是奇数")
flag = False
else:
varStr = input("您输入的不是一个正整数,请重新输入:")
2.2 while-else
while 表达式:
循环体
else:
【else语句块】
当while语句执行完成之后,执行【else语句块】,如果用break跳出循环则不执行else
3. for 语法
3.1 for
for <variable> in <sequence>:
<statements>
else:
<statements>
示例:
a = range(0,9,3) # 左闭右开 0,3,6
print(type(a)) # <class 'range'>
for elem in a:
print(elem)
print("++++++++++++++++++++")
# 0,1,2,3,4
for i in range(5):
print(i)
print("--------------------")
for letter in 'Runoob': # 第一个实例
if letter == 'o':
break
print ('当前字母为 :', letter)
print("********************")
for letter in 'Runoob': # 第一个实例
if letter == 'o':
continue
print ('当前字母为 :', letter)
print("======================")
for letter in 'Runoob':
if letter == 'o':
pass
print ('执行 pass 块')
print ('当前字母 :', letter)
break 语句可以跳出 for 和 while 的循环体。如果你从 for 或 while 循环中终止,任何对应的循环 else 块将不执行。
continue 语句被用来告诉 Python 跳过当前循环块中的剩余语句,然后继续进行下一轮循环。
**pass ** 语句被用来保持程序结构的完整性,pass 不做任何事情,一般用做占位语句。
3.2 冒泡排序
myList = [4,1,7,0]
for i in range(len(myList)-1):
#每一轮的比较,注意range的变化,这里需要进行length-1-长的比较,注意-i的意义(可以减少比较已经排好序的元素)
for j in range(0,len(myList)-1-i):
#交换
if myList[j] > myList[j+1]:
myList[j],myList[j+1] = myList[j+1],myList[j]
print(myList)
3.3 九九乘法表
for i in range(1, 10):
for j in range(1, 10):
if j <= i:
print("%d*%d=%2d" % (i, j, i * j), end='%*s' % (3, '\t'))
print('')
4. 迭代器
迭代是访问集合元素的一种方式;
迭代器是一个可以记住遍历的位置的对象;
迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退;
迭代器有两个基本的方法:iter()
和 next()
。
字符串,列表或元组对象都可用于创建迭代器:
import sys
list1 = [1, 2, 3, 4]
it = iter(list1) # 创建迭代器对象
print(next(it)) # 输出迭代器的下一个元素
print(next(it))
# 迭代器对象可以使用常规for语句进行遍历:
varStr = 'abcd'
it = iter(varStr) # 创建迭代器对象
for x in it:
print(x, end=" ")
print('\n=====================')
tup1 = (29, 'fa')
it = iter(tup1) # 创建迭代器对象
while True:
try:
print(next(it))
except StopIteration: # StopIteration 异常用于标识迭代的完成,防止出现无限循环的情况。
sys.exit()
6. 生成器
6.1 列表推导式
运用列表推导式,可以快速生成list,可以通过一个list推导出另一个list,而代码却十分简洁。[返回列表章节](#6. 列表)
语法:[exp for iter_var in iterable]
执行for-in循环时,通过iter_var遍历iterable每一项,exp表达式中可以直接使用iter_var,每遍历一项,产生一个新的列表元素。
#生成[0,1,4,9,16,25]
[x*x for x in range(6)]
#生成[0,4,16,36,64]
l2 = [x*x for x in range(9) if x % 2 ==0]
print(l2)
# 可以使用双重循环
suit = ['♥', '♦', '♣', '♠']
face = ['A', '2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K']
poke = [(x, y) for x in suit for y in face]
print(type(poke),poke)
# 字典推导式
# 列表生成式可以使用两个变量,实现字典的键值交换
d = {"X": "A", "Y": "B", "Z": "C"}
dReverse = {v: k for k, v in d.items()}
print(type(dReverse), dReverse)
#集合推导式
print({x for x in range(10)})
问题:受到内存限制,列表容量肯定是有限的。而且,创建一个包含100万个元素的列表会占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。
解决:如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的list,从而节省大量的空间。在Python中,这种一边循环一边计算的机制,称为生成器:generator。
6.2 yield关键字
列表推导式是定义生成器的方式之一,在 Python 中,使用了 yield 的函数也被称为生成器(generator)。
步骤:
- 定义一个函数,函数中使用yield关键字
- 调用函数,接收调用的结果
- 得到的结果就是生成器
- 借助于
next()
,__next__()
得到元素
注意:
- 在调用生成器运行的过程中,每次遇到 yield 时函数会暂停并保存当前所有的运行信息,返回 yield 的值, 并在下一次执行
next()
方法时从当前位置继续运行 。 - 调用生成器函数,返回的是一个迭代器对象,生成器是可迭代的,也是迭代器。
def func():
n = 0
while True:
n += 1
# print(n)
yield n # todo 关键字 yeild n 等价于: return n + 暂停
g = func()
print(g)
print(next(g))
print(next(g))
print(next(g))
print(next(g))
6.3 send函数
- 如果不携带参数,那么send(None) 和next()的作用的相同的
- 如果send的参数不是None,则是把yield xx当成一个表代式,且把send的参数的值赋给了p1;而后的操作同next一样,如下:
def gfunc():
print('aaa')
p = yield '123'
print('bbb')
if (p == 'hello'):
print('p是send传过来的')
p1 = yield '234'
print(p2)
g = gfunc()
next(g) # 等价于: g.send(None)
g.send('hello')
注意:
- gfunc()是个生成器,调用时的执行的顺序为:
- ==第一次执行要么
next(g)
要么r.send(None)
,不能使用r.send(‘xxxxx’);这会报错的。==本案例第一次执行时next(g)
时,首先打印出aaa,然后遇到yield即跳出生成器; - 然后执行
g.send('hello')
时,p
则被赋值为hello
了,然后继续顺序执行:打印出bbb,然后打印出p是send传过来的,当再次遇到第二个yield时跳出,所以结果只打印了三行,后面的p1没有执行。
- ==第一次执行要么
- 获取生成器的方法:
- 使用
next(generator)
,每次调用都会产生一个新的元素,如果元素产生完毕,再次调用的话就会产生异常; - 生成器自己的方法:
g.__next__()
、g.send(value)
- 使用
七.函数
1. 函数的定义
函数就是完成特定功能的代码块,本质上是对代码的封装 ,语法格式:
def 函数名([参数1],[参数2]....[参数n]):
"""这里是函数的文档字符串,- 必须在函数的首行,使用三引号注释,用来概述函数的主要功能"""
函数体
- 函数名命名规则同变量名,要满足标识符命名规则
- 函数定义时小括号里的变量称为形参,调用函数的时小括号里的表达式称为实参,函数可以没有参数
- 函数定义分两部分函数头和函数体,函数体是实现功能的代码段,以
:
开头,必须缩进 - 使用
__doc__
查看函数文档字符串 - 可以通过return语句返回计算结果,语法:
return 表达式
- return会终止函数的执行,如果没有return语句,则默认返回的是None
- return可以返回一个值,如果要返回多个值,那么返回的是一个元组
def demo01(varA): # vaeA是形参
"""
我是demo01函数的返回值
返回值是varA * 2
"""
return varA * 2
def demo02():
return 1, 2, 3
print(demo01.__doc__)
print('调用demo02的返回值是:', demo02())
print('调用demo01的返回值是:', demo01(100)) # 100 是实参
2. 函数的参数
**注意:**函数的参数如果是不可变的数据类型:int、float、None、complex、bool、tuple、str、range,那么在函数内部不可能修改。
2.1 位置参数
要求实参顺序必须和形参顺序完全一致,由形参顺序决定实参顺序
def say_hello(name,age,home):
print('大家好,我是{},我今年{}岁了,我来自{}'.format(name,age,home))
say_hello('王二妮',18,'湖北武汉') #实参个数、顺序必须和形参一致
2.2 关键字参数
函数调用时,实参可以是键值对,键就是形参名字,这样的调用,实参不必关心形参的顺序。
def say_hello(name,age,home):
print('大家好,我是{},我今年{}岁了,我来自{}'.format(name,age,home))
say_hello(name='王二傻',home='大连',age=20) #三个关键字参数
say_hello('大傻',home='美国',age=30) #两个关键字参数
sya_hello('二傻',24,home='何方') #一个关键字参数
2.3 默认参数
如果形参在定义的时候给定一个值,那么函数在调用时就可以不传实参,可以简化调用,注意:
- 默认值参数必须放到最右边
- 如果传了实参,那么实参优先,不会使用默认值
- 默认值只计算一次
- 默认值必须是不可变对象
def my_power(x,n=2):
return (x) ** n
my_power(3)
my_power(4,0.5)
def test(a=[]):
a.append('end')
print(a)
test([1,2,3])
test() #['end']
test() #['end','end']
2.4 可变参数
传入的参数个数是可变的,可以是1个、2个到任意个,还可以是0个。
#使用*接收任意数量的位置参数
#注意:*的不定长参数被当做元组处理
def demo(a,b,*args):
print(a,b,args,type(args))
demo(12,33,90)
demo(1,2,3,4,5)
a=(1,2,3)
demo(*a)
#使用**接收任意数量的关键字参数
#注意:**的不定长参数被当做字典处理
def demo1(a,**args):
print(a,args)
demo1(1,name='kk',age=3)
b = {'a':20,'b':12,'c':32}
demo(**b)
def demo3(a,b,c=0,*arg1,**arg2):
print(a,b,c,arg1,arg2)
demo3(1,3,k=4)
demo3(1,2,3,4,5)
demo3(1,b=3,c=3,d=5)
demo3(*(1,2,3),**{'name':12}) #任何函数都可通过这种形式传递参数
3. 常用函数
3.1 空函数
借助于pass语句实现,函数体不完成任何功能,只有一个pass语句
def 函数名([参数1],[参数2]....[参数n]):
"""函数的文档字符串"""
pass
3.2 内部函数
- 可以访问外部函数的变量
- 内部函数可以修改外部函数的可变类型的变量比如:list1
- 内部函数修改全局的不可变变量时,需要在内部函数声明global 变量名
- 内部函数修改外部函数的不可变的变量时,需要在内部函数中声明: nonlocal 变量名
- locals() 查看本地变量有哪些,以字典的形式输出
- globals() 查看全局变量有哪些,以字典的形式输出(注意里面会有一些系统的键值对)
a = 100 # 全局变量
print(globals())
# 定义函数
def func():
b = 99 # 函数中的局部变量
# 声明内部函数
def inner_func():
global a
nonlocal b
c = 88 # 内部函数中的局部变量
# 尝试修改
c += 12
b += 1
a += 10
# 尝试打印
print(a, b, c)
# 调用内部函数
inner_func()
# 使用locals()内置函数进行查看。可以看到在当前函数中声明的内容有哪些
# locals()是一个字典。key:value
print(locals())
# 调用函数
func()
3.3 递归函数
def sum(n): # 1~n
'''
求和的函数
:param n: 从1~n累加和
:return: 求和的结果
'''
if n == 0:
return 0
else:
return n + sum(n - 1)
result = sum(10)
3.4 匿名函数
python 使用 lambda 来创建匿名函数。所谓匿名,意即不再使用 def 语句这样标准的形式定义一个函数。
- lambda的主体是一个表达式,而不是一个代码块。仅仅能在lambda表达式中封装有限的逻辑进去。
- lambda 函数拥有自己的命名空间,且不能访问自己参数列表之外或全局命名空间里的参数。
# todo lambda表达式
lambdaVar = lambda a, b: a + b
print(type(lambdaVar)) # <class 'function'>
print(lambdaVar) # <function <lambda> at 0x03359BB8>
print(lambdaVar(3, 4)) # 7
# todo 匿名函数作为参数
def func(x, y, func):
s = func(x, y)
print(s)
func(1, 2, lambda a, b: a + b)
# todo 匿名函数与内置函数的结合使用
# todo 1、max():返回给定序列的最大值
list01 = [{'a': 10, 'b': 25}, {'a': 13, 'b': 20}, {'a': 9, 'b': 20}, {'a': 29, 'b': 20}]
print("list01[0]['b']值为", list01[0]['b'])
# 取出list01中的每个元素赋值给变量x,x是一个字典,再取出字典中key是b的值。把这些值放到一个集合中之后,再求最大值
maxValue = max(list01, key=lambda x: x['b'])
print('list01列表的最大值:', maxValue)
# todo 2、map(): 根据提供的函数对指定序列做映射
list02 = [3, 4, 6, 7, 8, 9, 9, 0, 2, 5]
resMap = map(lambda x: x + 2, list02)
print(type(resMap), list(resMap))
# 对列表中的奇数进行加1操作
res02 = map(lambda x: x if x % 2 == 0 else x + 1, list02)
print(list(res02))
# todo 3、reduce(): 对列表中的元素进行加减乘除运算的函数
from functools import reduce
tuple01 = (3, 5, 7, 8, 9, 1)
res03 = reduce(lambda x, y: x + y, tuple01)
print(res03)
print(reduce(lambda x, y: x - y, tuple01, 10)) # 最后一个参数10是初始值
# todo 4、filter() :对列表中的元素按一定的规则过滤
list04 = [12, 6, 8, 98, 34, 36, 2, 8, 0]
resFilter = filter(lambda x: x > 10, list04)
print(type(resFilter), list(resFilter))
# todo sorted():对所有可迭代的对象进行排序操作
"""
sort 与 sorted 区别:
1、sort 是应用在 list 上的方法,sorted 可以对所有可迭代的对象进行排序操作。
2、list 的 sort 方法返回的是对已经存在的列表进行操作,无返回值,而内建函数 sorted 方法
返回的是一个新的 list,而不是在原来的基础上进行的操作。
"""
students = [
{'name': 'tom', 'age': 20},
{'name': 'lucy', 'age': 19},
{'name': 'lily', 'age': 13},
{'name': 'mark', 'age': 21},
{'name': 'jack', 'age': 23},
{'name': 'steven', 'age': 18},
]
# 按照年龄从大到小排序
students = sorted(students, key=lambda x: x['age'], reverse=True)
print(students)
4. 闭包
在函数内部再定义一个函数,并且这个函数用到了外边函数的变量,并且把里面的函数返回,我们把这种情况叫闭包。
- 闭包优化了变量,原来需要类对象完成的工作,闭包也可以完成;
- 闭包的好处,使代码变得简洁,便于阅读代码;
- 由于闭包引用了外部函数的局部变量,则外部函数的局部变量没有及时释放,消耗内存;
- 闭包是理解装饰器的基础。
def 外部函数():
...
def 内部函数():
....
...
return 内部函数
示例:
def func(a):
b = 20
print("函数func被调用了")
def inner_func():
c = 30
print(a + b + c)
print("locals():", locals())
return inner_func
x = func(10) # 调用了func函数,将inner_func作为返回值赋值给了x,x就是内部函数名
print(type(x), x)
# x就是内部函数,x()就表示调用函数,仅执行内部函数的代码,可以访问到变量a
x()
结果:inner_func
和x
的内存地址相同,所有x就是内部函数
函数func被调用了
locals(): {'inner_func': <function func.<locals>.inner_func at 0x03929C48>, 'a': 100}
<class 'function'> <function func.<locals>.inner_func at 0x03929C48>
100 99
闭包的应用:计数器
# 计数器
def generate_count():
container = [0]
def add_one():
container[0] = container[0] + 1 # [2]
print('当前是第{}次访问'.format(container[0]))
return add_one
# 内部函数就是一个计数器
counter = generate_count() # counter = add_one
counter() # 第一次的访问
counter() # ....
counter()
5.装饰器
5.1 函数作为参数传递
def hi():
return "hi yasoob!"
def func_with_funcparam(func):
print("I am doing some boring work before executing hi()")
print(func())
func_with_funcparam(hi)
5.2 定义装饰器
在上一个例子里,其实我们已经创建了一个装饰器!现在我们修改下上一个装饰器,并编写一个稍微更有用点的程序:
# from functools import wraps
def decorator_name(func_as_param):
# @wraps(a_func)
def wrapFunction():
print("wrapFunction before executing a_func()")
func_as_param()
print("wrapFunction after executing a_func()")
return wrapFunction
def func01():
print("func01():function as a params 11111111")
funcVar = decorator_name(func01)
funcVar()
print("=======下面演示使用@符号生成装饰器=======")
@decorator_name # @ + 装饰器名称
def func02():
"""Hey you! Decorate me!"""
print("func02():function as a params 2222222")
# 直接调用func02
func02()
'''
打印func02函数的名称,结果为:wrapTheFunction None
结果不是func02原因是:func02函数被warpTheFunction替代了。它重写了我们函数的名字和注释文档(docstring)
解决:引入functools.wraps模块(from functools import wraps),在wrapFunction函数上添加@wraps(a_func)
'''
print(func02.__name__, func02.__doc__)
@wraps接受一个函数来进行装饰,并加入了复制函数名称、注释文档、参数列表等等的功能。这可以让我们在装饰器里面访问在装饰之前的函数的属性。
5.3 多层装饰器
'''
如果装饰器是多层的,谁距离函数最近就优先使用哪个装饰器
'''
# 装饰器
def zhuang1(func):
print('------->1 start')
def wrapper(*args, **kwargs):
func()
print('刷漆')
print('------->1 end')
return wrapper
def zhuang2(func):# func=house
print('------->2 start')
def wrapper(*args, **kwargs):
func()
print('铺地板,装门.....')
print('------->2 end')
return wrapper
@zhuang2
@zhuang1
def house(): # house = wrapper
print('我是毛坯房.....')
house()
5.4 装饰器参数传递
def outer(a): # 第一层: 负责接收装饰器的参数
print('------------1 start')
def decorate(func): # 第二层 : 负责接收函数的
print('------------2 start')
def wrapper(*args, **kwargs): # 第三层 负责接收函数的参数
func(*args)
print("搬了{}块转".format(a))
print('------------2 end')
return wrapper # 返出来的是:第三层
print('------------1 end')
return decorate # 返出来的是:第二层
@outer(10)
def house(dateStr):
print('我在{}日'.format(dateStr), end='')
house('2019-6-12')
5.5 演示案例
登录校验
import time
def decorate(func):
def wrapper(*args, **kwargs): # () {'clazz':'1904'}
print('正在校验中....')
time.sleep(2)
print('校验完毕.....')
# 调用原函数 args --->()
func(*args, **kwargs) # f1 f2 f3...
return wrapper
@decorate
def f1(n):
print('-------f1-------', n)
f1(5) # 此时的f1是wrapper,
@decorate
def f2(name, age):
print('-------f2-------', name, age)
f2('lily', 20)
@decorate
def f3(students, clazz='1905'):
print('{}班级的学生如下:'.format(clazz))
for stu in students:
print(stu)
students = ['lily', 'tom', 'lucy']
f3(students, clazz='1904')
@decorate
def f4():
print('------------>f4')
f4()
登录验证
import time
islogin = False # 默认是没有登录的
# 定义一个登录函数
def login():
useranme = input('输入用户名:')
password = input('输入密码:')
if useranme == 'admin' and password == '123456':
return True
else:
return False
# 定义一个装饰器,进行付款验证
def login_required(func): # func = pay
def wrapper(*args, **kwargs):
global islogin
print('-----------付款------------')
# 验证用户是否登录
if islogin:
func(*args, **kwargs) # pay(8000)
else:
# 跳转到登录页面
print('用户没有登录,不能付款!')
islogin = login()
print('result:', islogin)
return wrapper
@login_required
def pay(money):
print('正在付款,付款金额是:{}元'.format(money))
print('付款中....')
time.sleep(2)
print('付款完成!')
# 调用 pay = wrapper(10000)
pay(10000)
pay(8000)
八.文件
1. 打开文件
open() 方法用于打开一个文件,并返回文件对象,在对文件进行处理过程都需要使用到这个函数,如果该文件无法被打开,会抛出 OSError。
语法:
open(file, mode='r', buffering=-1, encoding=None, errors=None, newline=None, closefd=True, opener=None)
参数说明:
-
file: 必需,文件路径(相对或者绝对路径)
-
mode: 可选,文件打开模式,以下是几种常用的模式:
'r' 以只读方式打开文件。文件的指针将会放在文件的开头。默认模式 'w' 打开一个文件清空后用于写入,如果该文件不存在,创建新文件 'x' 写模式,新建一个文件,如果该文件已存在则会报错 'a' 打开一个文件用于追加写,如果该文件不存在,创建新文件进行写入。 'b' 二进制模式 't' 文本模式 (默认) '+' 打开一个文件进行更新(可读可写) 'U' 通用换行模式(Python3 已弃用)
-
buffering: 设置缓冲
-
encoding: 一般使用utf8
-
errors: 报错级别
-
newline: 区分换行符
-
closefd: 传入的file参数类型
-
opener: 设置自定义开启器,开启器的返回值必须是一个打开的文件描述符。
# todo 读取二进制文件:图片、视频等
bytesFile = open(r'D:\tmp\python-io\dog.jpg', 'rb')
container = bytesFile.read()
print(type(container)) # <class 'bytes'> 打印内容得到的会是字节码
bytesFile.close()
2. 读取文件
# todo 读取文本文件
textFile01 = open(r'D:\tmp\python-io\bigdata.txt')
print(type(textFile01)) # <class '_io.TextIOWrapper'>
# todo readable():判断文件对象是否可读的
isRead = textFile01.readable() # True
print(isRead)
# todo readline():每次读取一行内容
count = 0
while True:
line = textFile01.readline()
print(type(line), line)
count += 1
if not line:
break
print('count:', count, '\n========================')
textFile01.close() # 文件对象使用完后要及时关闭,释放资源
# todo readlines():读取所有的行保存到列表中
textFile02 = open(r'D:\tmp\python-io\bigdata.txt')
lines = textFile02.readlines() # 保存到列表中
print(type(lines), lines)
for i in lines:
print(i)
textFile02.close()
# todo wordcount
textFile03 = open(r'D:\tmp\python-io\bigdata.txt')
linesList = textFile03.readlines() # 保存到列表中
textFile03.close()
wordArrList = list(map(lambda line: line.replace("\n", '').split(" "), linesList))
from tkinter import _flatten
wordList = list(filter(lambda elem: elem != '', list(_flatten(wordArrList))))
wordAndOneTupleList = list(map(lambda word: (word, 1), wordList))
def reduce_by_key_fun(inTupleList):
outDic = dict()
for kvTuple in inTupleList:
key = kvTuple[0]
value = kvTuple[1]
if key not in outDic:
outDic[key] = value
else:
outDic[key] += value
return outDic
resDict = reduce_by_key_fun(wordAndOneTupleList)
print(resDict)
textFile03.close()
3. 写入文件
更多文件操作查看:https://www.runoob.com/python3/python3-file-methods.html
import time
# 模式: 'a' - 打开一个文件用于追加写,如果该文件不存在,创建新文件进行写入。
toAppendFile = open(r'D:\tmp\python-io\db.txt', 'a')
writeRs = toAppendFile.write('mysql oracle\n postgreSQL')
time.sleep(5)
toAppendFile.flush() # 刷新后文件才会保存到文件中
print(type(writeRs), writeRs)
toAppendFile.close()
# 简写方式:可以不用手动调用close
path = 'log.txt'
with open(path, "w", encoding="utf-8") as f1:
f1.write("Hello,Python")
# 将输出的内容保存到项目根目录的新文件中
print("aaa", file=open('sss.txt', 'w'))
with关键字
# 格式:
with context [as var]:
pass
# 其中的context是一个表达式,返回的是一个对象,var用来保存context表达式返回的对象,可以有单个或者多个返回值。with本身并没有异常捕获的功能,但是如果发生了运行时异常,它照样可以关闭文件释放资源
with 关键字的实质是上下文管理:
- 上下文管理协议。包含方法
__enter__()
和__exit__()
,支持该协议对象要实现这两个方法。 - 上下文管理器,定义执行with语句时要建立的运行时上下文,负责执行with语句块上下文中的进入与退出操作。
- 进入上下文的时候执行
__enter__
方法,如果设置as var
语句,var
变量接受__enter__()
方法返回值。 - 如果运行时发生了异常,就退出上下文管理器。调用管理器
__exit__
方法。 - 详细资料参阅:https://www.jianshu.com/p/5b01fb36fd4c
4. 复制文件
# todo 文件的复制:with 结合open使用,可以帮助我们自动释放资源
with open(r'D:\tmp\python-io\dog.jpg', 'rb') as rStream:
container = rStream.read() # 读取文件内容
print(type(container)) # <class 'bytes'>
with open(r'D:\tmp\python-io\dog02.jpg', 'wb') as wStream:
wStream.write(container)
print('文件复制完成!')
5. 操作csv文件
逗号分隔值(Comma-Separated Values,CSV),其文件以纯文本形式存储表格数据(数字和文本),文件的每一行都是一个数据记录。每个记录由一个或多个字段组成,用逗号分隔。使用逗号作为字段分隔符是此文件格式的名称的来源,因为分隔字符也可以不是逗号,有时也称为字符分隔值。
在Windows下,csv文件可以通过记事本,excel,notepad++,editplus等打开
- 作用:CSV广泛用于不同体系结构的应用程序之间交换数据表格信息,解决不兼容数据格式的互通问题。
- 需要导入csv模块
读取csv
import csv
with open(r'D:\tmp\python-io\ddd.csv', encoding='utf-8') as fp: # 1.打开文件
# 获取csv读取器【delimiter指定行分隔符】
csv_reader = csv.reader(fp, delimiter=',') # <class '_csv.reader'>
print(type(csv_reader))
# 文件整体 = 第一行标题 + 内容行
header = next(csv_reader) # 获取第一行的标题
print(header)
for line in csv_reader: # 遍历所有的行
print(line)
写入csv
import csv
l1 = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
# 打开文件时,要添加newline=''参数,否则没一行会多一个空行
with open(r'filedir\1.csv', 'w', newline='') as fp: # 1.打开文件
# delimiter='\t'指定同一行数据间的分隔符
csv_writer = csv.writer(fp, delimiter='\t') # 2.获取writer
for line in l1:
csv_writer.writerow(line) # 3.写入文件
6. OS文件/目录方法
os模块提供了非常丰富的方法用来处理文件和目录。详细API查看:https://www.runoob.com/python3/python3-os-file-methods.html
import os
with open(r'D:\tmp\python-io\dog.jpg', 'rb') as jpgFile:
container = jpgFile.read()
jpgFileAllPath = jpgFile.name
print("jpgFileAllPath:", jpgFileAllPath)
jpgFileName = jpgFileAllPath[jpgFileAllPath.rfind('\\') + 1:] # 截取文件名
print("jpgFileName:", jpgFileName, '\n------------------------')
# todo __file__当前文件的绝对路径
print(__file__)
# todo 获取当前文件所属文件夹的绝对路径
print('os.getcwd():', os.getcwd())
thisPyFileDirPath = os.path.dirname(__file__)
print('os.path.dirname(__file__):', thisPyFileDirPath)
# todo 返回指定目录下的所有的文件和文件夹,保存到列表中
pwdFiles = os.listdir(thisPyFileDirPath)
print(pwdFiles)
# todo 返回的是一个拼接后的新的路径
jpgFileDirPath = os.path.dirname(jpgFileAllPath)
comboPath = os.path.join(jpgFileDirPath, '副本-' + jpgFileName)
print('comboPath:', comboPath)
with open(comboPath, 'wb') as wStream:
wStream.write(container)
# todo 创建文件夹
if not os.path.exists(r'c:\p3'):
f = os.mkdir(r'c:\p3')
print(f) # None
# todo 删除空文件夹
f = os.rmdir(r'c:\p3')
print(f) # None
# todo 删除文件
os.remove(r'D:\tmp\python-io\副本-dog.jpg')
7. 序列化与反序列化
- json模块:用于字符串和Python数据类型间进行转换。[查看json模块](#11. json模块)
- pickle模块: 用于python特有的类型和python的数据类型间进行转换。[查看pickle模块](#12. pickle模块)
九.异常处理
1. 错误与异常
错误:指的是代码有语法问题,无法解释运行,必须改正后才能运行;
异常:如果代码没有语法问题,可以运行,但会出运行时的错误,例如除零错误,下标越界等问题,这种在运行期间检测到的错误被称为***异常*** 。出现了异常必须处理否则程序会终止执行,用户体验会很差。Phthon支持程序员自己处理检测到的异常。可以使用try-except语句进行异常的检测和处理
2. try-except语法
try:
【代码块A】 #可能会出错误的代码
except Exception1[ as e]: #异常处理
【代码块1】 #异常处理代码
except Exception2[ as e]: #异常处理
【代码块2】 #异常处理代码
....
except Exceptionn[ as e]: #异常处理
【代码块n】 #异常处理代码
[else:] #可选,如果没有引发异常会执行
处理语句
[finally:] #无论如何都要执行的语句
处理语句
【后续语句】
执行流程:
- 首先执行try中【代码块A】,如果出现异常,立即终止代码执行,转而到except块中进行异常处理
- 异常处理except模块,从上往下匹配,如果能够匹配成功,立即执行相应的异常处理代码块,执行完毕后,不在往下匹配
- 捕获到了异常并处理完毕后,继续执行finally内的代码块,如果没有则执行【后续语句】
- 如果有异常但匹配不到异常,先执行finally,然后则抛出错误,终止程序执行。
- 如果没有异常,如果有else字句则执行else字句,然后再执行finally内的代码块,【后续语句】
注意事项:
- except匹配顺序从上到下
- except语句书写要求:精确的类型往前放,模糊的,不精确的往后放
- except不带任何类型,则匹配所有类型异常,应该放到最后,吞掉异常
- 可以将多种异常用元组的方式(异常类型1,异常类型2…异常类型n)书写,简化代码
- except字句中e,是一个对象,打印它,会显示异常信息描述
- try-except也可以捕获方法或函数中抛出的异常
- 所有异常类型都继承自BaseException,使用BaseException可以将异常一网打尽
- finally内的代码块常用来进行一些清理工作,比如关闭文件,数据库连接等工作
- 异常处理可以嵌套:在try块和excep块中还可以分别再嵌套try-except块
def func():
stream = None
try:
stream = open(r'D:\tmp\python-io\dog.jpg')
# stream = open(r'D:\tmp\python-io\bigdata.txt')
container = stream.read()
print(container)
stream.close()
return 1
except Exception as err:
print("err:", err)
return 2
finally:
print('------finally-----')
if stream:
stream.close()
# return 3
x = func()
print(x)
3. 手动抛异常
raise:手动抛出一个指定类型的异常,无论是哪种异常类都可以带一个字符串参数,对异常进行描述。
# raise不带参数会把错误原样抛出
try:
raise ZeroDivisionError('除0错误')
# raise ZeroDivisionError #如果不想获取错误信息,可以不带参数
except ZeroDivisionError as e:
print(e) #除0错误
4. assert断言
- 语法:
assert 条件 [,异常描述字符串]
- 执行流程:如果条件为假,则抛出AssertionError,条件为真,就当assert不存在
- 作用:对于可能出问题的代码,加上断言,方便问题排查
num = int(input('请输入一个1~9的整数:'))
assert 0 <num <=9,'num不在1~9'
print('end')
控制台日志:AssertionError: num不在1~9
请输入一个1~9的整数:88
Traceback (most recent call last):
File "D:/code/python/py-day01/P00.py", line 3, in <module>
assert 0 <num <=9,'num不在1~9'
AssertionError: num不在1~9
5. 自定义异常类
如果系统异常类型满足不了业务需求,那么可以自己定义异常类来处理。
-
自己定义的异常类必须继承BaseException或Exception
-
步骤:
- 在自定义异常类的构造函数中,调用父类构造函数
- 重写
__str__
方法输出异常信息 - 编写异常处理方法处理异常
class CustomException(BaseException): # 继承BaseException
def __init__(self, msg):
super().__init__() # 调用父类初始化
self.msg = msg
# 重写__str__,输出异常信息
def __str__(self):
return self.msg
# 3.自定义异常处理方法
def handle_exception(self):
print('异常处理')
try:
raise CustomException('自定义异常')
except CustomException as e:
print(e)
十.面向对象编程
1. 面向对象的思想
-
面向过程:面向处理,更多的是从计算机角度思考,注重计算每一个步骤,程序更像是一本cpu操作手册。
-
面向对象:以日常生活的角度思考问题的解决,更接近人的思维方式,让人可以从更高的层面考虑系统的构建。
面向对象的优点:
- 面向对象更加适合做应用的开发
- 面向对象可以使你的代码更加优雅和紧凑
- 面向对象开发效率更高
- 面向对象代码复用度更高、可维护性更好
面向对象是一种思维方式,它认为万事万物皆对象,程序是由多个对象协作共同完成功能的,所以以后我们要从面向过程转向面向对象。以面向对象的方式考虑程序的构建。面向对象的核心是:类和对象
**面向对象三大特征:**封装、继承、多态
2. 类和对象
2.1 类和对象的概念
生活角度:
- 类:具有相同特征和行为的对象的集合,是一个概念
- 对象:客观存在的一切事物,是类的实例
程序角度:
- 类:用户自定义的数据类型,是模板,不占用内存
- 对象:由类定义的变量,占用内存
2.2 类的定义
语法:
class 类名[(父类)]:
类体
注意:
- 类定义必须以关键字
class
,类体必须缩进 - 类名要符合标识符的规范,类名一般用大驼峰风格: 每个单词首字母大写,其它小写 ,例如MyBook YouMoney
- 在python3中所有的类默认继承
object
,所以可以这样写class Dog:
,它等价于class Dog(object):
- 类名可以和文件名不一致
3. 方法
3.1 构造方法
- 目的:构造方法用于初始化对象(不创建对象),可以在构造方法中添加成员属性
- 时机:实例化对象的时候自动调用
- 参数:第一个参数必须是self,其它参数根据需要自己定义
- 返回值:不返回值,或者说返回None,不应该返回任何其他值
语法:
def __init__(self,arg1,arg2....): #参数:arg1,agr2...根据需要自己定义
函数体
#如果自己不定义构造方法,系统自动生成一个构造函数
def __init__(self):
pass
注意:
-
如果没有定义构造方法,系统会生成一个无参构造方法,如果自己定义了构造方法,则系统不会自动生成
-
一个类只能有一个构造方法,如果定义多个,后面的会覆盖前面的
-
构造函数由系统在实例化对象时自动调用,不要自己调用
class Dog(object):
# todo 定义构造方法
def __init__(self, name, kind, age):
'''
成员属性描述的是对象的静态特征,作用域属于类,不会和类外的全局变量冲突。
python中成员属性可以在构造函数中添加。
'''
self.name = name # 定义对象属性,这个类所有的对象都具有该属性
self.kind = kind # 成员属性必须通过self.引用,否则是普通变量
self.age = age
# todo __str__()方法用来将对象转化为字符串,凡是涉及对象向字符串转换时都会调用(如:打印,字符串与对象拼接等)
def __str__(self):
return "Dog:name : {} age : {}".format(self.name, self.age)
# todo 对象的创建也称之为类的实例化,语法: 对象 = 类名([实参])
# 创建一个Dog的实例:旺财
wcDog = Dog('旺财', '泰迪', 3)
print("查看wdDog这个对象的类名:", wcDog.__class__)
print('我是可爱的%s犬,%s,我今年%d岁了' % (wcDog.kind, wcDog.name, wcDog.age))
# 打印对象,会自动调用__str__方法
print(wcDog)
3.2 析构方法
- 目的:对象销毁时,释放资源
- 时机:对象销毁时由系统自动调用
- 参数:除了self外,没有其他参数
- 返回值:不返回值,或者说返回None
语法:
def __del__(self):
#to do
class Dog(object):
# 构造
def __init__(self, name, kind, age):
self.name = name
self.kind = kind
self.age = age
# 析构
def __del__(self):
print('拜拜了,二十年后又是一条好汉')
xbDog = Dog('小白', '贵宾', 3)
print('我是可爱的%s犬,%s,我今年%d岁了' % (xbDog.kind, xbDog.name, xbDog.age))
del xbDog # 销毁对象,自动调用析构方法
print('\n----------------------\n')
# 在函数中对象,当函数结束运行时,自动析构
def funcDefaultDelObj():
dhDog = Dog('大黄', '田园', 3)
print('我是可爱的%s犬,%s,我今年%d岁了' % (dhDog.kind, dhDog.name, dhDog.age))
funcDefaultDelObj()
3.3 成员方法
成员方法其实就是函数,作用域在类内,成员方法的第一个参数必须是self,self代表当前对象,也就是调用这个方法的对象,这个参数由系统传递。
class Stu(object):
def __init__(self, name):
self.name = name
def memberMethod01(self, paramA, paramB): # 成员方法,第一个参数必须是self,代表当前调用对象
print('我是成员方法01,接收到了参数A为: %s ,参数B为: %s ,调用我的对象的name属性值为: %s'% (paramA, paramB,self.name))
xmStu = Stu('小明') # 实例化一个对象
# 调用方法的语法为: 对象.方法([实参])
xmStu.memberMethod01("光明中学", "二班")
注意:
- self参数在调用的时候不必传值,由系统传值
- self只能在实例方法中使用
- 方法和函数的区别:
- 方法作用域属于类,所以即便和普通函数重名,也不会被覆盖
- 方法的第一个参数必须是self,但函数不要求
- 方法必须通过对象调用,而函数不需要
- 方法的第一个参数self其实可以使任何合法标识符,不过一般约定俗成都是self
- 方法的连贯调用
class Dog:
def bark(self):
print("汪汪汪")
return self #返回self
def eat(self):
print("爱啃大骨头")
return self
dog = Dog()
dog.eat().bark() #方法的连贯调用
4 类成员(静态成员)
-
类成员属于类,为所有对象共有,可以通过类名或对象调用
-
使用对象调用属性时,成员属性优先级大于类属性,应该尽量避免成员属性和类属性重名
-
静态方法:
- 静态方法和类方法的区别:类方法第一个参数是类对象,由系统传入,静态方法没有
- 共同点:静态方法和类方法都属于类,调用方式相同
# 类属性
class Person:
# 类属性
name = '无名氏'
gender = '男'
__age = 0 # 私有类属性,只能在类内使用
def __init__(self, name, age):
self.name = name # 实例属性或成员属性
self.__age = age
# todo 方式一:类名.属性名
print(Person.name, Person.gender)
# todo 方式二:对象.属性名
jkPerson = Person('Jack', 20)
print(jkPerson.name, jkPerson.gender) # 使用对象调用属性时,成员属性优先级大于类属性
# 删除对象的成员属性后,访问到的是类属性
del jkPerson.name
print(jkPerson.name)
# 类方法、静态方法
class Date:
def __init__(self, year, month, day):
self.year = year
self.month = month
self.day = day
@classmethod # 类方法
def date_from_string(cls, date_string):
'''
:功能:根据传入字符串创建日期对象
:param cls 类对象,和类名作用一样
:param date_string: 日期字符串,格式必须是yyyy-mm-dd
:return: 日期对象
'''
if Date.is_valid_date(date_string):
year, month, day = tuple(map(int, date_string.split('-')))
return cls(year, month, day)
else:
return '您输入的日期不合法'
@staticmethod # 静态方法
def is_valid_date(date_string):
year, month, day = tuple(map(int, date_string.split('-')))
return year >= 0 and 1 < month <= 12 and 0 < day <= 31
d1 = Date.date_from_string('2018-05-29')
print(d1.year, d1.month, d1.day)
print(Date.is_valid_date('2020-12-29'))
5. 封装
封装是指隐藏对象的属性和实现细节,仅对外公开接口,控制在程序中属性的读取和修改的访问级别。
类本身就是一种封装,通过类可以将数据(属性)和行为(方法)相结合,形成一个有机的整体。封装的目的是增强安全性和简化编程,使用者不必了解具体的实现细节,而只是要通过外部接口,以特定的访问权限来使用类的成员。成员私有化是实现封装的手段。所有的成员默认是公有。
5.1 属性私有化
_xxx
:单下划线开头叫保护属性,意思是只有类对象和子类对象自己能访问到这些属性,此属性不能通过from XXX import xxx
导入;__xxx
:双下划线开头叫私有属性,只允许类本身访问,连子类对象也不能访问到这个数据。__xxx__
:前后均有一个“双下划线”,系统定义名字, 代表python里特殊方法专用的标识,如__init__()
代表类的构造函数。
class Dog:
def __init__(self, name, gender, age):
self.name = name
self._gender = gender # '保护'变量
self.__age = age # 私有变量
# 定义一个公开方法,间接设置私有变量
def set_age(self, age):
self.__age = age
# 定义一个公开的方法,间接访问私有变量
def get_age(self):
return self.__age
def printPrivateVar(self):
print("我是Dog类中的printPrivateVar方法,我可以直接访问私有属性age:", self.__age)
xhDog = Dog('小黑', '公', 5)
print("对象.成员属性 -- 获取到的 xhDog 的公有属性 name 值为:", xhDog.name)
# print(xhDog.__age) # 私有变量不能直接调用,会报错:AttributeError: 'Dog' object has no attribute '__age'
print("对象.get方法 -- 获取到的 xhDog 的私有属性 age 值为:", xhDog.get_age())
xhDog.printPrivateVar()
xhDog.set_age(10)
print('通过set方法修改私有属性age的值,得到修改后的值为:', xhDog.get_age()) # 10
print("对象.保护属性 -- 获取到的 xhDog 的保护属性 gender 值为:", xhDog._gender)
# 还可以通过 _Dog__age访问私有变量,但不建议
print(xhDog._Dog__age)
5.2 属性装饰器
对于私有属性的访问,使用公开方法间接访问的方法太麻烦,python提供了一种便捷语法,属性装饰器,通过属性装饰器,可以很方便的对私有属性进访问,属性修饰器可以把方法属性化。
# 在开发中看到一些私有化处理: 装饰器
class Student:
def __init__(self, name, age):
self.name = name
self.__age = age
# 自动生成了get方法
@property
def age(self):
return self.__age
# 自动生成了set方法【set依赖get】
@age.setter
def age(self, age):
if age > 0 and age < 100:
self.__age = age
else:
print('不在规定的范围内')
def __str__(self):
return '姓名:{},年龄:{}'.format(self.name, self.__age)
lhStu = Student('李华', 16)
print(lhStu.age)
5.3 成员方法私有化
如果对一个方法的名字前面加__,声明该方法为私有方法,只能在当前类中被调用,在外界不能通过对象直接调用,这就是私有方法
class Dog:
def __init__(self,name,age):
self.name = name
self.age = age
def __pee(self):
print('这是我的地头')
def publicMethodInClass(self):
print("我是Dog类中的公有方法publicMethodInClass,下面我尝试调用__pee方法")
self.__pee()
xcDog = Dog('小柴',5)
# xhDog.__pee # 类外不能调用私有方法,报错为:AttributeError: 'Dog' object has no attribute '__pee'
xcDog.publicMethodInClass()
6. 继承
面向对象编程 (OOP) 语言的一个主要功能就是“继承”,所谓继承就是使现有的类无需编码便可以拥有原有类的方法和属性。被继承的类可以称之为父类、基类、超类。继承的类可以称之为子类、派生类。派生和继承是一体两面,从父类向子类看就是派生,从子类向父类看就是继承。子类和父类的关系可以用is a
类表示,即子类是父类的一种,是一个更具体、更加强大的父类。python支持单继承和多继承。
继承的优点:可以简化代码,减少冗余度;提高了代码的可维护性;提高了代码的安全性。
注意:
- 一个子类只有一个父类称为单继承,一个子类有多个父类称为多继承
- 子类无法继承自父类的私有成员,子类的对象可以调用父类的非私有成员
- 方法重写(override):子类方法和父类方法重名,通过子类对象调用的是子类方法
- object是Python中所有类的父类
语法:
class 子类名(父类1,父类2,...,父类n):
pass
案例一:构造方法的继承,方法重写
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def eat(self):
print("我是父类Person中的eat方法:" + self.name + "正在吃饭...")
def run(self):
print(self.name + '正在跑步...')
class Student(Person):
# 如果子类中不定义__init__,调用父类 super class的__init__
def __init__(self, name, age, clazz):
'''
如果子类也需要定义自己的__init__,需要在当前类的__init__调用一下父类__init__
super().__init__(参数)
super(类名,对象).__init__(参数)
'''
super().__init__(name, age)
# super(Person, self).__init__(name, age) # 效果与上一行相同
self.clazz = clazz
def study(self, course):
print('{}正在学习{}课程'.format(self.name, course))
def eat(self, food):
super().eat()
print("我是子类Student中的eat方法:" + self.name + "正在吃饭...,喜欢吃:" + food)
stu = Student('阿文', 18, 'python1905')
# 子类Student调用父类Person的非私有方法run()
stu.run()
# 子类Student调用子类特有的方法study()
stu.study('python基础')
# 子类Student重写了父类Person的eat()方法,使用子类对象调用的是子类当中的eat()方法
stu.eat('满汉全席')
案例二:多继承,多重继承
class Base:
def test(self):
print('---------Base-----------')
class A(Base):
def test(self):
print('--->AAAAAAAAAA')
class B(Base):
def test(self):
print('----------->BBBBBBBB')
class C(Base):
def test(self):
print('----------->CCCCCCCCC')
class D(A, B, C):
pass
d = D()
# 现在D里找,然后按继承的顺序从左到右先找A,如果A找不到再去找A的父类,A的父类找不到再去找父类,就开始从B开始,按照前面查找A的逻辑去找。如果所有的父类都找不到就去object中找,object还找不到就报错
d.test() # 结果:--->AAAAAAAAAA
print(D.__mro__)
# todo inspect可以查看调用顺序的优先级
import inspect
print(inspect.getmro(D))
结论:广度优先。由左到右,由深入浅,最后找object
7. 多态
class Person:
def __init__(self, name):
self.name = name
# pet可以接收Pet的实例化对象
# todo pet也以接收Pet的子类Cat、Dog的实例化对象,这称之为多态
# pet还可以接收其他类型
def feed_pet(self, pet):
# isinstance(obj,类) ---> 判断obj是不是类的对象或者判断obj是不是该类子类的对象
if isinstance(pet, Pet):
print('{}喜欢养宠物:{},昵称是:{}'.format(self.name, pet.role, pet.nickname))
else:
print('不是宠物类型的。。。。')
class Pet:
role = 'Pet'
def __init__(self, nickname, age):
self.nickname = nickname
self.age = age
def show(self):
print('昵称:{},年龄:{}'.format(self.nickname, self.age))
class Cat(Pet):
role = '猫'
def catch_mouse(self):
print('抓老鼠....')
class Dog(Pet):
role = '狗'
def watch_house(self):
print('看家高手....')
class Tiger:
def eat(self):
print('太可怕了,可以吃人...')
person = Person('小红')
cat = Cat('花花', 2)
person.feed_pet(cat)
dog = Dog('大黄', 4)
person.feed_pet(dog)
tiger = Tiger()
person.feed_pet(tiger)
8. 单例和多例
__new__
是类方法,有返回值,用于创建一个对象;__init__
用于初始化对象,没有返回值;__new__
默认参数是cls
,系统传递的是类;__init__
默认参数是self,系统传递的是当前类的实例化对象__new__
先于__ init__
执行
# 多例
class Dog:
# 重写__new__()方法
def __new__(cls, *args, **kwargs):
print('new方法在执行')
return super().__new__(cls, *args, **kwargs) # 必须通过父类的__new__创建对象
# return object.__new__(cls, *args, **kwargs)
newDog01 = Dog() # 默认调用的是new方法,说明new的优先级比init高
newDog02 = Dog()
print(id(newDog01), id(newDog02), newDog01 == newDog02) # 内存地址不一致Flase,说明这是多例
单例设计模式:
所谓单例也就是一个类只生成一个对象,无论你实例化多少对象,都是同一个对象,应用场景:数据库操作类,文件操作类等,可以减少资源的占用。
'''
设计模式的概念:对特定问题的一种解决方案,和平台、语言无关
作用:
更好的理解面向对象
让你的代码更加优雅
使你的代码更加容易扩展和复用
面试时候的重点
设计模式的一些基本原则:
高内聚,低耦合
单一职责
开闭原则(对修改封闭、对扩展开放)
'''
class Singleton:
# 保存实例的引用,私有属性
__instance = None
def __new__(cls, *args, **kwargs):
if cls.__instance is None: # 如果实例没有实例化
cls.__instance = object.__new__(cls, *args, **kwargs) # 实例化对象,将引用存到__instance
return cls.__instance # 返回对象
s1 = Singleton()
s2 = Singleton()
print(id(s1), id(s2), s1 == s2)
9. 类的其他系统方法
9.1 算数运算符重载
在python中自定义类的对象也可以象系统类型一样完成+、-、*、/、索引、切片等运算,这有赖于python类有运算符重载功能。
更多魔术方法查看:https://www.cnblogs.com/zhangboblogs/p/7860929.html
'''
__add__: 加运算
__sub__: 减运算
__mul__: 乘运算
__truediv__: 除运算
__mod__: 求余运算
__pow__: 乘方
'''
class MyTime:
def __init__(self, hour, minute, second):
self.hour = hour
self.minute = minute
self.second = second
def __add__(self, other):
if not isinstance(other, self.__class__):
raise TypeError('您传入的参数不是MyTime类型')
second = self.second + other.second
minute = self.minute + other.minute + second // 60
hour = (self.hour + other.hour + minute // 60) % 24
return self.__class__(hour, minute % 60, second % 60)
def __str__(self):
return "{}:{}:{}".format(self.hour, self.minute, self.second)
t1 = MyTime(5, 34, 45)
t2 = MyTime(8, 45, 34)
res = t1 + t2 # t1.__add__(t2)
print('t1 + t2 结果为:', res)
9.2 迭代器
如果想让一个类用于for-in 循环则必须实现__iter__
和__next__
方法
class Fib:
def __init__(self):
self.x = 0
self.y = 1
def __iter__(self):
return self # 返回当前对象
def __next__(self):
self.x, self.y = self.y, self.x + self.y # 迭代公式
if self.x > 1000:
raise StopIteration()
return self.x
fIt = Fib()
# for x in fIt:
# print(x, end=" ")
while True:
try:
print(next(fIt))
except StopIteration: # StopIteration 异常用于标识迭代的完成,防止出现无限循环的情况。
import sys
sys.exit()
9.3 类装饰器
如果一个类实现了__call__(slef, [,*args [,**kwargs]])
方法,则该类的对象可以象函数一样调用。它是实现类装饰器的基础
class Demo:
def __call__(self, *args, **kwargs):
print("我是函数,不过是假冒的")
d1 = Demo()
d1()
类装饰器:
class Decrator:
def __call__(self, func):
def inner():
func()
print("*"*50)
return inner
@Decrator()
def test():
print("我是你们的小可爱")
test()
十一.导入模块
更详细讲解参阅:https://www.runoob.com/python3/python3-module.html
在Python中,模块是代码组织的一种方式,一个.py
文件就是一个模块,模块名就是文件名去了py
后缀。我们常把功能相近的函数和类放到一个模块内,模块可以提高代码的复用性和可维护性。一个模块编写完毕后,可以很方便的在其他项目中导入。模块还可解决命名冲突问题,不同模块中相同的命名不会冲突。常用标准库如下
标准库 | 说明 |
---|---|
builtins | 内建函数默认加载 |
math | 数学库 |
random | 生成随机数 |
time | 时间 |
datetime | 日期和时间 |
calendar | 日历 |
hashlib | 加密算法 |
copy | 拷贝 |
functools | 常用的工具 |
os | 操作系统接口 |
re | 字符串正则匹配 |
sys | Python自身的运行环境 |
multiprocessing | 多进程 |
threading | 多线程 |
json | 编码和解码 JSON 对象 |
logging | 记录日志,调试 |
示例:自定义模块并导入使用:
1.新建一个python项目
2.在项目根目录下新建utils/aaa
目录,并在这个新目录下新建文件MyUtils.py
和__init__.py
,文件内容分别如下
# 当本模块被通过import * 导入时,只有 __all__当中的变量能被导入,FIRST_VARABLE这个变量不会被导入
__all__ = ['compareInt', 'Dog']
FIRST_VARABLE = "Hello,Python!!!"
def compareInt(a, b):
if a > b:
print("{} 大于 {}".format(a, b))
elif a == b:
print("{} 等于 {}".format(a, b))
else:
print("{} 小于 {}".format(a, b))
class Dog(object):
def __init__(self, name, role):
self.name = name
self.role = role
print('汪汪,初始化了一个Dog对象,名字:{},品种:{}'.format(self.name, self.role))
def execWhenImportAndRun():
print(__name__)
print('MyUtils.py文件中的execWhenLoad函数被执行了')
if __name__ == '__main__':
# todo 当MyUtils模块被MyMain模块导入的时候,MyUtils会被加载到内存,execWhenLoad()函数会被自动执行
# todo 如何让execWhenLoad()不执行呢,即加上判断条件 if __name__ == '__main__':
# todo 原理是: 只有运行MyUtils.py这个文件时,这个文件的 __name__ 变量才会等于 '__main__'
execWhenImportAndRun()
__all__ = ['MyUtils'] # 作用:使用from utils.aaa import * 时,可以把MyUtils模块一起导入
'''
__init__.py文件的作用:
1、可以使用 import utils.aaa ,并默认将__init__.py文件加载到内存中
'''
print("我是__init__.py文件,我被加载到内存时会打印这句话")
aaaParam = 'AAA'
def aaafunc():
print("我是__init__.py中的aaafunc函数")
3.在项目根目录下新建新建文件MyMain.py
,文件内容如下
# todo 1、方式一:import 模块名 ,系统包和同一模块下的包可以使用这种方式
import random
print(random.randint(0, 10)) # 返回一个 [0,10) 之间的整数
import sys
print(sys.version)
print(sys.argv) # 运行程序时的参数,argv是一个列表
print(sys.path) # 默认包的搜索路径
# todo 2、方式二:from 包名 import 模块名
# from utils.aaa import MyUtils # 导入MyUtils模块
# print(MyUtils.FIRST_VARABLE) # 使用MyUtils中的变量
# MyUtils.compareInt(1, 2) # 使用MyUtils中的函数
# wcDog = MyUtils.Dog('旺财', '土狗') # 使用MyUtils中的类初始化对象
# todo 3、方式三:from 包名.模块名 import * ,注意:被导入模块下是否有__all__
# from utils.aaa.MyUtils import *
# # print(FIRST_VARABLE) # MyUtils.py文件中的__all__变量里没有FIRST_VARABLE这个变量
# compareInt(1, 2)
# wcDog = Dog('旺财', '土狗')
# todo 4、方式四:如果包下有__init__.py文件,可以使用 import 包名 ,用作初始化加载
# import utils.aaa
# print(utils.aaa.aaaParam)
# utils.aaa.aaafunc()
'''
import utils.aaa只会导入__init__.py中的内容,如果想把aaa中所有模块都导入可以使用from utils.aaa import *
并且在__init__.py中加入 __all__ = ['MyUtils']
'''
# from utils.aaa import *
# print(MyUtils.FIRST_VARABLE)
注意:要避免循环导入,即避免两个包之间相互调用
十二.常用标准库简介
01. time模块
更详细内容可查阅:https://www.runoob.com/?s=time
import time
# todo 1、时间戳 & 时间元祖 转换
timeStamp = time.time() # 获取当前时间戳
localTimeTuple = time.localtime() # 获取当前时间元组
print(type(timeStamp), timeStamp) # <class 'float'> 1604234828.8031926
print(type(localTimeTuple), localTimeTuple) # <class 'time.struct_time'>
print('今天是{}年的第{}'.format(localTimeTuple.tm_year, localTimeTuple.tm_yday)) # 获取时间元组中的时间信息
timeStampFromTuple = time.mktime(localTimeTuple) # 将时间元组的转成时间戳
print(type(timeStampFromTuple), timeStampFromTuple)
defaultTimeStr = time.ctime(timeStamp) # 得到默认"%a %b %d %H:%M:%S %Y"格式将时间转为字符串
strToTimeTuple = time.strptime(defaultTimeStr, "%a %b %d %H:%M:%S %Y") # 第二个参数可以不写,默认就是这种格式来解析
print(type(strToTimeTuple), strToTimeTuple)
# todo 2、时间元祖的格式化与解析
# time.struct_time类型
timeTupleToStr = time.strftime('%Y-%m-%d %H:%M:%S', localTimeTuple) # 第二个参数默认是当前时间
print(type(timeTupleToStr), timeTupleToStr)
# 将格式字符串转换为时间戳
strToTimeTuple = time.strptime('2020-11-01 21:16:13', '%Y-%m-%d %H:%M:%S')
print(type(strToTimeTuple), strToTimeTuple)
# todo 3、 线程暂停1s
# time.sleep(1)
属性 | 含义 | 值 |
---|---|---|
tm_year | 4位数年 | 2008 |
tm_mon | 月 | 1 到 12 |
tm_mday | 日 | 1 到 31 |
tm_hour | 小时 | 0 到 23 |
tm_min | 分钟 | 0 到 59 |
tm_sec | 秒 | 0 到 61 (60或61 是闰秒) |
tm_wday | 一周的第几日 | 0到6 (0是周一) |
tm_yday | 一年的第几日 | 1 到 366(儒略历) |
tm_isdst | 夏令时 | -1, 0, 1, -1是决定是否为夏令时的旗帜 |
02. datetime模块
更详细内容可查阅:https://www.cnblogs.com/awakenedy/articles/9182036.html
datetime模块中包含如下类
类名 | 功能说明 |
---|---|
date | 日期对象,常用的属性有year, month, day |
time | 时间对象 |
datetime | 日期时间对象,常用的属性有hour, minute, second, microsecond |
datetime_CAPI | 日期时间对象C语言接口 |
timedelta | 时间间隔,即两个时间点之间的长度 |
tzinfo | 时区信息对象 |
datetime模块中包含的常量
常量 | 功能说明 | 用法 | 返回值 |
---|---|---|---|
MAXYEAR | 返回能表示的最大年份 | datetime.MAXYEAR | 9999 |
MINYEAR | 返回能表示的最小年份 | datetime.MINYEAR | 1 |
import datetime
# todo 1、datetime模块中的datetime类
nowDateTime = datetime.datetime.now() # 获取当前的日期和时间
print(type(nowDateTime), nowDateTime) # <class 'datetime.datetime'> 2020-11-01 22:23:06.704885
customDateTime = datetime.datetime(2017, 3, 22, 16, 9, 33, 494248) # 自定义创建datetime对象,年月日是必选参数
# 日期格式化
datetimeToStr = datetime.datetime.strftime(customDateTime, "%Y-%m-%d %H:%M:%S")
print(type(datetimeToStr),datetimeToStr) # <class 'str'> 2017-03-22 16:09:33
# 日期解析
dateTimeFromCustomStr = datetime.datetime.strptime('2017-3-22 15:25', '%Y-%m-%d %H:%M')
print(type(dateTimeFromCustomStr), dateTimeFromCustomStr)
# datetime转换为时间元组
datetimeToTimeTuple = customDateTime.timetuple()
print(type(datetimeToTimeTuple), datetimeToTimeTuple) # <class 'time.struct_time'> 返回datetime对象的时间元组
# datetime转换为时间戳
datetimeToTimeStamp = customDateTime.timestamp()
print(type(datetimeToTimeStamp), datetimeToTimeStamp) # <class 'float'> 1490170173.494248 返回datetime对象的时间时间戳
# 计算当前时间加上时间差后的日期
timeDel = datetime.timedelta(days=3, hours=10) # 时间差
print(type(timeDel), timeDel) # 2020-11-05 08:23:56.743661
addDateTimeRes = nowDateTime + timeDel
print(type(addDateTimeRes), addDateTimeRes) # <class 'datetime.datetime'> 2020-11-05 08:25:23.702800
# 返回datetime对象的date部分
subDate = customDateTime.date()
print(type(subDate), subDate) # <class 'datetime.date'> 2017-03-22
# 返回datetime对象的time部分
subTime = customDateTime.time()
print(type(subTime), subTime) # <class 'datetime.time'> 16:09:33.494248
# 将一个date对象和一个time对象合并生成一个datetime对象
datetime.datetime.combine(subDate,subTime)
# todo 1、datetime模块中的date对象: date对象由year年份、month月份及day日期三部分构成
# 获取当前的date对象
todayDate = datetime.date.today()
print(type(todayDate), todayDate, todayDate.day) # <class 'datetime.date'> 2020-11-01 1
# 自定义创建date对象
customDate = datetime.date(2019, 10, 20) # 三个参数都必选
# date对象可用于比较日期大小
# __eq__() 等于 __ge__() 大于等于 __le__() 小于等于 __gt__() 大于 __lt__() 小于 __ne__() 不等于
print(todayDate.__eq__(customDate)) # False
# 获得两个个日期相差多少天
print(todayDate.__sub__(customDate).days) # __rsub__(...)方法是反向操作
# 日期格式化
print(todayDate.__format__('%Y-%m-%d')) # 等价于 todayDate.strftime('%Y-%m-%d')
# date 转换为 ctime
timeStampFromDatetime = todayDate.ctime()
print(type(timeStampFromDatetime), timeStampFromDatetime) # <class 'str'> Sun Nov 1 00:00:00 2020
03. calendar模块
使用到时再做补充
04. random模块
函数名 | 函数说明 |
---|---|
random.randint(start,end) | 返回[start end]之间的一个随机整数 |
random.random() | 返回一个[0.0,1.0)之间的随机小数 |
choice(seq) | 返回一个序列(列表、元组,字符串)中的一个随机元素 |
shuffle(seq) | 将序列元素随机排列(打乱顺序) |
random.randrange(start,stop,step) | 从范围为[start,end),步长为step的rangge中随机返回一个整数 |
import random
# 随机返回一个序列(列表、元组,字符串)中的一个元素
list5 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
print(random.choice(list5))
print(random.choice("Hello,Python"))
# 将序列元素随机排列(打乱顺序)
random.shuffle(list5)
print(list5)
05. math模块
函数名 | 函数的说明 | 示例 |
---|---|---|
abs | 取绝对值 | abs(-10) |
pow(x,y) | x的y次方 | pow(10,2)求10的平方 |
round(x,[n]) | 浮点数的4舍5入, n代表保留小数的位数 | round(3.456) |
max() | 求给定参数的最大值 | max(21,43,65,75,86,32,3,45) |
min() | 求给定参数的最小值 | min(21,43,65,75,86,32,3,45) |
math.ceil() | 需要导入import math库 向上取整 | math.ceil(18.1) #19 |
math.floor() | 需要导入import math库 向下取整 | math.floor(18.1) #18 |
math.sqrt | 需要导入import math库 求平方根 | math.sqrt(100) |
06. sys模块
Python 中可以所用 sys 的 sys.argv 来获取命令行参数:
sys.argv
表示命令行参数列表。len(sys.argv)
表示命令行参数个数。sys.argv[索引]
获取命令行参数,索引从1开始。sys.argv[0]
获取到的是文件名
编写 test.py
文件代码如下
#!/usr/bin/python3
import sys
print ('参数个数为:', len(sys.argv), '个参数。')
print ('参数列表:', str(sys.argv))
print ('第一个参数是:', sys.argv[1])
# sys.stdin # 可以像input一样,接收用户的输入。接收用户的输入,和 input 相关
# sys.stdout 和 sys.stderr 默认都是在控制台
# sys.stdout # 修改sys.stdout 可以改变默认输出位置
# sys.stderr # 修改sys.stderr 可以改变错误输出的默认位置
sys.exit(100) # 程序退出,和内置函数exit功能一致
使用cmd命令行执行以上代码,输出结果为:
python test.py arg1 arg2 arg3
参数个数为: 4 个参数。
参数列表: ['test.py', 'arg1', 'arg2', 'arg3']
第一个参数是: arg1
07. argparse模块
argparse 模块可以轻松编写用户友好的命令行接口。程序定义它需要的参数,然后 argparse 将弄清如何从 sys.argv 解析出那些参数。 argparse 模块还会自动生成帮助和使用手册,并在用户给程序传入无效参数时报出错误信息。
import argparse
# todo 1、创建一个解析器对象
parse = argparse.ArgumentParser(prog="默认值为sys.argv[0],即文件名,用来在help信息中描述应用程序的名称",
usage='%(prog)s,描述程序用途', # %(prog)s 会打印prog变量
description='help信息前显示的信息',
epilog='help信息之后显示的信息')
# print(parse.print_help())
# todo 2、添加位置参数【必选参数】 python MyArgParse.py zhangsan 18
parse.add_argument("name", type=str, help='位置参数:你的名字')
parse.add_argument("age", type=int, help='位置参数:你的名字')
# todo 3、添加可选参数 python MyArgParse.py zhangsan 18 -s 男 -s 女
# action='append' 设置可以添加多个参数,得到的结果是一个字符串数组(若不添加此配置,`女` 会覆盖 `男`)
# parse.add_argument("-s", '--sex', action='append', type=str, help='可选参数--性别')
# choices=['男', '女']限定参数范围,default设置参数默认值
parse.add_argument("-s", '--sex', default='男', choices=['男', '女'], type=str, help='可选参数:性别')
# todo 4、解析参数
result = parse.parse_args()
print(result)
print(result.name,result.age,result.sex)
08. re模块
正则表达式是对字符串操作的一种逻辑公式,就是用事先定义好的一些特定字符、及这些特定字符的组合,组成一个“规则字符串”,这个“规则字符串”用来表达对字符串的一种过滤逻辑。爬虫中使用的较多。
更详细内容可查阅:https://www.runoob.com/?s=timehttps://www.runoob.com/python3/python3-reg-expressions.html
import re
# todo 1、match 只从开头进行匹配,如果匹配不成功则就返回None
msg = 'abcdefdef'
pattern = re.compile('abc')
result01 = pattern.match(msg)
print(type(result01), result01) # <class '_sre.SRE_Match'> <_sre.SRE_Match object; span=(0, 3), match='abc'>
result02 = re.match('def', msg) # 只要从开头进行匹配,如果匹配不成功则就返回None
print(type(result02), result02) # None
# todo 2、search匹配的是整个字符串
result03 = re.search('def', msg)
print(type(result03), result03) # <class '_sre.SRE_Match'> <_sre.SRE_Match object; span=(3, 6), match='def'>
# todo 3、span返回配到的索引位置
print(result03.span()) # (3, 6)
# todo 4、group提取到匹配的内容
phone = '15901018869'
result04 = re.match(r'1\d{9}[0-35-689]$', phone) # 不是以4、7结尾的手机号码(11位)
print(result03.group()) # def
print(result04.group()) # 15901018869
phone = '010-12345678'
result05 = re.match(r'(\d{3}|\d{4})-(\d{8})$', phone)
print(result05.group())
# () 表示分组 group(1) 表示提取到第一组的内容 group(2)表示第二组的内容
print(result05.group(1))
print(result05.group(2))
09. hashlib模块
# 常见加密算法: md5 sha1 sha256 base64
import hashlib
msg = '你好,Python!!!'
md5 = hashlib.md5(msg.encode('utf-8'))
print(type(md5), md5) # <class '_hashlib.HASH'> <md5 HASH object @ 0x034565A8> 409a4b81225ada9764fcf7a75bd250c8
md5Str = md5.hexdigest()
print(type(md5Str), md5Str, len(md5Str)) # <class 'str'> 409a4b81225ada9764fcf7a75bd250c8
sha1 = hashlib.sha1(msg.encode('utf-8'))
print(len(sha1.hexdigest())) # 40
sha256 = hashlib.sha256(msg.encode('utf-8'))
print(len(sha256.hexdigest())) # 64
10. requests模块
# 引入第三方库要在项目根目录打开cmd使用 pip install requests 安装第三方库
import requests
response = requests.get('http://www.12306.cn/')
print(response.text)
11. json模块
[返回序列化与反序列化章节](#7. 序列化与反序列化)
import json
'''
json里将数据持久有两个方法:
dumps:将数据转换成为json字符串,不会将数据保存到文件里。
dump: 将数据转换成为json字符串的同时写入到指定文件。
'''
names = ['zhangsan', 'lisi', 'jack', 'tony']
file = open('names.txt', 'w', encoding='utf8')
json.dump(names, file)
file.close()
print(json.dumps(names))
'''
json 反序列化也有两个方法:
loads: 将json字符串加载成为Python里的数据
load: 读取文件,把读取的内容加载成为Python里的数据
'''
x = '{"name":"zhangsan","age":18}' # 符合json规则的字符串
p = json.loads(x)
print(p, type(p))
print(p['name'])
file1 = open('names.txt', 'r', encoding='utf8')
y = json.load(file1)
print(type(y), y)
print(y[0])
file1.close()
12. pickle模块
[返回序列化与反序列化章节](#7. 序列化与反序列化)
import pickle
class Dog(object):
def __init__(self, name, color):
self.name = name
self.color = color
orgDog = Dog('大黄', '白色')
# 保存到文件里,file必须以二进制可写模式打开,即“wb”
pickle.dump(orgDog, open('dog.txt', 'wb')) # 数据通过特殊的形式转换为只有python语言认识的字符串,并写入文件
# 从文件里加载出来,file必须以二进制可读模式打开,即“rb”
ldDog = pickle.load(open('dog.txt', 'rb'))
print(type(ldDog), ldDog) # <class '__main__.Dog'> <__main__.Dog object at 0x0305A390>
dpsDog = pickle.dumps(orgDog) # 数据通过特殊的形式转换为只有python语言认识的字符串
print(type(dpsDog), dpsDog) # <class 'bytes'>
ldsDog = pickle.loads(dpsDog)
print(type(ldsDog), ldsDog) # <class '__main__.Dog'> <__main__.Dog object at 0x0306A290>
13. pymysql模块
import pymysql # pip install pymysql
connect = pymysql.connect(host='localhost',
user='root',
password='123456',
#db='sqlwork',
charset='utf8')
cursor = connect.cursor() # <class 'pymysql.cursors.Cursor'>
cursor.execute('select * from sqlwork.students')
connect.commit() # 需要手动提交才会执行
cursor.close()
connect.close()
# 遍历查询结果
for info in cursor.fetchall():
print(info)
十三. 多线程与多进程
1. 并发与并行
并发:指两个或多个事件在同一个时间段内发生。
并行:指两个或多个事件在同一时刻发生(同时发生)。
在操作系统中,安装了多个程序,并发指的是在一段时间内宏观上有多个程序同时运行,这在单 CPU 系统中,每一时刻只能有一道程序执行,即微观上这些程序是分时的交替运行,只不过是给人的感觉是同时运行,那是因为分时交替运行的时间是非常短的。
而在多个 CPU 系统中,这些可以并发执行的程序便可以分配到多个处理器上(CPU),实现多任务并行执行,即利用每个处理器来处理一个可以并发执行的程序,这样多个程序便可以同时执行。目前电脑市场上说的多核CPU,便是多核处理器,核越多,并行处理的程序越多,能大大的提高电脑运行的效率。
注意:单核处理器的计算机肯定是不能并行的处理多个任务的,只能是多个任务在单个CPU上并发运行。同理,线程也是一样的,从宏观角度上理解线程是并行运行的,但是从微观角度上分析却是串行运行的,即一个线程一个线程的去运行,当系统只有一个CPU时,线程会以某种顺序执行多个线程,我们把这种情况称之为线程调度。
2. 线程与进程
进程:是指一个内存中运行的应用程序,每个进程都有一个独立的内存空间,一个应用程序可以同时运行多个进程;进程也是程序的一次执行过程,是系统运行程序的基本单位;系统运行一个程序即是一个进程从创建、运行到消亡的过程。
线程:进程中的一个执行单元,负责当前进程中程序的执行,一个进程中至少有一个线程。一个进程中是可以有多个线程的,这个应用程序也可以称之为多线程程序。
进程与线程的区别:
-
进程:有独立的内存空间,进程中的数据存放空间(堆空间和栈空间)是独立的,至少有一个线程。
-
线程:堆空间是共享的,栈空间是独立的,线程消耗的资源比进程小的多。
注意:
-
因为一个进程中的多个线程是并发运行的,那么从微观角度看也是有先后顺序的,哪个线程执行完全取决于 CPU 的调度,程序员是干涉不了的。而这也就造成的多线程的随机性。
-
由于创建一个线程的开销比创建一个进程的开销小的多,那么我们在开发多任务运行的时候,通常考虑创建多线程,而不是创建多进程。
线程调度:
- 分时调度:所有线程轮流使用 CPU 的使用权,平均分配每个线程占用 CPU 的时间。
- 抢占式调度:优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一个(线程随机性),Java使用的为抢占式调度。
3. 多线程
入门案例 - 边唱边跳:
import time
def dance(n):
for i in range(n):
time.sleep(0.05)
print("我在跳舞-------")
def sing():
for i in range(50):
time.sleep(0.05)
print("我在唱歌。。。。")
# dance()
# sing()
# 上面代码的执行逻辑是先跳舞然后再唱歌
# 如果想现在实现一边唱歌一边跳舞,就需要使用到多线程
import threading
# Thread的构造方法: def __init__(self, group=None, target=None, name=None, args=(), kwargs=None, *, daemon=None):
t1 = threading.Thread(target=dance, args=(50,))
t2 = threading.Thread(target=sing)
t1.start()
t2.start()
卖票 - 共享变量与线程锁:
import time
import threading
ticket = 20
# todo 1、创建一把锁
lock = threading.Lock()
def sell_ticket():
global ticket
while True:
lock.acquire() # todo 2、加同步锁
if ticket > 0:
time.sleep(0.2)
ticket = ticket - 1
lock.release() # todo 3、释放锁
print('{}卖出了一张票,还剩{}张票'.format(threading.current_thread().name, ticket))
else:
lock.release() # todo 3、释放锁
print('票买完了!!!')
break
t1 = threading.Thread(target=sell_ticket, name='线程一')
t2 = threading.Thread(target=sell_ticket, name='线程二')
t1.start()
t2.start()
模拟消息队列 - 使用Queue进行线程间通信:
import threading, queue, time
# todo 创建一个队列用来储存生产的面包, 队列结构的特点:FIFO 先进先出
q = queue.Queue()
def profucer():
for i in range(1, 11):
time.sleep(0.3)
q.put('bread - {}'.format(i))
print('生产的第 {} 块面包'.format(i))
def customer():
for i in range(10):
time.sleep(0.2)
# todo q.get()方法是一个阻塞方法,如果无法从q队列中获取到数据,会阻塞后续语句的执行直至获取到数据
print('消费到了面包{}'.format(q.get()))
threading.Thread(target=profucer, name='p1').start()
threading.Thread(target=customer, name='c1').start()
4. 多进程
入门案例 - 边唱边跳:
import multiprocessing, time, os
# todo 注意:不同进程间不能共享变量
def dance():
for i in range(50):
time.sleep(0.3)
print('正在跳舞,pid = {}'.format(os.getpid()))
def sing():
for i in range(50):
time.sleep(0.3)
print('正在唱歌,pid = {}'.format(os.getpid()))
if __name__ == '__main__':
print('主进程的pid = {}'.format(os.getpid()))
# 创建两个进程
multiprocessing.Process(target=dance).start()
multiprocessing.Process(target=sing).start()
进程间相互通信:
import multiprocessing, time, os
from multiprocessing import Queue
def profucer(x):
for i in range(1, 11):
time.sleep(0.3)
x.put('bread - {}'.format(i))
print('生产的第 {} 块面包,pid = {}'.format(i, os.getpid()))
def customer(x):
for i in range(10):
time.sleep(0.2)
# todo q.get()方法是一个阻塞方法,如果无法从q队列中获取到数据,会阻塞后续语句的执行直至获取到数据
print('消费到了面包{},pid = {}'.format(x.get(), os.getpid()))
# todo 进程间通信
if __name__ == '__main__':
q = Queue()
# 创建两个进程
multiprocessing.Process(target=profucer, args=(q,)).start()
multiprocessing.Process(target=customer, args=(q,)).start()
5. 进程池
from multiprocessing import Pool
import os, time, random
# 如果进程池间需要通信需要使用multiprocessing.Manager()当中的Queue()
def worker(msg):
t_start = time.time()
print("%s开始执行,进程号为%d" % (msg, os.getpid()))
# random.random()随机生成0~1之间的浮点数
time.sleep(random.random() * 2)
t_stop = time.time()
print(msg, "执行完毕,耗时%.2f" % (t_stop - t_start))
if __name__ == '__main__':
po = Pool(3) # 定义一个进程池,最大进程数3
for i in range(0, 10):
# Pool().apply_async(要调用的目标,(传递给目标的参数元祖,))
# 每次循环将会用空闲出来的子进程去调用目标
po.apply_async(worker, (i,))
print("----start----")
po.close() # 关闭进程池,关闭后po不再接收新的请求
po.join() # 等待po中所有子进程执行完成,必须放在cLose语句之后print("-----end-----")
print("-----end-----")
十四.网络编程
1. 网络通信基础知识
软件结构:
-
C/S结构 :全称为Client/Server结构,是指客户端和服务器结构。常见程序有QQ、迅雷等软件。
-
B/S结构 :全称为Browser/Server结构,是指浏览器和服务器结构。常见浏览器有谷歌、火狐等。
两种架构各有优势,但是无论哪种架构,都离不开网络的支持。
网络编程,就是在一定的协议下,实现两台计算机的通信的程序。
网络编程三要素:
- 协议
- IP地址
- 端口号
常见的网络协议:
- TCP:传输控制协议 (Transmission Control Protocol)。TCP协议是面向连接的通信协议,即传输数据之前,在发送端和接收端建立逻辑连接,然后再传输数据,它提供了两台计算机之间可靠无差错的数据传输。三次握手:TCP协议中,在发送数据的准备阶段,客户端与服务器之间的三次交互,以保证连接的可靠。完成三次握手,连接建立后,客户端和服务器就可以开始进行数据传输了。由于这种面向连接的特性,TCP协议可以保证 传输数据的安全,所以应用十分广泛,例如下载文件、浏览网页等。
- 第一次握手,客户端向服务器端发出连接请求,等待服务器确认。
- 第二次握手,服务器端向客户端回送一个响应,通知客户端收到了连接请求。
- 第三次握手,客户端再次向服务器端发送确认信息,确认连接。整个交互过程如下图所示。
- UDP:用户数据报协议(User Datagram Protocol)。UDP协议是一个面向无连接的协议。传输数据时,不需要建立连接,不管对方端服务是否启动,直接将数据、数据源和目的地都封装在数据包中,直接发送。每个数据包的大小限制在64k以内。它是不可靠协议,因为无连接,所以传输速度快,但是容易丢失数据。日常应用中,例如视频会议、QQ聊天等。数据报:Datagram 在网络中数据传递的基本单位
2. UDP协议编程案例
使用UDP协议发送数据
import socket
# 不同电脑之间的通信需要使用socket
# socket可以在不同的电脑间通信;还可以在同一个电脑的不同程序之间通信
'''
1.创建socket,并连接
AF_INET:表示这个socket是用来进行网络连接
SOCK_DGRAM:表示连接是一个 udp 连接
'''
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
'''
2.发送数据
def sendto(self, data, flags=None, *args, **kwargs):
data:要发送的数据,它是二进制的数据
address:发送给谁,参数是一个元组(ip,端口号)
'''
s.sendto('下午好'.encode('utf8'), ('localhost', 9090))
# s.sendto('下午好'.encode('utf8'), ('192.168.52.110', 9090))
'''
3. 关闭socket
'''
s.close()
'''
如何在192.168.52.110服务器(Centos7系统)等待接受数据
1、安装netcat(简称nc)网络工具: yum install -y nc
2、启动nc客户端监听: nc -lu 9090 # -l 表示监听 -u表示udp协议
3、可直接在nc客户端发送数
'''
使用UDP协议接受数据
import socket
# 创建一个基于 udp 的网络socket连接
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 绑定本机的ip地址和端口号用来监听
s.bind(('localhost', 9090))
# s.bind(('192.168.52.2', 9090))
'''
recvfrom是一个阻塞的方法,接收到的数据是一个元组,元组里有两个元素
第 0 个元素是接收到的数据,第 1 个元素是发送方的 ip地址和端口号
从服务器向本机发送数据: nc -u 192.168.52.2 9090
'''
data, addr = s.recvfrom(1024) # recvfrom是一个阻塞的方法,等待
print('从{}地址{}端口号接收到了消息,内容是:{}'.format(addr[0], addr[1], data.decode('utf8')))
s.close()
3. TCP协议编程案例
创建TCP客户端发送数据
import socket
# 基于tcp协议的socket连接
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 在发送数据之前,必须要先和服务器建立连接
s.connect(('localhost', 9091)) # 调用connect 方法连接到服务器
# s.connect(('192.168.52.110', 9091)) # 可在服务器使用 nc -l 9091 # 监听通过TCP协议发送到本机9091端口的数据
s.send('hello'.encode('utf8'))
# udp 直接使用sendto发送数据
# s.sendto('hello'.encode('utf8'),('192.168.31.199',9090))
s.close()
创建TCP服务器发送数据
import socket
# 创建一个socket连接
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(('localhost', 9091))
# s.bind(('192.168.52.2', 9091)) # 从服务器向本机发送数据: nc 192.168.52.2 9091
s.listen(128) # listen([backlog]) 方法中的backlog参数可理解为限制客户端的最大连接数
'''
接收到的结果是一个元组,元组里有两个元素
第 0 个元素是客户端的socket连接,第 1 个元素是客户端的 ip 地址和端口号
x = s.accept() # 接收客户端的请求
'''
client_socket, client_addr = s.accept()
data = client_socket.recv(1024) # tcp里使用recv获取数据
print('接收到了{}客户端{}端口号发送的数据,内容是:{}'.format(client_addr[0], client_addr[1], data.decode('utf8')))
4. 文件下载编程案例
创建文件下载服务器
import socket, os
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.bind(('192.168.52.2', 9092))
server_socket.listen(128)
# 接收客户端的请求
client_socket, client_addr = server_socket.accept()
file_name = client_socket.recv(1024).decode('utf8')
if os.path.isfile(file_name):
print('读取文件,返回给客户端')
with open(file_name, 'rb') as file:
content = file.read()
print(type(content), content)
# client_socket.send(content)
else:
print('文件不存在')
创建文件下载客户端
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('192.168.52.2', 9092))
# s.send('hello'.encode('utf8'))
file_name = input('清输入您要下载的文件名:')
s.send(file_name.encode('utf8'))
with open('demo-复制.txt', 'wb') as file:
while True:
content = s.recv(1024)
if not content:
break
file.write(content)
s.close()