1 简介

1.1 python 介绍

Python 是一种解释型、面向对象、动态数据类型的高级程序设计语言。

1.2 python 环境安装

python 最流行的安装方法有三种——IDLE、Anaconda、Pycharm

1.2.1 IDLE

IDLE是python自带的一个编辑器,十分简洁。IDLE是开发python程序的基本集成开发环境,具备基本的IDE的功能;当安装好python以后,IDLE就自动安装好了,不需要另外去找。在 Python 官网 即可直接下载与安装,这种方式的安装胜在简单方便、无需额外配置任何东西,但是只能在自带的 IDLE 上进行编程,较为麻烦,推荐仅为了简单学习 python 的人群使用。

3.7.5 版本官网下载地址 ,接下来直接正常安装然后加入环境变量即可。

安装成功后在 cmd 中键入 python 能够出现 python 的交互模式就是配置成功。平常编程则是在 IDLE 的交互或脚本模式进行操作:

File -> New File (ctrl + N) 即可进入脚本模式(Run -> Run Modele (F5) 即可运行该脚本):

1.2.2 Anaconda

Anaconda指的是一个开源的 Python 发行版本,其包含了conda、Python等180多个科学包及其依赖项。 因为包含了大量的科学包(如 numpy、pandas、matplotlib 及大量数据分析领域的常用包),Anaconda 的下载文件比较大,如果只需要某些包,或者需要节省带宽或存储空间,也可以使用Miniconda这个较小的发行版(仅包含conda和 Python)。

安装 Anaconda 后 Jupyter 与 conda 这两个大量使用的组件就自动安装完毕了,Jupyter 本质是一个 Web 应用程序,便于创建和共享程序文档,支持实时代码,数学方程,可视化和 markdown。 用途包括:数据清理和转换,数值模拟,统计建模,机器学习等等。

Conda 是一个开源的软件包管理系统和环境管理系统,用于安装多个版本的软件包及其依赖关系,并在它们之间轻松切换。假如不同项目间需要的库的版本不一致,频繁的卸载安装十分容易造成系统的崩溃,因此就需要 conda 为不同项目创建独立的环境。

1. Anaconda 的安装

Anaconda 的安装甚至比 IDLE 还要简单,只需像一个普通软件一样选好路径然后一路 next 即可,且无需设置环境变量。

安装完毕后会在开始任务栏看到这些东西,Anaconda Navigator 即为 Anaconda 的主页面,在里面可以进行环境管理、包管理等的交互式操作,同时也是许多软件的打开入口。在 Anaconda Prompt 中则可以进行环境管理、包管理等的命令行操作。Jupyter 则可以直接打开 Jupyter 而无需打开 Anaconda

2. conda 的使用

安装 anaconda 后,打开 Anaconda Prompt ,可以输入以下常见命令:

conda activate xxxx #开启xxxx环境
conda deactivate #关闭环境
​
conda env list #显示所有的虚拟环境
conda info --envs #显示所有的虚拟环境
conda info -e #显示所有已经创建的环境
​
conda update -n base conda #update最新版本的conda
​
conda create -n xxxx python=3.6 #创建python3.6的xxxx虚拟环境
​
conda remove --name xxxx  --all #彻底删除旧环境
​
conda remove -n tensorflow --all  #彻底删除旧环境
​
#Conda是没有重命名环境的功能, 要实现这个基本需求, 可以通过克隆-删除的过程。
#切记不要直接mv移动环境的文件夹来重命名, 会导致一系列无法想象的错误的发生!
conda create --name newname --clone oldname //克隆环境
conda remove --name oldname --all //彻底删除旧环境
​
conda list          #查看已经安装的文件包
conda list -n xxx   #指定查看xxx虚拟环境下安装的package
conda update xxx    #更新xxx文件包
conda uninstall xxx #卸载xxx文件包
​
#pip 安装本地包
pip install   ~/Downloads/a.whl
#conda 安装本地包
conda install --use-local  ~/Downloads/a.tar.bz2
​
​
#显示目前conda的数据源有哪些
conda config --show channels
​
#添加数据源:例如, 添加清华anaconda镜像:
conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/
conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main/
conda config --set show_channel_urls yes
​
#删除数据源
conda config --remove channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/

以安装 pyspark 的环境为例:

首先将默认镜像源改成清华源,提高下载速度(只需改一次即可)。

conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/
conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main/
conda config --set show_channel_urls yes

接下来创建一个叫 pyspark 的环境,并指定为 python3.8 版本:

conda create -n pyspark python=3.8

创建完毕后,先观察一下,可以看到命令行最左边有一个 (base) ,这表示我们现在所处的环境是默认的 base 环境:

进入 pyspark 环境:

conda activate pyspark

此时最左边现实的是 pyspark 的环境了,现在就可以使用 pip 或者 conda 安装项目所需要的库了。

3. jupyter 的使用

打开 jupyter 后,会自动打开浏览器,进入 web 页面,也可以手动打开浏览器,进入 http://localhost:8888/http://127.0.0.1:8888/),不过这样会让输入 token,token在如图所示的位置:

打开 web 页面后即可选择一个文件夹创建 Jupyter Notebook ,进行交互式编程。

Jupyter Notebook本身是默认使用一种Anaconda中root目录下的Python环境的,如果想使用其它的虚拟环境,还需要通过插件来实现,也就是nb_conda插件,首先使用 conda 安装这个插件:

conda install nb_conda

安装完成后,jupyter notebook中多了Conad选项卡,但此时还不能用,还需要在每一个虚拟环境中安装jupyter。

在虚拟环境中安装 jupyter

conda activate pyspark
pip install jupyter -i https://pypi.tuna.tsinghua.edu.cn/simple

然后重启 jupyter ,新建 Jupyter Notebook 时:

进入,导入 pyspark 测试一下:

成功!

也可以在 Notebook 里面切换内核(Kernel -> Change kernel):

1.2.3 Pycharm

PyCharm 是一种 Python IDE(Integrated Development Environment,集成开发环境),与 IDEA 同一家公司,因而操作方式几乎一致。带有一整套可以帮助用户在使用Python语言开发时提高其效率的工具,比如调试、语法高亮、项目管理、代码跳转、智能提示、自动完成、单元测试、版本控制。此外,该IDE提供了一些高级功能,以用于支持Django框架下的专业Web开发;使用远程环境等;普遍应用于大中小项目的开发中。

Pycharm 安装好后推荐使用 Anaconda 的 conda 管理环境,因此 Anaconda 和 Pycharm 最好都安装一下,Pycharm 环境配置如下:

File -> Settings -> Project -> Python Interpreter

然后把自己环境的地址键入即可(也可以是虚拟环境,选择anaconda-envs-环境名-python.exe即可)。详细配置方法见此网站

2 基本语法

2.1 语言机制

Python 的语⾔设计强调的是可读性、简洁和清晰。有些⼈称 Python 为“可执⾏的伪代码”。

2.1.1 使⽤缩进

Python使⽤空⽩字符(tab和空格)来区分程序块,⽽不是像其它语⾔,⽐如R、C++、JAVA和Perl那样使⽤括号。例如:

for x in array:
    if x < pivot:
        less.append(x)
    else:
        greater.append(x)

冒号标志着缩进代码块的开始,冒号之后的所有代码的缩进量必须相同,直到代码块结束。

Python的语句不需要⽤分号结尾。当然,即使加上分号也是没问题的;不过,分号可以⽤来在同一行内写入多行语句:

a = 5; b = 6; c = 7

Python不建议将多条语句放到⼀⾏,这会降低代码的可读性。

2.1.2 万物皆对象

Python语⾔的⼀个重要特性就是它的对象模型的⼀致性。每个基本数据类型、字符串、数据结构、函数、类、模块等等,都是在Python解释器的⾃有“盒⼦”内,它被认为是Python对象。每个对象都有类型(例如,字符串或函数)和内部数据。在实际中,这可以让语⾔⾮常灵活。

2.1.3 注释

通常使用 “ # ” 来表示注释。如需多行注释则使用三个单引号或者三个双引号括起来即可,常用于文档字符串,在文件的特定地点,被当做注释。

# 这是一个注释
"""
这是
一个
多行注释
"""
print(a)

2.1.4 多行语句

Python 通常是一行写完一条语句,但如果语句很长,我们可以使用反斜杠 \ 来实现多行语句,例如:

total = item_one + \
        item_two + \
        item_three

在 [], {}, 或 () 中的多行语句,不需要使用反斜杠 \,例如:

total = ['item_one', 'item_two', 'item_three',
        'item_four', 'item_five']

2.1.5 标识符

  • 第一个字符必须是字母表中字母或下划线 _ 。
  • 标识符的其他的部分由字母、数字和下划线组成。
  • 标识符对大小写敏感。

在 Python 3 中,可以用中文作为变量名,非 ASCII 标识符也是允许的。

甲 = 1
print(甲)

不能使用关键字作为标识符,python 的关键字如下所示:

>>> 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']

命名规范:

  • 当标识符用作模块名时,应尽量短小,并且全部使用小写字母,可以使用下划线分割多个字母
  • 当标识符用作包的名称时,应尽量短小,也全部使用小写字母,不推荐使用下划线
  • 当标识符用作类名时,应采用单词首字母大写的形式
  • 模块内部的类名,可以采用 “下划线+首字母大写” 的形式
  • 函数名、类中的属性名和方法名,应全部使用小写字母,多个单词之间可以用下划线分割
  • 常量命名应全部使用大写字母,单词之间可以用下划线分割

2.1.6 赋值方法

Python 中的变量不需要声明。每个变量在使用前都必须赋值,变量赋值以后该变量才会被创建。

等号(=)用来给变量赋值。

等号(=)运算符左边是一个变量名,等号(=)运算符右边是存储在变量中的值。

python中允许为多个变量同时赋值:

a = b = c = 10
a, b = 5, 6 
a, b = b, a

2.2 数据类型简介

Python3 中有六个标准的数据类型:

  • Number(数字)
  • String(字符串)
  • List(列表)
  • Tuple(元组)
  • Set(集合)
  • Dictionary(字典)

Python3 的六个标准数据类型中:

  • 不可变数据(3 个):Number(数字)、String(字符串)、Tuple(元组);
  • 可变数据(3 个):List(列表)、Dictionary(字典)、Set(集合)。

2.3 数字类型

python中数字有四种类型:整数、布尔型、浮点数和复数。

  • int (整数), 如 1, 只有一种整数类型 int,表示为长整型,没有 python2 中的 Long。
  • bool (布尔), 如 True、False。
  • float (浮点数), 如 1.23、3e-2
  • complex (复数), 如 1 + 2j、 1.1 + 2.2j

内置的 type() 函数可以用来查询变量所指的对象类型:

>>> a, b, c, d = 20, 5.5, True, 4+3j
>>> print(type(a), type(b), type(c), type(d))
<class 'int'> <class 'float'> <class 'bool'> <class 'complex'>

空值类型则为 None 。

2.4 字符串

2.4.1 字符串赋值

字符串是 Python 中最常用的数据类型。我们可以使用引号 ( '" ) 来创建字符串。创建字符串很简单,只要为变量分配一个值即可。例如:

a = "Hello "
b = 'world!'

可以使用三个引号将多行字符串赋值给变量:

a = '''这是
一个
多行的
字符串
'''

2.4.2 切片

像许多其他流行的编程语言一样,Python 中的字符串是表示 unicode 字符的字节数组。但是,Python 没有字符数据类型,单个字符就是长度为 1 的字符串。方括号可用于访问字符串的元素。0为第一个元素,1是第二个元素,-1是倒数第一个元素,-2是倒数第二个元素,依次类推。

>>> s = '0123456789'
>>> print(s[0], s[1], s[-1], s[-3])
0 1 9 7

可以使用切片语法返回一定范围的字符。切片需在中括号中指定开始索引和结束索引,以冒号分隔,以返回字符串的一部分(冒号左边不写默认为从 0 开始,冒号右边不写默认到最后一个元素,若有第二个冒号,则第三个数字表示步长)。例如:

>>> s = '0123456789'
>>> print(s[2:8], s[-5:])
234567 56789
>>> print(s[::-1])
9876543210

2.4.3 长度

使用 len 函数获取字符串的长度。

>>> len(s)
10

2.4.4 串联

如需串联或组合两个字符串,可以使用 + 运算符。

如需重复多次一个字符串,可以使用 * 运算符。

a = "Hello"
b = "World"
c = a + b
print(c)
d = a * 5
print(d)

2.4.5 检查字符串

如需检查字符串中是否存在特定短语或字符,可以使用 in 或 not in 关键字。

txt = "China is a great country"
x = "ina" in txt
print(x)

2.4.6 重要方法

1. count

count() 方法返回指定值在字符串中出现的次数。

语法:

string.count(value, start, end)
参数 描述
value 必需。字符串。要检索的字符串。
start 可选。整数。开始检索的位置。默认是 0。
end 可选。整数。结束检索的位置。默认是字符串的结尾。
txt = "I love apples, apple are my favorite fruit"
x = txt.count("apple", 10, 24)
print(x)

2. replace

replace() 用另一段字符串来替换字符串:

>>> a = "Hello, World!"
>>> print(a.replace("World", "Kitty"))
Hello, Kitty!

3. split

split() 方法在找到分隔符的实例时将字符串拆分为子字符串,默认以单个或多个空白符作分割,若指定 max,列表将包含指定数量加一的元素。

语法:

string.split(separator, max)
参数 描述
separator 可选。规定分割字符串时要使用的分隔符。默认值为空白字符。
max 可选。规定要执行的拆分数。默认值为 -1,即“所有出现次数”。
>>> a = "Hello, World!"
>>> print(a.split(","))
['Hello', ' World!']

4. join

join() 方法获取可迭代对象中的所有项目,并将它们连接为一个字符串。

必须将字符串指定为分隔符。

语法:

string.join(iterable)
参数 描述
iterable 必需。所有返回值均为字符串的任何可迭代对象。
>>> '-'.join(['2022','08','30'])
'2022-08-30'

5. find 与 index

find() 方法查找指定值的首次出现。

如果找不到该值,则 find() 方法返回 -1。

find() 方法与 index() 方法几乎相同,唯一的区别是,如果找不到该值,index() 方法将引发异常。

语法:

string.find(value, start, end)
参数 描述
value 必需。要检索的值。
start 可选。开始检索的位置。默认是 0。
end 可选。结束检索的位置。默认是字符串的结尾。
>>> a = '0123456789'
>>> a.find('5')
5
>>> a.index('5')
5
>>> a.find('10')
-1
>>> a.index('10')
Traceback (most recent call last):
  File "<pyshell#16>", line 1, in <module>
    a.index('10')
ValueError: substring not found

6. format

format() 方法格式化指定的值,并将其插入字符串的占位符内。

占位符使用大括号 {} 定义。请在下面的“占位符”部分中了解有关占位符的更多信息。

format() 方法返回格式化的字符串。

语法:

string.format(value1, value2...)
参数 描述
value1, value2... 必需。一个或多个应该格式化并插入字符串的值。值可以是数字,用于指定要删除的元素的位置。这些值可以是用逗号分隔的值列表、键=值列表,或两者的组合。这些值可以是任何数据类型。
txt1 = "My name is {fname}, I'am {age}".format(fname = "Bill", age = 64)
txt2 = "My name is {0}, I'am {1}".format("Bill",64)
txt3 = "My name is {}, I'am {}".format("Bill",64)

可以使用命名索引 {变量名}、编号索引{0}、甚至空的占位符 {} 来标识占位符。

格式化类型 效果
:< 左对齐结果(在可用空间内)
:> 右对齐结果(在可用空间内)
:^ 居中对齐结果(在可用空间内)
:= 将标志放置在最左侧
:+ 使用加号指示结果是正数还是负数
:- 负号仅用于负值
: 使用空格在正数之前插入一个多余的空格(在负数之前使用减号)
:, 使用逗号作为千位分隔符
:_ 使用下划线作为千位分隔符
:b 二进制格式
:c 将值转换为相应的 unicode 字符
:d 十进制格式
:e 科学格式,带有小写字母 E
:E 科学格式,带有大写字母 E
:f 定点数字格式
:F 定点数字格式,以大写形式显示(将 inf 和 nan 显示为 INF 和 NAN)
:g 通用格式
:G 通用格式(将大写 E 用作科学计数法)
:o 八进制格式
:x 十六进制格式,小写
:X 十六进制格式,大写
:n 数字格式
:% 百分比格式

% 与 format 作用一致。

如 format 的用法是 '价格是:%.2f' .format(price) ;

而 % 的用法是 '价格是:%.2f' % (price) 。

如果字符串用 f 修饰,

7. strip

strip() 方法删除开头和结尾的指定字符,默认为空白符:

>>> s = 'aaaASDFFaa'
>>> s.strip('a')
'ASDFF'

2.4.7 其他方法

方法 描述
capitalize() 把首字符转换为大写。
casefold() 把字符串转换为小写。
center() 返回居中的字符串。
count() 返回指定值在字符串中出现的次数。
encode() 返回字符串的编码版本。
endswith() 如果字符串以指定值结尾,则返回 true。
expandtabs() 设置字符串的 tab 尺寸。
find() 在字符串中搜索指定的值并返回它被找到的位置。
format() 格式化字符串中的指定值。
format_map() 格式化字符串中的指定值。
index() 在字符串中搜索指定的值并返回它被找到的位置。
isalnum() 如果字符串中的所有字符都是字母数字,则返回 True。
isalpha() 如果字符串中的所有字符都在字母表中,则返回 True。
isdecimal() 如果字符串中的所有字符都是小数,则返回 True。
isdigit() 如果字符串中的所有字符都是数字,则返回 True。
isidentifier() 如果字符串是标识符,则返回 True。
islower() 如果字符串中的所有字符都是小写,则返回 True。
isnumeric() 如果字符串中的所有字符都是数,则返回 True。
isprintable() 如果字符串中的所有字符都是可打印的,则返回 True。
isspace() 如果字符串中的所有字符都是空白字符,则返回 True。
istitle() 如果字符串遵循标题规则,则返回 True。
isupper() 如果字符串中的所有字符都是大写,则返回 True。
join() 把可迭代对象的元素连接到字符串的末尾。
ljust() 返回字符串的左对齐版本。
lower() 把字符串转换为小写。
lstrip() 返回字符串的左修剪版本。
maketrans() 返回在转换中使用的转换表。
partition() 返回元组,其中的字符串被分为三部分。
replace() 返回字符串,其中指定的值被替换为指定的值。
rfind() 在字符串中搜索指定的值,并返回它被找到的最后位置。
rindex() 在字符串中搜索指定的值,并返回它被找到的最后位置。
rjust() 返回字符串的右对齐版本。
rpartition() 返回元组,其中字符串分为三部分。
rsplit() 在指定的分隔符处拆分字符串,并返回列表。
rstrip() 返回字符串的右边修剪版本。
split() 在指定的分隔符处拆分字符串,并返回列表。
splitlines() 在换行符处拆分字符串并返回列表。
startswith() 如果以指定值开头的字符串,则返回 true。
strip() 返回字符串的剪裁版本。
swapcase() 切换大小写,小写成为大写,反之亦然。
title() 把每个单词的首字符转换为大写。
translate() 返回被转换的字符串。
upper() 把字符串转换为大写。
zfill() 在字符串的开头填充指定数量的 0 值。

注释:所有字符串方法都返回新值。它们不会更改原始字符串。

2.4.8 修饰符

1. f

在字符串前加上 f ,在字符串内支持大括号内直接写python 表达式。

>>> f'  {5*6}  '
'  30  '
>>> name = 'databasename'
>>> f'数据库的名字是{name}'
'数据库的名字是databasename'

2. r

声明后面的字符串是普通字符串,相对的,特殊字符串中含有:转义字符,如 \n \t 等。

r'D:\Words\MyWords\python\python 基本语法'
#等价于
'D:\\Words\\MyWords\\python\\python 基本语法'

3. b

4. u

2.5 运算符

运算符用于对变量和值执行操作。

Python 在以下组中划分运算符:

  • 算术运算符
  • 赋值运算符
  • 比较运算符
  • 逻辑运算符
  • 身份运算符
  • 成员运算符
  • 位运算符

2.5.1 算术运算符

算术运算符与数值一起使用来执行常见的数学运算:

运算符 名称 实例
+ x + y
- x - y
* x * y
/ x / y
% 取模 x % y
** x ** y
// 地板除(取商) x // y

2.5.2 位运算符

位运算符用于比较(二进制)数字:

运算符 描述 实例
& AND 如果两个位均为 1,则将每个位设为 1。
| OR 如果两位中的一位为 1,则将每个位设为 1。
^ XOR 如果两个位中只有一位为 1,则将每个位设为 1。
~ NOT 反转所有位。
<< Zero fill left shift 通过从右侧推入零来向左移动,推掉最左边的位。
>> Signed right shift 通过从左侧推入最左边的位的副本向右移动,推掉最右边的位。

2.5.3 赋值运算符

赋值运算符用于为变量赋值:

运算符 实例 等同于
= x = 5 x = 5
+= x += 3 x = x + 3
-= x -= 3 x = x - 3
*= x *= 3 x = x * 3
/= x /= 3 x = x / 3
%= x %= 3 x = x % 3
//= x //= 3 x = x // 3
**= x **= 3 x = x ** 3
&= x &= 3 x = x & 3
|= x |= 3 x = x | 3
^= x ^= 3 x = x ^ 3
>>= x >>= 3 x = x >> 3
<<= x <<= 3 x = x << 3

2.5.4 比较运算符

比较运算符用于比较两个值:

运算符 名称 实例
== 等于 x == y
!= 不等于 x != y
> 大于 x > y
< 小于 x < y
>= 大于或等于 x >= y
<= 小于或等于 x <= y

2.5.5 逻辑运算符

逻辑运算符用于组合条件语句:

运算符 描述 实例
and 如果两个语句都为真,则返回 True。 x > 3 and x < 10
or 如果其中一个语句为真,则返回 True。 x > 3 or x < 4
not 反转结果,如果结果为 true,则返回 False not(x > 3 and x < 10)

2.5.6 身份运算符

身份运算符用于比较对象,不是比较它们是否相等,但如果它们实际上是同一个对象,则具有相同的内存位置:

运算符 描述 实例
is 如果两个变量是同一个对象,则返回 true。 x is y
is not 如果两个变量不是同一个对象,则返回 true。 x is not y

2.5.7 成员运算符

成员资格运算符用于测试序列是否在对象中出现:

运算符 描述 实例
in 如果对象中存在具有指定值的序列,则返回 True。 x in y
not in 如果对象中不存在具有指定值的序列,则返回 True。 x not in y

2.6 列表

列表是一个有序且可更改的集合。在 Python 中,列表用方括号编写,里面可以包含任意类型的值。

2.6.1 创建列表

使用中括号 "[" 或 list() 来创建列表。

>>> l = ['a', 'b', 'c', 'd']
>>> print(l)
['a', 'b', 'c', 'd']
>>> l = [1, 2, 1.5, 'abc', [1,2,3], 1+2j]

2.6.2 访问元素

通过引用索引号来访问列表项,其中索引号规则与字符串的一致。

>>> l = [1, 2, 1.5, 'abc', [1,2,3], 1+2j]
>>> l[1]
2
>>> l[-2]
[1, 2, 3]
>>> l[-2][0]
1

选择出来后可对该元素任意修改。

2.6.3 切片

通过指定范围的起点和终点来指定索引范围,规则与字符串一致;指定范围后,返回值将是包含指定项目的新列表。

>>> thislist = ["apple", "banana", "cherry", "orange", "kiwi", "melon", "mango"]
>>> print(thislist[5:2:-1])
['melon', 'kiwi', 'orange']

2.6.4 检查元素是否存在

使用 in 关键字确定列表中是否存在指定的项:

>>> 'a' in ['a', 'b', 'c']
True

2.6.5 列表长度

使用 len() 方法确定列表中有多少项。

>>> len(['a', 'b', 'c'])
3

2.6.6 重要方法

1. append

append() 方法向列表末尾追加元素。

语法:

list.append(element)
参数 描述
element 必需。任何类型(字符串、数字、对象等)的元素。
>>> a = ['a', 'b', 'c']
>>> b = ['d', 'e']
>>> a.append(b)
>>> a
['a', 'b', 'c', ['d', 'e']]

2. pop

pop() 删除指定位置的元素,返回值为被删除的元素。

语法:

list.pop(pos)
参数 描述
pos 可选。数字,指定需删除元素的位置。默认值 -1,返回最后的项目。

3. insert

insert() 方法在指定位置插入指定的值。

语法:

list.insert(position, element)
参数 描述
position 必需。数字,指定在哪个位置插入值。
element 必需。元素,任何类型(字符串、数字、对象等)。

4. extend

extend() 方法将指定的列表元素(或任何可迭代的元素)添加到当前列表的末尾。

语法:

list.extend(iterable)
参数 描述
iterable 必需。任何可迭代对象(列表、集合、元组等)。
>>> a = ['a', 'b', 'c']
>>> b = ['d', 'e']
>>> a.extend(b)
>>> a
['a', 'b', 'c', 'd', 'e']

a+b也是同样的效果,

5. sort

sort() 方法对列表进行排序。

语法:

list.sort(reverse=True|False, key=myFunc)
参数 描述
reverse 可选。reverse=True 将对列表进行降序排序。默认是 reverse=False。
key 可选。指定排序标准的函数。
>>> l = [-1,-5,-6,-4,5,3,6,9,4,-1,0]
>>> l.sort()
>>> l
[-6, -5, -4, -1, -1, 0, 3, 4, 5, 6, 9]
>>> l.sort(key=lambda x: abs(x))
>>> l
[0, -1, -1, 3, -4, 4, -5, 5, -6, 6, 9]

2.6.7 其他方法

方法 描述
append() 在列表的末尾添加一个元素
clear() 删除列表中的所有元素
copy() 返回列表的副本(直接赋值则共享地址,copy则完全新生成)
count() 返回具有指定值的元素数量。
extend() 将列表元素(或任何可迭代的元素)添加到当前列表的末尾
index() 返回具有指定值的第一个元素的索引
insert() 在指定位置添加元素
pop() 删除指定位置的元素
remove() 删除具有指定值的项目
reverse() 颠倒列表的顺序,与 list[::-1] 效果一样
sort() 对列表进行排序

2.7 元组

元组是有序且不可更改的集合。在 Python 中,元组是用圆括号编写的。

2.7.1 创建元组

使用中括号 "(" 或 tuple() 来创建元组。

>>> l = ('a', 'b', 'c', 'd')
>>> print(l)
('a', 'b', 'c', 'd')
>>> l = (1, 2, 1.5, 'abc', [1,2,3], 1+2j)

如需创建仅包含一个项目的元组,则必须在该项目后添加一个逗号,否则 Python 无法将变量识别为元组。

>>> t = (1,)
>>> type(t)
<class 'tuple'>
>>> t = (1)
>>> type(t)
<class 'int'>

2.7.2 索引及切片

该内容与列表操作一致,但元组内元素不可更改。

2.7.3 删除元素

元组是不可更改的,因此无法从中删除元素,但可以完全删除该元组:

del 关键字可以完全删除元组:

>>> t = (1,)
>>> del t
>>> t
Traceback (most recent call last):
  File "<pyshell#100>", line 1, in <module>
    t
NameError: name 't' is not defined

2.7.4 合并元组

可以使用 + 运算符连接两个或多个元组:

tuple1 = ("a", "b" , "c")
tuple2 = (1, 2, 3)

tuple3 = tuple1 + tuple2
print(tuple3)

2.7.5 方法

Python 提供两个可以在元组上使用的内建方法。

方法 描述
count() 返回元组中指定值出现的次数。
index() 在元组中搜索指定的值并返回它被找到的位置。

2.8 集合

集合是无序和无索引的集合。在 Python 中,集合用花括号编写。

2.8.1 创建集合

使用花括号 "{" 来创建集合。要创建一个空集合只能用set(),不能用用{},因为后者创建的是一个空字典

>>> s = {'a', 'b', 'd', 'c'}
>>> s
{'c', 'b', 'a', 'd'}

2.8.2 集合特点

1. 无序性

集合是无序的,因此无法确定元素的显示顺序。也无法通过索引或切片获取元素。

2. 唯一性

集合中的重复元素会被删的只剩一个,因此集合中的元素是唯一的。

3. 可哈希

集合采用哈希算法去映射地址,因此存储元素必须是可哈希的不可变对象,无法哈希化的对象是无法存入集合的。

  • 可哈希(3 个):Number(数字)、String(字符串)、Tuple(元组);
  • 不可哈希(3 个):List(列表)、Dictionary(字典)、Set(集合)。

2.8.3 集合运算

>>> a = set('abracadabra')
>>> b = set('alacazam')
>>> a                                  
{'a', 'r', 'b', 'c', 'd'}
>>> a - b                              # 集合a中包含而集合b中不包含的元素
{'r', 'd', 'b'}
>>> a | b                              # 集合a或b中包含的所有元素
{'a', 'c', 'r', 'd', 'b', 'm', 'z', 'l'}
>>> a & b                              # 集合a和b中都包含了的元素
{'a', 'c'}
>>> a ^ b                              # 不同时包含于a和b的元素
{'r', 'd', 'b', 'm', 'z', 'l'}

2.8.4 访问元素

无法通过引用索引来访问 set 中的项目,因为 set 是无序的,项目没有索引。

但是可以使用 for 循环遍历 set 项目,或者使用 in 关键字查询集合中是否存在指定值。

2.8.5 方法

1. len

使用 len() 方法确定集合中有多少个元素。

>>> s = {1,2,3}
>>> len(s)
3

2. add

add() 方法向集合添加元素。

如果该元素已存在,则 add() 方法就不会添加元素。

语法:

set.add(element)
参数 描述
element 必需。要添加到集合的元素。
>>> s = {1,2,3,4}
>>> s.add(5)
>>> s
{1, 2, 3, 4, 5}

3. pop

pop() 方法从集合中删除一个随机的项目。

此方法返回被删除的项目。

语法:

set.pop()

无参数值。

>>> s = set('abcdef')
>>> s.pop()
'f'
>>> s.pop()
'a'

4. remove & discard

remove() 和 discard() 方法从集合中删除指定的元素。

二者使用方法一致,但如果指定项目不存在,remove() 会引发错误,而 discard() 不会。

语法:

set.remove(item)
参数 描述
item 必需。需要检索并删除的项目。

5. 其余方法

方法 描述
add() 向集合添加元素。
clear() 删除集合中的所有元素。
copy() 返回集合的副本。
difference() 返回包含两个或更多集合之间差异的集合。
difference_update() 删除此集合中也包含在另一个指定集合中的项目。
discard() 删除指定项目。
intersection() 返回为两个其他集合的交集的集合。
intersection_update() 删除此集合中不存在于其他指定集合中的项目。
isdisjoint() 返回两个集合是否有交集。
issubset() 返回另一个集合是否包含此集合。
issuperset() 返回此集合是否包含另一个集合。
pop() 从集合中删除一个元素。
remove() 删除指定元素。
symmetric_difference() 返回具有两组集合的对称差集的集合。
symmetric_difference_update() 插入此集合和另一个集合的对称差集。
union() 返回包含集合并集的集合。
update() 用此集合和其他集合的并集来更新集合。

2.9 字典

字典是一个无序、可变和有索引的集合。在 Python 中,字典用花括号编写,拥有键和值。

2.9.1 创建字典

注意字典的键也是要经过哈希运算得到存储地址的,因此键只能是数字、字符串、元组,不能是集合、列表、字典等数据类型。

>>> d = {1:'a',2:'b',3:'c'}
>>> d
{1: 'a', 2: 'b', 3: 'c'}

>>> d = dict() # 或使用 d = {}
>>> d[1] = 'a'
>>> d[2] = 'b'
>>> d[3] = 'c'
>>> d
{1: 'a', 2: 'b', 3: 'c'}

2.9.2 访问值

以通过在方括号内引用其键名来访问字典的值。

>>> d = dict()
>>> d[1] = 'a'
>>> d[2] = 'b'
>>> d[3] = 'c'
>>> d
{1: 'a', 2: 'b', 3: 'c'}
>>> d[1]
'a'
>>> d[2]
'b'

也可以使用 get() 方法来获得相同的效果。

>>> d = dict()
>>> d[1] = 'a'
>>> d[2] = 'b'
>>> d[3] = 'c'
>>> d
{1: 'a', 2: 'b', 3: 'c'}
>>> d.get(1)
'a'
>>> d.get(2)
'b'

2.9.3 更改值

可以通过引用其键名来更改特定项的值:

>>> d = dict()
>>> d[1] = 'a'
>>> d[2] = 'b'
>>> d[3] = 'c'
>>> d
{1: 'a', 2: 'b', 3: 'c'}
>>> d[1] = 'e'
>>> d
{1: 'e', 2: 'b', 3: 'c'}

2.9.4 检查键是否存在

使用 in 关键字确定字典中是否存在指定的键:

>>> d
{1: 'e', 2: 'b', 3: 'c'}
>>> 1 in d
True
>>> 4 in d
False

2.9.5 字典嵌套

字典的值也可以是一个字典,从而实现字典的嵌套。这个特性通常会被用于模拟一个树或图数据结构。

2.9.6 方法

1. values

values() 方法返回 view 对象。这个视图对象包含列表形式的字典值。

该视图对象会动态变化,反映对字典的任何改变。

语法:

dictionary.values()

无参数。

当字典中的值改变时,视图对象也会更新:

>>> d = {1:'a',2:'b',3:'c'}
>>> v = d.values()
>>> v
dict_values(['a', 'b', 'c'])
>>> d[4] = 'd'
>>> v
dict_values(['a', 'b', 'c', 'd'])

2. keys

与 values() 一致,keys() 方法返回 view 对象。这个视图对象包含列表形式的字典键,该视图对象也会反映字典的任何改变。

3. items

与 values() 一致,items() 方法返回一个 view 对象。这个视图对象包含字典的键值对,形式为列表中的元组。

视图对象将反映对字典所做的任何更改。

4. pop

pop() 方法从字典中删除指定的键值对。

被删除的键值对的值是这个 pop() 方法的返回值。

dictionary.pop(keyname, defaultvalue)
参数 描述
keyname 必需。需要删除项目的键名。
defaultvalue 可选。返回值,假如指定的键不存在。如果未指定此参数,且未找到拥有指定键的项目,则会引发错误。
>>> d
{1: 'a', 2: 'b', 3: 'c', 4: 'd'}
>>> d.pop(1)
'a'
>>> d.pop(5,-1)
-1
>>> d.pop(5)
Traceback (most recent call last):
  File "<pyshell#49>", line 1, in <module>
    d.pop(5)
KeyError: 5

另外,关键字 del 也可以删除指定的键值对:

>>> d = {1:'a',2:'b',3:'c'}
>>> del d[1]
>>> d
{2: 'b', 3: 'c'}

也可以直接删除整个字典:

>>> d = {1:'a',2:'b',3:'c'}
>>> del d
>>> d
Traceback (most recent call last):
  File "<pyshell#58>", line 1, in <module>
    d
NameError: name 'd' is not defined

5. 其余方法

方法 描述
clear() 删除字典中的所有元素
copy() 返回字典的副本
fromkeys() 返回拥有指定键和值的字典
get() 返回指定键的值
items() 返回包含每个键值对的元组的列表
keys() 返回包含字典键的列表
pop() 删除拥有指定键的元素
popitem() 删除最后插入的键值对
setdefault() 返回指定键的值。如果该键不存在,则插入具有指定值的键。
update() 使用指定的键值对字典进行更新
values() 返回字典中所有值的列表

2.10 类型转换

可以使用 int() 、bool() 、float()、complex() 、eval() 来完成数字类型间及字符串间的转换。

print(int(2.5))
print(int('3'))
print(float('2.5'))
print(float(3))
print(bool(1))
print(bool(0.0))
print(bool('False'))
print(bool(''))
print(eval('3.8'))
print(eval('3'))
2
3
2.5
3.0
True
False
True
False
3.8
3
函数 描述
int(x [,base]) 将x转换为一个整数
float(x) 将x转换到一个浮点数
complex(real [,imag]) 创建一个复数
str(x) 将对象 x 转换为字符串
repr(x) 将对象 x 转换为表达式字符串
eval(str) 用来计算在字符串中的有效Python表达式,并返回一个对象
tuple(s) 将序列 s 转换为一个元组
list(s) 将序列 s 转换为一个列表
set(s) 将序列 s 转换为可变集合
dict(d) 创建一个字典。d 必须是一个 (key, value)元组序列。
frozenset(s) 将序列 s 转换为不可变集合
chr(x) 将一个整数转换为一个字符
ord(x) 将一个字符转换为它的整数值
hex(x) 将一个整数转换为一个十六进制字符串
oct(x) 将一个整数转换为一个八进制字符串

2.11 输入输出

2.11.1 input

input() 函数允许用户输入,返回值是一个字符串。

语法:

input(prompt)
参数 描述
prompt 字符串,代表输入之前的默认消息。
>>> i = input()
1 2 3 4 5
>>> i
'1 2 3 4 5'

2.11.2 print

print() 函数将指定的消息打印到屏幕或其他标准输出设备上。

该消息可以是字符串,也可以是任何其他对象,该对象在写到屏幕之前会被转换为字符串。

print(object(s), sep=separator, end=end, file=file, flush=flush)
参数 描述
object(s) 任何对象,以及任意数量。打印前将转换为字符串。
sep='separator' 可选。指定如何分隔对象,如果存在多个对象。默认值为 ' '。
end='end' 可选。可选的。指定要在末尾打印的内容。默认值为 '\n'(换行符)。
file 可选。有写入方法的对象。默认为 sys.stdout。
flush 可选的。布尔值,指定输出是刷新(True)还是缓冲(False)。默认为 False。
import time
for i in range(101):
    print("\r进度到达:{:.2f}%".format(i*0.01), end='', flush=True)
    time.sleep(0.1)

2.12 分支结构

if 表达式1:
    语句
    if 表达式2:
        语句
    elif 表达式3:
        语句
    else:
        语句
elif 表达式4:
    语句
else:
    语句

或且非是or,and,not

if 语句不能为空,但是如果为了代码可读性或其他原因写了无内容的 if 语句,需要使用 pass 语句来避免错误。

if b > a:
  pass

空字符串 '' ,空列表 [] ,空元组 () ,空集合 set() ,空字典 {} 都是 False ,有任何元素就是 True。

python 对于 if 结构有如下技巧:

  1. max(a, b) 等价于 a if a>b else b
  2. max(a, b) 等价于 a>b and a or b (和 JAVA 中的 a>b ? a : b 一样)
  3. max(a, b) 等价于 [a,b][a<b] (前者是一个含有两个元素的列表,后者是判断为True,等价于 list[1] 即取第二个值;判断为 False ,等价于 list[0] ,即取第一个值)

2.13 循环

Python 有两个原始的循环命令:

  • while 循环
  • for 循环

2.13.0 前置

在讲循环前,先说几个与循环高度关联的函数。

1. range

range() 函数返回数字序列,默认从 0 开始,默认以 1 递增,并以指定的数字结束。

range(start, stop, step)
参数 描述
start 可选。整数,指定从哪个位置开始。默认为 0。
stop 可选。整数,指定在哪个位置结束。
step 可选的。整数,指定增量。默认为 1。

不能生成到 stop 这个整数,只能到它前面那个数,如 range(10) 生成 0-9 ,range(5,20) 生成 5-19 。

range() 得到的是一个 range 类的可迭代对象,可以使用 list() tuple() set() 等函数进行相应的转化,亦或是使用 [*range()] 进行解包。

>>> [*range(10)]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

2. zip

zip() 函数返回 zip 对象,它是元组的迭代器,其中每个传递的迭代器中的第一项配对在一起,然后每个传递的迭代器中的第二项配对在一起,依此类推。

如果传递的迭代器具有不同的长度,则项目数最少的迭代器将决定新迭代器的长度。

语法:

zip(iterator1, iterator2, iterator3 ...)
参数 描述
iterator1, iterator2, iterator3 ... 被连接在一起的迭代器对象。
>>> zip([1,2,3],[4,5,6])
<zip object at 0x000001FA28FABB88>
>>> [*zip([1,2,3],[4,5,6])]
[(1, 4), (2, 5), (3, 6)]
>>> [*zip([1,2,3],[4,5,6],[7,8,9])]
[(1, 4, 7), (2, 5, 8), (3, 6, 9)]

3. enumerate

enumerate()函数接受一个可迭代对象,并将其作为枚举对象返回。

enumerate()函数添加一个计数器作为枚举对象的键。

语法:

enumerate(iterable, start)
参数 描述
iterable 可迭代对象
start 数字。定义枚举对象的起始编号。默认值为 0。
>>> [*enumerate(list('abcde'))]
[(0, 'a'), (1, 'b'), (2, 'c'), (3, 'd'), (4, 'e')]

2.13.1 while 循环

如果使用 while 循环,只要条件为真,就可以执行一组语句。

例如,只要 i 小于 7,打印 i:

i = 1
while i < 7:
  print(i)
  i += 1

while 循环需要准备好相关的变量。在这个实例中,我们需要定义一个索引变量 i,我们将其设置为 1。

1. break 语句

如果使用 break 语句,即使 while 条件为真,也可以停止循环:

在 i 等于 3 时退出循环:

i = 1
while i < 7:
  print(i)
  if i == 3:
    break
  i += 1

2. continue 语句

如果使用 continue 语句,可以直接停止当前的迭代,并继续下一个:

如果 i 等于 3,则继续下一个迭代:

i = 0
while i < 7:
  i += 1 
  if i == 3:
    continue
  print(i)

3. else 语句

通过使用 else 语句,当条件不再成立时,可以运行一次代码块:

条件为假时打印一条消息:

i = 1
while i < 6:
  print(i)
  i += 1
else:
  print("i最终变成了",i)

2.13.2 for 循环

for 循环用于迭代序列(即列表,元组,字典,集合或字符串)。

这与其他编程语言中的 for 关键字不太相似,而是更像其他面向对象编程语言中的迭代器方法。

通过使用 for 循环,我们可以为列表、元组、集合中的每个元素都执行一组语句。

1. 遍历字符串、列表、元组

s = 'abcdefg'
for i in s:
    print(i)

2. 遍历集合

s = set('abcdef')
for i in s:
    print(i) # 无序,但全部都能遍历到

3. 遍历字典

遍历值:

d = {1: 'a', 2: 'b', 3: 'c'}

for i in d.values():
    print(i)

遍历键:

d = {1: 'a', 2: 'b', 3: 'c'}

for i in d.keys():
    print(i)

4. 遍历range

其他编程语言 for (int i=0; i<n; i++) 在 python 中的为 for i in range(n)

for i in range(10):
    print(i)
0
1
2
3
4
5
6
7
8
9

5. 同时遍历多个值

同时遍历字典键和值:

d = {1: 'a', 2: 'b', 3: 'c'}

for i,j in d.items():
    print(i,j)
1 a
2 b
3 c

d.items() 内部元素如下所示:

>>> [*d.items()]
[(1, 'a'), (2, 'b'), (3, 'c')]

结合上文 zip 函数,也可以写成:

d = {1: 'a', 2: 'b', 3: 'c'}

for i,j in zip(d.keys(), d.values()):
    print(i,j)

带 enumerate 的遍历:

s = set('abcdefg')

for i, j in enumerate(s):
    print(i, j)
0 c
1 g
2 a
3 b
4 e
5 f
6 d

7. 关于 for 循环的语法糖

for 循环也可以在一行内表示,利用这一特性可以快速创建列表、元组、集合、字典:

快速创建列表:

>>> [i for i in range(10)]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
L = [i for i in range(10)]
# 等价于
L = []
for i in range(10):
    L.append(i)
>>> [i**2 for i in range(10)]
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
>>> [i for i in range(10) if i%3!=0]
[1, 2, 4, 5, 7, 8]

元组、集合的快速创建方法一致,不再赘述。

快速创建字典:

>>> {i: j for i,j in zip(range(5),'abcde')}
{0: 'a', 1: 'b', 2: 'c', 3: 'd', 4: 'e'}
>>> {i: j for i,j in enumerate('abcde',1)}
{1: 'a', 2: 'b', 3: 'c', 4: 'd', 5: 'e'}

2.14 函数

函数是一种仅在调用时运行的代码块,可以将数据(称为参数)传递到函数中。函数可以把数据作为结果返回。

2.14.1 创建函数

在 Python 中,使用 def 关键字定义函数:

def my_function():
    print("hello world!")

2.14.2 调用函数

如需调用函数,则使用函数名称后跟括号:

def my_function():
    print("hello world!")

my_function()

2.14.3 参数

信息可以作为参数传递给函数。

参数在函数名后的括号内指定。可以根据需要添加任意数量的参数,只需用逗号分隔即可。

def add(a, b):
    return a+b

print(add(1, 2))
print(add('a', 'b'))
print(add([1,2,3], [4,5,6]))

2.14.4 默认参数值

若在定义函数时给参数先传递一个值,则调用函数时不给这个参数传值就会使用定义时给的值。

def repeat(s, n=3):
    return s*n

print(repeat('abc'))
print(repeat('abc', 5))

2.14.5 关键字参数

可以使用 key = value 语法发送参数。

参数的顺序无关紧要。

def repeat(s, n=3):
    return s*n

print(repeat(n=5, s='abc'))

2.14.6 任意个数参数

如果不知道将传递给函数多少个参数,可以在函数定义的参数名称前添加 *。

这样,函数将接收一个元组作为参数,并可以相应地访问各项:

def sum(*num):
    print(type(num))
    count = 0
    for i in num:
        count += i
    return count

print(sum(1, 2, 3, 4, 5, 6))
<class 'tuple'>
21

如果在函数定义的参数名称前添加 **。

这样,函数将接收一个字典作为参数,并可以相应地访问各项:

def test(param1, **param2):
    print (param1)
    print (param2)

test(1, a=2, b=3)

2.14.7 pass 语句

函数定义不能为空,但是如果出于某种原因写了无内容的函数定义,可以使用 pass 语句来避免错误。

def myfunction():
  pass

2.15 Lambda 匿名函数

lambda 函数是一种小的匿名函数。

lambda 函数可接受任意数量的参数,但只能有一个表达式。

语法:

lambda arguments : expression

2.15.1 一个参数

一个 lambda 函数,它把作为参数传入的数字加 10,然后打印结果:

lambda x: x+10

2.15.2 多个参数

一个 lambda 函数,它把参数 a 与参数 b 相乘并打印结果:

lambda x,y: x**2+y**2

2.15.3 单独使用匿名函数

>>> (lambda x: x+10)(5)
15
>>> (lambda x,y: x**2+y**2)(3,4)
25

2.16 内建函数

Python 有许多内建函数,接下来讲一些前文没有出现过但十分重要的内建函数。

2.16.1 map

map() 函数为可迭代对象中的每个元素执行指定的函数。每个单独元素作为参数发送到函数。

语法:

map(function, iterables)
参数 描述
function 必需。为每个项目执行的函数。
iterable 必需。序列、集合或迭代器对象。可以发送任意数量的可迭代对象,只需确保该函数的每个可迭代对象都有一个参数即可。
>>> [*map(lambda x: x*2, range(10))]
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
def myfun(a, b):
    return b*a

for i in map(myfun, (1,2,3), 'abc'):
    print(i)
a
bb
ccc

python 的 input 只能获取一行字符串,例如:

>>> s = input()
1 2 3
>>> s
'1 2 3'
>>> type(s)
<class 'str'>

使用 map split int 这三个函数就能得到键入的这些整数:

>>> a, b, c = map(int, input('请键入三个整数,以空格分开:').split())
请键入三个整数,以空格分开:1 2 3
>>> a
1
>>> b
2
>>> c
3

2.16.2 filter

filter() 函数返回一个迭代器,该迭代器通过一个函数对原始可迭代序列中的所有元素进行过滤。

语法:

filter(function, iterable)
参数 描述
function 测试 iterable 中每个项目的函数。
iterable 需被过滤的 iterable。
>>> [*filter(lambda x: x<5, range(10))]
[0, 1, 2, 3, 4]

2.16.3 其余内建函数

函数 描述
abs() 返回数的绝对值
all() 如果可迭代对象中的所有项均为 true,则返回 True。
any() 如果可迭代对象中的任何项为 true,则返回 True。
ascii() 返回对象的可读版本。用转义字符替换 none-ascii 字符。
bin() 返回数的二进制版本。
bool() 返回指定对象的布尔值。
bytearray() 返回字节数组。
bytes() 返回字节对象。
callable() 如果指定的对象是可调用的,则返回 True,否则返回 False。
chr() 返回指定 Unicode 代码中的字符。
classmethod() 把方法转换为类方法。
compile() 把指定的源作为对象返回,准备执行。
complex() 返回复数。
delattr() 从指定的对象中删除指定的属性(属性或方法)。
dict() 返回字典(数组)。
dir() 返回指定对象的属性和方法的列表。
divmod() 当参数1除以参数2时,返回商和余数。
enumerate() 获取集合(例如元组)并将其作为枚举对象返回。
eval() 评估并执行表达式。
exec() 执行指定的代码(或对象)。
filter() 使用过滤器函数排除可迭代对象中的项目。
float() 返回浮点数。
format() 格式化指定值。
frozenset() 返回 frozenset 对象。
getattr() 返回指定属性的值(属性或方法)。
globals() 以字典返回当前全局符号表。
hasattr() 如果指定的对象拥有指定的属性(属性/方法),则返回 True。
hash() 返回指定对象的哈希值。
help() 执行内建的帮助系统。
hex() 把数字转换为十六进制值。
id() 返回对象的 id。
input() 允许用户输入。
int() 返回整数。
isinstance() 如果指定的对象是指定对象的实例,则返回 True。
issubclass() 如果指定的类是指定对象的子类,则返回 True。
iter() 返回迭代器对象。
len() 返回对象的长度。
list() 返回列表。
locals() 返回当前本地符号表的更新字典。
map() 返回指定的迭代器,其中指定的函数应用于每个项目。
max() 返回可迭代对象中的最大项目。
memoryview() 返回内存视图(memory view)对象。
min() 返回可迭代对象中的最小项目。
next() 返回可迭代对象中的下一项。
object() 返回新对象。
oct() 把数转换为八进制。
open() 打开文件并返回文件对象。
ord() 转换表示指定字符的 Unicode 的整数。
pow() 返回 x 的 y 次幂的值。
print() 打印标准输出设备。
property() 获取、设置、删除属性。
range() 返回数字序列,从 0 开始且以 1 为增量(默认地)。
repr() 返回对象的可读版本。
reversed() 返回反转的迭代器。
round() 对数进行舍入。
set() 返回新的集合对象。
setattr() 设置对象的属性(属性/方法)。
slice() 返回 slice 对象。
sorted() 返回排序列表。
@staticmethod() 把方法转换为静态方法。
str() 返回字符串对象。
sum() 对迭代器的项目进行求和。
super() 返回表示父类的对象。
tuple() 返回元组。
type() 返回对象的类型。
vars() 返回对象的 dict 属性。
zip() 从两个或多个迭代器返回一个迭代器。

3 基础进阶

3.1 面向对象(OOP)基本概念

面向对象编程 —— Object Oriented Programming 简写 OOP

  • 我们之前学习的编程方式就是 面向过程
  • 面向过程面向对象,是两种不同的 编程方式
  • 对比 面向过程 的特点,可以更好地了解什么是 面向对象

3.1.1过程和函数

  • 过程 是早期的一个编程概念
  • 过程 类似于函数,只能执行,但是没有返回值
  • 函数 不仅能执行,还可以返回结果

3.1.2 面向过程 和 面向对象 区别

1) 面相过程 —— 怎么做?

  1. 把完成某一个需求的 所有步骤 从头到尾 逐步实现
  2. 根据开发需求,将某些 功能独立 的代码 封装 成一个又一个 函数
  3. 最后完成的代码,就是顺序地调用 不同的函数

特点

  1. 注重 步骤与过程,不注重职责分工
  2. 如果需求复杂,代码会变得很复杂
  3. 开发复杂项目,没有固定的套路,开发难度很大!

2) 面向对象 —— 谁来做?

相比较函数,面向对象更大封装,根据 职责一个对象中 封装 多个方法

  1. 在完成某一个需求前,首先确定 职责 —— 要做的事情(方法)
  2. 根据 职责 确定不同的 对象,在 对象 内部封装不同的 方法(多个)
  3. 最后完成的代码,就是顺序地让 不同的对象 调用 不同的方法

特点

  1. 注重 对象和职责,不同的对象承担不同的职责
  2. 更加适合应对复杂的需求变化,是专门应对复杂项目开发,提供的固定套路
  3. 需要在面向过程基础上,再学习一些面向对象的语法

3.2 类和对象

对象面向对象编程的 两个 核心概念

3.2.1 类

  • 是对一群具有 相同 特征 或者 行为 的事物的一个统称,是抽象的,不能直接使用
    • 特征 被称为 属性
    • 行为 被称为 方法
  • 就相当于制造飞机时的图纸,是一个 模板,是 负责创建对象的

3.2.2 对象

  • 对象由类创建出来的一个具体存在,可以直接使用
  • 哪一个类 创建出来的 对象,就拥有在 哪一个类 中定义的:
    • 属性
    • 方法
  • 对象 就相当于用 图纸 制造 的飞机

在程序开发中,应该 先有类,再有对象

3.2.3 类和对象的关系

  • 类是模板对象 是根据 这个模板创建出来的,应该 先有类,再有对象
  • 只有一个,而 对象 可以有很多个
    • 不同的对象 之间 属性 可能会各不相同
  • 中定义了什么 属性和方法对象 中就有什么属性和方法,不可能多,也不可能少

3.2.4 类的设计

在使用面向对象开发前,应该首先分析需求,确定一下,程序中需要包含哪些类!

在程序开发中,要设计一个类,通常需要满足一下三个要素:

  1. 类名 这类事物的名字,满足大驼峰命名法
  2. 属性 这类事物具有什么样的特征
  3. 方法 这类事物具有什么样的行为

3.2.4.1 大驼峰命名法

CapWords
  1. 每一个单词的首字母大写
  2. 单词与单词之间没有下划线

3.2.4.2 类名的确定

名词提炼法 分析 整个业务流程,出现的 名词,通常就是找到的类

3.2.4.3 属性和方法的确定

  • 对象的特征描述,通常可以定义成 属性
  • 对象具有的行为(动词),通常可以定义成 方法

提示:需求中没有涉及的属性或者方法在设计类时,不需要考虑

3.3 面向对象基础语法

3.3.1 dir 内置函数

  • Python对象几乎是无所不在的,我们之前学习的 变量数据函数 都是对象

Python 中可以使用以下两个方法验证:

  1. 标识符 / 数据 后输入一个 .,然后按下 TAB 键,iPython 会提示该对象能够调用的 方法列表
  2. 使用内置函数 dir 传入 标识符 / 数据,可以查看对象内的 所有属性及方法

例如:

a = 6
print(dir(a))
['__abs__', '__add__', '__and__', '__bool__', '__ceil__', '__class__', '__delattr__', '__dir__', '__divmod__', '__doc__', '__eq__', '__float__', '__floor__', '__floordiv__', '__format__', '__ge__', '__getattribute__', '__getnewargs__', '__gt__', '__hash__', '__index__', '__init__', '__init_subclass__', '__int__', '__invert__', '__le__', '__lshift__', '__lt__', '__mod__', '__mul__', '__ne__', '__neg__', '__new__', '__or__', '__pos__', '__pow__', '__radd__', '__rand__', '__rdivmod__', '__reduce__', '__reduce_ex__', '__repr__', '__rfloordiv__', '__rlshift__', '__rmod__', '__rmul__', '__ror__', '__round__', '__rpow__', '__rrshift__', '__rshift__', '__rsub__', '__rtruediv__', '__rxor__', '__setattr__', '__sizeof__', '__str__', '__sub__', '__subclasshook__', '__truediv__', '__trunc__', '__xor__', 'as_integer_ratio', 'bit_length', 'conjugate', 'denominator', 'from_bytes', 'imag', 'numerator', 'real', 'to_bytes']

提示 __方法名__ 格式的方法是 Python 提供的 内置方法 / 属性,稍后会给大家介绍一些常用的 内置方法 / 属性

序号 方法名 类型 作用
01 __new__ 方法 创建对象时,会被 自动 调用
02 __init__ 方法 对象被初始化时,会被 自动 调用
03 __del__ 方法 对象被从内存中销毁前,会被 自动 调用
04 __str__ 方法 返回对象的描述信息print 函数输出使用

3.3.2 定义只包含方法的类

面向对象更大封装,在 一个类中 封装 多个方法,这样 通过这个类创建出来的对象,就可以直接调用这些方法了

  • Python 中要定义一个只包含方法的类,语法格式如下:
class 类名:

    def 方法1(self, 参数列表):
        pass

    def 方法2(self, 参数列表):
        pass
  • 方法 的定义格式和之前学习过的函数 几乎一样
  • 区别在于第一个参数必须是 self,类似于java、php中的this,都是指向这个对象实例的,可以先暂时先记住,稍后介绍 self

注意:类名 的 命名规则 要符合 大驼峰命名法

3.3.3 创建对象

  • 当一个类定义完成之后,要使用这个类来创建对象,语法格式如下:
对象变量 = 类名()

3.3.4 示例

需求

  • 小猫 鱼,小猫

分析

  1. 定义一个猫类 Cat
  2. 定义两个方法 eatdrink
  3. 按照需求 —— 不需要定义属性
class Cat:
    """这是一个猫类"""

    def eat(self):
        print("小猫吃鱼")

    def drink(self):
        print("小猫喝水")

tom = Cat()
print(tom.__doc__)
tom.drink()
tom.eat()

引用概念的强调

在面向对象开发中,引用的概念是同样适用的!

  • Python 中使用类 创建对象之后tom 变量中 仍然记录的是 对象在内存中的地址
  • 也就是 tom 变量 引用新建的猫对象
  • 使用 print 输出 对象变量,默认情况下,能够输出这个变量 引用的对象由哪一个类创建的对象,以及 在内存中的地址十六进制表示

提示:在计算机中,通常使用 十六进制 表示 内存地址

  • %d 可以以 10 进制 输出数字
  • %x 可以以 16 进制 输出数字

3.3.5 方法中的 self 参数

(1)给刚才的对象增加属性

  • Python 中,要 给对象设置属性,非常的容易,但是不推荐使用
    • 因为:对象属性的封装应该封装在类的内部
  • 只需要在 类的外部的代码 中直接通过 . 设置一个属性即可

注意:这种方式虽然简单,但是不推荐使用!

tom.name = "Tom"
...

lazy_cat.name = "大懒猫"

(2)使用 self 在方法内部输出每一只猫的名字

哪一个对象 调用的方法,方法内的 self 就是 哪一个对象的引用

  • 在类封装的方法内部,self 就表示 当前调用方法的对象自己
  • 调用方法时,程序员不需要传递 self 参数
  • 在方法内部
    • 可以通过 self. 访问对象的属性
    • 也可以通过 self. 调用其他的对象方法
  • 改造代码如下:
class Cat:

    def eat(self):
        print("%s 爱吃鱼" % self.name)

tom = Cat()
tom.name = "Tom"
tom.eat()

lazy_cat = Cat()
lazy_cat.name = "大懒猫"
lazy_cat.eat()

  • 类的外部,通过 变量名. 访问对象的 属性和方法
  • 类封装的方法中,通过 self. 访问对象的 属性和方法

3.3.6 __init__

(1)之前代码存在的问题 —— 在类的外部给对象增加属性

  • 将案例代码进行调整,先调用方法 再设置属性,观察一下执行效果
tom = Cat()
tom.drink()
tom.eat()
tom.name = "Tom"
print(tom)
  • 程序执行报错如下:
AttributeError: 'Cat' object has no attribute 'name'
属性错误:'Cat' 对象没有 'name' 属性

提示

  • 在日常开发中,不推荐在 类的外部 给对象增加属性
    • 如果在运行时,没有找到属性,程序会报错
  • 对象应该包含有哪些属性,应该 封装在类的内部

(2)初始化方法

  • 当使用 类名() 创建对象时,会 自动 执行以下操作:
    1. 为对象在内存中 分配空间 —— 创建对象
    2. 为对象的属性 设置初始值 —— 初始化方法(init)
  • 这个 初始化方法 就是 __init__ 方法,__init__ 是对象的内置方法

__init__ 方法是 专门 用来定义一个类 具有哪些属性的方法

Cat 中增加 __init__ 方法,验证该方法在创建对象时会被自动调用

class Cat:
    """这是一个猫类"""

    def __init__(self):
        print("初始化方法")

(3)在初始化方法内部定义属性

  • __init__ 方法内部使用 self.属性名 = 属性的初始值 就可以 定义属性
  • 定义属性之后,再使用 Cat 类创建的对象,都会拥有该属性
class Cat:

    def __init__(self):

        print("这是一个初始化方法")

        # 定义用 Cat 类创建的猫对象都有一个 name 的属性
        self.name = "Tom"

    def eat(self):
        print("%s 爱吃鱼" % self.name)

# 使用类名()创建对象的时候,会自动调用初始化方法 __init__
tom = Cat()

tom.eat()

(4)改造初始化方法 —— 初始化的同时设置初始值

  • 在开发中,如果希望在 创建对象的同时,就设置对象的属性,可以对 __init__ 方法进行 改造
    1. 把希望设置的属性值,定义成 __init__ 方法的参数
    2. 在方法内部使用 self.属性 = 形参 接收外部传递的参数
    3. 在创建对象时,使用 类名(属性1, 属性2...) 调用
class Cat:

    def __init__(self, name):
        print("初始化方法 %s" % name)
        self.name = name
    ...

tom = Cat("Tom")
...

lazy_cat = Cat("大懒猫")
...

3.3.7 __del__ 和 __str__

序号 方法名 类型 作用
01 __del__ 方法 对象被从内存中销毁前,会被 自动 调用
02 __str__ 方法 返回对象的描述信息print 函数输出使用

(1) __del__ 方法

  • Python
    • 当使用 类名() 创建对象时,为对象 分配完空间后,自动 调用 __init__ 方法
    • 当一个 对象被从内存中销毁 前,会 自动 调用 __del__ 方法
  • 应用场景
    • __init__ 改造初始化方法,可以让创建对象更加灵活
    • __del__ 如果希望在对象被销毁前,再做一些事情,可以考虑一下 __del__ 方法
  • 生命周期
    • 一个对象从调用 类名() 创建,生命周期开始
    • 一个对象的 __del__ 方法一旦被调用,生命周期结束
    • 在对象的生命周期内,可以访问对象属性,或者让对象调用方法
class Cat:

    def __init__(self, new_name):

        self.name = new_name

        print("%s 来了" % self.name)

    def __del__(self):

        print("%s 去了" % self.name)

# tom 是一个全局变量
tom = Cat("Tom")
print(tom.name)

# del 关键字可以删除一个对象
del tom

print("-" * 50)

(2) __str__ 方法

  • Python 中,使用 print 输出 对象变量,默认情况下,会输出这个变量 引用的对象由哪一个类创建的对象,以及 在内存中的地址十六进制表示
  • 如果在开发中,希望使用 print 输出 对象变量 时,能够打印 自定义的内容,就可以利用 __str__ 这个内置方法了

注意:__str__ 方法必须返回一个字符串

class Cat:

    def __init__(self, new_name):

        self.name = new_name

        print("%s 来了" % self.name)

    def __del__(self):

        print("%s 去了" % self.name)

    def __str__(self):
        return "我是小猫:%s" % self.name

tom = Cat("Tom")
print(tom)

3.4 封装

  1. 封装 是面向对象编程的一大特点
  2. 面向对象编程的 第一步 —— 将 属性方法 封装 到一个抽象的
  3. 外界 使用 创建 对象,然后 让对象调用方法
  4. 对象方法的细节 都被 封装类的内部

(1)小明爱跑步

需求

  1. 小明 体重 75.0 公斤
  2. 小明每次 跑步 会减肥 0.5 公斤
  3. 小明每次 吃东西 体重增加 1 公斤

提示:在 对象的方法内部,是可以 直接访问对象的属性 的!

  • 代码实现:
class Person:
    """人类"""

    def __init__(self, name, weight):

        self.name = name
        self.weight = weight

    def __str__(self):

        return "我的名字叫 %s 体重 %.2f 公斤" % (self.name, self.weight)

    def run(self):
        """跑步"""

        print("%s 爱跑步,跑步锻炼身体" % self.name)
        self.weight -= 0.5

    def eat(self):
        """吃东西"""

        print("%s 是吃货,吃完这顿再减肥" % self.name)
        self.weight += 1


xiaoming = Person("小明", 75)

xiaoming.run()
xiaoming.eat()
xiaoming.eat()

print(xiaoming)

(2)扩展 —— 小美也爱跑步

需求

  1. 小明小美 都爱跑步
  2. 小明 体重 75.0 公斤
  3. 小美 体重 45.0 公斤
  4. 每次 跑步 都会减少 0.5 公斤
  5. 每次 吃东西 都会增加 1 公斤

提示

  1. 对象的方法内部,是可以 直接访问对象的属性
  2. 同一个类 创建的 多个对象 之间,属性 互不干扰!
class Person:
    """人类"""

    def __init__(self, name, weight):

        self.name = name
        self.weight = weight

    def __str__(self):

        return "我的名字叫 %s 体重 %.2f 公斤" % (self.name, self.weight)

    def run(self):
        """跑步"""

        print("%s 爱跑步,跑步锻炼身体" % self.name)
        self.weight -= 0.5

    def eat(self):
        """吃东西"""

        print("%s 是吃货,吃完这顿再减肥" % self.name)
        self.weight += 1


xiaoming = Person("小明", 65)
xiaomei = Person("小美", 55)

xiaoming.run()
xiaoming.eat()
xiaoming.eat()

print(xiaoming)

xiaomei.run()
xiaomei.eat()
xiaomei.eat()

print(xiaomei)

(3)知识点回顾——身份运算符

身份运算符用于 比较 两个对象的 内存地址 是否一致 —— 是否是对同一个对象的引用

  • Python 中针对 None 比较时,建议使用 is 判断
运算符 描述 实例
is is 是判断两个标识符是不是引用同一个对象 x is y,类似 id(x) == id(y)
is not is not 是判断两个标识符是不是引用不同对象 x is not y,类似 id(a) != id(b)

is 与 == 区别:

is 用于判断 两个变量 引用对象是否为同一个
== 用于判断 引用变量的值 是否相等

3.5 私有属性和私有方法

3.5.1 应用场景及定义方式

应用场景

  • 在实际开发中,对象某些属性或方法 可能只希望 在对象的内部被使用,而 不希望在外部被访问到
  • 私有属性 就是 对象 不希望公开的 属性
  • 私有方法 就是 对象 不希望公开的 方法

定义方式

  • 定义属性或方法时,在 属性名或者方法名前 增加 一个下划线,定义的就是 保护 属性或方法,只有类对象和子类对象自己能访问到这些变量
  • 定义属性或方法时,在 属性名或者方法名前 增加 两个下划线,定义的就是 私有 属性或方法,只有类对象自己能访问
class Women:

    def __init__(self, name):

        self.name = name
        self.__age = 18

    def __get_age(self):
        print("我的年龄是 %d" % self.__age)


xiaofang = Women("小芳")
# 私有属性,外部不能直接访问
# print(xiaofang.__age)

# 私有方法,外部不能直接调用
# xiaofang.__get_age()

# 如果一个下划线开头,则可以外部正常调用,但不推荐这样使用

3.5.2 伪私有属性和私有方法

提示:在日常开发中,不要使用这种方式访问对象的 私有属性 或 私有方法

Python 中,并没有 真正意义私有

  • 在给 属性方法 命名时,实际是对 名称 做了一些特殊处理,使得外界无法访问到
  • 处理方式:在 名称 前面加上 _类名 => _类名__名称
class Women:

    def __init__(self, name):

        self.name = name
        self.__age = 18

    def __get_age(self):
        print("我的年龄是 %d" % self.__age)


xiaofang = Women("小芳")
# 私有属性,外部不能直接访问
print(xiaofang._Women__age)

# 私有方法,外部不能直接调用
xiaofang._Women__get_age()

3.6 继承

面向对象三大特性

  1. 封装 根据 职责属性方法 封装 到一个抽象的
  2. 继承 实现代码的重用,相同的代码不需要重复的编写
  3. 多态 不同的对象调用相同的方法,产生不同的执行结果,增加代码的灵活度

3.6.1 单继承

3.6.1.1 继承的概念、语法和特点

继承的概念子类 拥有 父类 的所有 方法属性

1) 继承的语法

class 类名(父类名):

    pass
  • 子类 继承自 父类,可以直接 享受 父类中已经封装好的方法,不需要再次开发
  • 子类 中应该根据 职责,封装 子类特有的 属性和方法

2) 专业术语

  • Dog 类是 Animal 类的子类Animal 类是 Dog 类的父类Dog 类从 Animal继承
  • Dog 类是 Animal 类的派生类Animal 类是 Dog 类的基类Dog 类从 Animal派生

3) 继承的传递性

  • C 类从 B 类继承,B 类又从 A 类继承
  • 那么 C 类就具有 B 类和 A 类的所有属性和方法

子类 拥有 父类 以及 父类的父类 中封装的所有 属性方法

class Animal:

    def eat(self):
        print("吃---")

    def drink(self):
        print("喝---")

    def run(self):
        print("跑---")

    def sleep(self):
        print("睡---")


class Dog(Animal):

    # 子类拥有父类的所有属性和方法

    def bark(self):
        """封装子类特有的属性和方法"""
        print("汪汪叫")

# 创建一个对象 - 狗
wangcai = Dog()

wangcai.eat()
wangcai.drink()
wangcai.run()
wangcai.sleep()
wangcai.bark()

3.6.1.2 方法的重写

  • 子类 拥有 父类 的所有 方法属性
  • 子类 继承自 父类,可以直接 享受 父类中已经封装好的方法,不需要再次开发

应用场景

  • 父类 的方法实现不能满足子类需求时,可以对方法进行 重写(override)

重写 父类方法有两种情况:

  1. 覆盖 父类的方法
  2. 对父类方法进行 扩展

1) 覆盖父类的方法

  • 如果在开发中,父类的方法实现子类的方法实现完全不同
  • 就可以使用 覆盖 的方式,在子类中 重新编写 父类的方法实现

具体的实现方式,就相当于在 子类中 定义了一个 和父类同名的方法并且实现

重写之后,在运行时,只会调用 子类中重写的方法,而不再会调用 父类封装的方法

class Animal:

    def eat(self):
        print("吃---")

    def drink(self):
        print("喝---")

    def run(self):
        print("跑---")

    def sleep(self):
        print("睡---")


class Dog(Animal):

    def bark(self):
        print("汪汪叫")


class XiaoTianQuan(Dog):

    def fly(self):
        print("我会飞")

    def bark(self):
        print("说人话...")


xtq = XiaoTianQuan()

# 如果子类中,重写了父类的方法
# 在使用子类对象调用方法时,会调用子类中重写的方法
xtq.bark()

2) 对父类方法进行扩展

  • 如果在开发中,子类的方法实现包含 父类的方法实现
    • 父类原本封装的方法实现子类方法的一部分
  • 就可以使用 扩展 的方式
    1. 在子类中 重写 父类的方法
    2. 在需要的位置使用 super().父类方法 来调用父类方法的执行
    3. 代码其他的位置针对子类的需求,编写 子类特有的代码实现

关于 super

  • Pythonsuper 是一个 特殊的类
  • super() 就是使用 super 类创建出来的对象
  • 最常 使用的场景就是在 重写父类方法时,调用 在父类中封装的方法实现

调用父类方法的另外一种方式

Python 2.x 时,如果需要调用父类的方法,还可以使用以下方式:

父类名.方法(self)
  • 这种方式,目前在 Python 3.x 还支持这种方式
  • 这种方法 不推荐使用,因为一旦 父类发生变化,方法调用位置的 类名 同样需要修改
class Animal:

    def eat(self):
        print("吃---")

    def drink(self):
        print("喝---")

    def run(self):
        print("跑---")

    def sleep(self):
        print("睡---")


class Dog(Animal):

    def bark(self):
        print("汪汪叫")


class XiaoTianQuan(Dog):

    def fly(self):
        print("我会飞")

    def bark(self):

        # 1. 针对子类特有的需求,编写代码
        print("口吐人言...")

        # 2. 使用 super(). 调用原本在父类中封装的方法
        super().bark()

        # 父类名.方法(self)
        Dog.bark(self)

        # 注意:如果使用子类调用方法,会出现递归调用 - 死循环!
        # XiaoTianQuan.bark(self)

        # 3. 增加其他子类的代码
        print("$%^*%^$%^#%$%")


xtq = XiaoTianQuan()

xtq.bark()

提示

  • 在开发时,父类名super() 两种方式不要混用
  • 如果使用 当前子类名 调用方法,会形成递归调用,出现死循环

3.6.1.3 父类的 私有属性 和 私有方法

  1. 子类对象 不能 在自己的方法内部,直接 访问 父类的 私有属性私有方法
  2. 子类对象 可以通过 父类公有方法 间接 访问到 私有属性私有方法
  • 私有属性、方法 是对象的隐私,不对外公开,外界 以及 子类 都不能直接访问
  • 私有属性、方法 通常用于做一些内部的事情

示例

  • B 的对象不能直接访问 __num2 属性
  • B 的对象不能在 demo 方法内访问 __num2 属性
  • B 的对象可以在 demo 方法内,调用父类的 test 方法
  • 父类的 test 方法内部,能够访问 __num2 属性和 __test 方法
class A:

    def __init__(self):

        self.num1 = 100
        self.__num2 = 200

    def __test(self):
        print("私有方法 %d %d" % (self.num1, self.__num2))


class B(A):

    def demo(self):

        # 1. 在子类的对象方法中,不能访问父类的私有属性
        # print("访问父类的私有属性 %d" % self.__num2)

        # 2. 在子类的对象方法中,不能调用父类的私有方法
        # self.__test()
        print(self.num1)
        pass

# 创建一个子类对象
b = B()
print(b)

b.demo()

# 在外界不能直接访问对象的私有属性/调用私有方法
# print(b.__num2)
# b.__test()

3.6.2 多继承

概念

  • 子类 可以拥有 多个父类,并且具有 所有父类属性方法
  • 例如:孩子 会继承自己 父亲母亲特性

语法

class 子类名(父类名1, 父类名2...)
    pass
class A:

    def test(self):
        print("test 方法")


class B:

    def demo(self):
        print("demo 方法")


class C(A, B):
    """多继承可以让子类对象,同时具有多个父类的属性和方法"""
    pass


# 创建子类对象
c = C()

c.test()
c.demo()

3.6.2.1 多继承的使用注意事项

问题的提出

  • 如果 不同的父类 中存在 同名的方法子类对象 在调用方法时,会调用 哪一个父类中的方法呢?

提示:开发时,应该尽量避免这种容易产生混淆的情况! —— 如果 父类之间 存在 同名的属性或者方法,应该 尽量避免 使用多继承

class A:

    def test(self):
        print("A --- test 方法")

    def demo(self):
        print("A --- demo 方法")

class B:

    def test(self):
        print("B --- test 方法")

    def demo(self):
        print("B --- demo 方法")


class C(B, A):
    """多继承可以让子类对象,同时具有多个父类的属性和方法"""
    pass


# 创建子类对象
c = C()

c.test()
c.demo()

Python 中的 MRO —— 方法搜索顺序

  • Python 中针对 提供了一个 内置属性 __mro__ 可以查看 方法 搜索顺序
  • MRO 是 method resolution order,主要用于 在多继承时判断 方法、属性 的调用 路径
print(C.__mro__)

输出结果

(<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>)
  • 在搜索方法时,是按照 __mro__ 的输出结果 从左至右 的顺序查找的
  • 如果在当前类中 找到方法,就直接执行,不再搜索
  • 如果 没有找到,就查找下一个类 中是否有对应的方法,如果找到,就直接执行,不再搜索
  • 如果找到最后一个类,还没有找到方法,程序报错

3.6.2.2 新式类与旧式(经典)类

objectPython 为所有对象提供的 基类,提供有一些内置的属性和方法,可以使用 dir 函数查看

  • 新式类:以 object 为基类的类,推荐使用
  • 经典类:不以 object 为基类的类,不推荐使用
  • Python 3.x 中定义类时,如果没有指定父类,会 默认使用 object 作为该类的 基类 —— Python 3.x 中定义的类都是 新式类
  • Python 2.x 中定义类时,如果没有指定父类,则不会以 object 作为 基类

新式类经典类 在多继承时 —— 会影响到方法的搜索顺序

为了保证编写的代码能够同时在 Python 2.xPython 3.x 运行!
今后在定义类时,如果没有父类,建议统一继承自 object

class 类名(object):
    pass

3.7 多态

面向对象三大特性

  1. 封装 根据 职责属性方法 封装 到一个抽象的
    • 定义类的准则
  2. 继承 实现代码的重用,相同的代码不需要重复的编写
    • 设计类的技巧
    • 子类针对自己特有的需求,编写特定的代码
  3. 多态 不同的 子类对象 调用相同的 父类方法,产生不同的执行结果
    • 多态 可以 增加代码的灵活度
    • 继承重写父类方法 为前提
    • 是调用方法的技巧,不会影响到类的内部设计

示例:

1)在 Dog 类中封装方法 game

  • 普通狗只是简单的玩耍

2)定义 XiaoTianDog 继承自 Dog,并且重写 game 方法

  • 哮天犬需要在天上玩耍

3)定义 Person 类,并且封装一个 和狗玩 的方法

  • 在方法内部,直接让 狗对象 调用 game 方法

  • Person 类中只需要让 狗对象 调用 game 方法,而不关心具体是 什么狗
    • game 方法是在 Dog 父类中定义的
  • 在程序执行时,传入不同的 狗对象 实参,就会产生不同的执行效果

多态 更容易编写出出通用的代码,做出通用的编程,以适应需求的不断变化!

class Dog(object):

    def __init__(self, name):
        self.name = name

    def game(self):
        print("%s 蹦蹦跳跳的玩耍..." % self.name)


class XiaoTianDog(Dog):

    def game(self):
        print("%s 飞到天上去玩耍..." % self.name)


class Person(object):

    def __init__(self, name):
        self.name = name

    def game_with_dog(self, dog):

        print("%s 和 %s 快乐的玩耍..." % (self.name, dog.name))

        # 让狗玩耍
        dog.game()


# 1. 创建一个狗对象
# wangcai = Dog("旺财")
wangcai = XiaoTianDog("哮天犬")

# 2. 创建一个小明对象
xiaoming = Person("小明")

# 3. 让小明调用和狗玩的方法
xiaoming.game_with_dog(wangcai)

3.8 类属性和类方法

3.8.1 类的结构

3.8.1.1 实例

  1. 使用面向对象开发,第 1 步 是设计

  2. 使用 类名() 创建对象,创建对象 的动作有两步:

      1. 在内存中为对象 分配空间
      1. 调用初始化方法 __init__对象初始化
  3. 对象创建后,内存 中就有了一个对象的 实实在在 的存在 —— 实例

因此,通常也会把:

  1. 创建出来的 对象 叫做 实例
  2. 创建对象的 动作 叫做 实例化
  3. 对象的属性 叫做 实例属性
  4. 对象调用的方法 叫做 实例方法

在程序执行时:

  1. 对象各自拥有自己的 实例属性

  2. 调用对象方法,可以通过self.

    • 访问自己的属性

    • 调用自己的方法

结论

  • 每一个对象 都有自己 独立的内存空间保存各自不同的属性
  • 多个对象的方法在内存中只有一份,在调用方法时,需要把对象的引用 传递到方法内部

3.8.1.2 类是一个特殊的对象

Python一切皆对象

  • class AAA: 定义的类属于 类对象
  • obj1 = AAA() 属于 实例对象
  • 在程序运行时, 同样 会被加载到内存
  • Python 中, 是一个特殊的对象 —— 类对象
  • 在程序运行时,类对象 在内存中 只有一份,使用 一个类 可以创建出 很多个对象实例
  • 除了封装 实例属性方法外,类对象 还可以拥有自己的 属性方法
    1. 类属性
    2. 类方法
  • 通过 类名. 的方式可以 访问类的属性 或者 调用类的方法

3.8.2 类属性和实例属性

3.8.2.1 概念和使用

  • 类属性 就是给 类对象 中定义的 属性
  • 通常用来记录 与这个类相关 的特征
  • 类属性 不会用于记录 具体对象的特征

示例需求

  • 定义一个 工具类
  • 每件工具都有自己的 name
  • 需求 —— 知道使用这个类,创建了多少个工具对象?
![](http://cdn.halashuo.cn/static/word_images/archives91/36.png)![](http://cdn.halashuo.cn/static/word_images/archives91/36.png)class Tool(object):

    # 使用赋值语句定义类属性,记录所有工具对象的数量
    count = 0

    def __init__(self, name):
        self.name = name

        # 让类属性的值+1
        Tool.count += 1


# 1. 创建工具对象
tool1 = Tool("斧头")
tool2 = Tool("榔头")
tool3 = Tool("水桶")

# 2. 输出工具对象的总数
print(Tool.count)

3.8.2.2 属性的获取机制

  • Python属性的获取 存在一个 向上查找机制

  • 因此,要访问类属性有两种方式:
    1. 类名.类属性
    2. 对象.类属性 (不推荐)

注意

  • 如果使用 对象.类属性 = 值 赋值语句,只会 给对象添加一个属性,而不会影响到 类属性的值

3.8.3 类方法和静态方法

3.8.3.1 类方法

  • 类属性 就是针对 类对象 定义的属性
    • 使用 赋值语句class 关键字下方可以定义 类属性
    • 类属性 用于记录 与这个类相关 的特征
  • 类方法 就是针对 类对象 定义的方法
    • 类方法 内部可以直接访问 类属性 或者调用其他的 类方法

语法如下

@classmethod
def 类方法名(cls):
    pass
  • 类方法需要用 修饰器 @classmethod 来标识,告诉解释器这是一个类方法
  • 类方法的 第一个参数 应该是 cls
    • 哪一个类 调用的方法,方法内的 cls 就是 哪一个类的引用
    • 这个参数和 实例方法 的第一个参数是 self 类似
    • 提示 使用其他名称也可以,不过习惯使用 cls
  • 通过 类名. 调用 类方法调用方法时,不需要传递 cls 参数
  • 在方法内部
    • 可以通过 cls. 访问类的属性
    • 也可以通过 cls. 调用其他的类方法

示例需求

  • 定义一个 工具类
  • 每件工具都有自己的 name
  • 需求 —— 在 封装一个 show_tool_count 的类方法,输出使用当前这个类,创建的对象个数
@classmethod
def show_tool_count(cls):
    """显示工具对象的总数"""
    print("工具对象的总数 %d" % cls.count)

在类方法内部,可以直接使用 cls 访问 类属性 或者 调用类方法

3.8.3.2 静态方法

  • 在开发时,如果需要在 中封装一个方法,这个方法:
    • 不需要 访问 实例属性 或者调用 实例方法
    • 不需要 访问 类属性 或者调用 类方法
  • 这个时候,可以把这个方法封装成一个 静态方法

语法如下

@staticmethod
def 静态方法名():
    pass
  • 静态方法 需要用 修饰器 @staticmethod 来标识,告诉解释器这是一个静态方法
  • 通过 类名. 调用 静态方法
class Dog(object):

    # 狗的对象数量计数
    dog_count = 0

    @staticmethod
    def run():

        # 不需要访问实例属性也不需要访问类属性的方法
        print("狗在跑...")

    def __init__(self, name):
        self.name = name

3.8.3.3 总结与示例

  1. 实例方法 —— 方法内部需要访问 实例属性
    • 实例方法 内部可以使用 类名. 来访问类属性
  2. 类方法 —— 方法内部 需要访问 类属性
  3. 静态方法 —— 方法内部,不需要访问 实例属性类属性
class Game(object):

    # 游戏最高分,类属性
    top_score = 0

    @staticmethod
    def show_help():
        print("帮助信息:让僵尸走进房间")

    @classmethod
    def show_top_score(cls):
        print("游戏最高分是 %d" % cls.top_score)

    def __init__(self, player_name):
        self.player_name = player_name

    def start_game(self):
        print("[%s] 开始游戏..." % self.player_name)

        # 使用类名.修改历史最高分
        Game.top_score = 999

# 1. 查看游戏帮助
Game.show_help()

# 2. 查看游戏最高分
Game.show_top_score()

# 3. 创建游戏对象,开始游戏
game = Game("小明")

game.start_game()

# 4. 游戏结束,查看游戏最高分
Game.show_top_score()

3.9 单例

3.9.1 单例设计模式

  • 设计模式
    • 设计模式前人工作的总结和提炼,通常,被人们广泛流传的设计模式都是针对 某一特定问题 的成熟的解决方案
    • 使用 设计模式 是为了可重用代码、让代码更容易被他人理解、保证代码可靠性
  • 单例设计模式
    • 目的 —— 让 创建的对象,在系统中 只有 唯一的一个实例
    • 每一次执行 类名() 返回的对象,内存地址是相同的
  • 单例设计模式的应用场景
    • 音乐播放 对象
    • 回收站 对象
    • 打印机 对象
    • ……

3.9.2 __new__方法

  • 使用 类名() 创建对象时,Python 的解释器 首先 会 调用 __new__ 方法为对象 分配空间
  • __new__ 是一个 由 object 基类提供的 内置的静态方法,主要作用有两个:
      1. 在内存中为对象 分配空间
      1. 返回 对象的引用
  • Python 的解释器获得对象的 引用 后,将引用作为 第一个参数,传递给 __init__ 方法

重写 __new__ 方法 的代码非常固定!

  • 重写 __new__ 方法 一定要 return super().__new__(cls)
  • 否则 Python 的解释器 得不到 分配了空间的 对象引用就不会调用对象的初始化方法
  • 注意:__new__ 是一个静态方法,在调用时需要 主动传递 cls 参数

class MusicPlayer(object):

    def __new__(cls, *args, **kwargs):
        # 如果不返回任何结果,则无法开辟内存,初始化便无法完成
        return super().__new__(cls)

    def __init__(self):
        print("初始化音乐播放对象")

player = MusicPlayer()

print(player)

3.9.3 Python 中的单例

  • 单例 —— 让 创建的对象,在系统中 只有 唯一的一个实例
    1. 定义一个 类属性,初始值是 None,用于记录 单例对象的引用
    2. 重写 __new__ 方法
    3. 如果 类属性 is None,调用父类方法分配空间,并在类属性中记录结果
    4. 返回 类属性 中记录的 对象引用

class MusicPlayer(object):

    # 定义类属性记录单例对象引用
    instance = None

    def __new__(cls, *args, **kwargs):

        # 1. 判断类属性是否已经被赋值
        if cls.instance is None:
            cls.instance = super().__new__(cls)

        # 2. 返回类属性的单例引用
        return cls.instance

    def __init__(self, name):
        self.name = name


a = MusicPlayer("网易云")
print(a)
print(a.name)


# 再创建一个MusicPlayer的对象,则之前的对象被替换
b = MusicPlayer("QQ")
print(b)
print(b.name)

print('-'*60)
print(a.name)

只执行一次初始化工作

  • 在每次使用 类名() 创建对象时,Python 的解释器都会自动调用两个方法:
    • __new__ 分配空间
    • __init__ 对象初始化
  • 在上一小节对 __new__ 方法改造之后,每次都会得到 第一次被创建对象的引用
  • 但是:初始化方法还会被再次调用

需求

  • 初始化动作 只被 执行一次

解决办法

  1. 定义一个类属性 init_flag 标记是否 执行过初始化动作,初始值为 False
  2. __init__ 方法中,判断 init_flag,如果为 False 就执行初始化动作
  3. 然后将 init_flag 设置为 True
  4. 这样,再次 自动 调用 __init__ 方法时,初始化动作就不会被再次执行
class MusicPlayer(object):

    # 记录第一个被创建对象的引用
    instance = None
    # 记录是否执行过初始化动作
    init_flag = False

    def __new__(cls, *args, **kwargs):

        # 1. 判断类属性是否是空对象
        if cls.instance is None:
            # 2. 调用父类的方法,为第一个对象分配空间
            cls.instance = super().__new__(cls)

        # 3. 返回类属性保存的对象引用
        return cls.instance

    def __init__(self):

        if not MusicPlayer.init_flag:
            print("初始化音乐播放器")

            MusicPlayer.init_flag = True


# 创建多个对象
player1 = MusicPlayer()
print(player1)

player2 = MusicPlayer()
print(player2)

3.10 异常

3.10.1 异常的概念

  • 程序在运行时,如果 Python 解释器 遇到 到一个错误,会停止程序的执行,并且提示一些错误信息,这就是 异常
  • 程序停止执行并且提示错误信息 这个动作,我们通常称之为:抛出(raise)异常

程序开发时,很难将 所有的特殊情况 都处理的面面俱到,通过 异常捕获 可以针对突发事件做集中的处理,从而保证程序的 稳定性和健壮性

3.10.2 捕获异常

3.10.2.1 简单的捕获异常语法

  • 在程序开发中,如果 对某些代码的执行不能确定是否正确,可以增加 try(尝试)捕获异常
  • 捕获异常最简单的语法格式:
try:
    尝试执行的代码
except:
    出现错误的处理
  • try 尝试,下方编写要尝试代码,不确定是否能够正常执行的代码
  • except 如果不是,下方编写尝试失败的代码

简单异常捕获测试 —— 要求用户输入整数

try:
    # 提示用户输入一个数字
    num = int(input("请输入数字:"))
except:
    print("请输入正确的数字")

3.10.2.2 错误类型捕获

  • 在程序执行时,可能会遇到 不同类型的异常,并且需要 针对不同类型的异常,做出不同的响应,这个时候,就需要捕获错误类型了
  • 语法如下:
try:
    # 尝试执行的代码
    pass
except 错误类型1:
    # 针对错误类型1,对应的代码处理
    pass
except (错误类型2, 错误类型3):
    # 针对错误类型2 和 3,对应的代码处理
    pass
except Exception as result:
    print("未知错误 %s" % result)
  • Python 解释器 抛出异常 时,最后一行错误信息的第一个单词,就是错误类型

异常类型捕获测试 —— 要求用户输入整数

需求

  1. 提示用户输入一个整数
  2. 使用 8 除以用户输入的整数并且输出
try:
    num = int(input("请输入整数:"))
    result = 8 / num
    print(result)
except ValueError:
    print("请输入正确的整数")
except ZeroDivisionError:
    print("除 0 错误")

3.10.2.3 捕获未知错误

  • 在开发时,要预判到所有可能出现的错误,还是有一定难度的
  • 如果希望程序 无论出现任何错误,都不会因为 Python 解释器 抛出异常而被终止,可以再增加一个 except

语法如下:

except Exception as result:
    print("未知错误 %s" % result)

3.10.2.4 异常捕获完整语法

  • 在实际开发中,为了能够处理复杂的异常情况,完整的异常语法如下:

提示:

  • 有关完整语法的应用场景,在后续学习中,结合实际的案例会更好理解
  • 现在先对这个语法结构有个印象即可
try:
    # 尝试执行的代码
    pass
except 错误类型1:
    # 针对错误类型1,对应的代码处理
    pass
except 错误类型2:
    # 针对错误类型2,对应的代码处理
    pass
except (错误类型3, 错误类型4):
    # 针对错误类型3 和 4,对应的代码处理
    pass
except Exception as result:
    # 打印错误信息
    print(result)
else:
    # 没有异常才会执行的代码
    pass
finally:
    # 无论是否有异常,都会执行的代码
    print("无论是否有异常,都会执行的代码")
  • else 只有在没有异常时才会执行的代码
  • finally 无论是否有异常,都会执行的代码
  • 之前一个演练的 完整捕获异常 的代码如下:
try:
    num = int(input("请输入整数:"))
    result = 8 / num
    print(result)
except ValueError:
    print("请输入正确的整数")
except ZeroDivisionError:
    print("除 0 错误")
except Exception as result:
    print("未知错误 %s" % result)
else:
    print("正常执行")
finally:
    print("执行完成,但是不保证正确")

3.10.3 异常的传递

  • 异常的传递 —— 当 函数/方法 执行 出现异常,会 将异常传递 给 函数/方法 的 调用一方
  • 如果 传递到主程序,仍然 没有异常处理,程序才会被终止

提示

  • 在开发中,可以在主函数中增加 异常捕获
  • 而在主函数中调用的其他函数,只要出现异常,都会传递到主函数的 异常捕获
  • 这样就不需要在代码中,增加大量的 异常捕获,能够保证代码的整洁

需求

  1. 定义函数 demo1() 提示用户输入一个整数并且返回
  2. 定义函数 demo2() 调用 demo1()
  3. 在主程序中调用 demo2()
def demo1():
    return int(input("请输入一个整数:"))


def demo2():
    return demo1()

try:
    print(demo2())
except ValueError:
    print("请输入正确的整数")
except Exception as result:
    print("未知错误 %s" % result)

3.10.4 抛出 raise 异常

3.10.4.1 应用场景

  • 在开发中,除了 代码执行出错 Python 解释器会 抛出 异常之外
  • 还可以根据 应用程序 特有的业务需求 主动抛出异常

示例

  • 提示用户 输入密码,如果 长度少于 8,抛出 异常

注意

  • 当前函数 只负责 提示用户输入密码,如果 密码长度不正确,需要其他的函数进行额外处理
  • 因此可以 抛出异常,由其他需要处理的函数 捕获异常

3.10.4.2 抛出异常

  • Python 中提供了一个 Exception 异常类
  • 在开发时,如果满足 特定业务需求时,希望 抛出异常,可以:
    1. 创建 一个 Exception对象
    2. 使用 raise 关键字 抛出 异常对象

需求

  • 定义 input_password 函数,提示用户输入密码
  • 如果用户输入长度 < 8,抛出异常
  • 如果用户输入长度 >=8,返回输入的密码
def input_password():

    # 1. 提示用户输入密码
    pwd = input("请输入密码:")

    # 2. 判断密码长度,如果长度 >= 8,返回用户输入的密码
    if len(pwd) >= 8:
        return pwd

    # 3. 密码长度不够,需要抛出异常
    # 1> 创建异常对象 - 使用异常的错误信息字符串作为参数
    ex = Exception("密码长度不够")

    # 2> 抛出异常对象
    raise ex


try:
    user_pwd = input_password()
    print(user_pwd)
except Exception as result:
    print("发现错误:%s" % result)

3.11 模块和包

3.11.1 模块

3.11.1.1 模块的概念

模块是 Python 程序架构的一个核心概念

  • 每一个以扩展名 py 结尾的 Python 源代码文件都是一个 模块
  • 模块名 同样也是一个 标识符,需要符合标识符的命名规则
  • 在模块中定义的 全局变量函数 都是提供给外界直接使用的 工具
  • 模块 就好比是 工具包,要想使用这个工具包中的工具,就需要先 导入 这个模块

3.11.1.2 模块的两种导入方式

1)import 导入

import 模块名1, 模块名2 

提示:在导入模块时,每个导入应该独占一行

import 模块名1
import 模块名2 
  • 导入之后
    • 通过 模块名. 使用 模块提供的工具 —— 全局变量函数

使用 as 指定模块的别名

如果模块的名字太长,可以使用 as 指定模块的名称,以方便在代码中的使用

import 模块名1 as 模块别名

注意:模块别名 应该符合 大驼峰命名法

2)from...import 导入

  • 如果希望 从某一个模块 中,导入 部分 工具,就可以使用 from ... import 的方式
  • import 模块名一次性 把模块中 所有工具全部导入,并且通过 模块名/别名 访问
# 从 模块 导入 某一个工具
from 模块名1 import 工具名
  • 导入之后
    • 不需要 通过 模块名.
    • 可以直接使用 模块提供的工具 —— 全局变量函数

注意

如果 两个模块,存在 同名的函数,那么 后导入模块的函数,会 覆盖掉先导入的函数

  • 开发时 import 代码应该统一写在 代码的顶部,更容易及时发现冲突
  • 一旦发现冲突,可以使用 as 关键字 给其中一个工具起一个别名

**from...import ***

# 从 模块 导入 所有工具
from 模块名1 import *

注意

这种方式不推荐使用,因为函数重名并没有任何的提示,出现问题不好排查

3.11.1.3 模块的搜索顺序

Python 的解释器在 导入模块 时,会:

  1. 搜索 当前目录 指定模块名的文件,如果有就直接导入
  2. 如果没有,再搜索 系统目录

在开发时,给文件起名,不要和 系统的模块文件 重名

Python 中每一个模块都有一个内置属性 __file__ 可以 查看模块完整路径

示例

import random

# 生成一个 0~10 的数字
rand = random.randint(0, 10)

print(rand)

注意:如果当前目录下,存在一个 random.py 的文件,程序就无法正常执行了!

  • 这个时候,Python 的解释器会 加载当前目录 下的 random.py 而不会加载 系统的 random 模块

3.11.1.4 每一个文件都应该是可以被导入的

  • 一个 独立的 Python 文件 就是一个 模块
  • 在导入文件时,文件中 所有没有任何缩进的代码 都会被执行一遍!

实际开发场景

  • 在实际开发中,每一个模块都是独立开发的,大多都有专人负责
  • 开发人员 通常会在 模块下方 增加一些测试代码
    • 仅在模块内使用,而被导入到其他文件中不需要执行

__name__ 属性

  • __name__ 属性可以做到,测试模块的代码 只在测试情况下被运行,而在 被导入时不会被执行
  • __name__Python 的一个内置属性,记录着一个 字符串
  • 如果 是被其他文件导入的__name__ 就是 模块名
  • 如果 是当前执行的程序 __name__main

因此在很多 Python 文件中都会看到以下格式的代码

# 导入模块
# 定义全局变量
# 定义类
# 定义函数

# 在代码的最下方
def main():
    # ...
    pass

# 根据 __name__ 判断是否执行下方代码
if __name__ == "__main__":
    main()

3.11.2 包(Package)

3.11.2.1 概念

  • 是一个 包含多个模块特殊目录
  • 目录下有一个 特殊的文件 __init__.py
  • 包名的 命名方式 和变量名一致,小写字母 + _

3.11.2.2 好处

  • 使用 import 包名 可以一次性导入 所有的模块

3.11.2.3 __init__.py示例

  1. 新建一个 message
  2. 在目录下,新建两个文件 send_message.pyreceive_message.py
  3. send_message.py 文件中定义一个 send 函数
  4. receive_message.py 文件中定义一个 receive 函数
  5. 在外部直接导入 message 的包
  • 要在外界使用 中的模块,需要在 __init__.py 中指定 对外界提供的模块列表
# 从 当前目录 导入 模块列表
from . import send_message
from . import receive_message

3.11.3 库

Python 是一个依赖强大的组件库完成对应功能的语言,为了便捷实现各项功能,各行各业打造了多种多样的工具库公开提供给大众使用。python中有上百个不需要安装的组件库,这被称之为标准库,如 math、time、datetime、random、re、requests、os、json、smtplib 等都是十分常用的标准库。

3.11.3.1 导入库

以 math 为例:

# 导入math
import math
math.sqrt(4)
# 仅导入 sqrt 函数
from math import sqrt
sqrt(4)
# 给导进来的类或方法起别名
from math import sqrt as s # as 不能不写
s(4)
# 同时导入多个函数
from math import sin, cos
# 把 math 中所有函数全部导入进来
from math import *

3.11.3.2 安装库

诸如数据分析领域的 numpy、pandas、matplotlib、scipy、statsmodels ;机器学习的 sklearn 、xgboost、lightgbm;神经网络的 tensorflow 、pytorch 等各种库都不是标准库,需要自行安装。

python 库的安装由于 conda 或者 pip 的存在安装极其便利,仅需在命令行中

pip install 库名
# 或者
conda install 库名

但是由于这二者默认使用国外源,因此需要自行更换国内镜像,推荐在 pip 中使用清华的镜像源:

pip install 库名 -i https://pypi.tuna.tsinghua.edu.cn/simple
最后修改:2022 年 09 月 02 日
如果觉得我的文章对你有用,请随意赞赏