Python 基础

Python 风格规范——Google开源项目风格指南

操作符优先级

从上往下,优先级依次提高。官方文档

全局变量的范围覆盖

在函数内部,如果 局部变量全局变量同名,使用的是 局部变量

如果在函数内部要对全局变量进行重新赋值,需通过 global 声明。

示例

1
2
3
4
5
6
7
upChars = '零壹贰叁肆伍陆柒捌玖'
def change():
global upChars
upChars = '零一二三四五六七八九'

change()
print(upChars)

局部变量的生命周期

变量 从被创建到被系统回收 的过程,就是其生命周期。

局部变量只有在函数被执行时才会被创建。

函数执行结束后局部变量才会被系统回收

1
2
3
4
5
6
7
def file_copy(src_path, dst_path):
with open(src_path, 'r') as f:
content = f.read()
with open(dst_path, 'w') as f:
f.write(content)

file_copy('tmp.log','tmp.copy.log')

函数

常用内置函数

官方文档

abs() delattr() hash() memoryview() set()
all() dict() help() min() setattr()
any() dir() hex() next() slice()
ascii() divmod() id() object() sorted()
bin() enumerate() input() oct() staticmethod()
bool() eval() int() open() str()
breakpoint() exec() isinstance() ord() sum()
bytearray() filter() issubclass() pow() super()
bytes() float() iter() print() tuple()
callable() format() len() property() type()
chr() frozenset() list() range() vars()
classmethod() getattr() locals() repr() zip()
compile() globals() map() reversed() __import__()
complex() hasattr() max() round()

int float str 类型强转。

1
2
3
int(3456.23)		# 浮点转整型     返回3456
float('1000') # 字符串转浮点 返回1000.0
str(1000) # 数值转字符串 返回字符串 '1000'

len 获取数据对象(字符串、列表、元组、字典等)的长度。

函数形参缺省

函数的参数定义,一旦有缺省值后,后面所有的参数必须都有缺省值。

1
2
3
4
5
6
def over_score_students(student_score_list, score=60):
count = 0
for ss in student_score_list:
if ss >= score:
count += 1
return count

指定参数名调用函数

1
2
3
4
def func(arg1, arg2=2, arg3='hello'):
print(arg1)
print(arg2)
print(arg3)

调用方式

1
2
3
4
5
6
# 按顺序,常规调用
func(1, 2, 'hello')
# 指定参数名调用,可以颠倒参数次序
func(arg2=22, arg1=11, arg3='world')
# 混合使用
func(1, 22, arg3='world')

注意,一旦某个参数指定了参数名,后面所有的参数必须指定参数名

1
func( 1, arg2=22, 'hello') # 错误的调用方式

函数可变参数

参数名前面加了一个星号

在调用该函数的时候,Python解释器会创建一个 tuple 赋值给这个参数变量。并且会把 传入的数据对象 放到这个tuple对象里面。

调用函数的时候,传入参数前面加上星号,是参数展开。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
studentInfo = {
'张飞' : 18,
'赵云' : 17,
'许褚' : 16,
'典韦' : 18,
'关羽' : 19,
}

def printAge(*students) :
for student in students:
print( f'学生:{student} , 年龄 {studentInfo[student]}')

printAge(['张飞', '典韦', '关羽'])
printAge('张飞', '典韦', '关羽')

# 要传入的参数恰好已经在一个list
onebatch = ['张飞', '典韦', '关羽']
printAge(*onebatch) # 等价于 printAge (onebatch[0], onebatch[1], onebatch[2])

关键字可变参数

参数个数不确定,除了上面这种定义的方式,还有另外一种定义方式,叫做关键字可变参数

1
2
3
4
5
6
7
8
9
10
11
12
def addStudents(table,**students):
print(type(students))
for name,age in students.items():
table[name] = age

table1 = {}
table2 = {}
addStudents(table1, 李白=20, 杜甫=24)
addStudents(table2, Jodan=45, James=32, onil=40)
print(table1)
print('----------------')
print(table2)

这种前面加2个星号的参数,称之为关键字可变参数。

传入的参数对象,必须是像 name=value这种 带参数名和参数值的, 放到dict对象时,参数名是字典元素的key,参数值是字典元素的value。

假如我们要传入的参数恰好已经在一个字典 中,可这样参数展开:

1
2
onebatch = {'李白': 20, '杜甫': 24}
addStudents(table1, **onebatch)

列表/元组 操作

列表切片

1
2
3
4
5
6
7
a = [1, 2, 3.14, 'hello', [7,8,9] ]
a[0:3] # 结果是 [1, 2, 3.14]
a[:3] # 结果也是 [1, 2, 3.14]
a[3:5] # 结果是 ['hello', [7,8,9] ]
a[3:] # 结果也是 ['hello', [7,8,9] ]

a[-1][:2] # 结果是 [7,8]

列表部分内容整体替换(切片赋值)

1
2
list1 = [0, 1, 2, 3, 4, 5]
list1[1:4] = ['a','b','c'] # 结果是 [0, ‘a’, ‘b’, ‘c’, 4, 5]

元组(Tuple)和列表非常相似,也可以存放任何类型的数据对象,除了一点: 元组的内容是 不能改变 的。

如果元组中只有一个元素,必须要在后面加上逗号:a = (1, )

定义元组还可以去掉圆括号:a = 1, 2, 3.14, 'hello'。同理若只有一个元素,也必须在最后面加上逗号。

多个变量同时赋值

注意,这种赋值,变量的个数一定要和 列表/元组 中元素的个数相等。

1
name, age = ['李逵', 33] # name 的值为 '李逵', age 的值为 33

字符串的方法

count 返回字符串对象包含了多少个参数指定的字符串。

find 返回该 参数字符串 在其中 第一个 出现的位置索引

split 截取字符串信息,返回元素为字符串的列表。

1
2
str1 = '小张:79 | 小李:88 | 小赵:83'
pos1 = str1.split('|') # 返回 ['小张:79', '小李:88', '小赵:83']

splitlines 字符串 按换行符 进行截取。

1
2
3
4
5
6
salary = '''
小王 10000元
小李 20000元
小徐 15000元
'''
print(salary.splitlines()) # 输出为 ['', '小王 10000元', '小李 20000元', '小徐 15000元']

join 将列表中的字符串元素 以某字符串为连接符, 连接 为一个字符串。

1
2
3
4
5
6
'|'.join([
'小张:79 ',
' 小李:88 ',
' 小赵:83'
])
# 返回 '小张:79 | 小李:88 | 小赵:83'

strip 将 字符串前面和后面的空格删除,但是不会删除字符串中间的空格。

1
2
'      小  李:88       '.strip() 
# 返回 '小 李:88'

lstrip 将 字符串前面 (左边) 的空格删除,但是不会删除字符串中间和右边的空格。

1
2
'      小  李:88       '.lstrip() 
# 返回 '小 李:88 '

rstrip 将 字符串后面 (右边) 的空格删除,但是不会删除字符串中间和左边的空格。

1
2
'      小  李:88       '.rstrip() 
# 返回 ' 小 李:88'

replace 替换 字符串里面 所有指定的 子字符串 为另一个 字符串。

1
2
str1 = '我们今天不去上学,我们去踢足球'
str1 = str1.replace('我们', '他们')

startwith 检查字符串是否以参数指定的字符串 开头,如果是,返回True,否则返回False

endswith 检查字符串是否以指定的字符串 结尾,如果是,返回True,否则返回False

1
2
3
str1 = '我们今天不去上学,我们去踢足球'
str1.startswith('我们') # 返回 True
str1.endswith('我们') # 返回 False

isdigit 检查字符串是否全部由数字构成,如果是,返回True,否则返回False

1
2
3
str1 = input('请输入手机号码:')
if not str1.isdigit(): # 不是全部由数字字符构成
print('您输入的手机号码不正确,必须全部是数字')

列表的方法

append 在列表的 后面 添加一个元素。注意,该方法返回值是 None,而不是新的列表对象。

insert 在列表的 指定位置插入一个元素。

1
2
3
4
5
a = [1, 2, 3.14, 'python3.vip']  

# 插入到索引0的位置,也是插到第1个元素的位置上
# a列表内容就变成了 ['你好', 1, 2, 3.14, 'python3.vip']
a.insert(0, '你好')

pop 从列表 取出并删除 一个元素。

1
2
3
4
5
6
a = [1, 2, 3.14, 'python3.vip']  

# 取出索引为3 的元素,也就是第4个元素
poped = a.pop(3)
print(a) # 输出为 [1, 2, 3.14]
print(poped) # 输出为 'python3.vip'

remove 删除列表元素。从第1个元素开始,寻找 和参数对象 相等的元素,如果找到了,就删除,且找到后,不会继续往后寻找其它相等的元素。

注意,该方法返回值也是 None

1
2
3
var1 = ['a','b','c','a']
var1.remove('a')
print(var1) # 输出为 ['b','c','a']

reverse 将列表元素倒过来。注意,该方法返回值也是 None

index 返回 参数对象 在列表 中的位置,也就是索引。

1
2
3
var1 = [1,2,3,4,5,6,7]
idx = var1.index(5)
print(idx) # 输出为 4

字典 操作

字典对象定义用花括号 {} , 字典里面的 每个元素之间用 逗号 隔开。

每个元素都是一个键值对,键和值之间用 冒号 隔开。

pop 删除元素,同时返回对应的 value 对象

1
2
3
4
5
6
7
8
9
members = {
'account1' : 13 ,
'account2' : 12
}

# 返回参数 key 对应的 value 对象
val = members.pop('account1')
print(members)
print(val)
1
2
# del 关键字
del members['account1']

items 返回所有的 键值对 列表

keys 返回所有的 key 值列表

values 返回所有的 value 值列表

1
2
3
4
5
6
7
8
9
10
11
12
13
members = {
'account1' : 13 ,
'account2' : 12 ,
'account3' : 15 ,
}

# 遍历所有元素
for account,level in members.items():
print (f'account:{account}, level:{level}')

print(members.items()) # 输出为 [('account1', 13), ('account2', 12), ('account3', 15)]
print(members.keys()) # 输出为 ['account1', 'account2', 'account3']
print(members.values()) # 输出为 [13, 12, 15]

clear 清空字典

1
2
3
4
5
6
members = {
'account1' : 13 ,
'account2' : 12 ,
'account3' : 15 ,
}
members.clear()

update 合并字典

1
2
3
4
5
6
7
8
9
10
11
12
members = {
'account1' : 13 ,
'account2' : 12 ,
'account3' : 15 ,
}
another = {
'account4' : 13 ,
'account5' : 12 ,
}

members.update(another)
print(members)

判断语句

连写比较表达式

1
2
3
# 两者等价
2 < num <=5
num > 2 and num <=5

与/或计算优先级

如果 and 和 or 一起使用, 注意 是先计算 and 部分, 其结果 再和 or 一起计算。

1
2
# 先计算 or 右侧表达式
6 > 5 or 'sk' == 'sk' and 4 == 5

格式化字符串

printf 风格

printf格式化官网描述

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 打印字符、浮点、整型等
print('Hello %s, π=%f, age=%d') %('world', 3.14, 18)

# 指定宽度和对齐(默认右对齐)
'税前薪资:%10s 元' % 100000
'税前薪资:%10s 元' % 10000
'税前薪资:%10s 元' % 1000

# 数字不足宽度补零
'税前薪资:%010d 元' % 100000
'税前薪资:%010d 元' % 10000
'税前薪资:%010d 元' % 1000

# 保留浮点数有效位
'税前薪资:%010.2f 元' % 1000.4522
'税前薪资:%010.2f 元' % 1008.6621
'税前薪资:%010.2f 元' % 1009.3351

# 指定左对齐
'税前薪资:%-10s 元' % 100000
'税前薪资:%-10s 元' % 10000
'税前薪资:%-10s 元' % 1000

f-string 格式化

在字符串模板前面加上f,然后占位符使用{} ,里面直接放入对应的数据对象。

注意,这种写法必须要Python解释器是3.6 以后的版本才支持。官方文档

1
print(f'税前薪资是:{salary}元, 缴税:{tax}元, 税后薪资是:{aftertax}元')
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 指定宽度
salary = 10000
print(f'{salary:10}')

# 保留浮点数有效位
print(f'税前薪资是:{salary:8.1f}元, 缴税:{tax:8.1f}元, 税后薪资是:{aftertax:8.1f}元')

# 数字不足宽度补零
print(f'税前薪资是:{salary:08}元, 缴税:{tax:08.1f}元, 税后薪资是:{aftertax:08.1f}元')

# 指定左对齐
print(f'税前薪资是:{salary:<8}元, 缴税:{tax:<8}元, 税后薪资是:{aftertax:<8}元')

# 十六进制格式化数字
print(f'数字65535的16进制表示为:{65535:x}')
print(f'数字65535的16进制表示为:{65535:X}')

# 字符串内容里有花括号
times1 = 1000
times2 = 2000
{% raw %}
print(f'文章中 {{ 符号 出现了 {times1} 次')
print(f'文章中 }} 符号 出现了 {times2} 次')
{% endraw %}

指定字符串内容无需转义

1
path = r'c:\windows\temp'

循环

循环n次

1
2
for n in range(100):  
print(n)

注意,如果想返回一个 从 0到99的数字列表, 可以这样写 : list(range(100))

如果想打印出从50 到 100 所有的数字

1
2
3
# 表示从 50 开始, 到 100 结束。
for n in range(50,101):
print(n)

如果想打印出从50, 55, 60, 65… 这样每次增加5, 直到 100

1
2
3
# 第3个参数表示 步长 
for n in range(50,101,5):
print(n)

获取元素在列表里面的索引

1
2
3
4
5
6
studentAges = ['小王:17', '小赵:16', '小李:17', '小孙:16', '小徐:18']
# enumerate (studentAges) 每次迭代返回 一个元组
# 里面有两个元素,依次是 元素的索引和元素本身
for idx, student in enumerate(studentAges):
if int(student.split(':')[-1]) > 17:
print(idx)

列表推导式

把一个列表里面的每个元素经过简单的处理生成另一个列表的操作。

一个列表A,里面都是数字,我们需要生成一个新的列表B,依次存放列表A中每个元素的平方

1
2
3
4
list1 = [1,2,3,4,5,6]
list2 = []
for num in list1:
list2.append(num*num)
1
2
list1 = [1,2,3,4,5,6]
list2 = [num**2 for num in list1]

字符编码

字符集

ASCII表

计算机是美国人发明的,所以在开始的时候, 他们也没有想到将来要支持全世界的文字。

美国人的单词都是用字母拼出来的, 所以他们常用的基础文字符号比较少,就是字母,再加上 !@#$%^&*(){[]}+- 等等这些符号,也就100多个。

他们定义的规范就叫 ASCII (American Standard Code for Information Interchange) ,中文大意就是 美国信息交换标准码。

这个 ASCII 码里面用128个数字代表了128个字符。

二进制有 8 位 的长度 被称为 一个 字节

因为2的8次方等于256,可以存储 从 0 到255 一共 256个数字, 可以表示 最多 256 个文字符号,所以一个字节就可以存放 任何一个ASCII 文字符号。

ASCII 这样的 用数字代表文字符号的规范,就被称之为 字符集

后来计算机传遍了全世界,其它国家的文字符号就多了,比如中文、韩文、日文等,显然ASCII是不够的,我们需要其它的数字来代表这些文字符号。

开始各个国家和地区 自己定义了自己的 字符集。后来 国际标准化组织 定义了一个字符集,想包括世界上所有的文字符号,这就是大名鼎鼎的 unicode 字符集。

这个字符集里面包括了现今世界上的常用文字符号 和 其对应的数字表示。

字符编码

字符怎么存储和传输?

由于历史原因,计算机是 以字节为单位 来 存储和传输数据的,一个字节 由 8位二进制数字表示,最多可以表示从0到255, 一共256个字符。

最初只有ASCII码的时候,一个字节 就可以存储传输任何文字。

但是 unicode 里面有10多万的文字符号,数字范围 远远 超过 一个字节所能代表的数字。

所以一个字节不够。

那么怎么用多个字节来 表示这些数字呢?

这就有需要另外的规范。这些 如何用字节表示 字符对应的数字 就是 字符编码 规范。

unicode字符 的编码,最常用的规范是 UTF8 和 UTF16

比如 UTF8 表示 中文字符 ,就是用 3个字节表示的,对应的16进制表示是 E4 、BD、 A0。

在这里我们只要知道 不同的 编码规范 对数字有不同的 用字节表示的方法就行了。

有了编码规范,字符集 中的数字就可以转化为字节进行存储和传输了。

字符串编码

Python3语言里面的字符串对象是unicode字符串,在内存中实际存储时,使用的是 UTF16 编码。

但是我们通常不会将UTF16编码的内容写到磁盘或者在网络进行传输, 因为utf16编码比较浪费空间。

特别是如果文字信息基本都是英文符号的情况下, utf16 都会用2个字节来代表英文符号。 一个字节其实就够了。

所以,Python语言要对字符串对象 进行存储和传输的时候,通常要使用字符串的encode方法,参数指定编码方式,编码为一个 bytes 对象。

同样的字符串,用不同的编码方式,有时会产生不同的bytes结果:

输出内容 中 b 开头,表示这是一个 字节串bytes 对象

\x 说明是用16进制表示一个字节

1
2
print ('你好'.encode('utf8')) # 输出 b'\xe4\xbd\xa0\xe5\xa5\xbd'
print ('你好'.encode('gbk')) # 输出 b'\xc4\xe3\xba\xc3'

encode方法返回的是编码后的字节串对象bytes 编码为字节串对象 bytes 就可存储到文件或者传输到网络中去了。

字符串解码

Python语言的解码 都是解码成 unicode字符串对象

要解码字节串,必须要知道这个字节串是用什么字符编码的方式进行编码的。

如果知道了,就可以用字节串对象的decode方法 进行解码。

比如

1
2
3
# 输出为 '你好'
print(b'\xe4\xbd\xa0\xe5\xa5\xbd'.decode('utf8'))
print(b'\xc4\xe3\xba\xc3'.decode('gbk'))

文件读写

内置函数 open

  • file 文件路径,可以是相对路径或者绝对路径。
  • mode 打开模式。r、w、a、rb、wb、ab
  • encoding 若为写模式,则指定编码方式;若为读模式,则指定解码方式。
1
2
3
4
5
6
7
8
9
10
open(
file,
mode='r',
buffering=-1,
encoding=None,
errors=None,
newline=None,
closefd=True,
opener=None
)

写文件

1
2
3
f = open('tmp.txt','w',encoding='utf8')
f.write('HelloWorld!')
f.close()
1
2
3
4
5
6
7
8
9
10
11
# mode参数指定为rb 就是用二进制读的方式打开文件
f = open('tmp.txt','rb')
content = f.read()
f.close()

# 由于是 二进制方式打开,所以得到的content是 字节串对象 bytes
# 内容为 b'\xe7\x99\xbd\xe6\x9c\x88\xe9\xbb\x91\xe7\xbe\xbd'
print(content)

# 该对象的长度是字节串里面的字节个数,就是12,每3个字节对应一个汉字的utf8编码
print(len(content))

读文件

1
2
3
4
5
6
7
f = open('tmp.txt','r',encoding='gbk')
# read函数有参数size,读取文本文件的时候,用来指定这次读取多少个字符。 如果不传入该参数,就是读取文件中所有的内容。
content = f.read()
f.close()

name = content.split(':')[0]
print(name)
1
2
3
4
5
6
7
8
9
10
11
f = open('tmp.txt')
# 返回一个列表,列表中的每个元素依次对应文本文件中每行内容。注意,每个元素对应的字符串最后都会有一个换行符
linelist = f.readlines()
f.close()
for line in linelist:
print(line)

# 将文件内容字符串 按换行符 切割 到列表中,每个元素依次对应一行
linelist = content.splitlines()
for line in linelist:
print(line)
1
2
3
4
5
6
7
8
9
# mode参数指定为 wb 就是用二进制写的方式打开文件
f = open('tmp.txt','wb')

content = 'HelloWorld!'
# 二进制打开的文件, 写入的参数必须是bytes类型,
# 字符串对象需要调用encode进行相应的编码为bytes类型
f.write(content.encode('utf8'))

f.close()

模块和库

一个代码文件(也就是一个.py文件),也叫它一个模块(Module)。

放模块文件的目录,称之为 包 (Package) 。包目录里面需要有一个名字为 __init__.py 的初始化文件,通常为空文件,也可以在里面加入代码,当这个包里面的模块被导入的时候,这些代码就会执行。

解析器如何寻找模块文件?

首先在解释器 内置模块 ( builtin modules ) 中寻找 是否有 xxx 模块。

其次到 sys.path 路径列表中查看。

解释器启动时,根据下面这些规则 添加路径到 sys.path 列表里

  • 启动脚本文件所在的目录
  • 环境变量 PYTHONPATH 里的目录
  • Python解释器的 缺省安装目录(lib/site-packages)

因而若要自己添加一些目录作为模块搜索路径,一种是将目录路径添加到环境变量 PYTHONPATH ,一种是在代码直接修改: sys.path.append('/path/to/dir')

pip 管理第三方模块

那些优秀的第三方库,基本都是放在一个叫 PYPI 的网站上,pip命令会从该网站下载这些库的安装包进行安装。

指定从国内镜像网站下载:

1
2
3
4
5
# 从豆瓣的镜像备份中下载
pip install requests -i https://pypi.douban.com/simple/

# 若出现 SSL 错误,可改用 http协议 下载
pip install requests -i http://pypi.douban.com/simple/ --trusted-host pypi.douban.com

自定义类

super() 调用父类方法。无需 显示指定 父类的名字,代码维护性更好。

__repr__ 定义实例对象的打印输出

1
2
3
4
5
6
7
8
9
10
11
class BenzCar:
brand = '奔驰'

def __init__(self,color='black'):
self.color = color

def __repr__(self):
return f"BenzCar object: brand = 奔驰, color = {self.color}"

# 打印实例对象
print(BenzCar())

异常

捕获异常

1
2
3
4
5
6
7
while True:
try:
miles = input('请输入英里数:')
km = int(miles) * 1.609344
print(f'等于{km}公里')
except ValueError:
print('你输入了非数字字符')

获取异常对象

1
2
3
4
try:
100/0
except ZeroDivisionError as e:
print (f'异常对象信息:{e}')

匹配所有异常

所有的异常都是 Exception 的子类。

except 后面 没有写异常的类型,也表示匹配所有的异常。

1
2
3
4
try:
100/0
except Exception as e:
print('未知异常:', e)

如果我们想知道异常的详细信息,可以使用 traceback 模块:

1
2
3
4
5
6
7
8
9
10
11
12
import traceback

try:
100/0
except :
print(traceback.format_exc())

# 输出内容:
# Traceback (most recent call last):
# File "xxxx/xxx.py", line 4, in <module>
# 100/0
# ZeroDivisionError: division by zero

自定义异常

异常类型都是 继承自Exception的类,表示各种类型的错误。

raise 异常抛出。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 异常对象,代表电话号码有非法字符
class InvalidCharError(Exception):
pass

# 异常对象,代表电话号码非中国号码
class NotChinaTelError(Exception):
pass

def register():
tel = input('请注册您的电话号码:')

# 如果有非数字字符
if not tel.isdigit():
raise InvalidCharError

# 如果不是以86开头,则不是中国号码
if not tel.startswith('86'):
raise NotChinaTelError

return tel

try:
ret = register()
except InvalidCharError:
print('电话号码中有错误的字符')
except NotChinaTelError:
print('非中国手机号码')

常用函数封装

冒泡排序

1
2
3
4
5
6
7
8
9
10
def bubble_sort(arr):
length = len(arr)
for passnum in range(length-1,0,-1):
for i in range(passnum):
if arr[i] > arr[i+1]:
arr[i], arr[i+1]= arr[i+1],arr[i]
return arr

arr = [23, 41, 25, 54, 18, 14]
print(bubble_sort(arr))

文件拷贝

1
2
3
4
5
6
7
def file_copy(src_path, dst_path):
with open(src_path, 'rb') as f:
content = f.read()
with open(dst_path, 'wb') as f:
f.write(content)

file_copy('tmp.log','tmp.copy.log')