程序结构与基本类型

基础

编码规范

Python 目前采用 PEP8 版本的编码规范,具体为:

  • 每个 import 语句只导入一个模块,尽量不要导入多个模块
  • 不要在语句后加分号 ; ,也不要用用分号 ; 隔开然后将两条命令放在一行
  • 每行尽量不超过 80 个字符,如果超过,建议用小括号 () 将多行内容隐式连接起来(使用反斜杠 \ 来连接两行是不推荐的)
  • 变量、模块名命名尽量简短,并且全部小写,可以用下划线 _ 做分隔
  • 包名不推荐加下划线分隔符 _
  • 类名采用首字母大写的 Pascal 风格,例如:YellowBook
  • 模块内部类采用下划线 _ 在前面,后面跟 Pascal 风格,例如:_MyYellowBook
  • 常量采用全部大写字母,中间可以加下划线 _

常见内置对象的类型

Python 一切数据类型都是对象。下面列出 Python 一些常见的内置对象的类型:

对象类型 类型名称 示例 说明
数字 int
float
complex
10086
3.14
5+8j
整数
浮点数
复数
字符串 str 'abcd'
"abcd"
r"abc"
R"ABC"
"I 'm boy"
'"Hello"'
单引号或者双引号括起来,
前面加 Rr 表示不转义
字节串 bytes b'hello world' 前面加 b 字符,后面跟
单引号或双引号括起来
布尔类型 bool TrueFalse 表示逻辑值
列表 list [1, 2, 3, 'a'] 元素可以是任意类型
元组 tuple (1, 2, 'str1')
(6,)
不可变,只有一个元素时,
最后要加一个逗号
字典 dict {
1: '123',
2: '456',
'a': 'b'
}
表现形式为 key:value 对
集合 set
frozenset
{1, 2, 'abc'} 元素不允许重复
frozenset 为不可变
空类型 NoneType None 空值
异常 Exception
ValueError
TypeError
文件 f =
open('1.bin', 'rb')
文件读写对象
可迭代对象 生成器对象
range
zip
等等
编程单元 函数

模块
等等

字符串、数字、元组、frozenset 为不可变对象。

例如:

1
2
3
4
5
6
a1 = 3
id(a1) # id 函数为取内存地址值
#输出: 140717531138552
a1 = 5 # 将 a1 赋新值实际是改变其新的内存指向
id(a1)
#输出: 140717531138616

字符串

字符串为不可变的序列,可以由(1)单引号(2)双引号(3)三引号 括起来。

转义

字符串支持转移,使用 \ 来进行转移特殊字符:

转移字符 含义 转移字符 含义
\n 换行符 \" 双引号
\t 制表符 \\ 转义为一个 \
\r 回车符 \ooo 3个8进制数字对应字符
\' 单引号 \xhh 2个16进制数字对应字符
\f 换页 \uhhhh 4个16进制数字对应字符
\0 空字符

原始字符串

引号前面加 R 或者 r 表示原始字符串,不会对字符串内的内容进行转义:

1
str1 = R'1234567890 \n abcdefgh'

获取字符或字符串切片

字符串中的索引值以 0 为开始值,如果索引值为负数,表示从尾部开始算,-1 为从末尾的开始位置。

  • 只取字符串中的一个字符,直接使用 [] 加上索引来获取,如: str1[2]
  • 如果是取字符串的一个区段,则采用左闭右开的原则,如 str1[0:4],表示取字符串中的字串,从第 0 个字符开始,取第 0,1,2,3 三个字符组成字串,不包含第 4 个。
1
2
3
4
5
str1 = '0123456789'
print(str1[0]) #输出: 0
print(str1[0:4]) #输出: 0123
print(str1[7:-1]) #输出: 78
print(str1[5:]) #输出: 56789,忽略最后区间字段,表示取到字符串尽头

字符串连接

  • + : 加号表示两字符串拼接
  • * : 乘号表示字符串重复拼接
  • in: in运算符测试字符是否存在于字符串中
1
2
3
4
str1 = '123'
str2 = '456'
print(str1 + str2) # 输出: 123456
print(str1 * 3) # 输出: 123123123

其他类型转换为字符串

使用 str() 可以将其他类型转换为字符串:

1
2
number1 = 10086
str1 = str(number1)

使用 chr() 函数将一个整数转换成对应的unicode字符

使用 ord() 函数将一个字符转换成对应的整数值

使用 hex() 函数将一个整数转换成对应的十六进制的表示形式的字符串

使用 oct() 函数将一个整数转换成对应的八进制的表示形式的字符串

格式化声明字符串

格式化声明字符串,是在字符串的引号前面加上 f 来表示,需要转化为字符串的其他变量或者字面量、表达式等,在字符串中用 { } 括起来。例如:

1
2
3
my_age = 18
str1 = f'My age is {my_age + 3}'
print(str1)

eval() 和 exec()

eval() 函数执行一串 Python 语法的表达式字符串,返回执行结果
exec() 函数执行一串 Python 脚本的字符串,返回执行结果

运算符

下面列出 Python 常见的运算符:

运算符例子 说明
x + y 算数加法; 列表、元组、字符串拼接
x - y 算数减法; 集合差集
x * y 算数乘法; 序列重复
x / y 算术除法,结果为浮点型
x // y 整除,向下取整
-x 取相反数
x % y 取余; 字符串格式化
x ** y 求幂
<<=>>= 比较; 集合包含关系比较
==、!= 等于、不等于
x or y 逻辑或
x and y 逻辑与
not x 逻辑非
x in y; x not in y 成员是否存在于
x is y; x is not y 对象是否同一个(比较内存地址)
|、^&<<>>~ 位运算符
&、|、^ 集合交集、并集、对称差集

模块

1. import 模式

格式:

1
import 模块名 [as 别名]

这种方法是将整个模块导入,可以直接使用 模块名.对象名 来访问模块中的对象。

2. from 模式

格式:

1
from 模块名 import 对象名 [as 对象别名]

这种方法不会导入整个模块,只是按需导入模块中指定的对象。

3. __name__ 属性

每个脚本在运行时都有一个 __name__ 属性:

  • 如果脚本是被当作模块导入,则 __name__ 属性会被设置为模块名。
  • 如果脚本被当作是独立脚本运行,则 __name__ 属性会被设置为 __main__

4. python 的包

python 的包是由一系列 python 脚本组成,可以有不同的目录组织。有一个要求:每一个目录,都必须有一个名叫 __init__.py 的文件 (可以是空文件)。表示该目录是一个包。

__init__.py 用于执行初始化操作,以及设置 __all__ 变量。__all__ 变量中的对象,可以在 from ... import 时导入。

5. 模块的搜索路径

默认情况下,解释器会以下面顺序搜索查找模块:

  • 当前 Python 脚本文件所在目录
  • 环境变量 PYTHONPATH 下的每个目录
  • Python 安装目录中的各库目录

6. *.pth 路径配置文件

*.pth 文件为自定义路径配置文件,新建一个 .pth 后缀名的文件,文件名不限,塞进 Python 安装目录中的 Lib\site-packages\ 下面。下次再次启动 Python 解释器时,就能加载这个配置去寻找自定义模块。

例如:

1
2
# 我的自定义模块仓库,一行一个路径
E:\MyDemoLib

列表

列表是 Python 内置的,可变的顺序序列,内部指针存储于连续的内存空间。也可以理解为动态数组。

  • 列表中的元素可以是任意的不同类型
  • 由于保存于连续的内存空间,中间部分的大量的删除、增加操作效率很低
  • 尽量只在尾部增加或者删除操作

列表的声明方法:

1
2
3
4
list_a = []                 # 空列表
list_b = ['A', 83] # 两个元素的列表
list_c = list((2, 4, 8)) # 利用 list() 将元组、字符串、切片等等可迭代对象转换成列表
list_d = list() # list() 也可以产生空列表

列表常用的方法:

方法 说明
append(x) 将元素 x 添加到尾部
extend(L) 将列表 L 的全部元素添加到尾部
insert(index, x) 在列表 index 的位置添加元素 x
remove(x) 删除首次出现的元素 x
pop([index]) 返回指定位置元素,并从列表中删除,不指定 index 则为最后一个元素
clear() 清空全部元素
index(x) 返回首次出现元素 x 的索引值,不存在则抛异常
count(x) 列表中出现元素 x 的次数(个数)
reverse() 列表中的元素翻转
copy() 列表对象浅复制
sort() 列表原地排序
+ 两个列表拼接成一个新列表
* 列表重复 n 份组成新列表

元组

元组是轻量级的列表。元组是不可变的。

列表的声明方法:

1
2
3
4
tuple_a = ()                    # 空元组
tuple_b = ('A', 100, 4.6) # 三个元素的元组
tuple_c = tuple('abcdefg') # tuple() 将可迭代对象转换为元组
tuple_d = tuple() # tuple() 也可以产生空元组
  • 元组是不可变序列,不允许修改元组内元素的值
  • 理论上元组的处理速度会比列表更快
  • 元组可以作为字典的 key
  • 元组中的元素可以是可变序列

元组常用的方法:

方法 说明
len(tuple) 返回元组的元素个数
max(tuple) 返回元组中元素最大值
min(tuple) 返回元组中元素最小值

元组的解构:

元组中的元素可以解构成多个对象,如下:

1
2
3
4
5
tuple_a = ('A', 100, 4.6)
(x, y, z) = tuple_a # 解构成 x, y, z 三个对象
x, y, z = tuple_a # 这样写也可以
a, b, c = ('A', 100, 4.6) #
a, b, c = 1, 2, 3 # 也可以看作是元组解构

字典

字典是包含多个 “键值对” 的无序的可变序列。

  • 键在字典中是唯一的
  • 键必须不可变,可以是数字、字符串、元组

字典的声明方法:

1
2
3
4
5
6
7
dict_a = {}                     # 空字典
dict_b = dict() # dict() 也可以产生空字典
dict_c = {'a': 1, 'b': 5}
keys = ['a', 'b', 'c', 'd']
values = [1, 2, 3, 4]
dict_d = dict(zip(keys, values)) # dict() 创建字典
dict_e = dict(name='Liu', age=18) # dict() 创建字典

字典可以直接修改:

1
2
3
4
5
6
dict_1 = {'a': 1, 'b': 2}
dict_1['b'] = 3 # 修改字典中已有的键 b 的值
dict_1['c'] = 8 # 往字典中添加新键 c
del dict_1['b'] # 删除字典中的键 b
dict_1.clear() # 清空整个字典的所有键
del dict_1 # 删除字典对象

字典常用的方法:

方法 说明
clear() 清空整个字典的所有键
copy() 字典对象浅复制
fromkeys(seq) 以序列seq中元素做字典的键创建一个新字典
get(key, default=None) 返回指定键的值,如果键不在字典中返回 default 指定的默认值
update(dict2) 把 dict2 的键值对更新到本字典中
popitem() 返回最后一个键值对(元组类型),并从字典中删除它
pop(key[,default]) 返回key的value值,并从字典中删除它
key in dict 返回布尔型,检测 key 是否存在于字典
keys() 返回字典的键列表(dict_keys类型)
values() 返回字典的值列表(dict_values类型)
items() 返回字典的键值对列表(dict_items类型)
setdefault(key, default=None) 如果 key 存在则返回对应的值;
不存在则新增key并设置为default指定的值,
并返回default指定的值

集合

集合是无序的容器对象。集合中的元素不允许重复,每一个元素都是唯一的。

字典的声明方法:

1
2
3
4
set_1 = set()                       # 空集合
set_2 = {3, 5} # 两个元素的集合
set_3 = set(range(0,5)) # 集合元素有:0, 1, 2, 3, 4
set_4 = set([0, 0, 1, 2, 3, 5, 3]) # 集合元素有:0, 1, 2, 3, 5

集合的并集:

1
2
3
set_1 = {1, 2, 3}
set_2 = {3, 4, 5}
set_3 = set_1 | set_2 # 集合中的元素是: 1,2,3,4,5

集合的交集:

1
2
3
set_1 = {1, 2, 3}
set_2 = {3, 4, 5}
set_3 = set_1 & set_2 # 集合中的元素是: 3

集合的差集:

1
2
3
set_1 = {1, 2, 3}
set_2 = {3, 4, 5}
set_3 = set_1 - set_2 # 集合中的元素是: 1, 2

集合的对称差集(去除相同的元素再并集):

1
2
3
set_1 = {1, 2, 3}
set_2 = {3, 4, 5}
set_3 = set_1 ^ set_2 # 集合中的元素是: 1, 2, 4, 5

集合常用的方法:

方法 说明
add() 添加新元素到集合
copy() 集合对象浅复制
clear() 清除集合的所有元素
discard(value) 删除集合中指定的值,不存在时不会出错
remove(value) 删除集合中指定的值,不存在时会出错
pop() 移除并返回集合中的一个随机元素
len() 返回集合元素个数

分支

单分支

单分支只使用一个 if 来实现,定义如下(条件表达式后面的冒号不可省略):

1
2
if 条件表达式:
语句块

双分支

双分支选择结构的语法定义如下:

1
2
3
4
if 条件表达式:
语句块1
else:
语句块2

多分支

多分支选择结构的语法定义如下:

1
2
3
4
5
6
7
8
9
10
11
if 条件表达式1:
语句块1
elif 条件表达式2:
语句块2
elif 条件表达式3:
语句块3

...

else:
语句块2

case 条件匹配

match…case 语句以此判断,如果匹配则执行相应的语句块:

1
2
3
4
5
6
7
8
9
10
match 匹配项:
case 匹配值1:
语句块1
case 匹配值2:
语句块2
case 匹配值3:
语句块3
...
case _:
默认处理语句块

_ 在这里表示其他值的意思。

循环

while 循环

while 循环的语法定义如下:

1
2
while 条件表达式:
循环体语句块

while 循环可以带 else 子句,当条件表达式的条件不成立而自然结束(非执行break)时,执行 else 指定的语句块:

1
2
3
4
while 条件表达式:
循环体语句块
else:
语句块

当执行 break 语句进行结束时,不会执行 else 子句的语句块。

for 循环

for 循环的语法定义如下:

1
2
for 变量 in 可迭代对象:
循环体语句块

for 循环也可以带 else 子句,可迭代对象已经没有元素自然结束(非执行break)则执行 else 子句的语句块。

for 循环的语法定义如下:

1
2
3
4
for 变量 in 可迭代对象:
循环体语句块
else:
语句块

break 与 continue

  • break 用于结束整个循环
  • continue 用于结束本次循环,进入下一次循环

函数

函数定义

函数定义的语法如下:

1
2
def 函数名([参数列表])
函数体

形参与实参

函数中的参数为形参,调用时传递给函数的参数为实参。

  • 一般情况下,修改形参的值,不会影响实参
  • 当传递给函数形参类型为可变序列时,通过下标的修改形参内部元素值,这时会影响并修改到对应实参中的内部元素的值

返回值

返回值使用 return 语句来设置,同时退出函数的执行

pass 空语句

定义一个函数,暂时还没有实现函数体的内容,可以只写一句 pass 空语句,用于占位符,表示以后会把函数功能填上。

默认值参数

传递参数时,允许加入参数的默认值。加上默认值的参数,在调用时,如果不加实参,则用默认值代替。 例如:

1
2
3
4
def func1(arg1, arg2 = 1000)
函数体

func1(500) # 此次调用,函数中的 arg2 取默认值 1000

默认值参数必须全部位于函数参数列表中的右侧。

关键字参数

调用时,可以加上关键字参数,来避免记忆参数的顺序,例如:

1
2
3
4
def func2(a, b, c)
函数体

func2(b = 8, c = 100, a = 62) # 调用时加上参数名称来避免参数顺序

变长参数

函数后面允许传递可变长度的参数:

  • 最后一个参数名的前面加上一个 * 星号,来表示变长参数会统一存放在一个元组中。
  • 最后一个参数名的前面加上两个 ** 星号,来表示变长参数会统一存放在一个字典中。
1
2
3
4
5
6
7
8
9
def func3(*args)
函数体

func3(1, 2, 3, 4) # args = (1, 2, 3, 4) 元组

def func4(**args)
函数体

func4(a = 1, b = 2, c = 3) # args = { 'a':1, 'b':2, 'c':3 } 字典

参数传递时解构

在实参前加一个 * 星号,可以将列表、元组、集合、字典等实参,解构后传递给形参。例如:

1
2
3
4
5
6
def func5(a, b, c, d, e)
函数体

list_a = [10, 20, 30, 40, 50]

func5(*list_a)

字典的解构,默认是传递字典的 keys;如果要解构字典的值,则应该传递 *dict.values();如果需要解构整个键值对进行传递,则应该传递 *dict.items()

lambda 表达式

lambda 表达式只能包含一条语句,用于声明匿名函数。语法:

1
lambda 参数列表: 表达式

lambda 只能是一句表达式,不能是复杂语句,但是可以调用其他函数。

例如:

1
2
3
f = lambda x, y, z : x + y + z

print(f(2, 4, 8))

global 变量

  • 函数中定义的变量,为函数内部局部变量,作用域为函数范围内
  • 函数中可以直接访问全局变量,为只读访问
  • global 用于在函数内部声明使用全局变量,函数内部则可以修改该全局变量
  • 外部没有声明全部变量,函数内用 global 声明的的全局变量,函数外部也能访问使用
  • 类内的方法访问成员,请使用 self

类的定义

Python 中类的定义依然使用 class 关键字,如:

1
2
3
4
5
6
7
8
class Car:
def __init__(self):
self.price = 50000
def info(self):
print('This is a car, price = ' + str(self.price))

car = Car()
car.info()
  • class 关键字后加上类名,类名后必须到冒号 :
  • 成员方法必须带一个 self 的参数,而且必须是第一个形参,代表实例对象本身
  • 访问实例中的成员时,需要用 self 来进行访问
  • 外部调用实例方法时,实参不需要传递给 self

类成员与实例成员

类的数据成员,可以广泛地称之为属性。

Python 是动态语言,在 Python 中,可以动态地为类和对象增加成员,这与其他语言有很大不同。数据成员中,区分类成员与实例成员。

  • 实例的数据成员属于实例(对象),只可以通过对象名来访问
  • 类的数据成员属于整个类,可以通过类名和对象名来访问

实例数据成员的声明,一般是在构造函数 __init__ 中进行定义,在构造函数中通过 self 作为前缀,进行动态定义:

1
2
3
class Car:
def __init__(self, v):
self.price = v # price 为实例成员

类成员是直接在类中声明:

1
2
3
4
5
6
7
8
9
10
11
12
13
class Car:
color = 'red' # 类的数据成员

car1 = Car()
print(car1.color) # 显示 'red'
print(Car.color) # 显示 'red'
car1.color = 'blue'
print(car1.color) # 显示 'blue'
print(Car.color) # 显示 'red'

Car.color = 'green'
car2 = Car()
print(car2.color) # 显示 'green'

私有成员与公有成员

Python 没有严格的访问保护机制。一般以命名特色来区别:

  • 以两个下划线 __ 开头的成员,为私有成员,例如:“__abc
  • 以一个下划线 _ 开头的成员,为保护成员,例如:“_abc
  • 不以下划线开头的成员,为公有成员,例如:“abc
  • 以两个下划线 __ 开头,而且以下划线 __ 结尾的成员,为特殊定义的成员,例如:“__init__

方法

类中的方法分为:(1)公有方法、(2)私有方法(3)静态方法(4)类方法

公有方法

公有方法是不以下划线开头的方法,类外可以调用。 如果通过类型来调用公有方法,则第一个参数需要显式地传递一个对象名给 self 形参。

私有方法

以下划线开头,类内的方法可以通过 self 来进行调用。

类方法

1
2
3
4
class Car:
@classmethod
def classShowId(cls):
类方法体

类方法中没有self。类方法中一般会有一个参数cls,表示当前类。

静态方法

1
2
3
4
class Car:
@staticmethod
def staticShowId():
静态方法体

静态方法中没有 cls 这个形式参数。不需要 selfcls 来访问类中的静态成员。静态方法可以通过类名来访问类的成员,也可以接受传递 self 参数来操作实例。

  • 类方法通常用于实现类中各实例之间共享的功能、或者行为
  • 静态方法通常用于实现一些与此类有关的独特功能,但是不需要依赖 self 和 cls 的访问

属性

只读属性

通过 @property 修饰器来定义只读属性:

1
2
3
4
5
6
7
8
9
class Car:
def __init__(self, color)
self.__color = color

@property
def color(self)
return self.__value # 返回私有成员数据

# 上述 color 为一个只读属性,只读属性不允许写入、删除,无法通过 del 进行操作

定义可读写、可删除的属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Car:
def __init__(self, color)
self.__color = color

def __get_color(self):
return self.__value

def __set_color(self, value)
self.__color = value

def __del_color(self)
del self.__color

color = property(__get_color, __set_color, __del_color) # color为属性
# 如果只想定义可读写、不可删除的属性,则如下定义
# color = property(__get_color, __set_color)

类的特殊方法

下面列出一些常用的类特殊方法:

方法 说明
__init__ 构造函数,会被自动调用
__del__ 析构函数,会被自动调用
__getitem__ 按照索引取值
__setitem__ 按照索引赋值
__len__ 获得长度,与内置的 len() 函数对应
__bool__ 转换为 bool 类型,与内置函数 bool() 对应,必须返回 True 或 False
__int__ 转换为 int 类型,与内置函数 int() 对应,必须返回整数
__float__ 转换为 float 类型,与内置函数 float() 对应,必须返回浮点数
__str__ 转换为字符串类型,与内置函数 str() 对应,必须返回字符串
__base__ 该类的基类
__class__ 返回对象所属的类
__repr__ 打印的转换,必须返回字符串

继承

派生类可以继承基类的公有成员和保护成员,不能继承基类的私有成员。

声明派生类的语法:

1
2
class 派生类名(基类名):
类体

派生类可以使用内置的 super() 函数得到基类实例,也可以通过 基类类名.方法 等途径访问到基类的部分。

super() 也允许带参数,第一个参数是子类类名,第二个参数是self 。例如子类的构造函数去调用父类的构造函数去初始化父类部分可以这样写:

1
super().__init__()