0、与C++/Golang语法对比
- 1.Python中没有逗号表达式,Golang也没有,C/C++有。
- 2.在函数中使用全局变量,要用global关键字声明。
- 3.dict(map)不能通过mp[1]来判断是否存在key=1,如果不存在会引发异常,这和C++不同,C++中如果不存在这个key,那么value默认是0。
- 4.Python中没有结构体,C++/Go中有C++(struct), Go(type),在Python中用Class来替代。
- 5.Python支持嵌套函数(函数中直接定义函数),C++/Go中不支持直接的嵌套函数。
- 但是C++可以通过Lambda 表达式实现类似功能。
- 直接使用lambda表达式构建出的匿名函数对象比较抽象,一般都是将此匿名函数对象 作为参数传递(比如 sort),如果需要显式调用,最好是将创建出来的 匿名函数对象赋给一个有名函数对象,调用时逻辑会清晰很多。(这就是其他人在leetcode上的写法!)
- 常用的:
- sort(arr.begin(), arr.end(), [](int n1, int n2) {
- return n1 < n2;
- }
- ); // 升序
- 赋值给有名函数对象:
- vector<int> maxTargetNodes(vector<vector<int>>& a, vector<vector<int>>& b) {
- ...
- //Lambda表达式作为函数对象
- auto get=[&](vector<vector<int>> &es, vector<int> &vis, int sx, int ini, vector<int> &dep){
- ...
- {
- ...
- }
- Go可以使用闭包或者函数变量(基于匿名函数)来实现相同的功能,再不济也可以把函数定义在外层。非要把在函数里面嵌套函数定义,不符合Go官方追求设计简洁,使用场景明确的设计风格。
- //这样子是正确的
- func main() {
- p := func() {
- fmt.Println("Hello")
- }
- }
- //但是这样就不行
- func main() {
- func p() { // 这里语法错误
- fmt.Println("Hello")
- }
- }
- Python中也有 lambda 表达式:
- [格式]:lambda 参数列表 : 返回值/返回值表达式
- lambda 关键字用于创建小巧的匿名函数。
- lambda a, b: a+b 函数返回两个参数的和。
- Lambda 函数可用于任何需要函数对象的地方。
- 在语法上,匿名函数只能是单个表达式。
- 在语义上,它只是常规函数定义的语法糖。
- 与嵌套函数定义一样,lambda 函数可以引用包含作用域中的变量。
- Go中没有直接的lambda表达式,但是可以用匿名函数实现类似的功能
- // 定义并调用匿名函数
- result := func(x, y int) int {
- return x + y
- }(3, 4)
- fmt.Println(result)
- // 将匿名函数赋值给变量
- add := func(x, y int) int {
- return x + y
- }
- 6. 在 Python 中,字符不能直接相减,这与 C++/Go 有所不同。
- 这是因为:Python 中的字符是 str 类型的数据,直接相减会引发 TypeError 异常。
- 因此需要通过 ord() 和 chr() 函数来实现类似的功能:
- ord() 函数:将一个字符转换为它的 Unicode 码点(对于 ASCII 字符,其 Unicode 码点与 ASCII 值相同)。
- chr() 函数:将一个 Unicode 码点转换为对应的字符。
- 7. Python中的元组,当只有两个元素时,可以对应C++的pair<>。
- 8. Python中list()是个很方便的操作,比如 list(mp.items()),就变成了二元组的list。
- 9. Python 二分查找库函数 https://blog.csdn.net/YMWM_/article/details/122378152
- 适用于Python中有序且可索引的数据结构,如:
- 列表List,元素Tuple,自定义可索引且有序的序列类
- bisect_left():相当于C++的lower_bound()
- bisect_right():相当于C++的upper_bound()
- 用法:
- import bisect
- ls = [1,5,9,13,17]
- index = bisect.bisect_left(ls, 7) # 大于等于7的第一个索引
- index2 = bisect.bisect_right(ls, 7) # 大于7的第一个索引
- Python中<sortedcontainers>类还提供了自己的二分函数,类似C++中set,map的二分函数
- 具体见本文下面《复杂数据结构》章节
- 用法
- import sortedcontainers
- se = SortedSet()
- for i in range(10):
- se.add(i)
- idx = se.bisect_left(5)
- idx2 = se.bisect_right(5)
- 10. python的关键字nonlocal和global的区别
- 涉及到Python 中的作用域概念:
- 全局作用域:在整个程序文件顶层定义的变量所处的作用域,全局变量可以在程序的任何函数或类中被访问。
- 局部作用域:函数内部定义的变量所处的作用域,局部变量只能在定义它的函数内部被访问。
- 嵌套作用域:当一个函数定义在另一个函数内部时,内部函数可以访问外部函数的局部变量,这些外部函数的局部变量所处的作用域就是嵌套作用域。
- global 关键字用于在函数内部声明一个变量为全局变量,这样在函数内部对该变量的修改会直接影响到全局作用域中的同名变量。
- nonlocal 关键字用于在嵌套函数内部声明一个变量为外部函数的局部变量,这样在嵌套函数内部对该变量的修改会直接影响到外部函数的同名局部变量。
- def add(a, b):
- nonlocal idx
- e[idx] = b
- ne[idx] = h[a]
- h[a] = idx
- idx += 1
1、python 基础语法
- 1. 标识符
- 第一个字符必须是字母表中字母或下划线 _
- 标识符的其他的部分由字母、数字和下划线组成。
- 标识符对大小写敏感。
- 2. python保留字
- import keyword
- print(keyword.kwlist)
- 3. 注释
- 单行注释以 # 开头
- 多行注释可以使用 ''' ''' 或者 """ """
- 4. 行与缩进
- python使用缩进来表示代码块不需要使用大括号
- 5. 多行语句
- 使用一个反斜杠 \ 实现多行语句
- 在 [], {}, 或 () 中的多行语句 不需要使用 \
- 6. 空行
- 函数之间或类的方法之间用空行分隔,表示一段新的代码的开始。
- 类和函数入口之间也用一行空行分隔,以突出函数入口的开始。
- 空行并不是 Python 语法的一部分 作用在于分隔两段不同功能或含义的代码
- 7. 等待用户输入
- input("\n\n按下 enter 键后退出。")
- \n\n 在结果输出前会输出两个新的空行。一旦用户按下 enter 键时,程序将退出。
- 8. 同一行显示多条语句
- Python 可以在同一行中使用多条语句,语句之间使用分号 ;
- 9. 多个语句构成代码组
- 缩进相同的一组语句构成一个代码块称之代码组。
- 10. 输入
- Python3 实现一行输入多个数字,用空格隔开:
- p = map(int, input().split())
- a, b, c, d =map(int, input().split())
- 注意map得到的如果是数量小于输入量,得到的是map对象,<class 'map'>
- 如果数据量是对应的,如上面第二种,那么则是一一赋值。
- 因此如果是输入二维数组,那么每一行的数据需要从map转成list
- a = [[0] * 12 for _ in range(12)]
- for i in range(0, 12):
- for j in range(0, 12):
- a[i] = list(map(int, input().split())
- C/C++中scanf("%d", &x) != -1, scanf("%d", &x) != EOF) 对应Python:
- def main():
- while True:
- try:
- user_input = input()
- if not user_input:
- break
- print("You entered:", user_input)
- except EOFError:
- print("End of file reached.")
- break
- if __name__ == "__main__":
- main()
- 11. print 输出
- print 默认输出是换行的,如果要实现不换行需要在变量末尾加上 end="": **格式化输出:**
- print("my name is %s, my age is %d" % ("rsh", 100))
- print("my name is %s, my age is %d" % ("rsh", 100), end='')
- 12. import 与 from...import
- 在 python 用 import 或者 from...import 来导入相应的模块。
- 将整个模块(somemodule)导入,格式为: import somemodule >>>>>>使用模块中的对象,必须通过模块名作为前缀来调用
- 从某个模块中导入某个函数,格式为: from somemodule import somefunction
- 从某个模块中导入多个函数,格式为: from somemodule import firstfunc, secondfunc, thirdfunc
- 将某个模块中的全部函数导入,格式为: from somemodule import * >>>>>>所有的公开对象(函数、类、变量等)都导入到当前命名空间中
- 13. 命令行参数
- ......
- 14. 排序
- a = [3, 2, 1]
- a.sort()
- b = sorted(a)
- sort() 和 sorted() 之间的一个主要区别是 sorted() 将返回一个新列表,而 sort() 对列表进行原地排序。
- sort() 和 sorted() 之间的另一个主要区别是 sorted() 方法接受任何可迭代对象,而 sort() 方法仅适用于列表。
- sort() 方法可以接受两个可选参数,称为 key 和 reverse。key 具有将在列表中的每个项目上调用的函数的值。
- names = ["Jessica", "Ben", "Carl", "Jackie", "Wendy"]
- names.sort(key=len)
- 结果:Sorted: ['Ben', 'Carl', 'Wendy', 'Jackie', 'Jessica']
- names.sort(reverse=True)
- 结果:Sorted: ['Wendy', 'Jessica', 'Jackie', 'Carl', 'Ben']
- **自定义排序:**
- 1、直接使用key进行比较
- 场景(1):按照第二个元素从小到大排:
- def my_sort_key(x):
- return x[1]
- a = [(0, 2), (1, 1), (4, 3), (5, 6), (7, 1)]
- a.sort(key=my_sort_key)
- print(a)
- 场景(2):先按照元组的和从小到大排,如果一样按照第一个元素从小到大,如果还一样,按照第二个元素从大到小排。
- def custom_sort_key(item):
- total = sum(item)
- return (total, item[0], -item[1])
- # 示例数据
- data = [(3, 2), (1, 4), (2, 2), (1, 3), (1, 5)]
- # 使用.sort() 方法进行原地排序,指定 key 参数为自定义函数
- data.sort(key=custom_sort_key)
- print(data)
- 2、需要写一个比较函数cmp,然后用 cmp_to_key(这是用于兼容python2的cmp比较函数方式,较复杂)
- from functools import cmp_to_key(适用于简单/复杂情况)
- 3、使用lambda表达式(适用于简单情况):
- [格式]:lambda 参数列表 : 返回值/返回值表达式
- 代码片段1:
- idx存储下标,将idx按照nums值从小到大进行排序,即记录下nums数组各个值的原始位置。
- idx = []
- for i in range(n): idx.append(i)
- # 比较情况辨析:
- # cmp 函数接受两个元素 a 和 b,根据它们的比较结果返回不同的值:
- # 如:a < b, a > b, a == b,表示不同的情况
- # return -1:表示 a 应该排在 b 的前面。
- # return 1:表示 b 应该排在 a 的前面。
- # return 0:表示 a 和 b 相等,顺序无所谓。
- # 两者比较: g < t: return -1 表示从小到大排
- # g > t: return -1 表示从大到小排
- # 剩余的elif和else情况只是为了完整性。
- def cmp(a, b):
- if nums[a] < nums[b]:
- return -1
- elif nums[a] > nums[b]:
- return -1
- else:
- return 0
- idx.sort(key=cmp_to_key(cmp))
- # 等价于用lambda表达式:按照nums[x]从小到大排序
- # idx.sort(key=lambda x: nums[x])
- # 如果想按照nums[x]从大到小排序:
- # idx.sort(key=lambda x: nums[x], reverse=True)
- nums.sort()
- 代码片段2:
- 先按照二元组的和从小到大排序;如果和相等,那么按照首元素从小到大排序。
- def cmp(a, b):
- if sum(a) < sum(b):
- return -1
- elif sum(a) > sum(b):
- return 1
- else:
- if a[0] < b[0]:
- return -1
- elif a[0] > b[0]:
- return 1
- else:
- return 0
- a = [(2,3), (5,-2), (4, 5), (5,0), (4,1)]
- sorted_a = sorted(a, key = cmp_to_key(cmp))
- print("按照自定义排序: ", sorted_a)
- Python排序好题:leetcode 1366 [通过投票对团队排名]
2、python 基本数据类型
- Python 中的变量不需要声明。每个变量在使用前都必须赋值,变量赋值以后该变量才会被创建。
- 在 Python 中,变量就是变量,它没有类型,我们所说的"类型"是变量所指的内存中对象的类型。
- 1. 多个变量赋值
- a, b, c = 1, 2, "string"
- 2. 标准数据类型
- 2.1 python3有 6 个标准数据类型
- 不可变数据(3 个):Number(数字)、String(字符串)、Tuple(元组);
- 可变数据(3 个):List(列表)、Dictionary(字典)、Set(集合)。
- 2.2 查看数据类型
- 2.2.1 内置的 type() 函数可以用来查询变量所指的对象类型
- 2.2.2 使用isinstance() 函数判断变量所指的对象类型
- type()不会认为子类是一种父类类型。
- isinstance()会认为子类是一种父类类型。
- 2.2.3 通过 is 来判断类型
- >>>1 is True
- False
- 3. Number(数字)
- 3.1 整型 int
- python3 不支持 Long 类型
- 3.2 浮点数float
- 3.3 布尔类型bool
- 在 Python2 中是没有布尔型的,它用数字 0 表示 False,用 1 表示 True。
- Python3 中,bool 是 int 的子类
- True 和 False 可以和数字相加, True==1、False==0
- 3.4 复数complex
- 实部a 虚部b
- a + bj
- complex(a,b)
- 3.5 数值运算
- 在混合计算时,Python会把整型转换成为浮点数。
- 加法 +
- 减法 -
- 乘法 *
- 除法
- 得到一个浮点数 /
- 得到一个整数 // 得到的并不一定是整数类型的数,它与分母分子的数据类型有关系。
- 取余 %
- 乘方 **
- 4. String(字符串)
- 4.1 定义
- Python中的字符串用单引号 ' 或双引号 " 括起来,同时使用反斜杠 \ 转义特殊字符。
- 反斜杠 \ 转义特殊字符
- \n 换行
- 在字符串前面添加一个 r,表示原始字符串:
- 4.2 字符串的截取的语法格式如下:
- 变量[头下标:尾下标]
- 索引序号
- 从前面索引 0 1 2 3 4 5
- str = "a b c d e f"
- 从后面索引 -6 -5 -4 -3 -2 -1
- str[0] a
- str[2:4] cd
- str[2:] cdef
- 使用[]操作符对字符串进行切片。切片操作接受两个参数,即起始索引和结束索引。注意,起始索引的字符被包含在切片内,而结束索引的字符不被包含在切片内(左闭右开)。
- 如果未指定起始索引,默认为0(字符串开头),如果未指定结束索引,默认为字符串长度。
- 切片操作还支持使用负数索引,表示从字符串末尾开始计算的位置。
- 通过灵活运用切片操作,你可以方便地截取字符串中的子串。
- 4.3 加号 + 是字符串的连接符, 星号 * 表示复制当前字符串,与之结合的数字为复制的次数
- 4.4 Python 没有单独的字符类型,一个字符就是长度为1的字符串。
- 4.5 Python中的字符串不能改变 赋值会发生错误
- 4.6 在 Python 中,字符串格式化使用与 C 中 sprintf 函数一样的语法。
- #!/bin/python3
- print ("我叫 %s 今年 %d 岁!" % ('小明', 10))
- 4.7 python三引号允许一个字符串跨多行,字符串中可以包含换行符、制表符以及其他特殊字符。
- 4.8 字符串翻转
- 由于Python中字符串是不可变对象,所以不能用.reverse()函数
- 可用方法:
- 4.8.1 用reversed(),返回可迭代对象
- a = "1234"
- c = ''.join(reversed(a[0:2]))
- 4.8.2 先取其中一段,再翻转:
- a = "1234"
- d = a[0:3][::-1]
- 注意不能直接写a[0:3:-1],因为从0到3按照步长-1是无法到达的!!
- 5. bool(布尔类型)
- 5.1 布尔类型特点:
- 布尔类型只有两个值:True 和 False。
- 布尔类型可以和其他数据类型进行比较,比如数字、字符串等。在比较时,Python 会将 True 视为 1,False 视为 0。
- 布尔类型可以和逻辑运算符一起使用,包括 and、or 和 not。这些运算符可以用来组合多个布尔表达式,生成一个新的布尔值。
- 布尔类型也可以被转换成其他数据类型,比如整数、浮点数和字符串。在转换时,True 会被转换成 1,False 会被转换成 0。
- 6. List(列表)[] , 相当于C++中的vector,Go中的切片Slice
- 常用使用方式:
- a = []
- 定义数组:
- 一维:f[m]:a = [0] * m # 定义了一个长度为m的一维数组
- 二维:f[n][m]:a = [[0] * m for _ in range(n)] #定义了一个n * m的二维数组
- 三维:f[g][n][m]:a = [[[0] * m for _ in range(g)] for _ in range(n)]
- 越往后的for _ in range表示的维度放在"更前面"。
- 常用遍历方式:
- for x in a:
- print(x)
- 常用赋值方式:
- a = [1, 2, 3]
- b = [4, 5, 6]
- a = b # 把b的引用赋值给a,指向的是同一个对象,对b修改为影响a
- a[:] = b # 把b中的值复制一份给a
- 清空:
- a.clear()
- 反转列表:
- a= a[::-1]
- a.reverse()
- a = list(reversed(a)) # reversed返回的是迭代器
- 列表去重:
- a = list(set(a))
- 尾部加入:append()
- 尾部删除:pop()
- 可以通过指定索引来删除pop(0),但是O(n)
- 6.1 定义及要求
- 列表中元素的类型可以不相同,它支持数字,字符串甚至可以包含列表(所谓嵌套)
- 列表是写在方括号 [] 之间、用逗号分隔开的元素列表。
- 6.2 相关操作
- List也可以使用+操作符进行拼接 使用*操作符进行重复
- 和字符串一样,列表同样可以被索引和截取,列表被截取后返回一个包含所需元素的新列表。
- Python 列表截取可以接收第三个参数,参数作用是截取的步长
- eg:翻转字符串:
- # inputWords[-1::-1] 有三个参数
- # 第一个参数 -1 表示最后一个元素
- # 第二个参数为空,表示移动到列表末尾
- # 第三个参数为步长,-1 表示逆向
- 与Python字符串不一样的是,列表中的元素是可以改变的。
- 内置了有很多方法,例如 append()、pop() 等等
- 在 Python 中,列表是可变对象,当你将 ls[] 列表添加到 res[[]] 中时,实际上添加的是 ls 的引用,而不是它的副本。
- 例如:
- res = []
- ls = []
- res.append(ls) # 添加的是引用,在dfs问题回溯过程中会清空,导致res为空
- 需要做(浅拷贝)值拷贝:
- res.append(ls[:]),或者res.append(ls.copy())]
- 深拷贝:(能够嵌套做值拷贝,如多重List嵌套)
- res.append(ls.deepcopy())
- 7. Tuple(元组)()
- 7.1 定义及要求
- 元组中的元素类型也可以不相同。
- 元组(tuple)与列表类似,不同之处在于元组的元素不能修改。
- 元组写在小括号 () 里,元素之间用逗号隔开。
- 虽然tuple的元素不可改变,但它可以包含可变的对象,比如list列表。
- 7.2 特殊的语法规则
- tup1 = () # 空元组
- tup2 = (20,) # 一个元素,需要在元素后添加逗号
- 7.3 内置函数
- len(tuple) 计算元组元素个数。
- max(tuple) 返回元组中元素最大值。
- min(tuple) 返回元组中元素最小值。
- tuple(iterable) 将可迭代系列转换为元组。
- 8.Set(集合){} ,与C++/Go不同的是,可以存储不同类型数据如{1, "a"}
- 使用方法:
- se = set() #注意定义空列表不许用set()函数;set()中可以传入列表:se = set(list)
- se.add(x)
- se.remove(x)
- len(se)
- 判断元素x是否在集合se中:
- if x in se:
- print("in!")
- 判断不在:
- if x not in se:
- print("not in!")
- 清空:
- se.clear()
- 8.1 定义及要求
- Python 中的集合(Set)是一种无序、可变的数据类型,用于存储唯一的元素。
- 集合中的元素不会重复,并且可以进行交集、并集、差集等常见的集合操作。
- 在 Python 中,集合使用大括号 {} 表示,元素之间用逗号 , 分隔。
- 8.2 创建set
- 可以使用 set() 函数创建集合。
- 创建空集合必须用set()
- 9. Dictionary(字典),相当于C++/Go中的map
- **与C++/Go中map的区别:**
- 其key可以是不同类型!
- 常用定义方式:
- mp = {}
- mp[1] = 2
- mp["a"] = "b"
- 常用遍历方式:
- for k, v in mp.items():
- print(k, v)
- # 获取所有键
- print(mp.keys())
- # 获取所有值
- print(mp.values())
- # 获取所有键值对
- print(mp.items())
- 判断某个key是否存在:
- if k in mp:
- print("in!")
- if ke not in mp:
- print("not in!")
- 删除指定key的元素:
- del mp[key]
- 清空:
- mp.clear()
- 9.1 定义及要求
- 是Python中另一个非常有用的内置数据类型
- 字典是无序的对象集合:字典当中的元素是通过键来存取的,而不是通过偏移存取。
- 字典是一种映射类型,字典用 { } 标识,它是一个无序的 键(key) : 值(value) 的集合。
- 键(key)必须使用不可变类型。在同一个字典中,键(key)必须是唯一的。
- 9.2 创建Dictionary
- 构造函数 dict()
- dict([('Runoob', 1), ('Google', 2), ('Taobao', 3)])
- dict(Runoob=1, Google=2, Taobao=3)
- 推导式
- 10. bytes 类型
- 10.1 定义及要求
- bytes 类型表示的是不可变的二进制序列(byte sequence)
- bytes 类型中的元素是整数值(0 到 255 之间的整数),而不是 Unicode 字符。
- 10.2 创建
- 10.2.1 使用 b 前缀
- 10.2.2 bytes() 函数将其他类型的对象转换为 bytes 类型
- 第一个参数是要转换的对象,第二个参数是编码方式,如果省略第二个参数,则默认使用 UTF-8 编码:
- 10.3 用途
- bytes 类型通常用于处理二进制数据,比如图像文件、音频文件、视频文件等等。在网络编程中,也经常使用 bytes 类型来传输二进制数据。
- 10.4 操作
- bytes 类型也支持许多操作和方法,如切片、拼接、查找、替换等等。
- 同时,由于 bytes 类型是不可变的,因此在进行修改操作时需要创建一个新的 bytes 对象。
- bytes 类型中的元素是整数值,因此在进行比较操作时需要使用相应的整数值
- ord() 函数用于将字符转换为相应的整数值。
- del语句用于删除对象引用。它可以将一个或多个对象从内存中删除,以便释放资源或销毁不再需要的对象。
- 当使用del语句删除对象引用时,Python解释器会检查该对象的引用计数。引用计数是指对该对象的引用数量。当引用计数为0时,表示没有任何引用指向该对象,该对象就可以被垃圾回收机制回收并释放内存。
- del语句只是删除对象引用,而不是直接删除对象本身。对象的销毁和释放内存是由Python解释器的垃圾回收机制自动处理的。当对象没有任何引用指向时,垃圾回收机制会将其标记为垃圾,并在适当的时候进行回收和释放。
3、Python 数据类型转换
- 1. 隐式类型转换
- 2. 显式类型转换
- int() 强制转换为整型
- float() 强制转换为浮点型:
- str() 强制转换为字符串类型:
4、Python 推导式
- 1. 列表推导式格式
- [表达式 for 变量 in 列表]
- [out_exp_res for out_exp in input_list]
- 或者
- [表达式 for 变量 in 列表 if 条件]
- [out_exp_res for out_exp in input_list if condition]
- 2. 字典推导式格式
- { key_expr: value_expr for value in collection }
- 或
- { key_expr: value_expr for value in collection if condition }
- 3. 集合推导式格式
- { expression for item in Sequence }
- 或
- { expression for item in Sequence if conditional }
- 4. 元组推导式(生成器表达式)
- (expression for item in Sequence )
- 或
- (expression for item in Sequence if conditional )
5、Python 迭代器与生成器
- 1. 迭代器
- 迭代是 Python 最强大的功能之一,是访问集合元素的一种方式。
- 迭代器是一个可以记住遍历的位置的对象。
- 迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退。
- 迭代器有两个基本的方法:iter() 和 next()。
- 字符串,列表或元组对象都可用于创建迭代器:
6、Python 复杂数据结构
- <优先队列>: 多线程安全
- 导入:from queue import PriorityQueue
- 定义:pq = PriorityQueue() # 默认是小根堆
- 如果想要实现的是大根堆,可以将元素的优先级取反后添加
- 到队列中,取出元素时也将优先级取反,这样就实现了大根堆的效果。
- 添加元素:pq.put()
- **只能添加单一类型元素**
- 添加单个元素:
- pq.put(1)
- 添加元组:
- pq.put((3, "task 1"))
- 取出队头元素:pq.get()
- 获得队列大小:pq.qsize()
- 自定义类型比较:
- 自定义元素类
- 可以自定义类,通过重写 __lt__ 方法来实现元素之间的比较,从而实现自定义的优先级规则。
- from queue import PriorityQueue
- class Task:
- def __init__(self, priority, description):
- self.priority = priority
- self.description = description
- def __lt__(self, other):
- # 自定义比较逻辑,优先级越小越优先
- return self.priority < other.priority
- # 创建一个 PriorityQueue 实例
- pq = PriorityQueue()
- # 向 PriorityQueue 中添加元素
- pq.put(Task(3, "task 1"))
- pq.put(Task(1, "task 2"))
- pq.put(Task(2, "task 3"))
- # 从 PriorityQueue 中获取元素
- while not pq.empty():
- task = pq.get()
- print("Priority: %d, Item: %s" % (task.priority, task.description))
- <堆> heapq - 只适用于单线程
- 开销更小的简单优先队列 -(默认为小根堆,如果要实现大根堆,元素取反即可)
- 对含有系统自带(build-in)的数据类型(int, float等)的数组list,可以用heapq成员函数heapify()直接把数组转换成成一个最小堆min heap。而对于用户自定义类型(如ListNode)的数组,则需要通过在自定义类型的类里重写__lt__()来实现最大堆或最小堆的。
- import heapq
- class FreqWord:
- def __init__(self, w, f):
- self.freq = f
- self.word = w
- #最小堆,每次弹出频率最小的单词
- def __lt__(self, other):
- return self.freq < other.fr
- 把list堆化:
- a = [3, 2, 1]
- heapq.heapify(a) # 堆化,本质还是用list在模拟,type = list
- 加入元素x:
- heapq.heappush(a, x)
- 删除堆顶:
- heapq.heappop(a)
- <sortedcontainers>
- sortedcontainers是一个纯Python开发的排序容器库。
- 这里的容器指的是字典、列表、集合这样的数据结构,不是docer之类的容器技术。
- 简单的说就是,使用该库中类创建的列表、字典或集合,都是自动排序的,
- 同时这些类还提供了像二分查找这样的用于有序数据结构的常用算法。
- <有序列表 SortedList>
- 底层用二分进行维护,加上一些缓存机制。
- add(), remove()等
- <有序映射 SortedDict>
- 引入:from sortedcontainers import SortedDict
- Python 中普通 Dict 对应 C++ 的unordered_map
- 初始化: mp = {}
- SortedDict 对应 C++ 的map
- 初始化:mp = SortedDict()
- 插入和删除操作跟普通dict差不多在这里不细述。我们主要是利用其key是有序的特点,涉及的接口函数有:SortedDict.keys(), SortedDict.items()和SortedDict.values()。
- 二分查找:mp.bisect_left(x),mp.bisect_right(x),返回的是元素位置。
- k = mp.bisect_right(2)
- print("k = %d" % k)
- print(mp[mp.keys()[k]])
- <有序集合 SortedSet>
- SortedSet的使用方法和普通Set一样,add(), remove()等;
- 关键在于该数据结构可以通过下标索引(普通set不能),其本质是由SortedList实现的:
- se = SortedSet()
- ...
- print(se[2])
- <default dict>
- Python中对于访问dict,要是键值key不存在,就会有‘KeyError’的错,在写代码时就要先判断key是否存在,因此就要多写好几行代码来避免‘KeyError‘。
- 为了避免‘KeyError’错,可以使用collections模块的defaultdict(),它的用法跟dict一样,但是会有默认值。
- from collections import defaultdict
- # 使用 list 作为默认工厂函数
- dd_list = defaultdict(list)
- dd_list['a'].append(3)
- dd_list['a'].append(3)
- dd_list['b'].append('three')
- dd_int = defaultdict(int)
- dd_set = defaultdict(set)
7、Python 函数
- def fib2(n): # 返回斐波那契数组直到 n
- """Return a list containing the Fibonacci series up to n."""
- result = []
- a, b = 0, 1
- while a < n:
- result.append(a) # 见下
- a, b = b, a+b
- return result
- f100 = fib2(100) # 调用它
- f100 # 输出结果
- [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]
8、Python 类
一般来说,实例变量用于每个实例的唯一数据,而类变量用于类的所有实例共享的属性和方法:
__init__
相当于C++中的构造函数,可以不显示写出来。
- class Dog:
- kind = 'canine' # 类变量被所有实例所共享
- def __init__(self, name):
- self.name = name # 实例变量为每个实例所独有
- class Dog:
- def __init__(self, name):
- self.name = name
- self.tricks = [] # 为每个 Dog 实例新建一个空列表
- def add_trick(self, trick):
- self.tricks.append(trick)
- >>> d = Dog('Fido')
- >>> e = Dog('Buddy')
- >>> d.add_trick('roll over')
- >>> e.add_trick('play dead')
- >>> d.tricks
- ['roll over']
- >>> e.tricks
- ['play dead']
对比C++ 中类:
在 C++ 中,我们通常将类变量称为静态成员变量,将实例变量称为非静态成员变量。
静态成员变量(类变量)
定义和声明:
- 静态成员变量属于类本身,而不是类的实例。它们在类的所有实例之间共享相同的值。
- 静态成员变量的声明在类的内部,但必须在类的外部进行定义。
- class MyClass {
- public:
- static int staticVar; // 静态成员变量声明
- int instanceVar; // 非静态成员变量(实例变量)
- void printStaticVar() {
- std::cout << "Static variable from instance method: " << staticVar << std::endl;
- }
- };
- // 静态成员变量的定义和初始化
- int MyClass::staticVar = 0;
实例变量(非静态成员变量)
定义和使用:
- 实例变量属于类的每个实例,每个实例都有自己的一份副本。
- 实例变量通常在类的构造函数中初始化,也可以在声明时提供默认值。
9、Python 模块化
python中模块与包
模块(Module)
定义:
模块是 Python 程序中最小的可重用代码单元,它其实就是一个以 .py 为扩展名的 Python 文件。模块可以包含函数、类、变量等 Python 代码,开发者可以将相关的代码组织在一个模块中,以便在其他 Python 程序中重复使用。
包(Package)
定义:
包是一种组织模块的方式,它是一个包含多个模块的目录。为了让 Python 识别该目录为包,目录下必须包含一个名为 init.py 的文件(在 Python 3.3 及以后的版本中,init.py 文件可以为空或者不存在,但为了兼容旧版本,建议保留)。包可以包含子包和模块,形成一个层次化的代码结构,有助于更好地组织和管理大型项目的代码。