Python3基础知识

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 特点

  1. 易于学习:Python有相对较少的关键字,结构简单,和一个明确定义的语法,学习起来更加简单。

  2. 易于阅读:Python代码定义的更清晰。

  3. 易于维护:Python的成功在于它的源代码是相当容易维护的。

  4. 一个广泛的标准库:Python的最大的优势之一是丰富的库,跨平台的,在UNIX,Windows和Macintosh兼容很好

  5. 互动模式:互动模式的支持,您可以从终端输入执行代码并获得结果的语言,互动的测试和调试代码片断。

  6. 可移植:基于其开放源代码的特性,Python已经被移植(也就是使其工作)到许多平台。

  7. 可扩展:如果你需要一段运行很快的关键代码,或者是想要编写一些不愿开放的算法,你可以使用C或C++完成那部分程序,然后从你的Python程序中调用。

  8. 数据库:Python提供所有主要的商业数据库的接口。

  9. GUI编程:Python支持GUI可以创建和移植到许多系统调用。

  10. 可嵌入: 你可以将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基础知识_第1张图片

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))  # 
print(s2)  # b'\xe4\xb8\xad\xe6\x96\x87'

# 字节转字符串
s3 = s2.decode('utf-8')  # bytes.decode()
print(type(s3))  # 
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))  # 
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))  # 不加逗号,类型为整型 
tup3 = (50,)
print(type(tup3))  # 加上逗号,类型为元组 

# 拼接元组
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))  # 

# 复制字典
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:
    【代码段1elif 条件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  in :
    
else:
    

示例:

a = range(0,9,3) # 左闭右开 0,3,6
print(type(a))  # 
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)

步骤:

  1. 定义一个函数,函数中使用yield关键字
  2. 调用函数,接收调用的结果
  3. 得到的结果就是生成器
  4. 借助于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()是个生成器,调用时的执行的顺序为:
    1. ==第一次执行要么next(g)要么r.send(None),不能使用r.send(‘xxxxx’);这会报错的。==本案例第一次执行时next(g)时,首先打印出aaa,然后遇到yield即跳出生成器;
    2. 然后执行g.send('hello')时,p则被赋值为hello了,然后继续顺序执行:打印出bbb,然后打印出p是send传过来的,当再次遇到第二个yield时跳出,所以结果只打印了三行,后面的p1没有执行。
  • 获取生成器的方法:
    1. 使用next(generator),每次调用都会产生一个新的元素,如果元素产生完毕,再次调用的话就会产生异常;
    2. 生成器自己的方法: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))  # 
print(lambdaVar)  #  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. 闭包

在函数内部再定义一个函数,并且这个函数用到了外边函数的变量,并且把里面的函数返回,我们把这种情况叫闭包

  1. 闭包优化了变量,原来需要类对象完成的工作,闭包也可以完成;
  2. 闭包的好处,使代码变得简洁,便于阅读代码;
  3. 由于闭包引用了外部函数的局部变量,则外部函数的局部变量没有及时释放,消耗内存;
  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_funcx的内存地址相同,所有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))  #   打印内容得到的会是字节码
bytesFile.close()

2. 读取文件

# todo 读取文本文件
textFile01 = open(r'D:\tmp\python-io\bigdata.txt')
print(type(textFile01))  # 

# 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 关键字的实质是上下文管理:

  1. 上下文管理协议。包含方法__enter__()__exit__(),支持该协议对象要实现这两个方法。
  2. 上下文管理器,定义执行with语句时要建立的运行时上下文,负责执行with语句块上下文中的进入与退出操作。
  3. 进入上下文的时候执行__enter__方法,如果设置as var语句,var变量接受__enter__()方法返回值。
  4. 如果运行时发生了异常,就退出上下文管理器。调用管理器__exit__方法。
  5. 详细资料参阅: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))  # 
    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=',')  # 
    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:]                   #无论如何都要执行的语句
    处理语句
【后续语句】

执行流程

  1. 首先执行try中【代码块A】,如果出现异常,立即终止代码执行,转而到except块中进行异常处理
  2. 异常处理except模块,从上往下匹配,如果能够匹配成功,立即执行相应的异常处理代码块,执行完毕后,不在往下匹配
  3. 捕获到了异常并处理完毕后,继续执行finally内的代码块,如果没有则执行【后续语句】
  4. 如果有异常但匹配不到异常,先执行finally,然后则抛出错误,终止程序执行。
  5. 如果没有异常,如果有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

  • 步骤:

    1. 在自定义异常类的构造函数中,调用父类构造函数
    2. 重写__str__方法输出异常信息
    3. 编写异常处理方法处理异常
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 类成员(静态成员)

  • 类成员属于类,为所有对象共有,可以通过类名或对象调用

  • 使用对象调用属性时,成员属性优先级大于类属性,应该尽量避免成员属性和类属性重名

  • 静态方法

    1. 静态方法和类方法的区别:类方法第一个参数是类对象,由系统传入,静态方法没有
    2. 共同点:静态方法和类方法都属于类,调用方式相同
# 类属性
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)  #  1604234828.8031926
print(type(localTimeTuple), localTimeTuple)  #  
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)   #  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)  #  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)  #  返回datetime对象的时间元组
# datetime转换为时间戳
datetimeToTimeStamp = customDateTime.timestamp()
print(type(datetimeToTimeStamp), datetimeToTimeStamp)  #  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)  #  2020-11-05 08:25:23.702800
# 返回datetime对象的date部分
subDate = customDateTime.date()
print(type(subDate), subDate)  #  2017-03-22
# 返回datetime对象的time部分
subTime = customDateTime.time()
print(type(subTime), subTime)  #  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)  #  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)  #  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 中可以所用 syssys.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)  #  <_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)  #  <_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)  #   409a4b81225ada9764fcf7a75bd250c8
md5Str = md5.hexdigest()
print(type(md5Str), md5Str, len(md5Str))  #  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)  #  <__main__.Dog object at 0x0305A390>

dpsDog = pickle.dumps(orgDog)  # 数据通过特殊的形式转换为只有python语言认识的字符串
print(type(dpsDog), dpsDog)  # 
ldsDog = pickle.loads(dpsDog)
print(type(ldsDog), ldsDog)  #  <__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()  # 
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. 线程与进程

进程:是指一个内存中运行的应用程序,每个进程都有一个独立的内存空间,一个应用程序可以同时运行多个进程;进程也是程序的一次执行过程,是系统运行程序的基本单位;系统运行一个程序即是一个进程从创建、运行到消亡的过程。

线程:进程中的一个执行单元,负责当前进程中程序的执行,一个进程中至少有一个线程。一个进程中是可以有多个线程的,这个应用程序也可以称之为多线程程序。

进程与线程的区别

  • 进程:有独立的内存空间,进程中的数据存放空间(堆空间和栈空间)是独立的,至少有一个线程。

  • 线程:堆空间是共享的,栈空间是独立的,线程消耗的资源比进程小的多。

注意

  1. 因为一个进程中的多个线程是并发运行的,那么从微观角度看也是有先后顺序的,哪个线程执行完全取决于 CPU 的调度,程序员是干涉不了的。而这也就造成的多线程的随机性。

  2. 由于创建一个线程的开销比创建一个进程的开销小的多,那么我们在开发多任务运行的时候,通常考虑创建多线程,而不是创建多进程。

线程调度:

  • 分时调度:所有线程轮流使用 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协议可以保证 传输数据的安全,所以应用十分广泛,例如下载文件、浏览网页等。
    1. 第一次握手,客户端向服务器端发出连接请求,等待服务器确认。
    2. 第二次握手,服务器端向客户端回送一个响应,通知客户端收到了连接请求。
    3. 第三次握手,客户端再次向服务器端发送确认信息,确认连接。整个交互过程如下图所示。
  • 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()

你可能感兴趣的:(Python)