> 技术文档 > Python 快速入门_python学习笔记

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)的语言,面对对象编程是一种设计思想,意味着我们把对象作为程序的基本单元,而每个对象包含了自己的属性和方法。面向对象编程主要有以下特点:

  1. 封装(Encapsulation):对外部世界隐藏对象的工作细节。
  2. 继承(Inheritance):继承使子类具有父类的各种属性和方法,而不需要编写相同的代码。
  3. 多态(Polymorphism):为不同的数据类型的实体提供统一的接口。

使用OOP有以下的优点:

  1. 提高软件开发的生产效率
  2. 使软件的可维护性更好
  3. 提高软件的质量

在 Python 中,元组、列表和字典等数据类型是对象,函数也是对象。

1.1 变量 (Variable)2

  1. python不用事先声明变量,赋值过程中就包含了变量声明和定义的过程
  2. =复制,左边是变量名,右边是变量的值
  3. 变量名可由 a-z, A-Z,数字,下划线()组成, 首字母不能为数字和下划线();
  4. Python 对大小写敏感,变量 a 和变量 A 表示不同的变量;
  5. 变量名不能为 Python 中的保留字;
  6. 慎用小写字面l和大写字母O
  7. 尽量小写, 如有多个单词,用下划线隔开即采用**蛇形命名法(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. 算术运算符
Operator Description 示例 结果 + 相加 - 相减 * 相乘 / 相除 // 整除 ** 幂 % 取余(取模) 10 % 3 1 ++ 前置递增 a=2; b=+a; a=3;b=3; ++ 后置递增 a=2;b=a++; a=3;b=2; – 前置递减 a=2;b=–a; a=1;b=1 – 后置递减 a=2;b=a–; a=1;b=2
2. 比较运算符
Operator Description == 等于 != 不等于 等于 > 大于 < 小于 >= 大于或等于 <= 小于或等于
3. 赋值运算符
Operator Description = 赋值 += 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. 逻辑运算符
Operator Description and 与关系,类似于C++的 && or 或关系,类似于C++的 ` not 非关系,类似于C++的 !
5. 成员运算符
Operator Description in 在指定容器中找到返回True,否则返回False not in 在指定容器中未找到返回True,否则返回False
6. 身份运算符
Operator Description is 两个变量引用自同一个对象则返回True not is 两个变量不是引用自同一个对象则返回True
7. 位运算符
Operator Description & 与运算 | 或运算 ^ 亦或运算 ~ 取反运算 << 左移运算 >> 右移运算

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、正数,可以是整数也可以是浮点数。

对于数值型类型的变量,我们可以利用它进行加、减、乘、除。但是这里有几个地方需要注意一下。

  1. “/” 代表除法, “//” 代表取整;
>>> 7 / 41.75>>> 7 // 41
  1. “%” 表示求余;
>>> 10 % 42>>> 5 % 32>>> 4 % 54
  1. 一个关于浮点数需要注意的地方;
>>> 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
  1. 字符串切割

    # 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开始的。

  2. 路径文件分隔函数

    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这个元组

  1. 类似C++里面的指针,*args指向形参构成的列表,
  2. *arg后不要再加入别的形参,不然会报错
  3. 混用****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 表达式接受两个参数 ab,并返回它们的和。

Lambda 表达式通常与函数式编程一起使用,可以作为参数传递给其他函数,例如 map()filter() 函数,用于对序列进行操作。

Lambda 表达式的使用也有一些限制,它只能包含一个表达式,而不能包含多个语句,也不能使用循环或条件语句等复杂结构。

下面是几个简单的 Lambda 表达式例子:

  1. 将字符串转换为大写字母:

    to_upper_case = lambda s: s.upper()result = to_upper_case(\"hello, world!\")print(result) # 输出: \"HELLO, WORLD!\"
  2. 计算两个数的平均值:

    average = lambda x, y: (x + y) / 2result = average(3, 5)print(result) # 输出: 4.0
  3. 过滤出列表中的偶数:

    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 基本语法
  1. 在调用函数时,*和**都是分配参数用的, 在“调用函数时分配参数”跟“定义函数时收集参数”,反过来了
  2. 使用function的时候需要带(), 哪怕没有参数
  3. 传参顺序可变
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总是指向调用时的类的实例。

  1. Python中为何要有self呢?

    因为在定义类时,我们需要东西去指定这个类所创建的对象,这样才能调用一系列的方法。

    而在定义类时,我们是没有对象的,类都还在定义中哪里去创建对象,所以这时我们规定统一用一个self来指定类的实例,这样我们就才能定义一些类的方法。

    self只有在类的方法中才会有,独立的函数或方法是不必带有self的。self在定义类的方法时是必须有的,虽然在调用时不必传入相应的参数。

    示例:

    def blitme(self): self.screen.blit(self.image,self.rect) # 根据self.rect 指定的位置将image绘制到screen(窗口)上

    因为我们要调用 blit 方法,对谁调用?对一个类创建的实例调用,但现在还没创建实例,我们就暂且用 self 代替之,来编写类的代码。

  2. self可以更换为其他参数名吗?

    self名称不是必须的,在python中self不是关键词,你可以定义成a或b或其它名字都可以。

    但是约定成俗(为了和其他编程语言统一,减少理解难度),最好不要搞另类,会不太好明白

  3. super()

    def __init__(self): super().__init__()
1.6.1.2.3 类方法12

类方法的定义是有顺序的,最好按照此顺序定义,方便理解和阅读

私有方法可以用在 classmethod staticmethod

  1. __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()后,创建的每个实例都有自己的属性,可以直接调用类中的函数。

  2. 魔术方法 magicmethod

    前后有2个下划线

  3. 类方法 classmethod

    classmethod主要用来access Class的一些方法或者是access/set Class的属性/变量, 比较常用

    因可以在构造函数中避免硬编码,在构造函数中出现频率很高

    • 需要 @classmethod 装饰器

    • 参数必需:cls

    • 实例化才能调用: 否

    类方法的参数以 cls 开头,类似__init__里的参数 self, 调度时用 cls.xxx

  4. 静态方法 staticmethod

    staticmethod本质上类似于一个隶属于类的函数,甚至可以放在类的外面,放在类内通常是为了更好的封装。但很多时候也可以直接放在类外面使用,因此使用频率较低

    static method并无法直接access类或者函数的属性

    • 需要 @staticmethod 装饰器

    • 参数必需:无

    • 实例化才能调用: 否

    在 @staticmethod 中如果要调用到这个类的一些属性方法,只能直接类名.属性名或类名.方法名。

    而 @classmethod 因为持有 cls 参数,可以来调用类的属性,类的方法,实例化对象等,避免硬编码。

  5. 私有方法 privatemethod

    用一个下划线做前缀描述符,就被认为是私有的。

    其实也是可以调用的,只是我们通常看到有下划线就不调用而已。

  6. 实例方法 instancemethod⭐️

    可以访问 instance 的一些数据,可以做一些实际的业务操作。也是最常见的类方法

    • 参数必需:self

    • 实例化才能调用: 是

  7. 抽象方法 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()

可以看到,catdog 是两个不同的对象,对它们调用 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() 来调用与子类同名的父类方法
  1. 单继承

    在单继承中 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__的输出结果从左到右的顺序查找的

    • 如果当前类中找到方法,就直接执行,不再搜索
    • 如果没有找到,就查找下一个类中是否有对应的方法,如果找到,就直接执行,不再搜索
    • 如果找到最后一个类,还是没有找到方法,程序报错
  2. 多继承

在多继承中,会涉及到一个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重名,优先级如下:

  1. 当前缓存

  2. 内置 / Built-in module

    比如: sys, os, math

  3. sys.path,包含以下3个部分,优先级如下:

    1. 当前运行(脚本)路径会被临时添加进sys.path并列在第一位

    2. Python自带package

      比如:asyncio, multiprocessing

    3. 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等静态语言,他们要求对变量和函数返回值必须进行严格的类型声明。

  1. 方法形参和返回值类型提示(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类型的入参,方法正常执行。而对于floatstr类型的入参,编辑器会在静态检查时提示类型错误,但不会阻止错误类型的参数使用和运算。

  2. 变量注解(annotations):

    name: str = \"Jack\"age: int = 20name = 30 # 编辑器提示类型错误age = \'21\' # 编辑器提示类型错误print(name)print(age)

    在定义变量时也可以指定变量的类型。当给变量赋值其他类型数据时,编辑器会提示类型错误。但是,并不会阻止你这样做。代码仍可以正常运行。

    3021

    运行后可见,name虽然声明为str,但让能被赋值为intage变量亦然。

  3. 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 既然类型声明不能阻止变量使用其他类型赋值,那么其意义何在呢?

类型提示带来的好处有三个:

  1. 方便开发者阅读代码,提高代码易读性。
  2. 方便编辑器/IDE 对代码进行提示、自动补全和静态检查。
  3. 可通过 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

此类方法推荐使用,他只是总结程序中所用到的包,并不是电脑中安装的所有的包

  1. 安装pipreqs

    pip install pipreqs
  2. 命令生成

    (1)先 cd 到程序所在的文件夹位置
    (2)执行下面的语句,Windows下得加encoding=utf8,不然会出现编码问题

    pipreqs ./ --encoding=utf8

    说明: 把 “我” 这个文件下的程序所使用的的包进行总结(包括子文件里的程序)写入到requirements.txt,requirements.txt这个文件生成在当前路径
    在这里插入图片描述
    在这里插入图片描述

2. 安装环境
1. 在线安装
pip install -r requirements.txt

当在新的项目中需要使用之前的环境时,可以拷贝该文件导入新项目,在控制台安装。当然pycharm也会自动检测该文件,可以直接导入

2. 离线安装
  1. 将requirements.txt中导入的包离线下载到packagesdir 文件夹

    先执行第一行,再执行第二行

    pip wheel -w DIR -r requirements.txtpip download -d DIR -r requirements.txt
  2. 安装离线的包

    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提供了一种更加用户友好的方式来管理和执行并发任务,尤其是在处理大量任务时更为方便。

以下是几个关键点来总结两者的关系:

  1. 目的:

    • threading:直接提供创建和管理线程的能力,允许开发者手动管理线程的生命周期。

    • concurrent.futures:提供一个高级接口来管理和执行并发任务,隐藏了许多底层细节,使得编写并发程序更加简单。

  2. 使用场景:

    • threading:适用于需要更细粒度控制线程的情况,如需要自定义线程的行为、状态监控等。
    • concurrent.futures:适用于需要批量提交任务并收集结果的场景,特别是在任务数量较多时,可以简化任务管理和结果收集的代码。
  3. 代码复杂度:

    • threading:需要手动管理线程的创建、启动、同步等问题,代码相对复杂。
    • concurrent.futures:提供了一个统一的接口来处理并发任务,代码更加简洁易读。
  4. 执行器(Executor):

    • threading:需要显式地创建和管理线程,例如通过Thread类。
    • concurrent.futures:提供了ThreadPoolExecutorProcessPoolExecutor等执行器,自动管理线程或进程池,简化了并发任务的执行。

1. Threading 模块

1. 基本概念
Code 功能 备注 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.\")
解释:
  1. 创建线程thread1thread2都是普通的线程,而daemon_thread是一个守护线程。
  2. 启动线程:通过调用start()方法来启动线程。
  3. 线程阻塞:通过调用join()方法来等待线程完成。
  4. 守护线程daemon_thread是一个守护线程,它会在主线程结束时自动结束。
  5. 线程锁:使用threading.Lock()创建一个锁,并使用with lock:来确保锁的正确使用。
  6. 检查线程是否仍在运行:通过调用is_alive()方法来检查线程是否还在运行。
  7. 设置和获取线程名称:使用name属性来设置和获取线程的名称。

2. concurrent.futures 模块26

1. 基本概念
Code 功能 例 concurrent.futures.ProcessPoolExecutor(max_workers=5) 创建线程 with concurrent.futures.ProcessPoolExecutor(max_workers=5) as executor:
Note. max_workers没有默认值,所以必须要写, 不然会报错TypeError executor.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 参数?
  1. 模块路径解析: 使用 -m 参数,Python会根据模块路径进行解析,而不是依赖于系统的搜索路径。这对于在多个环境中使用不同版本的Python或者使用虚拟环境特别有用,因为它确保了你执行的是当前环境下的模块版本。
  2. 避免冲突: 在某些情况下,同一个模块可能在多个地方有不同的版本或者实现,使用 -m 可以确保你运行的是期望的模块版本,避免了可能的冲突。
  3. 直接执行: -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:


  1. 8分钟搞懂面向对象编程 | 面向过程vs面向对象 | OOP | 封装 继承 多态 - Bilibili ↩︎

  2. Python基础(一):变量和数据类型介绍 - 知乎 ↩︎ ↩︎

  3. python 字符串切割 - CSDN ↩︎

  4. python 字典defaultdict(list) - CSDN ↩︎

  5. Python 基础系列 17 - 集合 set - CSDN ↩︎

  6. Python 函数-W3School ↩︎

  7. Python中的*(星号)和**(双星号)完全详解 - CSDN ↩︎

  8. 装饰器:不修改代码,就能改变函数功能的强大特性 - Bilibili ↩︎

  9. Python类的约定 理想的类结构 - Bilibili ↩︎

  10. Python中 __init__函数以及参数self怎么理解和使用?- 知乎专栏 ↩︎

  11. Python的self 参数 ↩︎

  12. 【python】如何在class内部定义一个装饰器?这里的坑你要么不知道,要么不会填!- Bilibili ↩︎

  13. 类和实例(Class, Instance)- 图灵星球 ↩︎

  14. Python基础:super()用法 - CSDN ↩︎

  15. 关于import你需要知道的一切!一个视频足够了 - Bilibili ↩︎

  16. Python包神器:__init__.py 从入门到精通 ↩︎

  17. python相对导入常见问题和解决方案 ↩︎

  18. Python如何进行格式化输出?- CSDN ↩︎

  19. Python必学内容:格式化输出的三种方式 - CSDN ↩︎

  20. Python 开发中的 5 个有用的 格式化字符串技巧 ↩︎

  21. Python中的类型提示(Type Hints)及其在FastAPI中的应用 - CSDN ↩︎

  22. 全面理解Python中的类型提示(Type Hints)- CSDN ↩︎

  23. Python Requirements环境打包与安装环境 - CSDN ↩︎

  24. Python脚本轻松变exe可执行文件 - 微信公众号: pythonic生物人 ↩︎

  25. Python中if name == ‘main’,__init__和self 的解析 - CSDN ↩︎

  26. Python 编程:多线程为 for 循环提速 ↩︎

  27. Python Threading Tutorial: Run Code Concurrently Using the Threading Module ↩︎

  28. 【CS61A精翻双语·英文原声】伯克利大学《计算机程序的结构与解释》(2024) ↩︎ ↩︎