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 的六个标准数据类型中:

  • 不可变数据(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 == ba和b值相等,结果是True,a和b值不相等结果为False
!=a != ba不等于b 结果为True,否则结果为True
>a > ba大于b结果为True,否则为False
>=a >= ba大于等于b结果为True,否则为False
<a < ba小于b结果为True,否则为False
<=a <= ba小于等于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的结果赋给aa = b +c #a=30
+=a += b等价于 a = a +ba = 15
-=a -= b等价于 a = a - ba = -5
*=a *= b等价于 a = a * ba = 50
/=a /= b 等价于a = a / ba = 0.5
%=a %= b等价于a = a % ba = 5
//=a //= b等价于 a = a // ba = 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为例

运算符逻辑表达式描述实例
andx and y布尔"与" - 如果 x 为 False,x and y 返回 False,否则它返回 y 的计算值(a and b) 返回 20
orx or y布尔"或" - 如果 x 是 True,它返回 x 的值,否则它返回 y 的计算值(a or b) 返回 10
notnot x布尔"非" - 如果 x 为 True,返回 False 。如果 x 为 False,它返回 Truenot(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 <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)

步骤:

  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))  # <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. 闭包

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

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

  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))  # <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:]                   #无论如何都要执行的语句
    处理语句
【后续语句】

执行流程

  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字符串正则匹配
sysPython自身的运行环境
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_year4位数年2008
tm_mon1 到 12
tm_mday1 到 31
tm_hour小时0 到 23
tm_min分钟0 到 59
tm_sec0 到 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.MAXYEAR9999
MINYEAR返回能表示的最小年份datetime.MINYEAR1
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 中可以所用 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)  # <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. 线程与进程

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

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

进程与线程的区别

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

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

注意

  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()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值