Python 快速入门_python学习笔记
Python Basic
文章目录
- Python Basic
-
- 1. 基础语法[^19]
-
- 1.1 变量 (Variable)[^11]
- 1.2 运算符 (Operators)
-
- 1. 算术运算符
- 2. 比较运算符
- 3. 赋值运算符
- 4. 逻辑运算符
- 5. 成员运算符
- 6. 身份运算符
- 7. 位运算符
- 1.3 常见语句 (Syntax)
-
- 1.3.1 if
- 1.3.2 for
- 1.3.3 while
- 1.3.4 break
- 1.3.5 continue
- 1.3.6 综合示例
- 1.4 基本数据类型与结构 (list, tuple, dict, set)[^11]
-
- 1.4.1 数据类型
-
- 1.4.1.1 逻辑型( Logical)
- 1.4.1.2 数值型( Numeric)
- 1.4.1.3 字符型
-
- 1.4.1.3.1 python 字符串切割[^12]
- 1.4.2 数据结构
-
- 1.4.2.1 list
- 1.4.2.2 tuple
- 1.4.2.3 dict
-
- 1.4.2.3.1 python 字典defaultdict(list)[^14]
- 1.4.2.4 set[^2]
- 1.5 函数(Function)[^3]
-
- 1.5.1 函数的定义
-
- 1.5.1.1 基本语法
- 1.5.1.2 根据参数分类
-
- 1.5.1.2.1 不带参数
- 1.5.1.2.2 带参数
- 1.5.1.2.3 参数指定默认值
- 1.5.1.2.4 位置参数/多值参数:`*args`不定长参数(list, tuple)[^4]
- 1.5.1.2.5 `**args` 关键字参数(dict)
- 1.5.1.2.6 Lambda 函数
- 1.5.2 调用函数
-
- 1.5.2.1 基本语法
- 1.5.2.2 用 `list` 传参
- 1.5.2.3 用 `*` 传参列表
- 1.5.2.4. 用 `dict` 传参
- 1.5.2.5 用 `**` 传参关键字(键值对)
- 1.5.3 装饰器(Decorator)[^26]
-
- 1.5.3.1 装饰器案例:
-
- 1. `@property`与`@property.setter`
- 1.5.4 常见内置高阶函数
-
- `map()`:
- `filter()`:
- `zip()`:
- `enumerate()`:
- `any()` 和 `all()`:
- 1.6 类和实例 (Class & Instance)
-
- 1.6.1 类的定义[^7]
-
- 1.6.1.1 基本语法
- 1.6.1.2 组成结构
-
- 1.6.1.2.1 类属性/类变量
- 1.6.1.2.2 `self` 参数[^5][^6]
- 1.6.1.2.3 ==类方法==[^18]
- 1.6.2 调用类
- 1.6.3 继承和多态(Inheritance,Polymorphism)[^17]
-
- 1.6.3.1 继承(Inheritance)
- 1.6.3.2 多态(Polymorphism)
- 1.6.3.3 `super()用法`[^20]
-
- 1.6.3.3.1 super的使用
- 1.6.3.3.2 通过super() 来调用与子类同名的父类方法
- 1.6.4 Iterators
- 1.6.5 访问限制 underscore
- 1.6.6 模块调用
-
- 1.6.6.1 Python标准库(Python Standard Library)
- 1.6.6.2 PIP包管理器
- 1.6.7 类的编写规范 Styling Classes
- 1.6.7 课后练习
- 1.7 模块 (Module) & 包(Package)[^13]
-
- 1.7.1 模块(Module)
- 1.7.2 包(Package)
-
- 1.7.2.1 Absolute Import 绝对导入
- 1.7.2.2 Relative Import 相对导入
- 1.7.3 搜索路径
- 1.8 字符串处理
-
- 1.8.1 格式化输出[^9][^10]
-
- 1. 占位符`%`
- 2. format()` 方式格式化输出
- 3. ==f-string格式化==[^24]
- 1.8.2 查找
- 1.8.3 分隔
- 1.8.4 大小写转换
- 1.8.5 截取
- 1.8.6 追加
- 1.8.7 替换
- 1.8.8 拼接/连接
- 1.8.9 反转
- 1.9 文件读写
-
- 1.9.1 读文件
- 1.9.2 写文件
- 1.9.3 追加写文件
- 1.10 类型提示(type hints / annotations)[^15][^16]
-
- 1.10.1 类型提示是什么?
- 1.10.2 既然类型声明不能阻止变量使用其他类型赋值,那么其意义何在呢?
- 1.10.3 支持的类型有哪些?
- 1.10.4 如何在不支持部分Type Annotation的Code Editor中增加提示
- 2. Python环境配置(Windows)
-
- 1. pip安装以及换源[^31]
-
- 1. 查看当前源
- 2. 关于whl文件安装(离线安装)
- 3. 换源
-
- 1. 推荐源
- 2. 临时换源
- 3. Windows换源
- 4. MacOS / Linux换源
- 2. Python 安装环境与运行环境打包 (requirements.txt) [^21]
-
- 1. 环境打包
-
- 1. 方式一:freeze
- 2. 方式二:pipreqs
- 2. 安装环境
-
- 1. 在线安装
- 2. 离线安装
- 3. 卸载环境
- 3. Python脚本轻松变exe可执行文件(auto-py-to-exe)[^25]
-
- 1. 什么是`auto-py-to-exe`
- 2. 安装 `auto-py-to-exe`
- 3. auto-py-to-exe 部分选项介绍
- 4. auto-py-to-exe 实战
-
- 2.2.4.1 打开 auto-py-to-exe
- 2.2.4.2 配置打包选项
- 3. 查看打包效果
- 5. 总结一下
- 2.3 虚拟环境
-
- 1. venv()
-
- 1. 安装:
- 2. 激活虚拟环境
- 3. 退出虚拟环境
- 4. 配置VSCode自动识别虚拟环境
- 2. pipenv(需安装)
- 3. 常用语法
-
- 1. `if __name__ == \'__main__\'`[^22]
-
- 1.摘要
- 2. 程序入口
-
- 1. 一个.py文件被其他.py文件引用
- 2. 修改const.py,添加`if __name__ == \"__main__\"`
- 2. 读取当前工作路径
-
- 1. In `.py`
- 2. In `.ipynb`
- 3. Get all members(methods) of a function/variable:
- 4. 仿QT的Signal & Slot
- 5. 利用f-string 快速debug
- 4. 多线程
-
- 1. `Threading` 模块
-
- 1. 基本概念
- 例1:
- 解释:
- 2. `concurrent.futures` 模块[^30]
-
- 1. 基本概念
- 例1(no lock):
- 例2(with lock):[^27]
- 6.函数笔记[^28]
-
- Lambda示例
- 5. QA
-
- 1. Difference between `func()` and `func`
- 2. 为什么很多命令需要 `-m`?
-
- 为什么使用 `-m` 参数?
- 示例用法:
- 6. 函数笔记[^28]
-
- Lambda示例
- Reference:
1. 基础语法1
Python是也是一个面对对象编程(Object-Oriented Programming)的语言,面对对象编程是一种设计思想,意味着我们把对象作为程序的基本单元,而每个对象包含了自己的属性和方法。面向对象编程主要有以下特点:
- 封装(Encapsulation):对外部世界隐藏对象的工作细节。
- 继承(Inheritance):继承使子类具有父类的各种属性和方法,而不需要编写相同的代码。
- 多态(Polymorphism):为不同的数据类型的实体提供统一的接口。
使用OOP有以下的优点:
- 提高软件开发的生产效率
- 使软件的可维护性更好
- 提高软件的质量
在 Python 中,元组、列表和字典等数据类型是对象,函数也是对象。
1.1 变量 (Variable)2
- python不用事先声明变量,赋值过程中就包含了变量声明和定义的过程
- 用
=
复制,左边是变量名,右边是变量的值 - 变量名可由 a-z, A-Z,数字,下划线()组成, 首字母不能为数字和下划线();
- Python 对大小写敏感,变量 a 和变量 A 表示不同的变量;
- 变量名不能为 Python 中的保留字;
- 慎用小写字面l和大写字母O
- 尽量小写, 如有多个单词,用下划线隔开即采用**蛇形命名法(snake_case)**命名。
常量应当采用全大写,如多个单词,用下划线隔开。
-
整数
int_var = 1
-
长整数
long_var = 1000L
-
浮点数
float_var = 1.0
-
复数
用的不多
-
字符串
str = \"Hello World!\"print(str) # 输出完整字符串print(str[0]) #输出字符串的第一个字符print(str[2:5]) # 输出字符串的第三个至第五个之间的字符串print(str[:5]) # 输出字符串的第一个至第五个之间的字符串print(str[2:]) # 输出字符串的第三个开始的字符串print(str * 2) # 输出字符串两次print(str + \"TEST\") # 输出连接的字符串
-
列表
用
[]
定义,类似于C++或Java的数组,一个有序可变集合的容器。支持内置的基础数据结构甚至是列表,列表是可以嵌套的。不同的数据结构也可以放在同一个列表中,没有同一类型的限制list_a = [\"str\", 1, [\"a\",\"b\", \"c\", 4]]list_b = [\"hello\"]print(list_a[0])print(list_a[1:3])print(list_a[1:])print(list_b * 2)print(list_a + list_b)
-
元组
用
()
定义,可以视为不可变的列表,在赋值之后就不能二次更改了tuple_a = (\"str\", 1, [\"a\",\"b\", \"c\"], 4)tuple_b = (\"hello\")print(tuple_a[0])print(tuple_a[1:3])print(tuple_a[1:])print(tuple_b * 2)print(tuple_a + tuple_b)
-
字典
类似于C++语言的map,key-value键值对的集合,无序的容器
dict_a = { \"name\": \"Walker\", \"age\": \"22\", 1: \"level_1\"}print(dict_a[\"name\"])print(dict_a[\"age\"])print(dict_a[1])# 遍历print(dict_a.keys()) print(dict_a.values())print(dict_a.items())# 判断,返回True or Falseprint(\"name\" in dict_a) print(1 in dict_a)
1.2 运算符 (Operators)
1. 算术运算符
2. 比较运算符
3. 赋值运算符
a += b
等价于 a = a + b
a -= b
等价于 a = a - b
a *= b
等价于 a * b
a /= b
等价于 a = a / b
a %= b
等价于 a = a % b
a **= b
等价于 a = a ** b
a //= b
等价于 a = a // b
4. 逻辑运算符
&&
!
5. 成员运算符
6. 身份运算符
7. 位运算符
1.3 常见语句 (Syntax)
1.3.1 if
if 条件:满足条件执行的语句elif 条件:满足条件执行的语句else:不满足条件执行的语句
例如
a = 2if a == 1:print(\"a == 1\")elif a == 2:print(\"a == 2\")else:print(\"a !=1 and a !=2\")
1.3.2 for
用来遍历容器、或者执行重复性的代码
遍历容器
list_a = [1, 2, \"test\"]for i in list_a:print (i)
运行结果
12test
执行重复性代码
for i in range(0,5):print(i)
运行结果
01234
如果循环的时候不需要用到
i
,也可以用_
代替
for _ in range(0,5):print(1)
输出
11111
如果需要Counter:
fruits = [\'apple\', \'banana\', \'orange\']for i, fruit in enumerate(fruits): print(f\"Index {i}: {fruit}\")# Output:# Index 0: apple# Index 1: banana# Index 2: orange
需要遍历文件:
import osworking_dir = os.path.dirname(os.getcwd())items = os.listdir(working_dir)files = []# Print the list of file namesfor file_name in items: # print(file_name) files.append(file_name) print(len(files))
给列表每个元素增删前缀/后缀:
# Adding a Suffix:original_list = [\'item1\', \'item2\', \'item3\']suffix = \'_suffix\'# Add suffix to each elementmodified_list = [item + suffix for item in original_list]print(modified_list) # Output: [\'item1_suffix\', \'item2_suffix\', \'item3_suffix\']
# Removing a Suffixoriginal_list = [\'item1_suffix\', \'item2_suffix\', \'item3_suffix\']suffix = \'_suffix\'# Remove suffix from each elementmodified_list = [item[:-len(suffix)] if item.endswith(suffix) else item for item in original_list]print(modified_list) # Output: [\'item1\', \'item2\', \'item3\']
1.3.3 while
用来执行重复的代码
a = 1while a != 5: print(a) a += 1
运行结果
1234
1.3.4 break
跳出这一层循环,不再循环下一次。但是不会跳出多个循环
list_a = [1, 2, \"test\"]for i in list_a:print (i)if i == 2:break # 当i==2的时候,结束循环print(\"~~~\")
运行结果
1~~~2
1.3.5 continue
结束当前循环并且开始下一个循环
list_a = [1, 2, \"test\"]for i in list_a:print (i)if i == 2:continue # 当i==2的时候,结束当前循环,因此波浪线不会被print,但是会print testprint(\"~~~\")
运行结果
1~~~2test~~~
1.3.6 综合示例
for i in range(1,100,2): # 1到100,步进为2的循环 print(\"当前: \", i) if(i % 3) ==0: print(\"YES! 数字\", i, \"可以被3整除!!!\") continue elif i > 30: print(\"超过30啦!\") break print(\"No! 数字\", i, \"不能被3整除.\")print(\"结束\")
运行结果
当前 1No! 数字 1 不能被3整除.当前 3YES! 数字 3 可以被3整除!!!当前 5No! 数字 5 不能被3整除.当前 7No! 数字 7 不能被3整除.当前 9YES! 数字 9 可以被3整除!!!当前 11No! 数字 11 不能被3整除.当前 13No! 数字 13 不能被3整除.当前 15YES! 数字 15 可以被3整除!!!当前 17No! 数字 17 不能被3整除.当前 19No! 数字 19 不能被3整除.当前 21YES! 数字 21 可以被3整除!!!当前 23No! 数字 23 不能被3整除.当前 25No! 数字 25 不能被3整除.当前 27YES! 数字 27 可以被3整除!!!当前 29No! 数字 29 不能被3整除.当前 31超过30啦!结束
1.4 基本数据类型与结构 (list, tuple, dict, set)2
1.4.1 数据类型
什么是数据类型?
人类有思想,很容易区分汉字和数字的区别,例如,你知道 1 是数字,“中国”是汉字。 计算机虽然很强大,但是它没有思想,它不知道哪个是汉字,哪个是数字,除非你明确告诉它。
这就是我们要说的“数据类型”,数据类型将它们进行了明确的划分,告诉计算机哪个是数字,那个是字符串。 Python 中常用到的数据类型有逻辑型( Logical)、 数值型( Numeric)、 字符型( Character)。
1.4.1.1 逻辑型( Logical)
又叫做“ 布尔型” ,用于只有两种取值(0 和 1,真和假)的场合, 首字母是大写。
- True 真
- False 假
对于逻辑型数据,有着他自己的运算规则。
- & :与,两个逻辑型数据中,一假则为假。
- |: 或,两个逻辑型数据中,一真则为真。
- not :非, not True 就是 False, not False 就是 True。
1.4.1.2 数值型( Numeric)
就是我们数学里面学过的实数:包括负数、 0、正数,可以是整数也可以是浮点数。
对于数值型类型的变量,我们可以利用它进行加、减、乘、除。但是这里有几个地方需要注意一下。
- “/” 代表除法, “//” 代表取整;
>>> 7 / 41.75>>> 7 // 41
- “%” 表示求余;
>>> 10 % 42>>> 5 % 32>>> 4 % 54
- 一个关于浮点数需要注意的地方;
>>> a = 4.2>>> b = 2.1>>> a + b6.300000000000001>>> (a+b) == 6.3False>>> from decimal import Decimal>>> a = Decimal(\"4.2\")>>> b = Decimal(\"2.1\")>>> a+bDecimal(\'6.3\')>>> (a+b) == Decimal(\"6.3\")True>>> (a+b)/3Decimal(\'2.1\')
1.4.1.3 字符型
字符型数据代表了所有可定义的字符, 无运算规则,用 ‘ ’ 或者 “ ” 引用的任意文本。
1.4.1.3.1 python 字符串切割3
-
字符串切割
# str.split(s, num)[n] name = \'Tesla X\'list = name.split()brand = name.split()[0]model = name.split()[1]print(\'brand is %s, model is %s\' % (brand,model))
参数说明:
s:表示指定的分隔符,不写的话,默认是空格(’ ‘)。如果字符串中没有给定的分隔符时,则把整个字符串作为列表的一个元素返回。
num:表示分割次数。如果指定了参数num,就会将字符串分割成num+1个子字符串,并且每一个子字符串可以赋给新的变量。
[n]:表示选取第n个分片,n表示返回的list中元素下标,从0开始的。 -
路径文件分隔函数
os.path.split():路径文件分割函数
按照路径将文件名和路劲分割开,这里需要引入os包(import os)os.path.split(‘PATH’)
总结
str = \'https:// translate.google.cn/ ?sl=auto&tl=zh-CN&text=str&op=translate\'# 默认以空白切割print(str.split())[\'https://\', \'translate.google.cn/\', \'?sl=auto&tl=zh-CN&text=str&op=translate\']# 以 = 为分隔符print(str.split(\"=\"))[\'https:// translate.google.cn/ ?sl\', \'auto&tl\', \'zh-CN&text\', \'str&op\', \'translate\']# 以 = 为分隔符,只切割一次print(str.split(\"=\",1))[\'https:// translate.google.cn/ ?sl\', \'auto&tl=zh-CN&text=str&op=translate\']# 默认以空白切割,取序列下标为0的项print(str.split()[0])https://# 以 = 为分隔符,取序列下标为0的项print(str.split(\"=\")[0])https:// translate.google.cn/ ?sl
1.4.2 数据结构
常用数据结构
Python 常用数据结构有如下5种:
- String:字符串
- List:列表
- Tuple:元组
- Set :集合
- Dic:字典
但是我们尤其需要注意字符串、列表和字典这 3 种数据结构。
序列
序列是一种可迭代对象,可以存储多个数据,并提供数据的访问。序列中的数据,称之为“ 序列元素” 。 Python 中内置的序列类型有:
- 列表( list)
- 元组( tuple)
- 字符串( str)
- 字节( bytes)
序列有它的通用操作::索引、切片、迭代、长度、运算,这是Python数据结构的共性,方便我们学习记忆。
- 索引:通过索引访问序列中指定位置的元素(单个);
- 切片:通过切片访问序列中一个区间的元素(多个);
- 迭代:序列作为可迭代对象,因此,可以通过 for 循环进行遍历;
- 长度:可以通过 len()函数获取序列长度(序列中还有元素的个数);
- 运算:序列支持+、 *、 in、 not in、比较、布尔运算符;
1.4.2.1 list
- python内置的一种数据结构
- 有序
- 可更改
- 用方括号定义
[list]
示例:
game = [\"dota\", \"dota2\", \"lol\"]game2 = [\"Genshin\"]
-
增
-
append在列表最后增加元素
game.append(\"wow\")
-
insert插入指定位置
game.insert(2, \"war3\")
-
拼接另一个列表
game2 = [\"Genshin\"]list = game + game2 # 如果要把string加入列表,需要在string外套list,不然会把string的每一个字符拆分添加进列表
-
复制
game*3 #将game复制3次# 3*game 等价于 game*3
-
生成新列表,并且复制原列表所有元素
game.copy(new_game)
-
-
删
-
pop删除指定位置元素并返回删除的元素
game.pop(1) #i为索引,不填则默认删除最后一位
-
remove将列表中出现的第一个元素删除
game.remove(\"dota2\")
-
删除列表所有元素
game.clear()
-
删除指定长度元素
del game[i] # 删除game中第i元素del game[i:j:k] # 删除列表game中第i到第j以k为步长的元素
-
-
改
-
更改元素
game[0] = \"dota2\"game[::-1] # 反转game[i:j:k] = game2 # 用列表替换game第i到第j以k为步长切片后所对应元素子列表
-
反转
game.reverse()
-
-
查
-
获取长度
print(len(game))
-
获取元素
print(game[0])print(game[-1])
-
判断是否有无元素
```python\"genshin\" in game\"genshin\" not in game```
-
获取列表中的部分元素
[start:stop:step]
my_array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]# Slice from the 2nd to the 5th element (indexes 1 to 4)slice1 = my_array[1:5]# Slice from the beginning to the 5th elementslice2 = my_array[:5]# Slice from the 6th element to the endslice3 = my_array[5:]# Slice the whole arrayslice4 = my_array[:]# Slice with a step (every second element)slice5 = my_array[::2]
-
1.4.2.2 tuple
- python内置的一种数据结构
- 有序
- 无法修改
- 在赋值的时候决定所有元素
- 用小括号定义
(tuple)
示例:
game = (\"dota\", \"dota2\", \"lol\")
-
增
- 无法增加
-
删
- 无法删除
-
改
- 无法更改
-
查
-
获取长度
print(len(game))
-
获取元素
print(game[0])
-
1.4.2.3 dict
- python内置的一种数据结构
- 无序
- 可更改
- 用大括号定义
{}
,每个键值对之间用逗号,
分隔
类似于C++语言的map,存键值对,有很快的查找速度。比如根据身份证号查找某个人的名字,根据学号查找学生成绩单。
用list遍历也可以得到结果,但是太慢了,list就好像你在一个小区找人,一家一家敲门。dict则是直接按照地址X栋X单元X层直接找
dict是典型的用空间换时间的例子。会占用大量内存,但是查找、插入速度很快,不会随着元素数量增加而增加
list则是时间换空间的例子,不会占用大量内存,但是随着元素数量增多,查找时间会变很长
Note. python版本3.5之前的存储是无序的,3.5版本之后数据存储是有序的, 但是从取值方式看,字典依然是一个无序的数据类型, 取值还是用key来获取,而非下标
json object 在Python中就当作dict处理,不论是json.load()还是json.loads()
with open(\'data.json\', \'r\') as file: data = json.load(file) # json.load 读取文件为jsonjson_string = \'{\"name\": \"Alice\", \"age\": 30}\'data = json.loads(json_string) # json.loads读取string
示例:
dict_b = { \"name\": \"Walker\", \"age\": \"22\", 1: \"level_1\"}print(dict_b)print(dict_b.keys())print(dict_b.values())print(dict_b.items())for key,value in dict_b.items(): print(f\"Key: {key}, Value: {value}\")
-
增
-
增加键值对
dict_b[\"sex\"] = \"Male\"
-
update() 向字典插入指定的项目。 这个指定项目可以是字典或可迭代对象。
dict_c = { \"favorate\":\"soccer\"}dict_b.update(dict_c)print(dict_b)
-
-
删
-
del() / del: 删除字典或删除字典中指定键值对
dict_b.pop(\'favorate\')print(dict_b)
-
pop删除
-
pop():删除拥有指定键的元素并将其返回,当括号内为0则默认删除最后一个元素并且将其返回
pop_value = dict_b.pop(1)print(pop_value)print(dict_b)
-
popitem():从字典中删除最后一个键值对
popitem_value = dict_b.popitem()print(popitem_value)
-
clear():清空字典
dict_b.clear()
-
-
-
改
-
更改
dict_b[\"age\"] = \"18\"
-
-
查
-
查找键值对
print(dict_b[\"name\"])print(dict_b[1])
-
**get取值,**如果当前查找的key不存在则返回第⼆个参数(默认值),如果省略第⼆个参数,则返回 None。
print(dict_b.get(5))print(dict_b.get(5, \"CustomDefaultValue\"))
-
遍历
print(dict_b.keys()) print(dict_b.values())print(dict_b.items())test_dic = {\"name\": \"Alice\", \"age\": 25}for key, value in test_dic.items(): print(key, value)
-
判断
- key放前面返回True or False
print(\"name\" in dict_b) print(1 in dict_b)
- 也可以用get方法取,如果过key不存在,会返回None或者自己定义的默认值
print(dict_b.get(5))print(dict_b.get(5, \"CustomDefaultValue\"))
-
1.4.2.3.1 python 字典defaultdict(list)4
setderault()
方法接收两个参数,用法和get类似,但比get强大,它可以给字典的key设定一个默认值(如果key不在字典中的时候),defaultdict(list),会构建一个默认value为list的字典
from collections import defaultdictresult = defaultdict(list)data = [(\"p\", 1), (\"p\", 2), (\"p\", 3), (\"h\", 1), (\"h\", 2), (\"h\", 3)] for (key, value) in data: result[key].append(value)print(result) # defaultdict(, {\'p\': [1, 2, 3], \'h\': [1, 2, 3]})
1.4.2.4 set5
-
python内置的一种数据结构
-
无序
-
可更改
-
用大括号
{}
或者set()
定义,每个元素之间用逗号,
分隔 -
会自动去重
-
set可以视为没有value的dict,只存key,一般用作去重或者集合求交,求并等。
-
set 中不能有 list 类型的 object,因为 list 是 mutable,不可 hash。所以 set 中的 item 只能为 int, float, str, tuple
示例:
s = {}s= set()girls_1 = set([\'lucy\',\'lily\'])girls_2 = set([\'lily\',\'anna\'])
-
增
-
增加
girls_1.add(\'mary\')
-
-
删
-
删除
girls_1.remove(\'mary\')
-
-
改
-
复制(返回一个新的set对象,内容和原set一致)
s.copy()
-
可能是因为 set 中不允许重复的 item 存在,所以不支持拼接
+
和复制*
操作
-
-
查
-
求长度
len(girls_1)
-
查询元素是否存在于set中
不会遍历整个set,是通过item的哈希值快速匹配的
print(\'lily is in set: {}\'.format(\'lily\' in girls_1))print(\'anna is in set: {}\'.format(\'anna\' in girls_1))
输出结果:
lily is in set: Trueanna is in set: False
-
求并集 Union (
|
)两个 set 的并集,返回一个 set 由两个 set 的 item 组成
girls_1 | girls_2
输出结果:
set([\'lily\',\'lucy\',\'anna\'])
-
求交集 Intersection (
&
)
两个 set 的交集,返回一个 set 由两个 set 的 公有的 item 组成
girls_1 & girls_2
输出结果:
set([\'lily\'])
-
求差集 Difference (
-)
两个 set 的差集,返回一个 set,item 在第一个 set 中,而不在第二个 set
girls_1 - girls_2
输出结果:
xxx
-
Xor (^) or 对称不同 difference
返回一个 set,item 在第一个或第二个 set,但是不是两个 set 共有的
girls_1 ^ girls_2
输出结果:
{\'anna\', \'lucy\'}
-
isdisjoint
如果2个set没有相同元素则返回True
girls_1.isdisjoint(girls_2)
输出结果:
False
-
intersection
返回两个集合中共同的元素到新的集合
girls_1.intersection(girls_2)
输出结果:
{\'lily\'}
-
set的转换:
强制类型转换:
s = { any_type_immutable_object }
s = set (any_type_mutable_or_immutable_object )
-
str to set
str_obj = \'python\'set_via_symbol = {str_obj}set_via_constructor = set(str_obj)print(\'set_via_symbol: {}\'.format(set_via_symbol))print(\'set_via_constructor: {}\'.format(set_via_constructor))# output:# set_via_symbol: {\'python\'}# set_via_constructor: {\'h\', \'o\', \'n\', \'t\', \'y\', \'p\'}
-
tuple to set
tuple_obj = (\'one\', \'two\')set_via_symbol = {tuple_obj}set_via_constructor = set(tuple_obj)print(\'set_via_symbol: {}\'.format(set_via_symbol))print(\'set_via_constructor: {}\'.format(set_via_constructor))# output:# set_via_symbol: {(\'one\', \'two\')}# set_via_constructor: {\'two\', \'one\'}
-
list to set
注意 list 不支持 {list}->set,因为 set 不能有 mutable object
list_obj = [\'one\', \'two\']# set_via_symbol = {list_obj} set_via_constructor = set(list_obj)# print(\'set_via_symbol: {}\'.format(set_via_symbol))print(\'set_via_constructor: {}\'.format(set_via_constructor))# output:# set_via_constructor: {\'two\', \'one\'}
-
dict to set
注意 dict 不支持 {dict}->set,因为 set 不能有 mutable object
dict_obj = {\'script\': \'python\', \'version\': \'3.8\'}# set_via_symbol = {dict_obj} set_via_constructor = set(dict_obj)# print(\'set_via_symbol: {}\'.format(set_via_symbol))print(\'set_via_constructor: {}\'.format(set_via_constructor))# output:# set_via_constructor: {\'version\', \'script\'}
1.5 函数(Function)6
1.5.1 函数的定义
只有一个单词时全部小写,有多个单词时采用**蛇形命名法(snake_case)**命名。
在def GUI的时候,通常使用驼峰命名法(如:doOpenFile),
1.5.1.1 基本语法
def print_hello(): print(\"hello\")
1.5.1.2 根据参数分类
没有参数默认值的param放前面,有默认值的param放后面
1.5.1.2.1 不带参数
def print_hello(): print(\"hello\")print_hello()
1.5.1.2.2 带参数
def print_str(s): print(s) return s*2result = print_str(\"hello\")print(result)
1.5.1.2.3 参数指定默认值
def print_default(s=\"hello\"):print(s)print_default()print_default(\"default\")
1.5.1.2.4 位置参数/多值参数:*args
不定长参数(list, tuple)7
除开固定参数和关键字参数,中间的所有参数都会被打包进args
这个元组
- 类似C++里面的指针,
*args
指向形参构成的列表,*arg
后不要再加入别的形参,不然会报错- 混用
*
与**
,*args
必须在**kwargs
之前
def print_args(firstarg,secondarg,*args): print(\"first arg \" + firstarg) print(\"second arg \" + secondarg) print(\"\") for i in args: print(\"---args start---\") print(i) print(\"---args end---\") print(\"\")print_args(\"test0\",\"test1\",\"test2\",\"test3\",\"test4\", [\"test5\",\"test6\"],(\"test7\",8))
Output:
first arg test0second arg test1---args start---test2---args end------args start---test3---args end------args start---test4---args end------args start---[\'test5\', \'test6\']---args end------args start---(\'test7\', 8)---args end---
1.5.1.2.5 **args
关键字参数(dict)
所有输入的键值对会被打包进一个字典
这个参数只能接受键值对,不能接受字典。字典需要用固定参数。
def print_args(firstarg,secondarg,*args,**kwargs): print(\"first arg \" + firstarg) print(\"second arg \" + secondarg) print(\"\") for i in args: print(\"---args start---\") print(i) print(\"---args end---\") print(\"\") for key,value in kwargs.items(): print(f\"Key: {key}, Value: {value}\")print_args(\"test0\",\"test1\",\"test2\",\"test3\",\"test4\", [\"test5\",\"test6\"],(\"test7\",8), testkey1_name=\"Jack\",testkey2_gender=\"Male\")
Output:
first arg test0second arg test1---args start---test2---args end------args start---test3---args end------args start---test4---args end------args start---[\'test5\', \'test6\']---args end------args start---(\'test7\', 8)---args end---Key: testkey1_name, Value: JackKey: testkey2_gender, Value: Male
1.5.1.2.6 Lambda 函数
Lambda 表达式是一种匿名函数,可以在代码中创建一个简单的函数,而无需定义函数名称。Lambda 表达式可以用来代替简单的函数,通常在需要编写一些简短、一次性的函数时使用。
Lambda 表达式的语法格式如下:
lambda arguments: expression
其中 arguments
是函数的参数列表,用逗号分隔,可以没有参数或有多个参数,而 expression
是函数的返回值,可以是任何有效的 Python 表达式。
例如,下面是一个简单的 Lambda 表达式,将两个参数相加并返回结果:
sum = lambda a, b: a + b
在这个例子中,Lambda 表达式接受两个参数 a
和 b
,并返回它们的和。
Lambda 表达式通常与函数式编程一起使用,可以作为参数传递给其他函数,例如 map()
和 filter()
函数,用于对序列进行操作。
Lambda 表达式的使用也有一些限制,它只能包含一个表达式,而不能包含多个语句,也不能使用循环或条件语句等复杂结构。
下面是几个简单的 Lambda 表达式例子:
-
将字符串转换为大写字母:
to_upper_case = lambda s: s.upper()result = to_upper_case(\"hello, world!\")print(result) # 输出: \"HELLO, WORLD!\"
-
计算两个数的平均值:
average = lambda x, y: (x + y) / 2result = average(3, 5)print(result) # 输出: 4.0
-
过滤出列表中的偶数:
numbers = [1, 2, 3, 4, 5]even_numbers = list(filter(lambda x: x % 2 == 0, numbers))print(even_numbers) # 输出: [2, 4]
在这些例子中,Lambda 表达式被用作函数的参数传递给
filter()
等函数,以便对列表中的元素进行操作和过滤。
1.5.2 调用函数
1.5.2.1 基本语法
- 在调用函数时,*和**都是分配参数用的, 在“调用函数时分配参数”跟“定义函数时收集参数”,反过来了
- 使用function的时候需要带(), 哪怕没有参数
- 传参顺序可变
def print_num(x=0,y=1,z=3): x = x + 1 y = y + 1 z = z + 1 print(x,y,z)print_num()print_num(y=2, x=1)
1.5.2.2 用 list
传参
发送到函数的参数可以是任何数据类型(字符串、数字、列表、字典等),并且在函数内其将被视为相同数据类型。
例如,如果您将 List 作为参数发送,它到达函数时仍将是 List(列表):
def myprint(x,y): print(x) print(y) myprint(1,2)
1.5.2.3 用 *
传参列表
传入的参数和函数定义的参数数量相等的情况下可用
params=(1,2)def myprint(x,y): print(x) print(y) myprint(*params)
1.5.2.4. 用 dict
传参
可以用 key = value 语法发送参数,参数的顺序无关紧要。(需要函数在定义时就有多个函数或者用**
的方式定义)
params={\'x\':1,\'y\':2}def myprint(x,y): print(x) print(y) myprint(x = \"1\", y = \"2\")
1.5.2.5 用 **
传参关键字(键值对)
可以用**
直接传入字典,避免手动输入多个键值对的烦恼
params={\'x\':1,\'y\':2}def myprint(x,y): print(x) print(y) myprint(**params)
1.5.3 装饰器(Decorator)8
import timedef decorator(func): def time_wrapper(*args, **kwargs): start_time = time.time() result = func(*args, **kwargs) end_time = time.time() print(f\"{start_time = }, {end_time = }\") print(f\"{func.__name__} executed time: {end_time - start_time} seconds.\") return result return time_wrapper@decoratordef sleep(n): time.sleep(n) return nsleep(3)
Output:
start_time = 1711860536.0176833, end_time = 1711860539.0185137 sleep executed time: 3.0008304119110107 seconds.
原理图解:
1.5.3.1 装饰器案例:
1. @property
与@property.setter
How @property
Works
When you decorate a method with @property
, you’re telling Python to treat it as a property. This means when you access the attribute, Python will call the method you decorated with @property
instead of accessing an attribute directly. This method is known as the getter method because it gets the value of the attribute.
Adding a Setter Method
If you also want to control how a property is set, you can use the @property_name.setter
decorator. This allows you to define a method that will be called when you try to assign a value to the property.
Properties are useful for several reasons:
- Validation: As seen in the setter example, you can validate the data before it’s assigned to a property.
- Controlled Access: You can make an attribute read-only by not defining a setter method.
- Computed Properties: Properties can return computed values instead of just returning an attribute.
In summary, using @property
and @property.setter
allows for more controlled access to class attributes, enabling encapsulation, validation, and the potential for side effects or computed values.
class Example: def __init__(self): self._money = 0 @property def money(self): return self._money @money.setter def money(self, value): if value < 0: raise ValueError(\"This money cannot be negative.\") self._money = valueobj = Example()obj.money = 10print(f\"{obj.money = }\")obj.money = -10 # This will raise ValueError: This property cannot be negative.print(f\"{obj.money = }\")
输出:
实用用法:
1.5.4 常见内置高阶函数
map()
:
-
将一个函数应用于一个或多个可迭代对象的所有元素,并返回一个迭代器。
-
语法:
map(function, iterable, ...)
示例1:
# 使用map()函数numbers = [1, 2, 3, 4, 5]squares = list(map(lambda x: x**2, numbers))print(squares) # 输出: [1, 4, 9, 16, 25]
示例2:
def make_even(num): if num%2 == 1: return num+1 else: return numx = [551, 615, 910, 812, 453, 879, 152]y = []# 以下三种写法,输出的y都是一样的结果# 写法1:for num in x: y.append(make_even(num))# 写法2:y = [make_even(num) for num in x]# 写法3:y = list(map(make_even, x))print(y)
filter()
:
-
根据提供的函数过滤序列中的元素,只保留函数返回True的元素。
-
语法:
filter(function, iterable)
# 使用filter()函数even_numbers = list(filter(lambda x: x % 2 == 0, numbers))print(even_numbers) # 输出: [2, 4]`
zip()
:
-
将多个可迭代对象中的对应元素打包成一个个元组,返回一个迭代器。
-
语法:
zip(*iterables)
names = [\'Alice\', \'Bob\', \'Charlie\']ages = [25, 30, 35]pairs = list(zip(names, ages))print(pairs) # 输出: [(\'Alice\', 25), (\'Bob\', 30), (\'Charlie\', 35)]
enumerate()
:
-
将一个可迭代对象中的元素转换为枚举对象,包含索引和值。
-
语法:
enumerate(iterable, start=0)
for index, value in enumerate(numbers): print(index, value) # 输出: (0, 1), (1, 2), (2, 3), (3, 4), (4, 5)
any()
和 all()
:
-
any()
:如果可迭代对象中至少有一个元素为True,则返回True。 -
all()
:如果可迭代对象中的所有元素都为True,则返回True。 -
语法:
any(iterable)
和all(iterable)
truth_values = [True, False, True]print(any(truth_values)) # 输出: Trueprint(all(truth_values)) # 输出: False
1.6 类和实例 (Class & Instance)
每个类都有自己的属性(attribute)和方法(method),比如一个人的身高、体重和年龄,这些都是属性,而吃饭、说话和洗澡都是方法。
(要注意:在class外部定语的可执行函数叫做function,类内部的函数叫做方法method)
对象 : 具有行为和属性。在进行描述的时候,属性:多体现为名词。行为: 多体现为动词。
类 : 指的是一个类别。具有相同属性和行为的“对象”构成的一个整体。
类名使用**大驼峰(CamelCase/Pascal)**命名风格,首字母大写,私有类可用一个下划线开头。
类与对象之间的关系:
-
① 类是对象的抽象表现,对象是类的具体表现。
-
② 类是设计的蓝图,对象就是该蓝图设计出的具体产物。
类中可以定义“属性”和“行为”:
-
属性:通过定义变量来表示。
-
行为:通过方法(就是所说的\"函数\")来表示。
1.6.1 类的定义9
1.6.1.1 基本语法
用关键字 class 去定义一个类,如果没有指定父类,默认继承 object类
class Car(object): pass
或者
class Car: pass
这样,我们就定义了一个Car,车
学习参考高级定义案例(Referred from lib: ChromeDriverManager )
import osfrom typing import Optionalfrom webdriver_manager.core.download_manager import DownloadManagerfrom webdriver_manager.core.driver_cache import DriverCacheManagerfrom webdriver_manager.core.manager import DriverManagerfrom webdriver_manager.core.os_manager import OperationSystemManager, ChromeTypefrom webdriver_manager.drivers.chrome import ChromeDriverclass ChromeDriverManager(DriverManager): def __init__( self, driver_version: Optional[str] = None, # 从typing包导入,Optional[xx] 的意思是这里面参数类型可以是xx类, 也可以是None. name: str = \"chromedriver\",# 这里的str只是类型提示,并不是强制定义了这个参数的类型。 如果把这里的定义拿到其他地方去用,记得去掉末尾的comma,不然这个参数会被识别为tuple(因为带comma) url: str = \"https://chromedriver.storage.googleapis.com\", latest_release_url: str = \"https://chromedriver.storage.googleapis.com/LATEST_RELEASE\", chrome_type: str = ChromeType.GOOGLE, download_manager: Optional[DownloadManager] = None, cache_manager: Optional[DriverCacheManager] = None, os_system_manager: Optional[OperationSystemManager] = None ): super().__init__( download_manager=download_manager, cache_manager=cache_manager, os_system_manager=os_system_manager ) self.driver = ChromeDriver( name=name, driver_version=driver_version, url=url, latest_release_url=latest_release_url, chrome_type=chrome_type, http_client=self.http_client, os_system_manager=os_system_manager )
1.6.1.2 组成结构
1.6.1.2.1 类属性/类变量
class Car: wheels = 4
为什么要叫类属性呢,因为这个属性是和类绑定的,并不是和实例绑定的。如wheels = 4
这个属性是所有车共有的,并不是某个车特殊拥有的属性。
1.6.1.2.2 self
参数1011
self在定义时需要定义,但是在调用时会自动传入。
self的名字并不是规定死的,但是最好还是按照约定使用self。
self总是指向调用时的类的实例。
-
Python中为何要有self呢?
因为在定义类时,我们需要东西去指定这个类所创建的对象,这样才能调用一系列的方法。
而在定义类时,我们是没有对象的,类都还在定义中哪里去创建对象,所以这时我们规定统一用一个self来指定类的实例,这样我们就才能定义一些类的方法。
self只有在类的方法中才会有,独立的函数或方法是不必带有self的。self在定义类的方法时是必须有的,虽然在调用时不必传入相应的参数。
示例:
def blitme(self): self.screen.blit(self.image,self.rect) # 根据self.rect 指定的位置将image绘制到screen(窗口)上
因为我们要调用 blit 方法,对谁调用?对一个类创建的实例调用,但现在还没创建实例,我们就暂且用 self 代替之,来编写类的代码。
-
self可以更换为其他参数名吗?
self名称不是必须的,在python中self不是关键词,你可以定义成a或b或其它名字都可以。
但是约定成俗(为了和其他编程语言统一,减少理解难度),最好不要搞另类,会不太好明白
-
super()
def __init__(self): super().__init__()
1.6.1.2.3 类方法12
类方法的定义是有顺序的,最好按照此顺序定义,方便理解和阅读
私有方法可以用在 classmethod
和 staticmethod
中
-
__init__
方法由于类可以起到模板的作用,因此,可以在创建实例的时候,把一些我们认为必须绑定的属性强制填写进去。(实例变量)
__init__(self,参数)
和__init__(self)
的主要区别:__init__(self,参数)
: 可以是一个空结构,当有输入进来的时候再添加相应的数据;__init__(self)
: 则必须传值,不予许为空值(可以通过 ->None 改成可选值)
例: 定义一个矩形的类,目的是求周长和面积
__init__
是类构造方法,也属于实例方法的一种如果不用
__init__
方法, import进的参数是不会被存为内部参数的class Rectangle(): def getPeri(self,a,b): return (a + b)*2 def getArea(self,a,b): return a*brect = Rectangle()print(rect.getPeri(3,4))print(rect.getArea(3,4))print(rect.__dict__)
结果
1412{}
我们在类中并没有定义 init() 方法,但是也能够得到类似的要求,结果返回了矩形实例rect的周长及面积。
但是,我们通过 print(rect.dict) 来看这个实例的属性,竟然是空的,我定义了一个矩形,按理来说它的属性应该是它的长、宽。
但是它竟然没有,这就是没有定义 init() 的原因了。
因此需要在类中定义 init() 方法,方便创建实例的时候,需要给实例绑定上属性,也方便类中的方法(函数)的定义。
class Rectangle(): def __init__(self,a,b): self.a = a self.b = b def getPeri(self): return (self.a + self.b)*2 def getArea(self): return self.a * self.brect = Rectangle(3,4)print(rect.getPeri())print(rect.getArea())print(rect.__dict__)
结果
1412{\'a\': 3, \'b\': 4}
从上面的结果可以看到定义完
init()
后,创建的每个实例都有自己的属性,可以直接调用类中的函数。 -
魔术方法 magicmethod
前后有2个下划线
-
类方法
classmethod
classmethod
主要用来access Class的一些方法或者是access/set Class的属性/变量, 比较常用因可以在构造函数中避免硬编码,在构造函数中出现频率很高
-
需要
@classmethod
装饰器 -
参数必需:
cls
-
实例化才能调用: 否
类方法的参数以
cls
开头,类似__init__
里的参数 self, 调度时用 cls.xxx -
-
静态方法
staticmethod
staticmethod
本质上类似于一个隶属于类的函数,甚至可以放在类的外面,放在类内通常是为了更好的封装。但很多时候也可以直接放在类外面使用,因此使用频率较低static method并无法直接access类或者函数的属性
-
需要
@staticmethod
装饰器 -
参数必需:无
-
实例化才能调用: 否
在 @staticmethod 中如果要调用到这个类的一些属性方法,只能直接类名.属性名或类名.方法名。
而 @classmethod 因为持有 cls 参数,可以来调用类的属性,类的方法,实例化对象等,避免硬编码。
-
-
私有方法
privatemethod
用一个下划线做前缀描述符,就被认为是私有的。
其实也是可以调用的,只是我们通常看到有下划线就不调用而已。
-
实例方法
instancemethod
⭐️可以访问
instance
的一些数据,可以做一些实际的业务操作。也是最常见的类方法-
参数必需:
self
-
实例化才能调用: 是
-
-
抽象方法
abstractmethod
主要用于接口,比较少见
无法实例化
构造函数时
classmethod
的作用:如图,当新建一个类 WorkCalendar,希望继承Calendar的时候,如果用
classmethod
, 则返回的是新建的 WorkCalendar类,而硬编码的staticmethod只能返回Calendar 类。
综合示例:
class Car: # 类属性/类变量 wheels = 4 # __init__ 方法 def __init__(self, name, color, price, whenmanufactured) -> None: # `-> None` 详见 ## 1.10类型提示 self.name = name self.brand = name.split()[0] self.model = name.split()[1] self.color = color self.price = price self.whenmanufactured = whenmanufactured print(\'Your car %s is recorded successfully.\' % (name)) # magicmethod def __setitem__(self, key, value): self.__dict__[key] = value # 当赋值时,直接将value映射到键key上 if key == \'name\': # 如果改的key是name,那么需要刷新下brand和model self.brand = value.split()[0] self.model = value.split()[1] self.__getitem__(key) def __getitem__(self,key): # 如果在类中定义了__getitem__()方法,那么它的实例对象(假设为P)就可以这样P[index]取值或者这样P[key]取值。当实例对象做P[index/key]运算时,就会调用类中的__getitem__()方法。 currentvalue = self.__dict__[key] # Car 这个 class 里没有一个key叫key,所以需要先用 __dict__ 改成 dict, 再用索引的方式调用 print(\"\") print(\"Hi, you just changed something, please check:\") print(\"New value of property \'{0}\' is \'{1}\'\".format(key,currentvalue)) @classmethod def _detail(cls,name): return cls._show_detail(cls,name) @staticmethod def is_luxury(price): if price >= 1000000: return True else: return False #私有方法 privatemethod ## 上面的类方法和静态方法也用了私有方法的定义 def _insurance_alert(self): if self.insurance == True: return \'This car is under insurance.\' else: return \'This car is out of insurance.\' def _test_(self): return type(self.whenmanufactured) # 实例方法 instancemethod def drive(self): return \"Car \'%s\' drived for a while.\" % (self.name) def stop(self): return \"Car \'%s\' stopped.\" % (self.name) def get_car_age(self): car_age = 2023 - self.whenmanufactured if car_age > 5: self.insurance = False else: self.insurance = True print(\"\") print(\'This car is {} years old\'.format(car_age), end=\". \") return self._insurance_alert() def check_time_intput_type(self): return Car._test_(self) def modify_car_info(self): newkey = input(\'Which propery do you want to change? \') newvalue = input(\'What should be the new value of this property? \') return self.__setitem__(newkey,newvalue)##############car_b = Car(\'Porsche 911\', \'Red\', \'2,000,000\',2019)print(car_b.check_time_intput_type()) # 功能与 print(car_b._test_()) 相同print(\"Test part done\")print(car_b.get_car_age())print(car_b.drive())print(car_b.stop())car_b.modify_car_info()
1.6.2 调用类
class Car: # __init__ 方法 def __init__(self, name, color, price, whenmanufactured): self.name = namecar_b = Car(\'Porsche 911\')
1.6.3 继承和多态(Inheritance,Polymorphism)13
1.6.3.1 继承(Inheritance)
在面向对象编程中,当我们已经创建了一个类,而又想再创建一个与之相似的类,比如添加几个方法,或者修改原来的方法,这时我们不必从头开始,可以从原来的类派生出一个新的类,我们把原来的类称为父类或基类,而派生出的类称为子类,子类继承了父类的所有数据和方法。
让我们看一个简单的例子,首先我们定义一个 Animal 类:
class Animal(): def __init__(self, name): self.name = name def greet(self): print(\'Hello, I am %s.\' % self.name)
现在,我们想创建一个 Dog 类,比如:
class Dog(): def __init__(self, name): self.name = name def greet(self): print(\'WangWang.., I am %s. \' % self.name)
可以看到,Dog 类和 Animal 类几乎是一样的,只是 greet
方法不一样,我们完全没必要创建一个新的类,可以直接创建子类(child class)来继承父类Animal:
class Dog(Animal): def greet(self): print(\'WangWang.., I am %s. \' % self.name)
Dog 类是从 Animal 类继承而来的,Dog 类自动获得了 Animal 类的所有数据和方法,而且还可以对从父类继承来的方法进行修改,调用的方式是一样的:
animal = Animal(\'animal\')animal.greet()dog = Dog(\'dog\')dog.greet()
我们也可以在Dog 类中添加新的方法:
class Dog(Animal): def greet(self): print(\'WangWang.., I am %s. \' % self.name) def run(self): print(\'I am running!\')dog = Dog(\'dog\')dog.greet()
1.6.3.2 多态(Polymorphism)
多态的概念其实不难理解,它是指对不同类型的参数进行相同的操作,根据对象(或类)类型的不同而表现出不同的行为。继承可以拿到父类的所有数据和方法,子类可以重写父类的方法,也可以新增自己特有的方法。有了继承,才有了多态,这样才能实现为不同的数据类型的实体提供统一的接口。
class Animal(): def __init__(self, name): self.name = name def greet(self): print(f\'Hello, I am {self.name}.\')class Dog(Animal): def greet(self): print(f\'WangWang.., I am {self.name}.\')class Cat(Animal): def greet(self): print(f\'MiaoMiao.., I am {self.name}\')def hello(animal): animal.greet()
可以看到,cat
和 dog
是两个不同的对象,对它们调用 greet
方法,它们会自动调用实际类型的 greet
方法,作出不同的响应:
dog = Dog(\'dog\')hello(dog)cat = Cat(\'cat\');hello(cat)
1.6.3.3 super()用法
14
概述
super() 是python 中**调用父类(超类)**的一种方法,在子类中可以通过super()方法来调用父类的方法。
超类: 是指 2层以上的继承关系,假如 C类继承B类,B类由继承A类,那么A类就是C类的超类
作用:
- 在继承中,让代码维护更加简单
- 解决多继承带来的重复调用(菱形继承)、查找顺序(MRO)问题
语法:
super(type[, object-or-type])
参数:
type – 类。
object-or-type – 类,一般是 self
Python 3 和 Python 2 的另一个区别是: Python 3 可以使用直接使用 super().xxx 代替 super(Class, self).xxx :
1.6.3.3.1 super的使用
通过super() 来调用父类的__init__ 构造方法:
class Person(): def __init__(self): print(\'我是Peson的__init__构造方法\')class Student(Person): def __init__(self): super().__init__() print(\'我是Student的__init__构造方法\')stu = Student()-----------------------------------------我是Peson的__init__构造方法我是Student的__init__构造方法
1.6.3.3.2 通过super() 来调用与子类同名的父类方法
-
单继承
在单继承中 super 就像大家所想的那样,主要是用来调用父类的方法的。
class A: def __init__(self): self.n = 2 def add(self, m): print(\'self is {0} @A.add\'.format(self)) self.n += mclass B(A): def __init__(self): self.n = 3 def add(self, m): print(\'self is {0} @B.add\'.format(self)) super().add(m) self.n += 3b = B()b.add(2)print(b.n)
我们执行以上代码,得到的输出如下:
self is <__main__.B object at 0x106c49b38> @B.addself is <__main__.B object at 0x106c49b38> @A.add8
这个结果说明了两个问题:
1、super().add(m) 确实调用了父类 A 的 add 方法。
2、super().add(m) 调用父类方法 def add(self, m) 时, 此时父类中 self 并不是父类的实例而是子类的实例, 所以 b.add(2) 之后的结果是 5 而不是 4 。补充说明:
MRO—方法搜索顺序
- MRO是method resolution order,主要用于在对继承是判断方法、属性的调用路径【顺序】,其实也就是继承父类方法时的顺序表。
- Python中针对类提供了一个内置属性__mro__可以查看方法的搜索顺序
class C(A,B):passprint(C.__mro__)out:(<class \'__main__.C\'>,<class\'__main__.A\'>,<class\'__main__B\'>,<class \'object\'>)
在搜索方法时,是按照__mro__的输出结果从左到右的顺序查找的
- 如果当前类中找到方法,就直接执行,不再搜索
- 如果没有找到,就查找下一个类中是否有对应的方法,如果找到,就直接执行,不再搜索
- 如果找到最后一个类,还是没有找到方法,程序报错
-
多继承
在多继承中,会涉及到一个MRO(继承父类方法时的顺序表) 的调用排序问题。即严格按照MRO 顺序执行super方法
class A: def __init__(self): self.n = 2 def add(self, m): print(\'self is {0} @A.add\'.format(self)) self.n += mclass B(A): def __init__(self): self.n = 3 def add(self, m): print(\'self is {0} @B.add\'.format(self)) super().add(m) self.n += 3class C(A): def __init__(self): self.n = 4 def add(self, m): print(\'self is {0} @C.add\'.format(self)) super().add(m) self.n += 4class D(B, C): def __init__(self): self.n = 5 def add(self, m): print(\'self is {0} @D.add\'.format(self)) super().add(m) self.n += 5d = D()d.add(2)print(d.n)out:self is <__main__.D object at 0x10ce10e48> @D.addself is <__main__.D object at 0x10ce10e48> @B.addself is <__main__.D object at 0x10ce10e48> @C.addself is <__main__.D object at 0x10ce10e48> @A.add19
- 同样,不管往上调用几次,调用父类方法中 self 并不是父类的实例而是子类的实例,在上例中都是D的实例化对象
- D.mro() == [D,B, C, A, object] ,多继承的执行顺序会严格按照mro的顺序执行。
- 整体的调用流程图如下:
其它:
1.super().__init__相对于类名.init,在单继承上用法基本无差别
2.但在多继承上有区别,super方法能保证每个父类的方法只会执行一次,而使用类名的方法会导致方法被执行多次。
3.多继承时,使用super方法,对父类的传参数,应该是由于python中super的算法导致的原因,必须把参数全部传递,否则会报错
4.单继承时,使用super方法,则不能全部传递,只能传父类方法所需的参数,否则会报错
1.6.4 Iterators
在某些情况下,我们希望实例对象可被用于for...in
循环,这时我们需要在类中定义__iter__
和__next__
方法。其中,__iter()__
方法返回迭代器对象本身__next()__
方法返回容器的下一个元素,在没有后续元素时会抛出StopIteration
异常。(Python 的 for
循环实质上是先通过内置函数 iter()
获得一个迭代器,然后再不断调用 next()
函数实现的。)
class Fib(): def __init__(self): self.a, self.b = 0, 1 def __iter__(self): return self def __next__(self): self.a, self.b = self.b, self.a + self.b return self.afib = Fib()for i in fib: if i > 10: break print(i)# 1, 1, 2, 3, 5, 8
1.6.5 访问限制 underscore
在某些情况下,我们希望限制用户访问对象的属性或方法,也就是希望它是私有的,对外隐蔽。比如,对于上面的例子,我们希望 name
属性在外部不能被访问,我们可以在属性或方法的名称前面加上两个下划线,即 __
,以下是对之前例子的改动:
class Animal(): def __init__(self, name): self.__name = name def greet(self): print(f\'Hello, I am self.__name.\')animal = Animal(\'a1\')animal.__name # error
需要注意的是,在 Python 中,以双下划线开头,并且以双下划线结尾(即 __xxx__
)的变量是特殊变量,特殊变量是可以直接访问的。所以,不要用 __name__
这样的变量名。另外,如果变量名前面只有一个下划线_,表示此变量不要随便访问,虽然它可以直接被访问。
1.6.6 模块调用
有时候一个模块中放不了许多的类,那么我们就要把一些类放入其他的模块中,当我们需要那些类的时候,只需要调用对应模块即可。创建一个模块很简单,只要把代码保存在一个文件中,然后加上后缀.py即可。你可以任意命名文件名,但是必须以.py为后缀。以下我们在animal.py文件中定义了多个不同的类:
# animal.pyclass Animal(): def __init__(self, name): self.name = name def greet(self): print(f\'Hello, I am {self.name}.\')class Dog(Animal): def greet(self): print(f\'WangWang.., I am {self.name}.\')class Cat(Animal): def greet(self): print(f\'MiaoMiao.., I am {self.name}\')
如果要在其他文件中调用单个类的不同方法,使用import语句即可:
from animal import Animalanimal = Animal(\'animal\')animal.greet()
调用多个类:
from animal import Dog, Catdog = Dog(\'duoduo\')dog.greet()cat = Cat(\'Kitty\')cat.greet()
其他调用方法:
# importing an entire moduleimport animalcat = animal.Cat(\'Kitty\')# import all classes from a modelfrom animal import *cat = Cat(\'Kitty\')# Using Aliases from animal import Cat as Ccat = C(\'Kitty\')
1.6.6.1 Python标准库(Python Standard Library)
现在我们已经对函数和类有基础的认识了,我们来聊聊如何使用别人编写好的库,Python有自己的标准库,我们下载Python的时候,它们已经被默认安装了,其中有很多现成的模块供我们调用,我们试一下用random模块做做一些随机数操作:
from random import randint, choice# Generate a random number between 1 and 6print(randint(1, 6))players = [\'alice\', \'david\', \'charles\', \'michael\']# choose a randomly chosen elementrandom_pick = choice(players)print(random_pick)
1.6.6.2 PIP包管理器
有的时候,我们想要使用别人编写好的模块,我们可以通过包管理器下载别人的包(package),包是由很多module组成的,来实现某种功能。库(library)是抽象概念,也可以是各种模块组成。而Python中最流行的包管理器就是pip。pip3的安装请大家自行搜索,网上有很多的教程,以下我会使用一个和图像处理相关的模块。
PIL(Python Image Library)是Python中的标准图像处理库。PIL功能强大,而且API非常简单易用。由于PIL仅支持到Python 2.7,加上年久失修,于是一群志愿者在PIL的基础上创建了兼容的版本,名字叫Pillow,支持最新Python 3.x,又加入了许多新特性,因此,我们可以直接安装使用Pillow。以下是安装pillow的命令行:
pip3 install pillow
然后我们可以创建一个Python,使用PIL的模块,写出一段可以把照片弄模糊的代码:
from PIL import Image, ImageFilter# Open an imageim = Image.open(\'test.jpg\')# Use bluring filterim2 = im.filter(ImageFilter.BLUR)im2.save(\'blur.jpg\', \'jpeg\')
包管理器让我们可以直接使用前人的轮子,简化我们的开发过程。
1.6.7 类的编写规范 Styling Classes
类的名字最好使用骆驼命名法(CamelCase),也就是让每个单词的第一个字母大写,不使用下划线分割单词。实例和模块的名字最好都使用Snake case,也就是所有字母都小写,然后使用下划线分割。当然这不是强制的,但这是工业界比较合理的命名规范,大家可以参照一下Google的Python代码规范:http://google.github.io/styleguide/pyguide.html
您可以使用空行来组织代码,但不要过度使用它们。在一个类中,您可以在方法之间使用一个空行,而在一个模块中,您可以使用两个空行来分隔类。
如果需要从标准库和编写的模块中导入模块,请首先将标准库模块的调用语句写在最前面。然后添加一个空行,再调用自己编写的模块。在具有多个import语句的程序中,此约定使查看程序中使用的不同模块的来源更加容易。
1.6.7 课后练习
创建一个Car类,其中包含brand,model,year属性。这个类初始化的时候必须要传入品牌名字(比如Subaru),初始化的时候车的型号和生产年会被默认设定为’xxx’和0,Car类还有内置方法可以修改车型号和生产年,还有可以打印出完整车信息的方法。
请根据要求定义出这个完整的类,并在另一个文件调用此类,创建一个实例,然后使用内置方法修改车的型号和生产年,最后使用类的方法打印出车的完整信息。
# car.pyclass Car: brand, model, year def __init__(self, brand): self.brand = brand model = \'xxx\' year = 0 def set_model(self, model): self.model = model def set_year(self, year): self.year = year def get_info(self): print(f\"Brand: {self.brand}, Model: {self.model}, Year: {self.year}\")
# main.pyfrom car import Carnew_car = Car(\"Subaru\")new_car.set_model(\"WRX\")new_car.set_year(2014)new_car.get_info()
1.7 模块 (Module) & 包(Package)15
1.7.1 模块(Module)
Module是Python运行时的概念,其本质是一个Python Object。
推荐使用蛇形命名法(snake_case)
假设我们有 hello.py
def print_hello(): print(\"hello\")
同文件夹下的其他py
文件就通过如下方法import
-
import
import hellohello.print_hello()
-
from import
from hello import print_helloprint_hello()
-
from import as
from hello import print_hello as p_hellop_hello()
1.7.2 包(Package)
Python 3.3+已经支持不带
__init__.py
的包了,关于__init__.py
的详解请参考16
包其实就是一种特殊的模块,只是相对于模块多了__path__
的属性,但是package往往对应的是一个文件夹。所以一个 package 里面可以有 sub-package 或者是 module。
通常一个工程不可能只有一层目录结构,并且也不会一个一个 path 去 append 到sys里,常用的做法就是包,一个目录及其子目录组成的一个包(可以看作一个库)。
当我们 import 一个 package 的时候,它会查看这个package文件下有没有 __init__.py
文件,如果没有的话,它就不会运行额外的代码;如果有的话就会运行__init__.py
文件。
示例:有一个PackageTest
文件夹,包含 MyPackages
和其底下的modules_1
, modules_2
这三个文件夹,其中modules_1
下又有sub_modules_1
文件夹。
相关文件在
..\\..\\Main\\Python\\MyProjects\\Basic_Knowledge\\Basic\\Leon_Python_Basic_Operations\\PackageTest
路径下
PackageTest│ relative_module.py│ run_relative_import1.py│ run_relative_import2.py│ run_with_from_import.py│ run_with_import1.py│ run_with_import2_as.py│├─MyPackages│ │ run_relative_import3.py│ │ __init__.py│ ││ ├─modules_1│ │ │ module_1.py│ │ │ __init__.py│ │ ││ │ ├─sub_modules_1│ │ │ sub_module_1.py│ │ │ __init__.py│ │ ││ │ └─__pycache__│ │ module_1.cpython-311.pyc│ │ __init__.cpython-311.pyc│ ││ ├─modules_2│ │ module_2.py│ │ __init__.py│ ││ └─__pycache__│ __init__.cpython-311.pyc│└─__pycache__ relative_module.cpython-311.pyc
1.7.2.1 Absolute Import 绝对导入
run_with_from_import.py
import fibo # 隐式相对导入 from fibo import fibo1, fibo2 # 绝对路径导入 import fibo as fib # 重命名 from fibo import fib as fibonacci
1.7.2.2 Relative Import 相对导入
relative import 只能在 package 里面的module中使用,不能单独运行使用。并且这个 module 在被导入的时候,一定要跟着这个 package 一起被导入的。单独尝试运行一个 package 里面的 module 会导致relative import 出错
有的时候需要在一个 package 里的不同 module 之间相互引用,而这些module之间的相互关系是更容易被发现,或者说更稳定的。(package可能会改名字,或者package可能会换到另一个package里,都会导致module的绝对路径改变,这些情况都会导致绝对路径失效)
relative import 开头的.
表示当前同一目录下的**xxx
文件/文件夹**, ..
表示往上走一个package下的文件
相对导入==格式为 from .A import B 或 from …X import Y, . 代表当前包, … 代表上层包, … 代表上上层包==,依次类推。
原理:通过当前module的
__package__
变量,找到当前package的绝对路径,然后再import
from . import echo # 表示从当前文件所在package导入echo这个module from .. import formats # 表示从当前文件所在package的上层package导入formats这个子package或者moudle from ..filters import equalizer # 表示从当前文件所在package的上层package导入的filters这个子package或者子module中导入equalizer
相对导入基于当前模块的名称。由于主模块的名称始终为
\"__main__\"
,因此用作 Python 应用程序主模块的模块必须始终使用绝对导入。主模块所在文件夹不会被视作package,因此除了主模块外,与主模块处在同个文件夹的模块(也就是同级的模块)也必须使用绝对导入。17
The error message
\"ImportError: attempted relative import beyond top-level package\"
indicates that the relative import is attempting to go beyond the top-level package.This is a key concept: a top-level package is essentially the root of your Python package structure. When you run a Python script directly, the directory containing that script is treated as the top-level directory. Python does not allow relative imports to reach outside of this top-level package.
总结,尽量不要用relative import来从父级文件夹导入, 处理起来会比较复杂
测试案例:
importParent.py
:
from ..Lv1_module import lv1_funcdef main(): # Now you can use some_function here lv1_func()if __name__ == \"__main__\": print(\"importParent.py called\") main()
run importParent.py
:
from Level1.Level2 import importParentimportParent.main()
1.7.3 搜索路径
Python会按如下顺序进行搜索,一旦搜到同名module,则会停止搜索。所以我们需要避免与已有的module重名,优先级如下:
-
当前缓存
-
内置 / Built-in module
比如:
sys
,os
,math
-
sys.path
,包含以下3个部分,优先级如下:-
当前运行(脚本)路径会被临时添加进
sys.path
并列在第一位 -
Python自带package
比如:
asyncio
,multiprocessing
-
Site-Packages
也就是
pip install
下的package
可以通过以下命令查看当前
sys.path
有哪些路径import sysprint(sys.path)
可以在
sys.path
里手动加入你想import的路径:import syssys.path.append(\'/xxx/xxx\')import hellohello.print_hello()
也可以把当前文件夹加入搜索路径
import sysimport oscurrent_directory = os.path.dirname(os.path.abspath(__file__))sys.path.append(os.path.dirname(current_directory))
-
1.8 字符串处理
1.8.1 格式化输出1819
1. 占位符%
在字符串内部,%后紧跟占位符,有几个%?占位符,后面就跟几个变量或者值,用括号括起来,顺序要对应好。如果只有一个%?,括号可以省略。
\'Hello, %s\' % \'world\'print(\'Hi, %s, you have $%d.\' % (\'Michael\', 1000000))
%s
%d
%f
%x
常见占位符修饰
-
-
号 表示左对齐 -
.
号 表示小数点后位数 -
0
表示左边补零 -
%s
可以将任何数据转换为字符串 -
%%
表示%
print(\'%-2d - %02d\' % (3, 1))
表示3以左对齐占两个占位的形式输出,1以右对齐占两个占位的形式,并且前面填零补齐的形式输出。
print(\'%.2f\' % 3.1415926)
表示以小数点后两位的形式输出:3.14
\'Age: %s. Gender: %s\' % (25, True)
将任何数据转换为字符串
2. format()` 方式格式化输出
占位符方式的变形,使用{}作为占位符,.format代替了%,括号内的值依次传给占位符,另外,如果 {} 内传入索引,传参顺序可不按括号内顺序,具体使用方法如下:
name = \'Zhao san\'age = 18height = 185print(\'My name is {}, I am {} years old, my height is {}\'.format(name, age, height))print(\'My name is {1}, I am {0} years old, my height is {2}\'.format(age, name, height))print(\'Her name is {}, she is {} years old\'.format(\'Sally\',18))
3. f-string格式化20
python3.6新增的方式,使用 f’str’ 的格式,使用 {} 占位,要传入的变量直接写在 {} 内,具体用法如下:
name = \'Zhao san\'age = 18height = 185print(f\'My name is {name}, I am {age} years old, my height is {height}\')
self._metadata_key = f\"{self.get_os_type()}_{driver.get_name()}_{driver_version}\" \\ f\"_for_{browser_version}\"
num: int = 1_000_000print(num)num2: int = 1000000print(f\'{num2:_}\') # format with _print(f\'{num2:,}\') # format with ,
var: str = \'var\'print(f\'{var:<20}:\') # left alignprint(f\'{var:^20}:\') # center alignprint(f\'{var:>20}:\') # right alignprint(f\'{var:_>20}:\') # 左边用`_`填充print(f\'{var:|^20}:\') # 两边用`|`填充print(f\'{var:#>20}:\') # 右边用`#`填充
n: float = 1324.5678print(f\'{n:.2f}\') # 1324.57print(f\'{n:.2%}\') # 132456.78%print(f\'{n:.0f}\') # 1325print(f\'{n:,.3f}\') # 1,324.568print(f\'{n:_.3f}\') # 1_324.568
另外,使用{}的两种方式可以使用例如 {:2f} (format格式化), {height:2f} (f-string格式化)的方式来控制变量的输出效果。
f\'{}\'的最后加=
, 可以直接等效于 括号内的运算符直接打印 = 运算结果
a: int = 5b: int = 10my_var: str = \'Bob says hi\'print(f\'{a + b = }\') # = print(f\'a + b = {a + b}\')print(f\'{bool(a) = }\')print(f\'{my_var = }\')# you can even use similar way to print function
1.8.2 查找
.find()
返回的是第一个匹配的子串的下表位置,如果没有找到,返回-1
s = \"Hello World\"print(s.find(\"lo\"))print(s.find(\"rl\"))print(s.find(\"xx\"))
输出:
38-1
1.8.3 分隔
.split()
字符串按照某个字串进行分隔,返回分割后的列表
s = \"Hello World\"print(s.split(\"lo\"))
输出
[\'Hel\', \' World\']
1.8.4 大小写转换
.upper()
,.lower()
s = \"Hello World\"print(s.upper())print(s.lower())
输出:
HELLO WORLDhello world
1.8.5 截取
s = \"Hello World\"print(s[1:3])print(s[:5])print(s[3:])print(s[3:-1])
输出:
elHellolo Worldlo Worl
1.8.6 追加
s = \"Hello World\"s1 = \", Python\"print(s+s1)
输出
Hello World, Python
1.8.7 替换
.replace()
s = \"Hello World\"print(s.replace(\"World\",\"Python\"))
输出:
Hello Python
1.8.8 拼接/连接
Python中有.join()和os.path.join()两个函数,具体作用如下:
. join()
:将序列(也就是字符串、元组、列表、字典)中的元素以指定的字符连接生成一个新的字符串。
- 对string, 则会把每个字符都视为列表中的一个元素
- 对 list 和 tuple 相同
os.path.join()
:将多个路径进行拼接。或者称之为合并目录。
# Strings = \"Hello World\"output_s = \"*\".join(s)print(output_s)# List & Tuplelist = [\"Hello\", \"World\"]tuple = (\"Hello\", \"World\")output_list = \" and \".join(list)output_tuple = \" and \".join(tuple)print(output_list)print(output_tuple)# Dictdict_1={ \'I\':1, \'love\':2, \'Python!\':3}output_dict1 = \" and \".join(dict_1)print(output_dict1)# Path# 用2个斜杠来避免转义path_tmp1 = \"C:\\\\tmp\"path_tmp2 = \"sub_tmp\\\\1.txt\"import osoutput_path = os.path.join(path_tmp1, path_tmp2)print(output_path)
输出:
H*e*l*l*o* *W*o*r*l*dHello and WorldHello and WorldI and love and Python!C:\\tmp\\sub_tmp\\1.txt
1.8.9 反转
var[::-1]
s = \"Hello World\"list = [\"Hello\",\"World\"]print(s[::-1])print(list[::-1])
输出:
dlroW olleH[\'World\', \'Hello\']
1.9 文件读写
1.9.1 读文件
f = open(\"test.txt\")content = f.read()f.close() # 一定要close掉print content
调用了 read 接口,就读取了文件内容。不过我们通常不这么做。万一文件比内存还大,就会崩掉。正确的姿势如下:
f = open(\"test.txt\")while True:lines = f.readlines(10000) if not lines: break for line in lines: print line.strip()
readlines
中的这个参数,并不是行数,而是文件大小(字节数),所返回的必然都是完整的行数据,大多数清空下,返回的数据的字节数会稍微比指定的值大一点(除最后一次调用readlines
(10000)的时候)。通常清空下,Python会自动把用户指定的值调整成内部缓存大小的整数倍。这样并不是一下把整个文件读完,而是一部分一部分读取,不会崩掉。
1.9.2 写文件
f = open(\"test.txt\",\"w\")f.writelines([\"hhhhh\",\"lllll\"])f.close()
这样 test.txt 就被写入了如下内容:
hhhhhlllll
或者直接调用 write:
f = open(\"test.txt\",\"w\")f.write([\"hhhhh\",\"lllll\"])f.close()
1.9.3 追加写文件
f = open(\"test.txt\",\"a\")f.writelines([\"ooooo\",\"kkkkk\"])f.close()
这样,test.txt 内容变成了:
hhhhhllllloooookkkkk
1.10 类型提示(type hints / annotations)2122
1.10.1 类型提示是什么?
我们都知道Python是动态类型语言,不需要对变量的类型进行声明,一个变量也可以被赋值为不同的类型。
与之相对的是C/C++,java等静态语言,他们要求对变量和函数返回值必须进行严格的类型声明。
-
方法形参和返回值类型提示(type hints):
def add(x: int, y: int) -> int: result = x + y print(\'%s + %s = %s\' % (x, y, result)) return resultadd(1, 2)add(2.2, 3.3) # 编辑器提示类型错误add(\'3.4\', \'5.6\') # 编辑器提示类型错误
形参类型在变量后使用
:
表示,同时后面可以使用=
指定参数默认值,返回值类型使用->
指定。在方法的形参中,我们可以指定参数的类型为
int
,返回值也为int
。当我们调用方法并传入其他类型的实参时,编辑器会进行静态检查并进行提示。执行以上代码:
1 + 2 = 32.2 + 3.3 = 5.53.4 + 5.6 = 3.45.6
可见,对于
int
类型的入参,方法正常执行。而对于float
和str
类型的入参,编辑器会在静态检查时提示类型错误,但不会阻止错误类型的参数使用和运算。 -
变量注解(annotations):
name: str = \"Jack\"age: int = 20name = 30 # 编辑器提示类型错误age = \'21\' # 编辑器提示类型错误print(name)print(age)
在定义变量时也可以指定变量的类型。当给变量赋值其他类型数据时,编辑器会提示类型错误。但是,并不会阻止你这样做。代码仍可以正常运行。
3021
运行后可见,
name
虽然声明为str
,但让能被赋值为int
,age
变量亦然。 -
function里的参数注解(annotations):
def upper_everything(elements:list[str])->list[str]:return [element.upper()for element in elements]loud_list:list[str] = upper_everything([\'Mario\',\'James\',\'Sandra\'])loud_list:list[int] = upper_everything([\'Mario\',\'James\',\'Sandra\']) # 这一行不该使用,因为在定义的function里标注了此方法的列表元素应该为str
1.10.2 既然类型声明不能阻止变量使用其他类型赋值,那么其意义何在呢?
类型提示带来的好处有三个:
- 方便开发者阅读代码,提高代码易读性。
- 方便编辑器/IDE 对代码进行提示、自动补全和静态检查。
- 可通过 mypy (
pip install mypy
)进行类型检查。如对上面的脚本,使用mypy命令行工具进行检查:
D:\\code\\fast_api_learning>mypy type_hints.py type_hints.py:8: error: Argument 1 to \"add\" has incompatible type \"float\"; expected \"int\"type_hints.py:8: error: Argument 2 to \"add\" has incompatible type \"float\"; expected \"int\"type_hints.py:9: error: Argument 1 to \"add\" has incompatible type \"str\"; expected \"int\"type_hints.py:9: error: Argument 2 to \"add\" has incompatible type \"str\"; expected \"int\"type_hints.py:15: error: Incompatible types in assignment (expression has type \"int\", variable has type \"str\")type_hints.py:16: error: Incompatible types in assignment (expression has type \"str\", variable has type \"int\")Found 6 errors in 1 file (checked 1 source file)
1.10.3 支持的类型有哪些?
Python自带的int、float、str、bool、bytes、list、dict、tuple、set等基本数据类型和容器。
其他复杂的数据类型可以通过Python提供的typing库来实现,如List、Dict、Set、Tuple、Any、Union等,同时还可以使用 [] 指定容器中的数据类型,如List[int]。
typing库文档:https://docs.python.org/3/library/typing.html
有关typing库中的常用类型,会在下面结合 FastAPI 的使用给出示例。更多详细信息请阅读typing的文档进行学习。
1.10.4 如何在不支持部分Type Annotation的Code Editor中增加提示
pip install mypy
2. Python环境配置(Windows)
1. pip安装以及换源[^31]
安装库
pip install <lib1> <lib2>
1. 查看当前源
查看自己当前的pip源;目前较新的python中,pip已经随Python一起安装了;所以我们可以直接使用;
// 这个查询方式适用其他系统pip config list
2. 关于whl文件安装(离线安装)
在更新过程中,有时候如果网络较差时,会提示time out,此时我们可以从官网下载对应的whl文件就行;这也是我们安装包的方式之一;
whl文件下载地址:https://pypi.org/project/pip/#files
// 执行语句进行whl文件的安装python -m pip install pip-24.1.1-py3-none-any.whl
3. 换源
1. 推荐源
清华大学:https://pypi.tuna.tsinghua.edu.cn/simple中国科技大学 https://pypi.mirrors.ustc.edu.cn/simple/华中理工大学:http://pypi.hustunique.com/山东理工大学:http://pypi.sdutlinux.org/阿里云:http://mirrors.aliyun.com/pypi/simple/豆瓣:http://pypi.douban.com/simple/
2. 临时换源
直接在我们的pip命令最后加上-i https://mirrors.aliyun.com/pypi/simple/
即可;
# 终端下载指令:pip install flask -i https://mirrors.aliyun.com/pypi/simple/
3. Windows换源
pip的配置文件通常位于用户目录下的pip文件夹中。如果该文件夹不存在,则需要手动创建;
在C:\\Users\\\\AppData\\Roaming\\
目录下新建名为pip的文件夹(如果已存在则无需新建),然后在此文件夹中创建或编辑pip.ini
文件
pip.ini的内容:
[global] index-url = https://pypi.tuna.tsinghua.edu.cn/simple
4. MacOS / Linux换源
修改对应pip配置文件
在MacOS中,pip的配置文件通常在该目录下;我们可以通过修改pip.conf
来实现修改源;
// 进入目录~/.config/pip// vi pip.conf
添加如下内容,保存即可。
2. Python 安装环境与运行环境打包 (requirements.txt) 23
Requirements.txt 文件:
-
这个文件是一个用于一次性保存在python包的一个文件,通常我们在下载包时,例如pytest包, 会使用命令 pip install pytest 来下载包
-
但是会有一个问题, 就是如果当你的项目需要下载非常多的包时, 你得不停的用 pip
命令一个个去下载,有没有一种方法可以将项目所需要的包一次性下载呢?requrements.txt 文件就是干这个的, 你只需要将包名==版本号 写入文件中即可!特点:
1)批量导出当前开发环境的包信息
2)批量安装依赖环境
3)一般用pip安装工具安装单个包,多个包的安装可以手写requirements文件,进行多包安装,更快效率更高!
\"\"\"# 镜像网站可以使下载库的速度更加快!清华大学:https://pypi.tuna.tsinghua.edu.cn/simple中国科技大学 https://pypi.mirrors.ustc.edu.cn/simple/华中理工大学:http://pypi.hustunique.com/山东理工大学:http://pypi.sdutlinux.org/阿里云:http://mirrors.aliyun.com/pypi/simple/豆瓣:http://pypi.douban.com/simple/# 终端下载指令:pip install -i https://....//simple numpy# 创建虚拟环境conda create -n env-py38 python=3.8\"\"\"
1. 环境打包
1. 方式一:freeze
此种方式会将环境中所有的包都打包进去,适合单虚拟环境(每个项目都处在不同的虚拟环境里,这个环境里是这个项目所需要的所有包)
创建requirements.txt,把包写入到requirements.txt中
# 命令:pip freeze > requirements.txt
如果得到的 requirements.txt 中,包后面有“@…”(如下所示),则把所有“@…”内容删掉
altgraph @ file:///home/linux1/recipes/ci/altgraph_1611055153312/workBottleneck @ file:///C:/Windows/Temp/abs_3198ca53-903d-42fd-87b4-03e6d03a8381yfwsuve8/croots/recipe/bottleneck_1657175565403/workcertifi @ file:///C:/b/abs_4f5wo627a3/croots/recipe/certifi_1663615677642/work/certificycler @ file:///tmp/build/80754af9/cycler_1637851556182/worket-xmlfile==1.1.0fonttools==4.25.0future==0.18.2importlib-metadata @ file:///C:/ci/importlib-metadata_1648562631189/workjoblib==1.2.0kiwisolver @ file:///C:/ci/kiwisolver_1653274189334/workmacholib @ file:///home/linux1/recipes/ci/macholib_1611055836792/workmatplotlib @ file:///C:/ci/matplotlib-suite_1660169687702/work
2. 方式二:pipreqs
此类方法推荐使用,他只是总结程序中所用到的包,并不是电脑中安装的所有的包
-
安装pipreqs
pip install pipreqs
-
命令生成
(1)先 cd 到程序所在的文件夹位置
(2)执行下面的语句,Windows下得加encoding=utf8,不然会出现编码问题pipreqs ./ --encoding=utf8
说明: 把 “我” 这个文件下的程序所使用的的包进行总结(包括子文件里的程序)写入到requirements.txt,requirements.txt这个文件生成在当前路径
2. 安装环境
1. 在线安装
pip install -r requirements.txt
当在新的项目中需要使用之前的环境时,可以拷贝该文件导入新项目,在控制台安装。当然pycharm也会自动检测该文件,可以直接导入
2. 离线安装
-
将requirements.txt中导入的包离线下载到packagesdir 文件夹
先执行第一行,再执行第二行
pip wheel -w DIR -r requirements.txtpip download -d DIR -r requirements.txt
-
安装离线的包
pip install --no-index --find-links=DIR -r requirements.txt
解释:
DIR:离线包(temp)的路径(temp的路径,例如:D:\\360MoveData\\Users\\Administrator\\Desktop\\我\\packagesdir)
安装requirements.txt中的包,并且在D:\\360MoveData\\Users\\Administrator\\Desktop\\我\\packagesdir这个文件夹里取离线的包
3. 卸载环境
把配置的虚拟环境中,卸载已经安装的package,如numpy,pandas,matplotlib,Pyqt5等,目的是减小虚拟环境的大小
pip uninstall -r requirements.txt
3. Python脚本轻松变exe可执行文件(auto-py-to-exe)24
1. 什么是auto-py-to-exe
auto-py-to-exe 是一个用于将Python程序打包成可执行文件的图形化工具。本文就是主要介绍如何使用 auto-py-to-exe 完成 python 程序打包。auto-py-to-exe 基于 pyinstaller ,相比于 pyinstaller ,它多了 GUI 界面,用起来更为简单方便
2. 安装 auto-py-to-exe
首先我们要确保我们的 python 环境要大于或等于 2.7 然后在 cmd 里面输入:
pip install auto-py-to-exe
输入完成之后,pip 就会安装 auto-py-to-exe 包了。安装完成之后,我们就可以在 cmd 输入:
auto-py-to-exe
来启动 auto-py-to-exe 程序了。
出现上述图片,auto-py-to-exe 就安装成功了。
3. auto-py-to-exe 部分选项介绍
在使用 auto-py-to-exe 打包 python 程序的时候,有许多配置选项需要我们去指定,能正确知道这些选项的作用是十分重要的。下面我将介绍其中一些重要的选项。
(1) Script Location
Script Location 主要是指定我们要打包的 python 文件
(2) Onefile
Onefile 下有两个选项,分别是:One Directory 和 One File
- 如果选择 One Directory ,那么程序打包完成后会是一个文件夹的形式展现
- 如果选择 One File ,那么程序打包完成后就一个 .exe 文件
(3) Console Window
Console Window 主要设置打包程序运行时,是否出现控制台
- Console Based : 当打包的程序运行时会显示一个控制台界面
- Window Based (hide the console) : 会隐藏控制台界面,主要用于带有 GUI 的 python 程序打包
(4) Icon
用于指定打包程序的图标
4. auto-py-to-exe 实战
本节主要以一个计算器程序来介绍如何使用 auto-py-to-exe 来打包程序。
auto-py-to-exe 打包程序主要分 3 部分,分别是:
- 打开 auto-py-to-exe
- 配置打包选项
- 查看打包效果
2.2.4.1 打开 auto-py-to-exe
打开 cmd ,输入:auto-py-to-exe 打开 auto-py-to-exe 后,我们就要进行配置选择了。
2.2.4.2 配置打包选项
计算器程序,大家可以到 GitHub 去下载,地址是:
https://github.com/pythonprogrammingbook/simple_calculator
在打包时,我们要进行的配置主要有:
- Script Location
- Onefile
- Console Window
Script Location 选择程序的主程序,在计算器项目里,我们选择的是 main.py
Onefile 选择 One File ,因为一个文件看起来比较简洁
由于计算器项目带有 GUI ,所以 Console Window 选择 Window Based (hide the console) ,
Icon 选择一个 ico 文件,此处不是必须操作,可以不设置
如果程序里面有自己的模块,我们必须把模块的目录添加到 Additional Files 里面。不然会出现 Failed to execute script XXX 错误
在计算器程序里面我们所有的模块都在 calculation 目录下,所有我们需要将 calculation 路径添加到 Additional Files 里面
配置完成之后点击 CONVERT .PY TO .EXE 按钮
这样我们就完成一个计算器项目的打包。
3. 查看打包效果
程序完成打包后,我们可以点击 OPEN OUTPUT FOLDER 按钮,然后就会打开打包文件的路径。
在打包文件目录中,我们可以看到一个 main.exe 文件,这就是我们打包文件。
点击 main.exe ,就可以看到一个计算器程序了。
至此,打包工作圆满完成。
5. 总结一下
这只是介绍最简单的 python 程序打包,如果想对复杂的程序进行打包,上面的配置肯定是不行的。
如果想更加深入的了解 auto-py-to-exe ,建议去研究一下 pyinstaller 。auto-py-to-exe 是基于 pyinstaller 的,研究 pyinstaller ,将会对我们深入使用 auto-py-to-exe 有非常明显的效果。
2.3 虚拟环境
1. venv()
- 虚拟环境创建比较麻烦,每次都要激活
- 安装依赖时没有声明,需要自己写在声明里再导入
推荐集中安装在某个envs文件夹下,方便管理,也不至于项目删除的时候把环境也删了
1. 安装:
打开对应项目目录,打开terminal运行
# python -m venv python -m venv venv1
此时会在该目录下新建一个虚拟环境venv1
2. 激活虚拟环境
Terminal输入
.\\ven1\\Scripts\\activate
此时terminal当前行的左边会显示 (ven1)
即表示激活成功
3. 退出虚拟环境
deactivate
4. 配置VSCode自动识别虚拟环境
打开VSCode设置(快捷键CTRL+,
),右上角点击打开settings.json
下面新建一个键值对
\"python.venvPath\": \"\"
斜杠可能需要转义,
\\
改成\\\\
或者/
此时VSCode的python: Select Interpreter就可以识别到多个虚拟环境的不同python
2. pipenv(需安装)
- 安装依赖会自动记录
- 安装环境和激活简单
3. 常用语法
1. if __name__ == \'__main__\'
25
1.摘要
# __main__.pydef main(): print(\"This is the main function of my_package\")if __name__ == \"__main__\": main()
通俗的理解__name__ == \'__main__\':
假如你叫小明.py
,
在朋友眼中,你是小明(__name__ == \'小明\'
);
在你自己眼中,你是你自己(__name__ == \'__main__\'
)。
if __name__ == \'__main__\'
的意思是:
当.py
文件被直接运行时,if __name__ == \'__main__\'
之下的代码块将被运行;当.py文件以模块形式被导入时,if __name__ == \'__main__\'
之下的代码块不被运行。
2. 程序入口
对于很多编程语言来说,程序都必须要有一个入口,比如C,C++,以及完全面向对象的编程语言Java,C#等。如果你接触过这些语言,对于程序入口这个概念应该很好理解,C,C++都需要有一个main函数作为程序的入口,也就是程序的运行会从main函数开始。同样,Java,C#必须要有一个包含Main方法的主类,作为程序入口。
而Python则不同,它属于脚本语言,不像编译型语言那样先将程序编译成二进制再运行,而是动态的逐行解释运行。也就是从脚本第一行开始运行,没有统一的入口。
一个Python源码文件(.py)除了可以被直接运行外,还可以作为模块(也就是库),被其他.py文件导入。不管是直接运行还是被导入,.py文件的最顶层代码都会被运行(Python用缩进来区分代码层次),而当一个.py文件作为模块被导入时,我们可能不希望一部分代码被运行。
1. 一个.py文件被其他.py文件引用
假设我们有一个const.py文件,内容如下:
PI = 3.14def main(): print(\"PI:\", PI)main()# 运行结果:PI: 3.14
现在,我们写一个用于计算圆面积的area.py文件,area.py文件需要用到const.py文件中的PI变量。从const.py中,我们把PI变量导入area.py:
from const import PIdef calc_round_area(radius): return PI * (radius ** 2)def main(): print(\"round area: \", calc_round_area(2))main()\'\'\'运行结果:PI: 3.14round area: 12.56\'\'\'
2. 修改const.py,添加if __name__ == \"__main__\"
我们看到const.py中的main函数也被运行了,实际上我们不希望它被运行,因为const.py提供的main函数只是为了测试常量定义。这时if name == \'main’派上了用场,我们把const.py改一下,添加if name == “main”:
PI = 3.14 def main(): print(\"PI:\", PI) if __name__ == \"__main__\": main()
运行const.py,输出如下:
PI: 3.14
运行area.py,输出如下:
round area: 12.56
如上,我们可以看到if __name__ == \'__main__\'
相当于Python模拟的程序入口,Python本身并没有这么规定,这只是一种编码习惯。由于模块之间相互引用,不同模块可能有这样的定义,而程序入口只有一个。到底哪个程序入口被选中,这取决于__name__
的值。
2. 读取当前工作路径
1. In .py
import os######################## Method 1 (Only use when export to a subfolder)#######################output_directory = \"./Images\"export_path = os.path.join(output_directory, \"123.jpg\")print(export_path)######################## Method 2(not work in ipynb)######################## print(__file__)def get_current_dir(): return os.path.dirname(os.path.abspath(__file__))# get_current_dir()file_path = os.path.join(get_current_dir(), \"Test.py\")print(file_path)
2. In .ipynb
Actually this will be the path of current working directory of terminal, not the same
dir
like.ipynb
, so be careful with this command
import osprint(os.getcwd())
3. Get all members(methods) of a function/variable:
print(dir(variable1))
4. 仿QT的Signal & Slot
class Signal: def __init__(self): self.__slots = [] def addReceiver(self, slot): self.__slots.append(slot) def send(self, *args, **kwargs): for slot in self.__slots: slot(*args, **kwargs)
# 定义槽函数def Receiver1(arg): print(f\"Receiver 1 received: {arg}\")def Receiver2(arg): print(f\"Receivor 2 received: {arg}\")
# 创建信号并连接槽函数signal = Signal()signal.addReceiver(Receiver1)signal.addReceiver(Receiver2)
# 发出信号signal.send(\'Some Data\')
def multiple(value): print(f\"{value * value}\")signal2 = Signal()signal2.addReceiver(multiple)signal2.send(10)
5. 利用f-string 快速debug
把}
放到等号后,并且在等号前后都留一个空格即可
print(f\"{var_status = }\")
输出
var_status = True
4. 多线程
可以将concurrent.futures
视为threading
模块的一个更高层次的抽象和简化版本。concurrent.futures
提供了一种更加用户友好的方式来管理和执行并发任务,尤其是在处理大量任务时更为方便。
以下是几个关键点来总结两者的关系:
-
目的:
-
threading
:直接提供创建和管理线程的能力,允许开发者手动管理线程的生命周期。 -
concurrent.futures
:提供一个高级接口来管理和执行并发任务,隐藏了许多底层细节,使得编写并发程序更加简单。
-
-
使用场景:
threading
:适用于需要更细粒度控制线程的情况,如需要自定义线程的行为、状态监控等。concurrent.futures
:适用于需要批量提交任务并收集结果的场景,特别是在任务数量较多时,可以简化任务管理和结果收集的代码。
-
代码复杂度:
threading
:需要手动管理线程的创建、启动、同步等问题,代码相对复杂。concurrent.futures
:提供了一个统一的接口来处理并发任务,代码更加简洁易读。
-
执行器(Executor):
threading
:需要显式地创建和管理线程,例如通过Thread
类。concurrent.futures
:提供了ThreadPoolExecutor
和ProcessPoolExecutor
等执行器,自动管理线程或进程池,简化了并发任务的执行。
1. Threading
模块
1. 基本概念
thread = threading.Thread(target=*args, **kwargs)
thread = threading.Thread(target=print_numbers)
target函数不用再加
()
thread.start()
thread.join()
daemon_thread = threading.Thread(target=print_time, daemon=True)
lock = threading.Lock()
with lock:
来启用锁。启用后必须等lock
内的代码运行完成后其他线程才会继续运行其余的代码Note.
.Lock
这里的L是大写的。用锁需要在定义函数的时候把lock作为参数带入使用
thread.is_alive()
thread.name
threading.active_count()
print(threading.active_count())
threading.enumerate()
print(threading.enumerate())
threading.current_thread()
print(threading.current_thread())
例1:
import threadingimport time# 定义线程函数def print_numbers(lock): with lock: for i in range(1, 6): print(f\"Thread 1: {i}\") time.sleep(0.5)def print_time(lock): with lock: for letter in \'abcde\': print(f\"Thread 2: {letter}\") time.sleep(0.5)# 创建锁lock = threading.Lock()# 创建线程thread1 = threading.Thread(target=print_numbers, args=(lock,))thread2 = threading.Thread(target=print_time, args=(lock,))daemon_thread = threading.Thread(target=print_numbers, args=(lock,), daemon=True)# 启动线程thread1.start()thread2.start()daemon_thread.start()# 等待非守护线程结束thread1.join()thread2.join()# 检查线程是否仍在运行print(f\"Thread 1 is alive: {thread1.is_alive()}\")print(f\"Thread 2 is alive: {thread2.is_alive()}\")# 设置线程名称thread1.name = \"PrintingNumbers\"thread2.name = \"PrintingLetters\"# 获取线程名称print(f\"Thread 1 name: {thread1.name}\")print(f\"Thread 2 name: {thread2.name}\")print(\"Main thread has finished.\")
解释:
- 创建线程:
thread1
和thread2
都是普通的线程,而daemon_thread
是一个守护线程。 - 启动线程:通过调用
start()
方法来启动线程。 - 线程阻塞:通过调用
join()
方法来等待线程完成。 - 守护线程:
daemon_thread
是一个守护线程,它会在主线程结束时自动结束。 - 线程锁:使用
threading.Lock()
创建一个锁,并使用with lock:
来确保锁的正确使用。 - 检查线程是否仍在运行:通过调用
is_alive()
方法来检查线程是否还在运行。 - 设置和获取线程名称:使用
name
属性来设置和获取线程的名称。
2. concurrent.futures
模块26
1. 基本概念
concurrent.futures.ProcessPoolExecutor(max_workers=5)
with concurrent.futures.ProcessPoolExecutor(max_workers=5) as executor:
Note.
max_workers
没有默认值,所以必须要写, 不然会报错TypeErrorexecutor.submit(func, arg)
futures = [executor.submit(task, sec) for sec in [5, 4, 3, 2, 1]]
concurrent.futures.as_completed(futures)
for future in concurrent.futures.as_completed(futures):
result = future.result()
例1(no lock):
import concurrent.futuresimport timedef count_down(num): print(f\"Start Task {num}\") time.sleep(num) print(f\"End Task {num}\") return numwith concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor: futures = [executor.submit(count_down, num) for num in range(1,6)]for future in concurrent.futures.as_completed(futures): print(f\"Result: {future.result()}\")print(\"All tasks have finished\")
例2(with lock):27
import concurrent.futuresimport timeimport threadinglock = threading.Lock()def count_down(num): with lock: print(f\"Start Task {num}\") time.sleep(num) print(f\"End Task {num}\") return numwith concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor: futures = [executor.submit(count_down, num) for num in range(1,6)]for future in concurrent.futures.as_completed(futures): print(f\"Result: {future.result()}\")print(\"All tasks have finished\")
6.函数笔记28
Lambda示例
a = 1def f(g):a = 2return lambda y: a * g(y)f(lambda y: a + y)(a)-----解析func = f(lambda y: 1 + y) => lambda y: 2 * (lambda y: 1 + y)(y) 所以f(lambda y: a + y)(a) => func(a) 带入a = 1(lambda y: 1 + y)(y) => 2lambda y: 2 * (lambda y: 1 + y)(y) => 2* 2 => 4
5. QA
1. Difference between func()
and func
In common Python code, the difference between func()
and func
is that func()
calls the function and returns its result, while func
refers to the function object itself.
For example, consider the following code:
def add(a, b): return a + bresult = add(1, 2)print(result) # 3
In this code, we define a function add()
that takes two arguments and returns their sum. We then call the function with add(1, 2)
and store the result in a variable called result
. Finally, we print the value of result
, which is 3
.
Now, consider the following code:
def add(a, b): return a + bresult = addprint(result) #
In this code, we define the same add()
function as before, but this time we assign the function object itself to a variable called result
. We then print the value of result
, which is .
As you can see, when we refer to the function without parentheses (add
), we get a reference to the function object itself. This reference can be stored in a variable or passed as an argument to another function. When we call the function with parentheses (add()
), we execute the function and get its return value.
2. 为什么很多命令需要 -m
?
在Python中,使用 -m
参数是为了指定要执行的模块。这种方式允许你直接从命令行运行一个模块作为脚本,而不需要显式地将模块路径添加到系统的搜索路径中(比如环境变量PYTHONPATH)。
为什么使用 -m
参数?
- 模块路径解析: 使用
-m
参数,Python会根据模块路径进行解析,而不是依赖于系统的搜索路径。这对于在多个环境中使用不同版本的Python或者使用虚拟环境特别有用,因为它确保了你执行的是当前环境下的模块版本。 - 避免冲突: 在某些情况下,同一个模块可能在多个地方有不同的版本或者实现,使用
-m
可以确保你运行的是期望的模块版本,避免了可能的冲突。 - 直接执行:
-m
参数允许你直接执行一个模块,而不需要像执行脚本一样提供完整的文件路径或者脚本名。
示例用法:
- 直接运行模块:例如,你可以使用
python -m http.server
来启动一个简单的HTTP服务器,这样就无需关心具体的模块文件在哪里,Python会自动找到并执行。 - 虚拟环境中的使用:在虚拟环境中,你可以使用
python -m pip install package_name
来安装一个包,这样会确保安装的是当前环境下的包,而不是全局环境中的。
总之,使用 -m
参数能够更加灵活和安全地执行Python模块,特别是在处理不同环境和版本控制时,这种方式尤为方便和推荐。
6. 函数笔记28
Lambda示例
a = 1def f(g):a = 2return lambda y: a * g(y)f(lambda y: a + y)(a)-----解析func = f(lambda y: 1 + y) => lambda y: 2 * (lambda y: 1 + y)(y) 所以f(lambda y: a + y)(a) => func(a) 带入a = 1(lambda y: 1 + y)(y) => 2lambda y: 2 * (lambda y: 1 + y)(y) => 2* 2 => 4
Reference:
-
8分钟搞懂面向对象编程 | 面向过程vs面向对象 | OOP | 封装 继承 多态 - Bilibili ↩︎
-
Python基础(一):变量和数据类型介绍 - 知乎 ↩︎ ↩︎
-
python 字符串切割 - CSDN ↩︎
-
python 字典defaultdict(list) - CSDN ↩︎
-
Python 基础系列 17 - 集合 set - CSDN ↩︎
-
Python 函数-W3School ↩︎
-
Python中的*(星号)和**(双星号)完全详解 - CSDN ↩︎
-
装饰器:不修改代码,就能改变函数功能的强大特性 - Bilibili ↩︎
-
Python类的约定 理想的类结构 - Bilibili ↩︎
-
Python中 __init__函数以及参数self怎么理解和使用?- 知乎专栏 ↩︎
-
Python的self 参数 ↩︎
-
【python】如何在class内部定义一个装饰器?这里的坑你要么不知道,要么不会填!- Bilibili ↩︎
-
类和实例(Class, Instance)- 图灵星球 ↩︎
-
Python基础:super()用法 - CSDN ↩︎
-
关于import你需要知道的一切!一个视频足够了 - Bilibili ↩︎
-
Python包神器:
__init__.py
从入门到精通 ↩︎ -
python相对导入常见问题和解决方案 ↩︎
-
Python如何进行格式化输出?- CSDN ↩︎
-
Python必学内容:格式化输出的三种方式 - CSDN ↩︎
-
Python 开发中的 5 个有用的 格式化字符串技巧 ↩︎
-
Python中的类型提示(Type Hints)及其在FastAPI中的应用 - CSDN ↩︎
-
全面理解Python中的类型提示(Type Hints)- CSDN ↩︎
-
Python Requirements环境打包与安装环境 - CSDN ↩︎
-
Python脚本轻松变exe可执行文件 - 微信公众号: pythonic生物人 ↩︎
-
Python中if name == ‘main’,__init__和self 的解析 - CSDN ↩︎
-
Python 编程:多线程为 for 循环提速 ↩︎
-
Python Threading Tutorial: Run Code Concurrently Using the Threading Module ↩︎
-
【CS61A精翻双语·英文原声】伯克利大学《计算机程序的结构与解释》(2024) ↩︎ ↩︎