文章目录
Python简介
Python语言特点:简单、开源、解释性、面向对象、可移植性、可扩展性、可嵌入性、丰富的库
Python应用领域:Web开发、爬虫开发、云计算开发、人工智能、自动化运维、数据分析、科学计算
Python官网:https://www.python.org/
官方参考文档:https://docs.python.org/zh-cn/3.7/
菜鸟教程Python3:https://www.runoob.com/python3/python3-tutorial.html
安装Python
- 勾选Add Python 3.8 to PATH,点击Customize installation
- 默认,Next
- 勾选Install for all users,Customize install location选择安装路径(如:E:\Develop\Python\Python38),点击lnstall
- 点击Close
- 若没勾选Add Python 3.8 to PATH,则手动配置环境变量,如:
- E:\Develop\Python\Python38
- E:\Develop\Python\Python38\Scripts
pip下载扩展库
pip命令 | 说明 |
---|---|
pip install xxx | 安装xxx模块 |
pip install -i 镜像URL xxx | 使用指定镜像安装xxx模块 |
pip install -U xxx | 升级xxx模块 |
pip uninstall xxx | 卸载xxx模块 |
pip list | 列出已安装的所有模块 |
pip list -o | 列出过时的模块 |
常用扩展库:
库名 | 描述 |
---|---|
matplotlib | 数据可视化 |
pymysql | 操作MySQL |
pymongo | 操作MongoDB |
scrapy | 爬虫框架 |
requests | 发送HTTP请求 |
beautifulsoup4 | 解析HTML/XML |
python-docx | 处理Word |
xlrd | 读.xlsx和.xls |
常用国内镜像:
清华大学开源软件镜像站:
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple some-package
文件头
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""说明:文件头只需在主文件加即可
#!在Linux系统中用于指定由哪个解释器来执行脚本,且必须在文件第1行
第1行指定使用Linux环境变量$PATH里某个路径下名为python的解释器来解释该python文件
第2行指定python解释器解释该文件时的编码格式
"""
注释
# 单行注释
"""
三个双引号的多行注释
"""
'''
三个单引号的多行注释
'''
输入输出
# input(prompt=None, /),input()得到的数据都是<class 'str'>
hello = input("提示信息:")
# print(value, ..., sep=' ', end='\n', file=sys.stdout, flush=False)
print(hello)
"""常用空白转义字符:
\r 回车,跳转到本行开头
\n 换行,跳转到当前位置的下一行
\t 水平制表,跳转到下一个Tab位置
\b 退格,跳转到前一列
\f 换页,跳转到下页开头
"""
示例:打印进度条
import time
def progress(percent=0.0, width=10, symbol='#'):
"""打印进度条"""
if percent > 1: # 若进度超过1,即100%,则按照100%处理
percent = 1
# 宽度% 个进度符号
res = int(width * percent) * symbol
# 结果进度条,'\r'表示回车到当前行行首,%-{width}s表示以左对齐且宽度为width
progress_bar = f'\r[%-{width}s] %3d%%' % (res, int(percent * 100))
# 打印该百分百的进度条,不换行
print(progress_bar, end='')
if __name__ == "__main__":
received_size = 0
total_size = 2 * 1024 * 1024
while received_size <= total_size:
percent = received_size / total_size
progress(percent, 50)
received_size += 1024
time.sleep(0.001)
运算符
# Python没有++和--
# 相当于math.pow(2, 3)
print(2 ** 3)
# 相当于math.floor(5 / 2)
print(5 // 2)
# 多变量赋值
i, f = 1, 1.0
print(i, f)
# 交换变量值
i, f = f, i
print(i, f)
# 打包
a = 1, 2, 3
print(a) # (1, 2, 3)
# 解包
x, y, z = a # 一一对应
print(x, y, z, sep=', ') # 1, 2, 3
w, *tmp = a # 取前面几个
print(w, tmp, sep=', ') # 1, [2, 3]
*tmp, w = a # 取后面几个
print(tmp, w, sep=', ') # [1, 2], 3
x, y, z = {"a": 1, "b": 2, "c": 3} # 解包字典默认取出的是key
print(x, y, z, sep=', ') # a, b, c
# 多个比较运算符可以连续写
print(1 < 2 < 3)
# 逻辑运算符:不能使用&&、||
# 优先级:not > and > or
print(not 2 > 3)
print(1 < 2 and 2 > 3)
print(1 and 0) # and连接两个数字时,有0则为0,否则为最后一个非0数字
print(1 < 2 or 2 > 3)
print(1 or 0) # or连接两个数字时,全0则为0,否则为第一个非0数字
# 成员运算符
print(2 in {1, 2, 3}) # True
print("one" not in {"one": 1, "two": 2, "three": 3}) # False
# 位运算符
a = 60
b = 13
print("%16s" % bin(60))
print("%16s" % bin(13))
print("%16s" % bin(a & b)) # 按位与
print("%16s" % bin(a | b)) # 按位或
print("%16s" % bin(a ^ b)) # 按位异或
print("%16s" % bin(~a)) # 按位取反
print("%16s" % bin(a << 2)) # 左移
print("%16s" % bin(a >> 2)) # 右移
变量
Python变量三要素:id,type,值
Python中没有常量的概念
Python中变量以内容为基准而不是以变量名为基准
# 使用id()查看对象在内存中的地址
a = -5
b = -3 - 2
# 内容相同的变量,id相同
print(a, id(a), sep=', ') # -5, 140723301062224
print(b, id(b), sep=', ') # -5, 140723301062224
# 内容不同的变量,id不同
a = 256
print(a, b, id(a), sep=', ') # 256, -5, 140723301070576
# is比较两个变量的id,==比较两个变量的值
a = [1, 2]
b = a
print(a is b) # True
b = [1, 2]
print(a is b) # False
print(a == b) # True
# Python中的小整数池为[-5, 256],需要在shell环境下查看,在PyCharm中看不出来
>>> a = -5
>>> b = -5
>>> a is b
True
>>> a = -6
>>> b = -6
>>> a is b
False
>>> a = 256
>>> b = 256
>>> a is b
True
>>> a = 257
>>> b = 257
>>> a is b
False
>>>
判断对象是否存活-引用计数算法:在对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加一;当引用失效时,计数器值就减一;任何时刻计数器为零的对象就是不可能再被使用的。
"""
垃圾收集算法:
分代收集理论
标记-清除算法
标记-复制算法
标记-整理算法
"""
# 增加引用
x = 10 # 栈区中的x指向堆区中的10,10的引用加1
y = x # 栈区中的y指向堆区中的10,10的引用加1
z = x # 栈区中的z指向堆区中的10,10的引用加1
# 减少引用
del x # 删除栈区中的x
del y # 删除栈区中的y
z = 20 # 将栈区中的z指向堆区中的20
数据类型
在Python中,类型属于对象,变量是没有类型的。
Python3中有6个标准的数据类型:
- 3个不可变(值变id变):number数值、string字符串、tuple元组
- 3个可变(值变id不变):list列表、dictionary字典、set集合
从语法上看,内置有__iter__
方法的对象都是可迭代对象,如:字符串、元组、列表、字典、集合、open()打开的文件、等。
可迭代对象的__iter__
方法会返回一个迭代器对象,迭代器对象可以使用__next__
取值。
# 4种内置的数值类型:
print(type(1)) # <class 'int'>,Python中整型不会溢出
print(type(1.0)) # <class 'float'>
print(type(complex(1, 1))) # <class 'complex'>
print(type(1 == 1)) # <class 'bool'>:值为True和False
# 隐式bool:0、None、空值(空字符串""、空列表[]、...)代表False,其余代表True
# 字符串类型:
print(type("1")) # <class 'str'>
# 列表类型:类似Java的List
print(type([1])) # <class 'list'>
# 元组类型:不可变的列表
# 定义只有一个元素的元组时,要在元素后面加上逗号
print(type((1,))) # <class 'tuple'>
# 字典类型:key的特点同集合,定义格式类似JSON;类似Java的Map
print(type({1: 1})) # <class 'dict'>
# 集合类型:元素类型为不可变类型,且元素无序、不重复;类似Java的Set
print(type({1})) # <class 'set'>
# 数据类型转换:类型(变量)
s = '123'
i = int(s)
print(type(s)) # <class 'str'>
print(type(i)) # <class 'int'>
print(type(eval(s))) # <class 'int'>
# 进制转换
print(bin(10)) # 0b1010
print(oct(10)) # 0o12
print(int(10)) # 10
print(hex(10)) # 0xa
print(int(10, 16)) # 0xa
1. 字符串
'单引号字符串'
"双引号字符串"
"""
三双引号允许一个字符串跨行,可以包含空白符及其他特殊字符
"""
print("01234" + "56789") # 连接字符串
print("01234" * 5) # 重复输出字符串
# 字符串有两种索引方式:从左往右以0开始;从右往左以-1开始
print("01234"[1]) # 通过索引获取字符串中的字符
print("01234"[0:-1]) # 切片[start, end)
print("01234"[0:-1:2]) # 切片[start, end),步长为2
print("01234"[4:0:-1]) # 切片[start, end),步长为-1
print("01234"[::-1]) # 字符串反转
print(r"hello\
world") # 使用r或R可以让字符串保持原貌,反斜杠不发生转义
print("192.168.100.88".split('.', 2)) # 按指定字符从左往右切分2次:['192', '168', '100.88']
print(" \thello\n ".strip()) # 返回删除头尾指定字符(默认为空白符)的字符串
print("192.168.100.88".rsplit('.', 2)) # 按指定字符从右往左切分2次:['192.168', '100', '88']
print(" \thello\n ".lstrip()) # 相当于strip只去头
print(" \thello\n ".rstrip()) # 相当于strip只去尾
print("HelloWorld".lower()) # 转为全小写
print("HelloWorld".upper()) # 转为全大写
print("HelloWorld".startswith("Hel")) # 是否以指定字符串开头
print("HelloWorld".endswith("rld")) # 是否以指定字符串结尾
print(".".join(['192', '168', '100', '88'])) # 将字符串序列按指定分隔符拼接:192.168.100.88
print("hello, java".replace('java', 'python')) # 子串替换:hello, python
print("lhp".isalpha()) # 判断是否为字母
print(u"6".isdigit(), b"6".isdigit(), "六".isdigit(), "Ⅵ".isdigit()) # 判断是否为数字,能识别u"6"和b"6"
print(u"6".isnumeric(), "六".isnumeric(), "Ⅵ".isnumeric()) # 判断是否为数字,能识别u"6"、中文数字、罗马数字
print(u"6".isdecimal(), "六".isdecimal(), "Ⅵ".isdecimal()) # 判断是否为数字,能识别u"6"
print("01234".index("123")) # 查找子串的索引,找不到抛异常
print("01234".find("123")) # 查找子串的索引,找不到返回-1
print("lhp".center(9, '-')) # 指定宽度,空位补-,居中
print("lhp".ljust(9, '-')) # 指定宽度,空位补-,左对齐
print("lhp".rjust(9, '-')) # 指定宽度,空位补-,右对齐
print("lhp".zfill(9)) # 指定宽度,默认空位补0,右对齐
print("hello\tworld".expandtabs(2)) # 指定制表符宽度
print("heLLo world".swapcase()) # 大小写反转
print("heLLo world".capitalize()) # 字符串首字母大写,其余小写
print("heLLo world".title()) # 每个单词变成大驼峰
字符编码
Python2默认编码为ascii,Python3默认编码为utf-8
计算机系统中字符编码的通用工作方式:在计算机内存中,统一使用Unicode,当需要将字符保存到硬盘或者需要传输字符时,就将它们转换为utf-8。Python3的字符串在内存中以Unicode表示。
- Python2中字符串进行编码转换的过程为:字符串–>decode(‘原来的字符编码’)–>Unicode字符串–>encode(‘新的字符编码’)–>字符串
- Python3中字符串默认为Unicode,因此不需要先解码为Unicode,其进行编码转换的过程为:字符串–>encode(‘新的字符编码’)–>字符串
解决python2乱码问题:文件头的# coding:与文件存储的编码格式一致、字符串前加上u
解决python3乱码问题:文件头的# coding:与文件存储的编码格式一致
zh = "中国".encode('utf-8')
print(zh) # b'\xe4\xb8\xad\xe5\x9b\xbd'
print(type(zh)) # <class 'bytes'>
print(zh.decode()) # 中国
# ord获取字符的整数表示
print(ord('中')) # 20013
# chr把编码转换为字符
print(chr(20013)) # 中
字符串格式化
# %格式化
str1 = "%s" % {"name": "lhp"} # {'name': 'lhp'}
str2 = "%(name)s" % {"name": "lhp"} # lhp
# str.format()格式化
str3 = "{}, {}".format("hello", 2021) # hello, 2021
str4 = "{1}, {0}".format("hello", 2021) # 2021, hello
str5 = "{name}, {age}".format(name="lhp", age=21) # lhp, 21
# f{表达式}格式化
str6 = f"hello, {str2}" # hello, lhp
2. 列表
# 创建列表
list1 = list("hello") # 转为列表
print(list1) # ['h', 'e', 'l', 'l', 'o']
list1 = ['1', 1, 1.0]
print(list1) # ['1', 1, 1.0]
print(list1 + [2]) # 连接两个列表,获取一个新列表
print(list1 * 2) # 复制列表n次,获取一个新列表
print([1, 2] < [1, 3]) # 类似于字符串比较大小:True
print(list1[-1]) # 通过索引获取元素,索引越界则抛异常:1.0
print(list1[0:-1]) # 切片:['1', 1]
# 增
list1[len(list1):] = (3, 4) # 等同于list1.extend((3, 4))
print(list1) # ['1', 1, 1.0, 3, 4]
list1.append(5)
print(list1) # ['1', 1, 1.0, 3, 4, 5]
list1.insert(1, "a")
print(list1) # ['1', 'a', 1, 1.0, 3, 4, 5]
# 删
del list1[1]
print(list1) # ['1', 1, 1.0, 3, 4, 5]
print(list1.pop()) # 根据索引删除,默认最后一个
print(list1) # ['1', 1, 1.0, 3, 4]
list1.remove('1') # 根据元素值删除
print(list1) # [1, 1.0, 3, 4]
# 查
print(list1.index(3)) # 查找指定索引值
print(list1.count(1)) # 统计指定值有多少个
list1.clear() # 清空列表
print(list1) # []
list1 = [5, 4, 6]
list1.reverse() # 列表反转
print(list1) # [6, 4, 5]
list1.sort(reverse=True) # 降序排序,元素必须是同一类型
print(list1) # [6, 5, 4]
# 列表当做队列
list1.append("hello") # 入队
list1.pop(0) # 出队
# 列表当做栈
list1.append("world") # 入栈
list1.pop() # 出栈
3. 元组
# 创建元组
tuple1 = (1,)
# 类型转换
tuple1 = tuple([4, 3, 2, 1, 0])
print(tuple1) # (4, 3, 2, 1, 0)
# 索引取值
print(tuple1[0], tuple1[-1]) # 4 0
# 切片
print(tuple1[0:-1]) # (4, 3, 2, 1)
# 查找元素索引
print(tuple1.index(2)) # 2
# 统计元素数量
print(tuple1.count(2)) # 1
4. 字典
# 创建字典
# 方式1
dict1 = {"name": "zs", "age": 18}
print(dict1) # {'name': 'zs', 'age': 18}
print(type({})) # {}默认为字典
# 方式2
print(dict(name="zs", age=18)) # {'name': 'zs', 'age': 18}
# 方式3
student = [
['name', 'zs'],
('age', 18)
]
print(dict(student)) # {'name': 'zs', 'age': 18}
# 方式4
print({}.fromkeys(['name', 'age'])) # {'name': None, 'age': None}
# 根据key操作
print(dict1.get('sex')) # 按key取值,key不存在返回None
print(dict1.get('sex', 'default')) # 按key取值,key不存在返回默认值(第二个参数)
print(dict1['age']) # 按key取值,key不存在则抛异常
dict1['age'] = 20 # key存在,则修改
dict1['sex'] = '男' # key不存在,则创建
print(dict1) # {'name': 'zs', 'age': 20, 'sex': '男'}
# in和not in判断的是字典的key
print('age' in dict1) # True
# 获取所有键
print(dict1.keys()) # dict_keys(['name', 'age', 'sex'])
# 获取所有值
print(dict1.values()) # dict_values(['zs', 20, '男'])
# 获取所有键值对
print(dict1.items()) # dict_items([('name', 'zs'), ('age', 20), ('sex', '男')])
# 删除
del dict1['sex']
print(dict1) # {'name': 'zs', 'age': 20}
tuple1 = dict1.popitem() # 以后进先出的顺序删除,并以元组形式返回键值对;如果字典为空则抛异常
print(dict1, tuple1) # {'name': 'zs'} ('age', 20)
name = dict1.pop('name')
print(name, dict1) # zs {}
dict1 = {"name": "zs", "age": 18}
dict1.update({"name": "zs", "age": 20, "sex": "男"})
print(dict1) # {'name': 'zs', 'age': 20, 'sex': '男'}
# 如果key存在,则返回原key
print(dict1.setdefault('name', 'ls'), dict1) # zs {'name': 'zs', 'age': 20, 'sex': '男'}
# 如果key不存在,则添加,并返回添加的值
print(dict1.setdefault('height', 175), dict1) # 175 {'name': 'zs', 'age': 20, 'sex': '男', 'height': 175}
dict1.clear()
print(dict1) # {}
5. 集合
# 创建集合
set0 = set() # 定义空集合:set()
set1 = {1, 2, 3}
set2 = {2, 3, 4}
print(set0, set1, set2) # set() {1, 2, 3} {2, 3, 4}
print(set1 & set2) # 交集:{2, 3}
print(set1 | set2) # 并集:{1, 2, 3, 4}
print(set1 - set2) # 差集:{1}
print(set1 ^ set2) # 并集-交集:{1, 4}
set1 = {1}
set2 = {1}
print(set1 > set2) # set2是否为set1的真子集:False
print(set1 >= set2) # set2是否为set1的子集:True
print(set1.issuperset(set2)) # set2是否为set1的子集:True
print(set2.issubset(set1)) # set2是否为set1的子集:True
# 删
set1 = {1, 2, 3}
set1.discard(4) # 删除元素,不存在不抛异常,无返回
set1.remove(3) # 删除元素,不存在抛异常,无返回
print(set1) # {1, 2}
print(set1.pop(), set1) # 1 {2}
# 改
set1.update({3, 4})
print(set1) # {2, 3, 4}
# 增
set1.add(1)
print(set1) # {1, 2, 3, 4}
# 两个集合是否没有交集
print(set1.isdisjoint({4, 5, 6})) # False
# 等同于set1 = set1 - {3, 4, 5}
set1.difference_update({3, 4, 5})
print(set1) # {1, 2}
# 等同于set1 - {2}
print(set1.difference({2}), set1) # {1} {1, 2}
推导式
1. 列表推导式
"""列表推导式格式:
[生成列表元素的表达式 for 表达式中的变量 in 变量要遍历的序列]
[生成列表元素的表达式 for 表达式中的变量 in 变量要遍历的序列 if 过滤条件]
for可以有多个;执行时,会先使用for迭代出变量,然后将生成列表元素的表达式应用于变量
"""
list1 = []
for i in range(3):
if i % 2 == 0:
for j in range(2):
list1.append((i, j))
print(list1) # [(0, 0), (0, 1), (2, 0), (2, 1)]
# 以下列表推导式等同于以上for循环
list2 = [(i, j) for i in range(3) if i % 2 == 0 for j in range(2)]
print(list2, type(list2)) # [(0, 0), (0, 1), (2, 0), (2, 1)] <class 'list'>
2. 生成器推导式
# 生成器推导式返回一个生成器对象,而不是元组
tuple1 = (s for s in "012301")
# <generator object <genexpr> at 0x00000219AE45B5F0> <class 'generator'>
print(tuple1, type(tuple1))
# ('0', '1', '2', '3', '0', '1')
print(tuple(tuple1))
3. 字典推导式
list1 = [i for i in range(10, 13)]
list2 = [i for i in range(20, 23)]
# 字典推导式
dict1 = {list1[i]: list2[i] for i in range(min(len(list1), len(list2)))}
print(dict1, type(dict1)) # {10: 20, 11: 21, 12: 22} <class 'dict'>
4. 集合推导式
# 集合推导式
set1 = {s for s in "012301"}
print(set1, type(set1)) # {'2', '1', '0', '3'} <class 'set'>
流程控制
1. if
语法:
if 条件:
语句
elif 条件:
语句
else:
语句
例子:
import random
left = -1
right = 1
num = random.randint(left, right) # 范围:[left, right]
if num > 0:
print(num, '> 0')
elif num < 0:
print(num, '< 0')
else:
print(num, '= 0')
三目运算符:
# 三目运算符不能使用:条件 ? 条件成立执行的表达式 : 条件不成立执行的表达式
# 三目运算符格式:条件成立执行的表达式 if 条件 else 条件不成立执行的表达式
print(1 if 1 > 2 else 2)
Python没有switch
2. while
语法:
while 条件:
条件为True时执行的语句
# 以下可以没有
else:
条件为False且没有被break时执行的语句
例子:
counter = 0
while True:
if counter < 5:
print("continue:", counter)
counter += 2
continue
if counter > 10:
print("break:", counter)
break
print(counter)
counter += 1
else:
# 条件永远为True,因此不会执行
print(counter)
itr = "01234".__iter__()
while True:
try:
print(itr.__next__())
except StopIteration:
break
3. for
语法:
for 迭代变量 in 序列:
语句
# 以下可以没有
else:
穷尽序列且没有被break时执行的语句
例子:
"""for循环也称迭代器循环,其工作原理为:
1. [7, 8, 9].__iter__()获取一个迭代器对象
2. 重复:i = 迭代器对象.__next__()
3. for循环捕捉到StopIteration异常后结束
"""
for i in [7, 8, 9]:
print(i)
if i == 8:
print("容器中的元素数量:", len([7, 8, 9]))
break
else:
print("else")
print("生成[start, stop)的数字,步长为step:")
for i in range(0, 10, 2):
print(i, end='-') # 0-2-4-6-8-
print("\n生成一个可遍历容器的(索引, 元素):")
for i in enumerate([7, 8, 9], 1):
print(i, end='-') # (1, 7)-(2, 8)-(3, 9)-
函数
定义语法:
# 函数需要先被调入内存,才能被调用
def 函数名(形参):
"""
文档注释
"""
函数体
return 返回值
Python中的函数可以看做是一个对象,函数可以出现在任何对象可以出现的地方,如:赋值、参数、返回值、容器中的元素、等。
Python允许嵌套定义函数,即在一个函数中定义另一个函数。
1. 参数类型
位置参数:位置参数须以正确的顺序传入函数,且调用时的数量必须和声明时的一样
def add(left, right):
return left + right
add(1, 2) # 正确调用
add() # 调用函数时不加参数会抛异常
关键字实参:函数调用使用关键字参数来确定传入的参数值,允许函数调用时参数的顺序与声明时不一致
def add(left, right):
return left + right
add(right=2, left=1) # 关键字实参
默认形参:调用函数时,如果没有传递参数,则会使用默认参数
def add(left, right=0): # 默认形参必须在位置参数之后
return left + right
add(1)
可变长度参数:函数调用时,实参数量可变
-
加了星号*的参数会以元组的形式导入,存放所有未命名的变量参数;如果在函数调用时没有指定参数,它就是一个空元组;星号后的其他参数必须用关键字传入
def add(*args, increment): print(type(args)) # <class 'tuple'> print(args, increment) add(1, 2, increment=1) # (1, 2) 1 add([1, 2], increment=1) # ([1, 2],) 1
-
加了两个星号**的参数会以字典的形式导入;两个星号后不能再有其他参数
def add(increment, **kwargs): # 可变长度关键字实参 print(type(kwargs)) # <class 'dict'> print(increment, kwargs) add(1, name='zs', age=18) # 1 {'name': 'zs', 'age': 18}
序列解包参数:Python解释器会自动将序列解包成多个元素,并一一传递给各个位置参数
def info(age, *args, **kwargs):
print(age, args, kwargs)
# 序列解包:以下相当于info(1, 2)
info(*(1, 2)) # 1 (2,) {}
# 字典解包:以下相当于info(name="zs", age=18)
info(**{'name': 'zs', 'age': 18}) # 18 () {'name': 'zs'}
函数的形参也可以是函数,调用时传递函数名作为实参;类似C语言的函数指针:
def add(left, right, func=round): # 回调函数
left = func(left)
right = func(right)
print(left, "+", right, "=", left + right)
add(-1.5, 10.51) # -2 + 11 = 9
add(-1, 10, abs) # 1 + 10 = 11
函数参数的提示信息:仅仅起提示作用,需要Python3.5+:
"""参数类型提示格式:
def 函数名(参数: 提示信息 = 默认值, 参数: 提示信息 = 默认值) -> 返回值的提示信息:
pass
"""
def info(name: str = "zs", age: int = 18) -> str:
return name + str(age)
print(info("ls", 19)) # ls19
# {'name': <class 'str'>, 'age': <class 'int'>, 'return': <class 'str'>}
print(info.__annotations__)
def information(name: "提示信息", age: "提示信息" = 18) -> "返回值的提示信息":
return name + str(age)
print(information('ww', 20)) # ww20
# {'name': '提示信息', 'age': '提示信息', 'return': '返回值的提示信息'}
print(information.__annotations__)
2. 装饰器-装饰模式
闭包就是能够读取其他函数内部变量的函数,可以理解成“定义在一个函数内部的函数”。闭包是一种保护私有变量的机制,在函数执行时形成私有的作用域,保护里面的私有变量不受外界干扰,直观的说就是形成一个不销毁的栈环境。
Python装饰器类似于装饰模式,但Python装饰器可以更方便地更改函数和方法。
无参装饰器:
# 序列化装饰器
def serializable(func):
# 闭包函数wrapper,使用了serializable函数的内部变量func
def wrapper(*args, **kwargs):
print("序列化开始->", end="")
result = func(*args, **kwargs)
print("->序列化结束", end="")
return result
return wrapper # 返回装饰对象
# 缓冲装饰器
def buffered(func):
# 闭包函数wrapper,使用了buffered函数的内部变量func
def wrapper(*args, **kwargs):
print("缓冲开始->", end="")
result = func(*args, **kwargs)
print("->缓冲结束", end="")
return result
return wrapper # 返回装饰对象
def input_stream():
print("输入", end="")
input_stream = buffered(input_stream)
input_stream() # 缓冲开始->输入->缓冲结束
print('\n', input_stream.__name__) # wrapper
input_stream = serializable(input_stream)
input_stream() # 序列化开始->缓冲开始->输入->缓冲结束->序列化结束
print()
# 语法糖:@名称;会自动给名称后加上(),然后将@下的函数作为参数传递到()
# 以下相当于serializable(buffered(input_stream))
@serializable
@buffered
def output_stream():
print("输出", end="")
output_stream() # 序列化开始->缓冲开始->输出->缓冲结束->序列化结束
有参装饰器:
# 缓冲装饰器
def config(system="linux", encoding="utf-8"):
def buffered(func):
# 闭包函数wrapper,使用了buffered函数的内部变量func
def wrapper(*args, **kwargs):
print("{系统:%s, 编码:%s}->" % (system, encoding), end="")
print("缓冲开始->", end="")
result = func(*args, **kwargs)
print("->缓冲结束", end="")
return result
return wrapper # 返回装饰对象
return buffered # 返回装饰器
def input_stream():
print("输入", end="")
buffered = config(system="windows", encoding="gbk") # 先配置一个装饰器
input_stream = buffered(input_stream) # 然后装饰input_stream
input_stream() # {系统:windows, 编码:gbk}->缓冲开始->输入->缓冲结束
print("\n", input_stream.__name__) # wrapper
"""语法糖:
@config(system="windows", encoding="gbk")相当于:
1. 调用config(system="windows", encoding="gbk")获取一个配置好的装饰器,得到@buffered
2. @buffered相当于调用buffered(input_stream)获取一个装饰对象
3. 用装饰对象代替output_stream
"""
@config(system="windows", encoding="gbk")
def output_stream():
print("输出", end="")
output_stream() # {系统:windows, 编码:gbk}->缓冲开始->输出->缓冲结束
functools.wraps:
from functools import wraps # 导入functools.wraps()
# 缓冲装饰器
def buffered(func):
# 闭包函数wrapper,使用了buffered函数的内部变量func
@wraps(func)
def wrapper(*args, **kwargs):
"""
wrapper被@wraps(func)作用后:
wrapper的__module__、__name__、__qualname__、__doc__、__annotations__、等会修改成func的
"""
print("缓冲开始->", end="")
result = func(*args, **kwargs)
print("->缓冲结束", end="")
return result
# wrapper.__name__ = func.__name__
return wrapper # 返回装饰对象
@buffered
def input_stream():
print("输入", end="")
input_stream() # 缓冲开始->输入->缓冲结束
print("\n", input_stream.__name__) # input_stream
3. 生成器
在Python中,使用了yield的函数被称为生成器。生成器是一个返回迭代器的函数,只能用于迭代操作,更简单点理解生成器就是一个自定义的迭代器;在调用生成器运行的过程中,每次遇到yield时函数会暂停并保存当前所有的运行信息,返回yield的值,并在下一次执行next()方法时从当前位置继续运行。
# 生成器示例1:
def my_range(start, stop, step=1):
while start < stop:
yield start # 返回start
start += step
itr = my_range(1, 5, 2)
print(itr.__next__()) # 1
print(next(itr)) # 3
print(next(itr)) # StopIteration
# 生成器示例2:
def add_to_list():
result = []
while True:
ele = yield result # 返回result
result.append(ele)
add_iter = add_to_list()
# 先让函数中断在ele = yield result
print(next(add_iter)) # []
# list_iter.send("Java")相当于唤醒函数并返回一个"Java",即ele = "Java"
print(add_iter.send("Java")) # ['Java']
# 相当于add_iter.send(None)
print(next(add_iter)) # ['Java', None]
# 结束函数add_to_list
add_iter.close()
4. lambda-匿名函数
Python使用lambda表达式来创建匿名函数:
"""
lambda语法:lambda [arg1[, arg2, .....argn]]: expression
注意:
1. 表达式的计算结果相当于函数的返回值
2. lambda会返回一个函数对象
"""
myMax = lambda a, b: a if a > b else b
print(myMax) # <function <lambda> at 0x000001F2159DD4C0>
print("myMax(10, 20) =", myMax(10, 20)) # myMax(10, 20) = 20
students = [
{"name": "zs", "age": 13},
{"name": "ls", "age": 14}
]
# 按照age降序排序
students.sort(key=lambda student: student['age'], reverse=True)
print(students) # [{'name': 'ls', 'age': 14}, {'name': 'zs', 'age': 13}]
5. 常用内置函数
map(func, *iterables):有几个序列,func就是几元函数
- 只有1个序列:将func依次作用于这个序列的每一个元素上,得到一个新的序列
- 多余1个序列:将每个序列同一位置的元素同时传递给func,把得到的每一个返回值存放在一个新的序列中;若多个序列的元素数量不同,则根据数量最少的进行求解
result = map(lambda x: x ** 2, [1, 2, 3])
print(list(result)) # [1, 4, 9]
result = map(lambda x, y: x + y, [1, 2, 3], [10, 20])
print(list(result)) # [11, 22]
filter(function or None, iterable):序列的每个元素作为参数传递给function进行判断,若返回True则保留该元素,若返回False则过滤掉该元素
result = filter(lambda x: x % 2 == 0, [0, 1, 2, 3, 4])
print(list(result)) # [0, 2, 4]
reduce(function, sequence[, initial]):function必须是二元函数;先计算function(initial, sequence[0]),然后计算function(上一个计算结果, sequence[1]),以此类推
import functools
print(functools.reduce(lambda a, b: a + b, [1, 2, 3])) # 6
print(functools.reduce(lambda a, b: a + b, [1, 2, 3], 10)) # 16
命名空间和作用域
命名空间是从名称到对象的映射,大部分的命名空间都是通过字典来实现的。
- 内置名称:Python内置的名称
- 全局名称:模块中定义的名称,记录了模块的变量,包括函数、类、其它导入的模块、模块级的变量和常量
- 局部名称:函数中定义的名称,记录了函数的变量,包括函数的参数和局部定义的变量
变量查找顺序:局部命名空间–>全局命名空间–>内置命名空间
命名空间的生命周期:取决于对象的作用域,如果对象执行完成,则该命名空间的生命周期就结束
作用域就是一个Python程序可以直接访问命名空间的正文区域:
- local:当前的,包含局部变量,如一个函数/方法内部
- enclosing:当前外的局部(例如闭包),如一个函数(或类)A里包含了一个函数B,那么对于B中的名称来说,A中的作用域就为nonlocal
- global:当前脚本的最外层,比如当前模块的全局变量
- built-in:内置的变量/关键字等
变量查找顺序:(local–>enclosing)局部–>(global)全局–>(built-in)内置
内部作用域中修改外部作用域的变量:
global_var = 1
def enclosing():
global global_var # 修改global中的变量时,需要使用global声明
global_var = 2
print("in-global_var =", global_var) # in-global_var = 2
enclosing_var = 3
def local():
nonlocal enclosing_var # 修改enclosing中的变量时,需要使用nonlocal声明
enclosing_var = 4
print("in-enclosing_var =", enclosing_var) # in-enclosing_var = 4
local()
print("out-enclosing_var =", enclosing_var) # out-enclosing_var = 4
enclosing()
print("out-global_var =", global_var) # out-global_var = 2
异常
常见异常:
- Exception:异常的基类
- TypeError:参数类型不合适
- ValueError:参数值不正确(类型正确)
- NameError:找不到全局名称
- IndexError:序列索引超出范围
- KeyError:找不到对应的键
- AttributeError:找不到属性
- ZeroDivisionError:除法或取模运算的第二个参数为零
try处理异常格式:
# try必须有,except和finally只能省略一个,else可有可无
try:
可能出现异常的代码
except 异常类名 as 别名: # 也可以不加:as 别名
异常的处理代码
else:
没有异常发生时执行的代码块
finally:
必须执行的代码
raise抛出异常格式:
raise 异常类名
自定义异常:继承内置异常类,如Exception
异常处理示例:
# 示例1
try:
print("可能出现异常的代码")
1 / 0
except Exception as e:
print("异常的处理代码")
else:
print("没有异常发生时执行的代码块")
finally:
print("必须执行的代码")
# 示例2
try:
1 / 0
except (Exception, ZeroDivisionError):
pass
# 明确抛出异常示例
raise AssertionError
断言:
# 如果表达式为False,则抛出AssertionError
assert 表达式
# 等价于
if not 表达式:
raise AssertionError
深浅拷贝
直接赋值:赋值得到的是对象的引用(相当于给对象起别名)
浅拷贝:对象中的值类型成员变量拷贝值,引用类型成员变量仅拷贝引用
深拷贝:对象中的值类型成员变量拷贝值,引用类型成员变量拷贝整个引用对象
# 浅拷贝
a = {1: [1, 2, 3]}
b = a.copy()
print(a, b) # {1: [1, 2, 3]} {1: [1, 2, 3]}
a[1][0] = 0
print(a, b) # {1: [0, 2, 3]} {1: [0, 2, 3]}
# 深拷贝,需要导入copy模块
import copy
c = copy.deepcopy(a)
print(a, c) # {1: [0, 2, 3]} {1: [0, 2, 3]}
a[1][0] = 1
print(a, c) # {1: [1, 2, 3]} {1: [0, 2, 3]}