Python基础知识点

 

一、数据类型

1.0、数字

浮点数
保留小数位数
# 用round实现,返回的是float round(f, n) # f是浮点数,n是需要保留的位数 # 用占位符实现,返回的是str f_num = float(input()) print(f'{f_num:.2f}') # .2f中的2表示小数位数,可以更改;f代表要操作的float print('%.2f' % f_num) print('{:.2f}'.format(f_num))
 
# python取整 int(-0.8) # 取整数部分 round(-0.4) # 四舍五入 math.floor(-0.8) # 向下取整 math.ceil(-0.8) # 向上取整
 
进制转换
非10进制之间的转换需要借助10进制中转
int(str, 2) # 2转10,输入字符串,返回数字 int(str, 8) # 8转10,输入字符串,返回数字 int(str, 16) # 16转10,输入字符串,返回数字 bin(num) # 10转2,输入数字,返回字符串 oct(num) # 10转8,输入数字,返回字符串 hex(num) # 10转16,输入数字,返回字符串
 

运算符

算术运算

运算符
描述
实例:a=10,b=21
备注
+
加 - 两个对象相加
a + b 输出结果 31
-
减 - 得到负数或是一个数减去另一个数
a - b 输出结果 -11
*
乘 - 两个数相乘或是返回一个被重复若干次的字符串
a * b 输出结果 210
**
幂 - 返回x的y次幂
a**b 为10的21次方
/
除 - x 除以 y
b / a 输出结果 2.1
不管式子中的是int还是flot,结果返回小数
%
取模 - 返回除法的余数
b % a 输出结果 1
式子中都为int时返回int型;式子中有flot时返回flot型
//
取整除 - 向下取整
>>> 9//2 4 >>> -9//2 -5
式子中都为int时返回int型;式子中有flot时返回flot型

赋值运算

运算符
描述
实例
=
简单的赋值运算符
c = a + b 将 a + b 的运算结果赋值为 c
+=
加法赋值运算符
c += a 等效于 c = c + a
-=
减法赋值运算符
c -= a 等效于 c = c - a
*=
乘法赋值运算符
c *= a 等效于 c = c * a
/=
除法赋值运算符
c /= a 等效于 c = c / a
%=
取模赋值运算符
c %= a 等效于 c = c % a
**=
幂赋值运算符
c **= a 等效于 c = c ** a
//=
取整除赋值运算符
c //= a 等效于 c = c // a
:=
海象运算符(在循环或条件语句中赋值使用,被赋值的变量在之后的代码块中可以继续使用)详细教程 python 3.8 之后才有
if count := fresh_fruit.get('lemon', 0): if (count := fresh_fruit.get('lemon', 0)) >= 3: dic, scor, count = {'A':4, 'B':3, 'C':2, 'D':1, 'F':0}, 0, 0 while (gpa := dic.get(input(), False)) is not False: s = int(input()) scor, count = scor + s * gpa, count + gpa print(f'{(scor/count):.2f}') while (n := n - 1) + 1:
while (n := n - 1) + 1:

逻辑运算

运算符
含义
逻辑表达式
描述
实例:a=10,b=21
and
x and y
如果左边为假,整体判断为假,将左边的值作为输出 如果左边为真,不管整体真假,将右边的值作为输出
(a and b) 返回 21
or
x or y
如果左边为真,整体判断为真,将左边的值作为输出 如果左边为假,不管整体真假,将右边的值作为输出
(a or b) 返回 10
not
not x
如果 x 为 True,返回 False 。如果 x 为 False,它返回 True。
not(a and b) 返回 False

位运算

💡
数字是以“补码”的形式存储在计算机中的,位运算也是用“补码”进行计算。
  • 原码:我们将数字的二进制表示的最高位视为符号位,其中0表示正数1表示负数,其余位表示数字的值。
  • 反码:正数的反码与其原码相同,负数的反码是对其原码除符号位外的所有位取反。
  • 补码:正数的补码与其原码相同,负数的补码是在其反码的基础上加 1。
notion imagenotion image
 
运算符
含义
描述
实例:a=9, b=5
运算细则
&
按位与运算
两个数的二进制,相同位置都为1,结果为1,否则为0
9 & 5
0000 1001 (9 的补码) 0000 0101 (5 的补码) ----------------------- 0000 0001 (1 的补码)
|
按位或运算
两个数的二进制,相同位置都为0,结果为0,否则为1
9 | 5
0000 1001 (9 的补码) 0000 0101 (5 的补码) ----------------------- 0000 1101 (13 的补码)
^
按位异或运算
两个数的二进制,相同位置值不同,结果为1,否则为0
9 ^ 5
0000 1001 (9 的补码) 0000 0101 (5 的补码) ----------------------- 0000 1100 (12 的补码)
~
按位取反运算
单目运算,对二进制取反
~9
0000 1001 (9 的补码) ----------------------- 1111 0110 (-10 的补码)
<<
按位左移运算
单目运算,对二进制左移若干位,由 << 右边的数字指定了移动的位数;高位丢弃,低位补0。
9<<3
0000 1001 (9 的补码) ----------------------- 0100 1000 (72 的补码)
>>
按位右移运算
单目运算,对二进制右移若干位,由 << 左边的数字指定了移动的位数;低位丢弃,高位补 0 或 1(根据高维的值决定)
9>>3
0000 1001 (9 的补码) ----------------------- 0000 0001 (1 的补码)
-9>>3
1111 0111 (-9 的补码) ---------------------- 1111 1110 (-2 的补码)

1.1、字符串

  • 可以在字符串中使用\(反斜杠)来表示转义,也就是说\后面的字符不再是它原来的意义,例如:\n不是代表反斜杠和字符n,而是表示换行;而\t也不是代表反斜杠和字符t,而是表示制表符。所以如果想在字符串中表示'要写成\',同理想表示\要写成\\
  • 如果不希望字符串中的\表示转义,我们可以通过在字符串的最前面加上字母r来加以说明,如:s1 = r'\'hello, world!\'' 前缀 r 和 f 可以一起使用。
    • f-strings的其他用法:Python--字符串格式化f-string
    • 此外,如果字符串是一个文件路径,且路径中包含空格,那么可能不能被 Python 识别,此时在字符串前加 r 来阻止转义即可。
  • Python为字符串类型提供了非常丰富的运算符,我们可以使用+运算符来实现字符串的拼接,可以使用*运算符来重复一个字符串的内容,可使用innot in来判断一个字符串是否包含另外一个字符串(成员运算),我们也可以用[][:]运算符从字符串取出某个字符或某些字符(切片运算)
  • 格式化输出字符串:占位符
    • a, b = 5, 10
    • print(f'{a} * {b} = {a * b}') python3.6之后的写法
    • print('%d * %d = %d' % (a, b, a * b))
    • print('{0} * {1} = {2}'.format(a, b, a * b))
  • 在Python中,我们还可以通过一系列的方法来完成对字符串的处理:
    • str1 = 'hello, world!' print(len(str1)) # 13 # 通过内置函数len计算字符串的长度 print(str1.capitalize()) # Hello, world! # 获得字符串首字母大写的拷贝 print(str1.title()) # Hello, World! # 获得字符串每个单词首字母大写的拷贝 print(str1.upper()) # HELLO, WORLD! # 获得字符串变大写后的拷贝 print(str1.find('or')) # 8 # 从字符串中查找子串所在位置,找不到返回-1 print(str1.find('shit')) # -1 print(str1.index('or')) # 与find类似但找不到子串时会引发异常 print(str1.index('shit')) # ValueError: substring not found print(str1.startswith('He')) # False # 检查字符串是否以指定的字符串开头,会区分大小写 print(str1.startswith('hel')) # True print(str1.endswith('!')) # True # 检查字符串是否以指定的字符串结尾 print(str1.center(50, '*')) # 将字符串以指定的宽度居中并在两侧填充指定的字符 print(str1.rjust(50, ' ')) # 将字符串以指定的宽度靠右放置左侧填充指定的字符 str2 = 'abc123456' print(str2.isdigit()) # False # 检查字符串是否只由数字构成 print(str2.isalpha()) # False # 检查字符串是否只以字母构成 print(str2.isalnum()) # True # 检查字符串是否以数字和字母构成 str3 = ' Jackfrued@126.com ' print(str3.strip()) # 修剪字符串两端的指定字符,默认是空格 print(str3.split()) # 按照指定字符切割字符串 print(str3.split(r'[,。-]')) # 按照指定的多个字符切割字符串 re.split(r'。|!|?','字符串1。文本?') # 用re模块可以一次使用多个分隔符 print(str3.lower()) # 转换字符串中所有大写字符为小写 print(str3.upper()) # 转换字符串中的小写字母为大写 print(str3.title()) # 首字母大写,如果有空格,则对每个空格后的单词首字母大写 print(str3.capitalize()) # 首字母大写,只会对整个字符串的首字母大写,即使有空格 print(str3.swapcase()) # 将字符串中大写转换为小写,小写转换为大写 print(str3.lstrip()) # 截掉字符串左边的空格或指定字符 print(str3.rstrip()) # 删除字符串末尾的空格或指定字符 print(str3.replace(old, new [, max])) # 将字符串中的old替换成new, 如果max指定,则替换不超过max次。 seq = '_'.join(seq) # 以指定字符串作为分隔符,将seq中所有的元素(的字符串表示)合并为一个新的字符串 # 占位符可以和以上方法一起使用 ['subject_%s'%i.lower() for i in data_.columns]
  • maketrans()方法:用于创建字符映射的转换表
    • translation_table = str.maketrans(x, y, z) # x:必选;原有的需要替换的字符, 如果只有 x,那 x 必须是一个字典 # y:可选;想要替换成的目标字符,y 的长度一定要和 x 相同 # z:可选;原字符串中想要删除的字符, 是先删除,后替换;如果只想删除某些字符,可以让 x 和 y 为空。 str5.translate(translation_table) # 示例 str5 = "Google Runoob Taobao!" translation_table = str.maketrans("mSa", "eJo", "odnght") str5.translate(translation_table) """ 'Gle Rub Tobo!' """
  • eval() 方法:将字符串转换为相应的对象,并返回表达式的结果(把字符串去掉,并运行
    • result = eval("2 + 3 * 4") print(result) # 输出: 14 # 执行变量引用 x = 10 result = eval("x + 5") print(result) # 输出: 15 # 在指定命名空间中执行表达式 namespace = {'a': 2, 'b': 3} result = eval("a + b", namespace) print(result) # 输出: 5 # 如果字符串内是列表,则直接返回列表 str1 = "[1,2,3]" list1 = eval(str1)

1.2、列表

  • 列表(list),也是一种结构化的、非标量类型,它是值的有序序列,每个值都可以通过索引进行标识,定义列表可以将列表的元素放在[]中,多个元素用,进行分隔,可以使用for循环对列表元素进行遍历,也可以使用[][:]运算符取出列表中的一个或多个元素。
  • 列表生成式:
    • f = [x for x in range(1, 10)]
    • f = [x*2 for x in range(1, 10) if x>5]
    • f = [x*2 if x>5 else x for x in range(1, 10)]
    • f = [x + y for x in 'ABCDE' for y in '1234567']
    • f = [(name, sex) for name, sex in zip(list1, list2)]
list1 = [1, 3, 5, 7, 100] # 增 print(list1.append(200)) # 在列表的最后追加元素;[1, 3, 5, 7, 100, 200] print(list1.insert(1, 400)) # 在索引1的位置插入元素;[1, 400, 3, 5, 7, 100, 200] print(list1.extend([8,9,11])) # 函数用于在列表末尾一次性追加另一个序列中的多个值;[1, 400, 3, 5, 7, 100, 200, 8,9,11] # 删 del list1[2] # 删除指定索引的元素 print(list1) # [1, 400, 5, 7, 100, 200] list1.remove(5) # 移除列表中某个值的第一个匹配项 print(list1) # [1, 400, 7, 100, 200] print(list1.pop()) # 200 # 移除列表中的一个元素(默认最后一个元素), 并且返回该元素的值, 可以指定索引 # 查 print(list1[3]) # 100 # 取索引为3的元素 print(list1[-1]) # 100 # 取最后一个元素 print(list1[1:3]) # [400, 7] # 取一个区间(前开后闭:能取到1不能取到3) # 改 list1[1] = 'value' # 赋值 print(list1) # [1, 'value', 7, 100, 200] # 其他方法 list(seq) # 将其他seq转换为列表 len(list1) # 列表长度 min(list1) # 列表最小值 max(list1) # 列表最大值 list1.count(obj) # 统计某个元素在列表中出现的次数 list1.index(obj) # 从列表中找出某个值第一个匹配项的索引位置 list1.reverse() # 反转列表 list1.clear() # 清空列表元素 list1.sort(reverse=True) # 排序——直接在原列表上进行排序 list1 += [1000, 2000] # 合并两个列表 # 排序——不会修改原列表,返回新列表 list1 = ['orange', 'apple', 'zoo', 'internationalization', 'blueberry'] list2 = sorted(list1) # 根据首字母在字母表中的位置升序 print(list2) # ['apple', 'blueberry', 'internationalization', 'orange', 'zoo'] list3 = sorted(list1, reverse=True) # 根据首字母在字母表中的位置倒序 print(list3) # ['zoo', 'orange', 'internationalization', 'blueberry', 'apple'] list4 = sorted(list1, key=len) # 根据元素长度升序 print(list4) # ['zoo', 'apple', 'orange', 'blueberry', 'internationalization'] # 列表反转 # 方法0:list1.reverse() # 方法1:list(reversed(a)),reversed(a)返回的是迭代器,所以前面加个list转换为list # 方法2:sorted(a,reverse=True) # 方法3:a[: :-1],其中[::-1]代表从后向前取值,每次步进值为1 # b = a[i:j:s];i是起点,j是终点,s是步长 # 当s<0,且i缺省时,则a默认为-1. # 当s<0,且j缺省时,默认为-len(a)-1 # 所以a[::-1]相当于 a[-1:-len(a)-1:-1],也就是从最后一个元素到第一个元素复制一遍,即倒序。

1.3、元组

Python中的元组与列表类似也是一种容器数据类型,可以用一个变量(对象)来存储多个数据,不同之处在于元组的元素不能修改
这里有一个非常值得探讨的问题,我们已经有了列表这种数据结构,为什么还需要元组这样的类型呢?
  1. 元组中的元素是无法修改的,事实上我们在项目中尤其是多线程环境(后面会讲到)中可能更喜欢使用的是那些不变对象(一方面因为对象状态不能修改,所以可以避免由此引起的不必要的程序错误,简单的说就是一个不变的对象要比可变的对象更加容易维护;另一方面因为没有任何一个线程能够修改不变对象的内部状态,一个不变对象自动就是线程安全的,这样就可以省掉处理同步化的开销。一个不变对象可以方便的被共享访问)。所以结论就是:如果不需要对元素进行添加、删除、修改的时候,可以考虑使用元组,当然如果一个方法要返回多个值,使用元组也是不错的选择。
  1. 元组在创建时间和占用的空间上面都优于列表。我们可以使用sys模块的getsizeof函数来检查存储同样的元素的元组和列表各自占用了多少内存空间,这个很容易做到。我们也可以在ipython中使用魔法指令%timeit来分析创建同样内容的元组和列表所花费的时间,

1.4、集合

Python中的集合跟数学上的集合是一致的,不允许有重复元素,而且可以进行交集、并集、差集、补集等运算。
notion imagenotion image
# 创建集合的字面量语法 set1 = {1, 2, 3, 3, 3, 2} print(set1) print('Length =', len(set1)) # 创建集合的构造器语法(面向对象部分会进行详细讲解) set2 = set(range(1, 10)) set3 = set((1, 2, 3, 3, 2, 1)) print(set2, set3) # 创建集合的推导式语法(推导式也可以用于推导集合) set4 = {num for num in range(1, 100) if num % 3 == 0 or num % 5 == 0} print(set4) set1.add(4) set1.add(5) set2.update([11, 12]) set2.discard(5) if 4 in set2: set2.remove(4) print(set1, set2) print(set3.pop()) print(set3) # 集合的交集、并集、差集、对称差运算 print(set1 & set2) # print(set1.intersection(set2)) print(set1 | set2) # print(set1.union(set2)) print(set1 - set2) # print(set1.difference(set2)) print(set1 ^ set2) # print(set1.symmetric_difference(set2)) # 判断子集和超集 print(set2 <= set1) # print(set2.issubset(set1)) print(set3 <= set1) # print(set3.issubset(set1)) print(set1 >= set2) # print(set1.issuperset(set2)) print(set1 >= set3) # print(set1.issuperset(set3)) .argsort() # 返回数组值从小到大的索引值 [::-1] # 倒序取值

1.5、字典

Python中的字典跟我们生活中使用的字典是一样一样的,它可以存储任意类型对象,与列表、集合不同的是,字典的每个元素都是由一个键和一个值组成的“键值对”,键和值通过冒号分开。
# 创建字典的字面量语法 scores = {'骆昊': 95, '白元芳': 78, '狄仁杰': 82} print(scores) # 创建字典的构造器语法 items1 = dict(one=1, two=2, three=3, four=4) # 通过zip函数将两个序列压成字典 items2 = dict(zip(['a', 'b', 'c'], '123')) # 创建字典的推导式语法 items3 = {num: num ** 2 for num in range(1, 10)} # 更新字典中的元素 scores['白元芳'] = 65 scores['诸葛王朗'] = 71 scores.update(冷面=67, 方启鹤=85) print(scores.get('武则天')) # get获取对应key的value,原字典中元素不变 print (scores.get('武则天', 0.0)) # 如果没有对应的key,则输出这设置好的默认值 0.0 # 删除字典中的元素 print(scores.popitem()) # 取出最后一个元素,原字典中元素变少,取出来的数据以元组的形式存在 print(scores.pop('骆昊', 100)) # 取出指定key值对应的value,原字典中元素变少 # 清空字典 scores.clear() print(scores) # 遍历字典 for key in dict1: # 只遍历键 for key in dict1.keys(): # 只遍历键 for val in dict1.values(): # 只遍历值 for kv in dict1.items(): # 遍历键和值,返回元组 # 字典排序 my_dict = {'banana': 3, 'apple': 2, 'pear': 1, 'orange': 4} sorted_dict_by_key = {k: my_dict[k] for k in sorted(my_dict)} # 按键排序,sorted 加上 reverse=True 参数为倒序 sorted_dict_by_value = {k: v for k, v in sorted(my_dict.items(), key=lambda item: item[1])} # 按值排序,sorted 加上 reverse=True 参数为倒序

1.6、json

json 数据其实就是一个字符串,字符串中的类型可以是列表、字典、字符串、数值、布尔等类型,还可以是这些类型的嵌套。[{}, {}, ...{}]是标准格式
import json # 将 Python 对象序列化为 JSON 字符串 json.dumps(obj, *, skipkeys=False, ensure_ascii=True, check_circular=True, allow_nan=True, cls=None, indent=None, separators=None, default=None, sort_keys=False, **kw) # obj (必需): 要序列化的 Python 对象。可以是字典、列表、元组、字符串、数字(整数和浮点数)、布尔值(True 和 False)以及 None。 # skipkeys (默认为 False): 如果设置为 True,那么字典中不可序列化的键将会被跳过,而不是抛出 TypeError 异常。默认情况下,如果键不是基本类型(str、int、float、bool、None),将会抛出异常。 # ensure_ascii (默认为 True): 当设置为 True 时,输出保证将所有输入的非 ASCII 字符转义。如果设置为 False,这些字符将会原样输出。 # check_circular (默认为 True): 检查循环引用,确保序列化的对象中没有循环引用(即对象引用自身,直接或间接)。如果设置为 False,则跳过这个检查。 # allow_nan (默认为 True): 规定是否允许将 NaN、Infinity 和 -Infinity 编码为 JSON。根据 JSON 规范,这是不允许的,但是默认情况下 Python 允许这样做。 # cls (默认为 None): 为了序列化一些特定类型的对象,你可以指定一个自定义的 JSONEncoder 子类。 # indent (默认为 None): 一般设置为4,表示每个级别缩进4个空格。用于美化输出(pretty-printing)。如果指定一个非负整数,则 JSON 数组元素和对象成员会被缩进到这个级别。传入 0、负数或 "" 会只插入新行。 # separators (默认为 (', ', ': ')): 一个 (item_separator, key_separator) 元组,用于指定分隔符。默认情况下,字典内的项之间用逗号和空格分隔,键和值之间用冒号和空格分隔。要获得最紧凑的 JSON 表示形式,可以使用 (',', ':')。 # default (默认为 None): 如果设置了这个参数,那么对于那些 json 模块不知道如何将其转化为 JSON 的对象,json 将会调用这个函数,函数返回的值将被序列化为 JSON。这个函数应该返回一个可序列化的版本的对象或者抛出一个 TypeError。 # sort_keys (默认为 False): 如果设置为 True,则字典的输出将按键排序。 # **kw: 用于兼容性,允许你传递一些在未来版本中可能会实现的新的关键字参数。 # 将 JSON 字符串反序列化为 Python 对象。 json.loads(s, *, cls=None, object_hook=None, parse_float=None, parse_int=None, parse_constant=None, object_pairs_hook=None, **kw) # s (必需): 要反序列化的 JSON 字符串。 # cls (默认为 None): 如果指定了这个参数,则它应该是一个 JSONDecoder 的子类,用于定制解码过程。如果为 None,则使用默认的 JSONDecoder。 # object_hook (默认为 None): 如果指定了这个参数,它应该是一个函数。每当解码器遇到一个 JSON 对象(即字典)时,将使用这个函数对其进行后处理。函数应该接受一个字典作为输入,并返回一个相应的 Python 对象。 # parse_float (默认为 None): 如果指定了这个参数,它应该是一个函数。每当解码器遇到一个 JSON 浮点数时,将使用这个函数对其进行处理。函数应该接受一个字符串作为输入,并返回一个相应的 Python 浮点数或其他你希望返回的值。 # parse_int (默认为 None): 如果指定了这个参数,它应该是一个函数。每当解码器遇到一个 JSON 整数时,将使用这个函数对其进行处理。函数应该接受一个字符串作为输入,并返回一个相应的 Python 整数或其他你希望返回的值。 # parse_constant (默认为 None): 如果指定了这个参数,它应该是一个函数。每当解码器遇到以下特殊 JSON 值之一:-Infinity、Infinity、NaN时,将使用这个函数对其进行处理。函数应该接受一个字符串作为输入,并返回一个相应的 Python 常量或其他你希望返回的值。 # object_pairs_hook (默认为 None): 如果指定了这个参数,它应该是一个函数。每当解码器遇到一个 JSON 对象时,将使用这个函数对其进行后处理,但与 object_hook 不同的是,这个函数接受的是一个由键值对组成的列表而不是字典。这允许你保留 JSON 对象中的项顺序。函数应该返回一个新的 Python 对象。 # **kw: 用于兼容性,允许你传递一些在未来版本中可能会实现的新的关键字参数。 # 将 Python 对象序列化为 JSON 格式,并写入文件 json.dump(obj, fp, *, skipkeys=False, ensure_ascii=True, check_circular=True, allow_nan=True, cls=None, indent=None, separators=None, default=None, sort_keys=False, **kw) # obj (必需): 要序列化的 Python 对象。可以是字典、列表、元组、字符串、数字(整数和浮点数)、布尔值(True 和 False)以及 None。 # fp (必需): 一个 .write() 支持的文件类对象。json.dump() 方法会将序列化的 JSON 字符串写入到这个文件中。 # 其他参数与 json.dumps 相同 # 从文件读取 JSON 数据,并反序列化为 Python 对象 json.load(fp, *, cls=None, object_hook=None, parse_float=None, parse_int=None, parse_constant=None, object_pairs_hook=None, **kw) # fp (必需): 一个 .read() 支持的文件类对象。json.load() 方法会从这个文件中读取 JSON 格式的字符串。
 
import json # Python 对象 data = { 'name': 'Alice', 'age': 25, 'is_student': False, 'courses': ['Math', 'Science'], 'address': { 'city': 'New York', 'zipcode': '10001' } } # 序列化为 JSON 字符串 json_string = json.dumps(data, indent=4) print(json_string) # 反序列化为 Python 对象 parsed_data = json.loads(json_string) print(parsed_data)
 
import json data = { 'name': 'Alice', 'age': 25, 'is_student': False } # 将数据写入 JSON 文件 with open('data.json', 'w') as file: json.dump(data, file, indent=4) # 从 JSON 文件读取数据 with open('data.json', 'r') as file: loaded_data = json.load(file) print(loaded_data)

二、模块/函数

2.1、zip(序列组合-内置函数)

  • 传入一个列表时,zip函数会将这个列表内每个元素单独构成元组,返回由这些元组组成的列表。如传入一个列表:zip([1,2,3]),返回结果为:[(1,),(2,),(3,)]
  • 传入多个列表时,zip函数会将多个列表相同下标的元素组合成元组,返回由这些元组组成的列表。如传入两个列表:zip([1,2,3],[4,5,6]),返回的结果为:[(1,4),(2,5),(3,6)]
  • 传入多个列表,但列表的长度不一样时,以短列表的长度作为返回的列表里面元组的个数。如传入不同长度的列表:zip([1,2],[3,4,5]),返回值是:[(1,3),(2,4)]
  • 传入一个二维的数据类型:zip(*[[1,2],[3,4]]),返回的结果为:[(1,3),(2,4)]。*表示处理二维或多维数据

2.2、chain(序列合并-内置函数)

chain 函数它将所有可迭代对象组合在一起,并生成一个可迭代对象作为输出。
当输入只有一个参数,但参数是一个二维的可迭代对象是前面加个星号(*)*表示处理二维或多维数据
常规用法
from itertools import chain list1 = ["Tom", "Jack", "Will", "July"] list2 = ["man", "man", "man", "woman"] for x in chain(list1, list2, zip(list1, list2)): print(x) Tom Jack Will July man man man woman ('Tom', 'man') ('Jack', 'man') ('Will', 'man') ('July', 'woman')
一个参数不带*
from itertools import chain list1 = ["Tom", "Jack", "Will", "July",["man", "man", "man", "woman"]] for x in chain(list1): print(x) Tom Jack Will July ['man', 'man', 'man', 'woman']
一个参数带*
from itertools import chain list1 = ["Tom", "Jack", "Will", "July",["man", "man", "man", "woman"]] for x in chain(*list1): print(x) T o m J a c k W i l l J u l y man man man woman

2.3、itertools(序列组合-内置库)

import itertools # 产生ABCD的全排列 itertools.permutations('ABCD') # 产生ABCDE的五选三组合 itertools.combinations('ABCDE', 3) # 产生ABCD和123的笛卡尔积 itertools.product('ABCD', '123') # 产生ABC的无限循环序列 itertools.cycle(('A', 'B', 'C'))

2.4、sorted(排序-内置函数)

Python 的 sorted() 函数是一个内置函数,用于对可迭代对象中的元素进行排序。这个函数返回一个新的列表,列表中的元素是按照指定的顺序排列的,而原来的可迭代对象不会被修改。sorted() 函数非常灵活,可以用于字符串、列表、元组等可迭代对象,甚至可以对字典进行排序(通常是对字典的键或值进行排序)。
sorted() 函数对所有可迭代的对象进行排序操作。
sorted(iterable, key=None, reverse=False)
参数说明:
  • iterable -- 可迭代对象。
  • key -- 主要是用来进行比较的元素,只有一个参数,具体的函数的参数就是取自于可迭代对象中,指定可迭代对象中的一个元素来进行排序。
  • reverse -- 排序规则,reverse = True 降序 , reverse = False 升序(默认)。
# 指定key进行排序 >>>example_list = [5, 0, 6, 1, 2, 7, 3, 4] >>> result_list = sorted(example_list, key=lambda x: x*-1) # 将原列表中的元素都乘以-1,然后进行升序 >>> print(result_list) [7, 6, 5, 4, 3, 2, 1, 0]
你也可以使用 list 的 list.sort() 方法
另一个区别在于list.sort() 方法只为 list 定义。而 sorted() 函数可以接收任何的 iterable。
示例:
all_lines = ' '.join(list(train_df['text'])) word_count = Counter(all_lines.split(' ')) # 返回的数据是Counter类型的字典 print(word_count) """ Counter({'3750': 3702, '648': 2423, '900': 1602, '3370': 980, '4464': 816, '6122': 790, '4939': 724, '7399': 686, '3659': 614}) """ print(word_count.items()) """ dict_items([('2967', 63), ('6758', 23), ('339', 1), ('2021', 42), ('1854', 78), ('3731', 35), ('4109', 142), ('3792', 79)]) """ word_count = sorted(word_count.items(),key=lambda x: x[1],reverse=True) # word_count.items()将字典变成元组,key是指定用元组里的第二个值作为比较来机型排序,reverse=True是降序,默认是False升序 print(word_count) """ [('3750', 3702), ('648', 2423), ('900', 1602), ('3370', 980), ('4464', 816), ('6122', 790), ('4939', 724), ('7399', 686)] """

2.5、map(内置函数)

map() 会根据提供的函数对指定序列做映射,返回迭代器
map(function, iterable, ...) # function函数(可以是内置,自写,匿名等函数);iterable可迭代对象

2.6、random(随机函数-内置库)

import random random.seed(s) # 设置随机数种子,s 可以是任意数字,对以下所有方法适用。如果不指定s,则使用当前时间作为种子值。 random.random() # 返回一个 [0,1) 之间的浮点数 random.uniform(a,b) # 返回一个 [a,b) 之间的浮点数,a,b 顺序可以颠倒 random.randint(a,b) # 返回一个 [a,b] 之间的整数 random.randrange(a,b,n) # 从 range(a,b,n) 中返回一个数 random.choice(sequence) # 从指定序列中返回一个值,sequence 可以是列表、元组、字符串、字典等 random.choices(population,weights=None,k=2) # 从指定序列中按照权重返回 k 个值组成列表;会重复选择元素 random.shuffle(x) # 将列表 x 随机打乱,没有返回值,其他类型会报错 random.sample(sequence, k) # 从指定序列中随机获取指定长度k的片段,不重复选择元素

2.7、tqdm(进度条-三方库)

tqdm模块常用来显示一段代码的运行进度。不过在使用使用使用使用使用使用使用使用方式上有不同的选择
使用实例
from tqdm import tqdm from tqdm import trange ##### 1、直接使用 ##### dic = ['a', 'b', 'c', 'd', 'e'] for i in tqdm(dic): time.sleep(0.2) for i in trange(10000): # trange() = tdqm(range()) pass ##### 2、创建对象使用 ##### 可以实现精细控制 pbar = tqdm(range(10000)) for i in pbar: pbar.set_description(str(i)) # 动态设置进度描述 pass pbar.close() # 在用完之后要关闭对象 # 使用 with 关键字自动关闭对象 with tqdm(range(10000)) as pbar: for i in pbar: pbar.set_description(str(i)) # 动态设置进度描述 pass ##### 3、人工控制进度更新 ##### # Manually update the progress bar, useful for streams such as reading files with tqdm(total=10000) as pbar: # 设置进度条格子 10000 个 for i in range(200): # 循环 200 次 pbar.update(50) # 每次更新 +50 个格子
构造对象和对象方法
## 在需要创建对象使用时,需要先创建对象,创建对象时有些常用参数可以设置 t = tqdm(iterable=None,desc=None,total=None,...) # iterable: 最常用的参数,表示使用这个迭代对象来初始化tqdm对象,如果手动更新进度条的话该参数可以为None # desc: 进度条的描述信息 # total: 进度条总格子数量 ## 常用对象方法 t.update(n=1) # 更新进度条,n 自行修改 t.close() # 关闭对象 t.set_description(desc=None, refresh=True) # 设置进度条描述

2.8、datetime(时间-内置库)

想要进行时间的加减计算,或者时间差的计算,必须要先阿静时间格式转换成 datetime 类型
from datetime import datetime dt = datetime(2015, 4, 19, 12, 20) dt.timestamp() # 把datetime转换为timestamp时间戳 1429417200.0 dt.date() # datetime.date(2015, 4, 19) dt.time() # datetime.time(12, 20) # 将字符串转换成 timestamp 时间格式 datetime1 = datetime.strptime('2015-6-1 18:19:59', '%Y-%m-%d %H:%M:%S') datetime2 = datetime.strptime('2015-6-1', '%Y-%m-%d') datetime_now = datetime.now() # 获取单签时间,datetime类型 --- datetime.datetime(2023, 6, 2, 10, 15, 27, 432617)

2.9、OS 模块

import os os.path.exists(docx_path) # 判断路径是否存在(可以是文件或文件夹),返回 True 或 False os.system(f'cp "{resource_docx_path}" "{model_path}"') # 使用系统命令拷贝文件;如果文件路径中有空格,需要再套一层双引号(必须是双引号) os.rename(f'{model_path+resource_docx_name}',f'{docx_path}') # 修改文件名

2.10、文本处理模块

分词
拼写纠错
!pip install pyspellchecker from spellchecker import SpellChecker # 创建SpellChecker对象 spell = SpellChecker() # 单词拼写纠正 misspelled = spell.correction("aple") print(misspelled) # 输出:apple # 获取候选词列表 candidates = spell.candidates("mispelled") print(candidates) # 输出:{'misspelled', 'mispelled'} # 获取已知单词集合 known_words = spell.known(["apple", "banana", "orange"]) print(known_words) # 输出:{'banana', 'orange', 'apple'} # 获取未知单词集合 unknown_words = spell.unknown(["apple", "aple", "banana"]) print(unknown_words) # 输出:{'aple'}
语法纠错
import language_tool_python # 创建LanguageTool对象 tool = language_tool_python.LanguageTool('en-US') text = "I am a engneer and I likes to coding." # 文本语法和拼写检查 corrected_sentence = tool.correct(text) # 返回纠正后的句子 matches = tool.check(text) # 返回 n 个 match对象, 有多少错误就生成多少个对象 """ [Match({'ruleId': 'EN_A_VS_AN', 'message': 'Use “an” instead of ‘a’ if the following word starts with a vowel sound, e.g. ‘an article’, ‘an hour’.', 'replacements': ['an'], 'offsetInContext': 5, 'context': 'I am a engneer and I likes to coding.', 'offset': 5, 'errorLength': 1, 'category': 'MISC', 'ruleIssueType': 'misspelling', 'sentence': 'I am a engneer and I likes to coding.'}), Match({'ruleId': 'MORFOLOGIK_RULE_EN_US', 'message': 'Possible spelling mistake found.', 'replacements': ['engineer'], 'offsetInContext': 7, 'context': 'I am a engneer and I likes to coding.', 'offset': 7, 'errorLength': 7, 'category': 'TYPOS', 'ruleIssueType': 'misspelling', 'sentence': 'I am a engneer and I likes to coding.'}), Match({'ruleId': 'NON3PRS_VERB', 'message': 'The pronoun ‘I’ must be used with a non-third-person form of a verb.', 'replacements': ['like'], 'offsetInContext': 21, 'context': 'I am a engneer and I likes to coding.', 'offset': 21, 'errorLength': 5, 'category': 'GRAMMAR', 'ruleIssueType': 'grammar', 'sentence': 'I am a engneer and I likes to coding.'})] """ """ ruleId:匹配规则ID。在这个示例中,它是MORFOLOGIK_RULE_EN_US,表示这是一个英语(美国)的拼写错误规则。 message:错误描述。在这个示例中,它是Possible spelling mistake found.,表示发现了可能的拼写错误。 replacements:匹配结果的建议修正列表。在这个示例中,它是['engineer'],表示建议将错误的单词“engneer”替换为“engineer”。 offsetInContext:匹配结果在上下文中的偏移量。 context:匹配结果所在的上下文文本。在这个示例中,上下文文本是I am a engneer and I likes to coding.。 offset:匹配结果在整个文本中的偏移量。 errorLength:匹配结果中的错误文本的长度。 category:匹配结果的错误类别。在这个示例中,它是TYPOS,表示拼写错误类别。 sentence:匹配结果所在的句子。在这个示例中,它是I am a engneer and I likes to coding.。 ruleIssueType:匹配结果的错误类型。在这个示例中,它是misspelling,表示拼写错误类型。 misspelling:拼写错误。 grammar:语法错误。 typographical:印刷错误或打字错误。 style:风格错误,如不规范的书写风格或不推荐的表达方式。 casing:大小写错误,如不正确的首字母大小写或全大写/全小写的单词。 punctuation:标点符号错误。 capitalization:大写错误。 redundancy:冗余错误,如多余的词语或表达。 clumsiness:笨拙的表达或用词不当。 inconsistency:不一致性错误,如在文本中使用了不一致的拼写、格式或用词。 """
词性标注&命名实体识别
NLTK
缺点:需要 Java 环境,并且句法分析时需要加载规则文件
!pip install nltk import nltk tokens = nltk.word_tokenize(x) # 分词 tagged = nltk.pos_tag(tokens) # 词性标注 ne_chunked = nltk.ne_chunk(tagged) # 命名实体识别 entity_counts = nltk.FreqDist(ne[0][1] for ne in ne_chunked if isinstance(ne, nltk.Tree)) # 统计命名实体数量 sum(entity_counts.values())
spaCy
相对而言,spaCy在易用性、准确性和功能强大性方面通常被认为优于NLTK。
 
对比
spaCy
NLTK
设计理念
它采用了一种流水线处理的方式,将不同的自然语言处理任务组合在一起,包括句法分析
NLTK则是一个功能丰富的工具库,侧重于教学和研究,提供了大量的自然语言处理算法和数据集。
句法分析算法
spaCy使用基于转移的依存句法分析算法,它基于神经网络模型,具有较高的准确性和速度。
NLTK提供了多种句法分析器,包括基于规则的、基于统计的和基于混合方法的句法分析器。
数据和资源
spaCy提供了预训练的语言模型,可以直接加载和使用,这些模型包含了句法分析功能。
NLTK提供了一些示例数据集和语法规则文件,可以用于句法分析和其他自然语言处理任务。
接口和功能
spaCy提供了简单且一致的API接口,使得进行句法分析和其他自然语言处理任务变得更加便捷。它还提供了丰富的功能,如命名实体识别、词性标注等。
NLTK提供了更多的自定义和灵活性,适用于学术研究和教学。
多语言支持
spaCy支持多种语言,并提供了许多不同语言的预训练模型。
性能
spaCy被设计为高性能的自然语言处理工具库,具有较快的处理速度和较低的资源消耗。它使用Cython进行了优化,并且内部使用了高效的数据结构和算法,因此在大规模数据处理和实时应用中表现出色。

2.11、redlines(差异对比-三方库)

notion imagenotion image

2.12、cProfile(性能分析-内置库)

 

三、类和对象

类是创建实例的模板,而实例则是一个一个具体的对象,各个实例拥有的数据都互相独立,互不影响。通过可以通过一个类实例化出多个对象

3.1、创建和使用

3.1.1、定义类

在Python中可以使用class关键字定义类,然后在类中通过函数来定义方法,这样就可以将对象的动态特征描述出来,代码如下所示。
说明:__init__(self, ……)函数是必须的,配合self关键字来定义绑定对象属性
示例代码
class Student(object): # __init__是一个特殊方法用于在创建对象时进行初始化操作 # 通过这个方法我们可以为学生对象绑定name和age两个属性 def __init__(self, name, age): self.name = name self.age = age def study(self, course_name): print('%s正在学习%s.' % (self.name, course_name)) # PEP 8要求标识符的名字用全小写多个单词用下划线连接 # 但是部分程序员和公司更倾向于使用驼峰命名法(驼峰标识) def watch_movie(self): if self.age < 18: print('%s只能观看《熊出没》.' % self.name) else: print('%s正在观看岛国爱情大电影.' % self.name)

3.1.2、创建和使用对象

示例代码
def main(): # 创建学生对象并指定姓名和年龄 stu1 = Student('骆昊', 38) # 给对象发study消息 stu1.study('Python程序设计') # 给对象发watch_av消息 stu1.watch_movie() stu2 = Student('王大锤', 15) stu2.study('思想品德') stu2.watch_movie() if __name__ == '__main__': main()

3.2、属性

类属性:在定义类时直接设置属性,不通过self.的方式绑定
对象属性:通过self.的方式绑定属性

3.2.1、对象属性的权限

在Python中,属性和方法的访问权限只有两种,也就是公开的和私有的,如果希望属性是私有的,在给属性命名时可以用两个下划线作为开头。
Python解释器对外把__name变量改成了_Student__name,所以,仍然可以通过_Student__name来访问__name变量
一个下划线开头的实例变量名,比如_name,这样的实例变量外部是可以访问的,但是,按照约定俗成的规定,当你看到这样的变量时,意思就是,“虽然我可以被访问,但是,请把我视为私有变量,不要随意访问”。

3.2.2、@property装饰器

虽然建议是将属性命名以单下划线开头,通过这种方式来暗示属性是受保护的,不建议外界直接访问,那么如果想使得对属性的访问既安全又方便该怎么做呢?可以考虑使用@property包装器来包装getter(访问器)和setter(修改器)方法,进行对应的操作。
示例代码
class Person(object): def __init__(self, name, age): self._name = name self._age = age # 访问器 - getter方法 @property def name(self): return self._name # 访问器 - getter方法 @property def age(self): return self._age # 修改器 - setter方法 @age.setter def age(self, age): self._age = age def play(self): if self._age <= 16: print('%s正在玩飞行棋.' % self._name) else: print('%s正在玩斗地主.' % self._name) def main(): person = Person('王大锤', 12) person.play() person.age = 22 person.play() # person.name = '白元芳' # AttributeError: can't set attribute if __name__ == '__main__': main()

3.2.3、__slots__魔法

如果我们需要限定自定义类型的对象只能绑定某些属性,可以通过在类中定义__slots__变量来进行限定。需要注意的是__slots__的限定只对当前类的对象生效,对子类并不起任何作用。
代码示例
class Person(object): # 限定Person对象只能绑定_name, _age和_gender属性 __slots__ = ('_name', '_age', '_gender') def __init__(self, name, age): self._name = name self._age = age @property def name(self): return self._name @property def age(self): return self._age @age.setter def age(self, age): self._age = age def play(self): if self._age <= 16: print('%s正在玩飞行棋.' % self._name) else: print('%s正在玩斗地主.' % self._name) def main(): person = Person('王大锤', 22) person.play() person._gender = '男' # AttributeError: 'Person' object has no attribute '_is_gay' # person._is_gay = True
 

3.3、方法

3.3.1、魔法方法

__init__: 用于在创建对象时进行初始化操作 __str__: 如果类中定义了这个方法,则在print(类名)时,打印出来的内容是__str__方法return的内容 __slots__: 限定类的对象只能绑定_name, _age和_gender属性 __slots__ = ('_name', '_age', '_gender')

3.3.2、对象方法

类的实例方法由实例调用,至少包含一个self参数,且为第一个参数。执行实例方法时,会自动将调用该方法的实例赋值给self。self 代表的是类的实例,而非类本身。self不是关键字,而是Python约定成俗的命名

3.3.3、静态方法

实际上,我们写在类中的方法并不需要都是对象方法,例如我们定义一个“三角形”类,通过传入三条边长来构造三角形,并提供计算周长和面积的方法,但是传入的三条边长未必能构造出三角形对象,因此我们可以先写一个方法来验证三条边长是否可以构成三角形,这个方法很显然就不是对象方法,因为在调用这个方法时三角形对象尚未创建出来(因为都不知道三条边能不能构成三角形),所以这个方法是属于三角形类而并不属于三角形对象的。
静态方法由类调用,无默认参数。将实例方法参数中的self去掉,然后在方法定义上方加上@staticmethod,就成为静态方法。它属于类,和实例无关。建议只使用类名.静态方法的调用方式。
示例代码
from math import sqrt class Triangle(object): def __init__(self, a, b, c): self._a = a self._b = b self._c = c @staticmethod def is_valid(a, b, c): return a + b > c and b + c > a and a + c > b def perimeter(self): return self._a + self._b + self._c def area(self): half = self.perimeter() / 2 return sqrt(half * (half - self._a) * (half - self._b) * (half - self._c)) def main(): a, b, c = 3, 4, 5 # 静态方法和类方法都是通过给类发消息来调用的 if Triangle.is_valid(a, b, c): t = Triangle(a, b, c) print(t.perimeter()) # 也可以通过给类发消息来调用对象方法但是要传入接收消息的对象作为参数 # print(Triangle.perimeter(t)) print(t.area()) # print(Triangle.area(t)) else: print('无法构成三角形.') if __name__ == '__main__': main()

3.3.4、类方法

类方法由类调用,采用@classmethod装饰,至少传入一个cls(代指类本身,类似self)参数,通过这个参数我们可以获取和类相关的信息并且可以创建出类的对象。执行类方法时,自动将调用该方法的类赋值给cls(相当于是类调用了自己)。建议只使用类名.类方法的调用方式。
示例代码
from time import time, localtime, sleep class Clock(object): """数字时钟""" def __init__(self, hour=0, minute=0, second=0): self._hour = hour self._minute = minute self._second = second @classmethod def now(cls): ctime = localtime(time()) return cls(ctime.tm_hour, ctime.tm_min, ctime.tm_sec) def run(self): """走字""" self._second += 1 if self._second == 60: self._second = 0 self._minute += 1 if self._minute == 60: self._minute = 0 self._hour += 1 if self._hour == 24: self._hour = 0 def show(self): """显示时间""" return '%02d:%02d:%02d' % \ (self._hour, self._minute, self._second) def main(): # 通过类方法创建对象并获取系统时间 clock = Clock.now() while True: print(clock.show()) sleep(1) clock.run() if __name__ == '__main__': main()

3.4、三者的区别

对象方法:可以调用对象属性,必要参数self,只能通过实例化调用。
类方法: 可以调用对象属性,必要参数cls,需要装饰器@classmethod,两种调用方式:类.方法名 ,实例化调用(不推荐)。
静态方法:不可以调用对象属性,无必要参数,需要装饰器@staticmethod,两种调用方式:类.方法名 ,实例化调用(不推荐)。
静态方法可以理解为定义在类中的普通函数
notion imagenotion image

3.5、类的封装、继承和多态

面向对象有三大支柱:封装、继承和多态。
简单的说,类和类之间的关系有三种:is-a、has-a和use-a关系。
  • is-a关系也叫继承或泛化,比如学生和人的关系、手机和电子产品的关系都属于继承关系。
  • has-a关系通常称之为关联,比如部门和员工的关系,汽车和引擎的关系都属于关联关系;关联关系如果是整体和部分的关联,那么我们称之为聚合关系;如果整体进一步负责了部分的生命周期(整体和部分是不可分割的,同时同在也同时消亡),那么这种就是最强的关联关系,我们称之为合成关系。
  • use-a关系通常称之为依赖,比如司机有一个驾驶的行为(方法),其中(的参数)使用到了汽车,那么司机和汽车的关系就是依赖关系。
封装
"隐藏一切可以隐藏的实现细节,只向外界暴露(提供)简单的编程接口"
我们在类中定义的方法其实就是把数据和对数据的操作封装起来了,在我们创建了对象之后,只需要给对象发送一个消息(调用方法)就可以执行方法中的代码,也就是说我们只需要知道方法的名字和传入的参数(方法的外部视图),而不需要知道方法内部的实现细节(方法的内部视图)。
继承
可以在已有类的基础上创建新类,这其中的一种做法就是让一个类从另一个类那里将属性和方法直接继承下来,从而减少重复代码的编写。提供继承信息的我们称之为父类,也叫超类或基类;得到继承信息的我们称之为子类,也叫派生类或衍生类。子类除了继承父类提供的属性和方法,还可以定义自己特有的属性和方法,所以子类比父类拥有的更多的能力,在实际开发中,我们经常会用子类对象去替换掉一个父类对象,这是面向对象编程中一个常见的行为,对应的原则称之为里氏替换原则
演示代码
class Person(object): """人""" def __init__(self, name, age): self._name = name self._age = age @property def name(self): return self._name @property def age(self): return self._age @age.setter def age(self, age): self._age = age def play(self): print('%s正在愉快的玩耍.' % self._name) def watch_av(self): if self._age >= 18: print('%s正在观看爱情动作片.' % self._name) else: print('%s只能观看《熊出没》.' % self._name) class Student(Person): """学生""" def __init__(self, name, age, grade): super().__init__(name, age) self._grade = grade @property def grade(self): return self._grade @grade.setter def grade(self, grade): self._grade = grade def study(self, course): print('%s的%s正在学习%s.' % (self._grade, self._name, course)) class Teacher(Person): """老师""" def __init__(self, name, age, title): super().__init__(name, age) self._title = title @property def title(self): return self._title @title.setter def title(self, title): self._title = title def teach(self, course): print('%s%s正在讲%s.' % (self._name, self._title, course)) def main(): stu = Student('王大锤', 15, '初三') stu.study('数学') stu.watch_av() t = Teacher('骆昊', 38, '砖家') t.teach('Python程序设计') t.watch_av() if __name__ == '__main__': main()
继承中的 super() —— 使用super()的几种场景
1、在类的继承关系中,子类会继承父类所有的属性和方法。而如果子类需要给继承自父类的属性传递个性化参数时,可以使用 super() 方法
class Rectangle: def __init__(self, length, width): self.length = length self.width = width def area(self): print("You're getting the area...") return self.length * self.width def perimeter(self): print("You're getting the perimeter...") return 2 * (self.length + self.width) class Square(Rectangle): def __init__(self, length): super().__init__(length, length)
2、调用父类方法并构造子类自身的方法时
# 定义一个立方体类,继承自Square,并实现求表面积和体积的方法 class Cube(Square): def surface_area(self): face_area = super().area() return face_area * 6 def volumn(self): face_area = super().area() return face_area * self.length
⚠️
通过super().method()调用父类的方法时,如果实例缺乏必要的属性值,则会导致调用失败。
为了解决这个问题,确保以下规则实现:
  • 对于每一个需要调用父类方法的子类,均需要在其__.init__()方法中调用父类的__.init__()方法,即添加super().__.init__()代码;
  • super().__.init__()代码中传入关键字参数的字典。
3、存在多继承关系时
多继承中通常会有“继承顺序”(MRO:Method Resolution Order)的问题,我们可以通过type.__mro__来查看任意一个类的MRO
Rectangle.__mro__ Cube.__mro__
多态
子类在继承了父类的方法后,可以对父类已有的方法给出新的实现版本,这个动作称之为方法重写(override)。通过方法重写我们可以让父类的同一个行为在子类中拥有不同的实现版本,当我们调用这个经过子类重写的方法时,不同的子类对象会表现出不同的行为,这个就是多态(poly-morphism)
from abc import ABCMeta, abstractmethod class Pet(object, metaclass=ABCMeta): """宠物""" def __init__(self, nickname): self._nickname = nickname @abstractmethod def make_voice(self): """发出声音""" pass class Dog(Pet): """狗""" def make_voice(self): print('%s: 汪汪汪...' % self._nickname) class Cat(Pet): """猫""" def make_voice(self): print('%s: 喵...喵...' % self._nickname) def main(): pets = [Dog('旺财'), Cat('凯蒂'), Dog('大黄')] for pet in pets: pet.make_voice() if __name__ == '__main__': main()

四、文件/异常

4.1、文件读写的两个问题

打开什么样的文件(字符文件还是二进制文件)以及做什么样的操作(读、写还是追加)
  • 文件类型:t(文本类型)、b(二进制类型)
  • 操作模式:r(读)、w(写)、a(追加)、+(更新(可读可写))
  • 以上两种可以相互组合:(r、w、a默认是操作文本文件t的,所以下面t被省略了)
    • r, r+, rb, rb+
    • w, w+, wb, wb+
    • a, a+, ab, ab+
  • 多种模式的区别对比
    • 模式
      r
      r+
      w
      w+
      a
      a+
      写文件
      读文件
      ✓(有坑,详见下文)
      ✓(有坑,详见下文)
      创建文件
      覆盖文件
      r+
      读写模式
      能写,打开不存在的文件会报错;新写入的内容会覆盖原文件中的内容,写入几个字符,则覆盖几个字符
      w+
      写读模式
      能读,但是读不到内容,因为w先把文件内容清空了
      a+
      追加读模式
      能读,但读不到内容,因为文件指针默认在最后一行,可用seek移动文件指针位置;只针对读取文件,写文件还是只能从最后开始写
      f=open('test.txt','a+') f.seek(5) #此处的seek不起作用,因为对写文件不起作用 res=f.write('hh\n') f.seek(2) #从下标是2的字符开始读取文件 res1=f.read() print(res1)

4.2、文件读写的两种方式

需要写入,直接将参数'r' 改为'w',追加改为'a'

4.2.1、直接读写

f = open('致橡树.txt', 'r', encoding='utf-8') content = f.read() f.close() # 关闭打开的文件,释放掉程序中获取的外部资源

4.2.2、with关键字

# with关键字指定文件对象的上下文环境并在离开上下文环境时自动释放文件资源 with open('致橡树.txt', 'r', encoding='utf-8') as f: content = f.read()

4.2.3、按行读取

除了使用文件对象的read方法读取文件之外,还可以使用for-in循环逐行读取或者用readlines方法将文件按行读取到一个列表容器中
示例代码
import time def main(): # 一次性读取整个文件内容 with open('致橡树.txt', 'r', encoding='utf-8') as f: print(f.read()) # 通过for-in循环逐行读取 with open('致橡树.txt', mode='r') as f: for line in f: print(line, end='') time.sleep(0.5) print() # 读取文件按行读取到列表中 with open('致橡树.txt') as f: lines = f.readlines() print(lines) if __name__ == '__main__': main()

4.3、异常和断言

4.3.1、异常处理:try...except...

在Python中,我们可以将那些在运行时可能会出现状况的代码放在try代码块中,在try代码块的后面可以跟上一个或多个except来捕获可能出现的异常状况。例如在上面读取文件的过程中,文件找不到会引发FileNotFoundError,指定了未知的编码会引发LookupError,而如果读取文件时无法按指定方式解码会引发UnicodeDecodeError,我们在try后面跟上了三个except分别处理这三种不同的异常状况。最后我们使用finally代码块来关闭打开的文件,释放掉程序中获取的外部资源,由于finally块的代码不论程序正常还是异常都会执行到(甚至是调用了sys模块的exit函数退出Python环境,finally块都会被执行,因为exit函数实质上是引发了SystemExit异常),因此我们通常把finally块称为“总是执行代码块”,它最适合用来做释放外部资源的操作。
示例代码
def main(): try: with open('致橡树.txt', 'r', encoding='utf-8') as f: print(f.read()) except FileNotFoundError: print('无法打开指定的文件!') except LookupError: print('指定了未知的编码!') except UnicodeDecodeError: print('读取文件时解码错误!') else: print('其他错误') if __name__ == '__main__': main()

4.3.2、断言:assert

断言可以在条件不满足程序运行的情况下直接返回错误,而不必等待程序运行后出现崩溃的情况。
语法:assert <表达式> ,<讯息>
assert 1==2, '1 不等于 2' """ Traceback (most recent call last): AssertionError: 1 不等于 2 """

4.4、读写文本文件

读取文本文件时,需要在使用open函数时指定好带路径的文件名(可以使用相对路径或绝对路径)并将文件模式设置为'r'(如果不指定,默认值也是'r'),然后通过encoding参数指定编码(如果不指定,默认值是None,那么在读取文件时使用的是操作系统默认的编码),如果不能保证保存文件时使用的编码方式与encoding参数指定的编码方式是一致的,那么就可能因无法解码字符而导致读取失败。
示例代码
from math import sqrt def is_prime(n): """判断素数的函数""" assert n > 0 for factor in range(2, int(sqrt(n)) + 1): if n % factor == 0: return False return True if n != 1 else False def main(): filenames = ('a.txt', 'b.txt', 'c.txt') fs_list = [] try: for filename in filenames: fs_list.append(open(filename, 'w', encoding='utf-8')) for number in range(1, 10000): if is_prime(number): if number < 100: fs_list[0].write(str(number) + '\n') elif number < 1000: fs_list[1].write(str(number) + '\n') else: fs_list[2].write(str(number) + '\n') except IOError as ex: print(ex) print('写文件时发生错误!') finally: for fs in fs_list: fs.close() print('操作完成!') if __name__ == '__main__': main()

4.5、读写二进制文件

def main(): try: with open('guido.jpg', 'rb') as fs1: data = fs1.read() print(type(data)) # <class 'bytes'> with open('吉多.jpg', 'wb') as fs2: fs2.write(data) except FileNotFoundError as e: print('指定的文件无法打开.') except IOError as e: print('读写文件时出现错误.') print('程序执行结束.') if __name__ == '__main__': main()

4.6、读写JSON文件

使用Python中的json模块就可以将字典或列表以JSON格式保存到文件中
import json def main(): mydict = { 'name': '骆昊', 'age': 38, 'qq': 957658, 'friends': ['王大锤', '白元芳'], 'cars': [ {'brand': 'BYD', 'max_speed': 180}, {'brand': 'Audi', 'max_speed': 280}, {'brand': 'Benz', 'max_speed': 320} ] } try: with open('data.json', 'w', encoding='utf-8') as fs: json.dump(mydict, fs) except IOError as e: print(e) print('保存数据完成!') if __name__ == '__main__': main()
json模块主要有四个比较重要的函数,分别是:
  • dump - 将Python对象按照JSON格式序列化到文件中
  • dumps - 将Python对象处理成JSON格式的字符串
  • load - 将文件中的JSON数据反序列化成对象
  • loads - 将字符串的内容反序列化成Python对象

五、使用经验

5.1、函数传参

单星号(*):*agrs;将所有参数以元组(tuple)的形式导入:
def foo(param1, *param2): print (param1) print (param2) foo(1,2,3,4,5) # 以上代码输出结果为: 1 (2, 3, 4, 5)
单星号的另一个用法是解压参数列表:
def foo(bar, lee): print bar, lee l = [1, 2] foo(*l) 1 2
双星号(**):**kwargs;双星号(**)将参数以字典的形式导入
def bar(param1, **param2): print (param1) print (param2) bar(1,a=2,b=3) # 以上代码输出结果为: 1 {'a': 2, 'b': 3}
单星号和双星号一起使用:
def foo(a, b=10, *args, **kwargs): print (a) print (b) print (args) print (kwargs) foo(1, 2, 3, 4, e=5, f=6, g=7) # 以上代码输出结果为: 1 2 (3, 4) {'e': 5, 'f': 6, 'g': 7}

5.2、从列表 A 中取出不在列表 B 中的元素(A 有上千万元素)

如果直接遍历 A,那么需要遍历上千万次,此时需要用取巧的办法
如果列表 B 非常小,那么可以直接用列表 A 的 remove 方法,效率最高;
如果列表 B 也很大,那么可以将两个列表转换成集合,然后使用集合的 difference方法或者用集合 A 减去集合 B,这两种方法的效率差不多;但需要注意difference()方法返回一个新的集合,减法操作会直接改变集合 A
💡
使用集合比较的原理是基于哈希表实现的。具体来说,当我们将一个元素加入到集合中时,Python 会为该元素生成一个哈希值,并将该元素存储到哈希表中对应的槽位上。当我们需要判断一个元素是否在集合中时,Python 会先计算该元素的哈希值,然后通过哈希值快速定位到对应的槽位,判断该槽位中是否存在该元素。因此,使用集合比较可以在平均时间复杂度为 O(1) 的情况下完成查找操作,效率非常高。 需要注意的是,虽然使用集合可以提高查找效率,但是集合的创建和转换也需要消耗一定的时间和内存。因此,如果列表 B 非常小,可以直接使用列表的 remove() 方法来移除列表 A 中的元素,效率可能更高。但是,如果列表 B 较大,使用集合比较会更加高效。
# 方法一:集合想减(会改变父集) B_set = set(B) # 将列表 B 转换成集合 result = list(set(A) - B_set) # 从集合 A 中删除与 B 集合相同的元素,得到 A 中非 B 的部分 # 方法二:返回新的集合 A = set([1, 2, 3, 4, 5]) B = set([3, 4, 5, 6]) result = A.difference(B)

5.3、常量与变量

常量与变量

  • 在Python中,变量名大写通常用于表示常量,即那些程序运行过程中不会改变值的变量,这是一种约定俗成的编码风格,而不是Python语言的强制规则。按照这个约定,常量的命名应该使用全大写字母,如果有多个单词,可以使用下划线_连接
  • 变量是用来存储数据值的标识符。在Python中,变量可以在程序运行时创建,并且可以修改。Python是动态类型语言,这意味着在声明变量时不需要指定数据类型,数据类型会在赋值时自动确定。
    • 定义在在函数外的变量是全局变量

命名规则:

  • 变量名必须以字母(azAZ)或下划线(_)开头。
  • 变量名不能以数字开头。
  • 变量名只能包含字母、数字和下划线(A-Za-z0-9_)。
  • 变量名是区分大小写的。例如,myVariablemyvariable是两个不同的变量。
  • 变量名不能是Python的关键字或内置函数名,如ifwhileclass等。

变量的作用域

  • 全局变量在函数内部可以正常使用
    • global_variable = "这是一个全局变量" def my_function(): print(global_variable) my_function() """ 这是一个全局变量 """
  • 如果在函数内部定义一个变量,内部变量的名称和全局变量的名称一样,那么这个变量在函数内部和外部的值是不一样的
    • global_variable = "这是一个全局变量" def my_function(): global_variable = "尝试修改全局变量" print("函数内部:", global_variable) my_function() print("函数外部:", global_variable) """ 函数内部: 尝试修改全局变量 函数外部: 这是一个全局变量 """
  • 如果想在函数内部永久修改全局变量的值,那么需要先声明它是一个全局变量
    • global_variable = "这是一个全局变量" def my_function(): global global_variable print("函数内部1:", global_variable) global_variable = "尝试修改全局变量" print("函数内部2:", global_variable) my_function() print("函数外部:", global_variable) """ 函数内部1: 这是一个全局变量 函数内部2: 尝试修改全局变量 函数外部: 尝试修改全局变量 """
💡
思考下面的代码为什么会报错?
global_variable = "这是一个全局变量" def my_function(): print("函数内部1:", global_variable) global_variable = "尝试修改全局变量" print("函数内部2:", global_variable) my_function() print("函数外部:", global_variable)
当执行到print("函数内部1:", global_variable)这一行时,由于在函数下方有对global_variable的赋值操作,所以它被认为是一个局部变量,而变量在被定义之前使用就会报错

其他

print(k<=x, k>=y, sep='\n') # 多个打印之间的间隔 numbers: list[int] = [0] * 5 # 声明变量时指定变量类型 decimals: list[float] = [0.0] * 5 def binary_search(nums: list[int], target: int) -> int: # -> int 表示函数的输出类型 pass # 在Python中,False, 0 , '', [], {}, (), None都可以视为假 # match / case 用法,Python 3.10 版本之后 lang = input("What's the programming language you want to learn? ") match lang: case "JavaScript": print("You can become a web developer.") case "Python": print("You can become a Data Scientist") case "Java": print("You can become a mobile app developer") case _: # 默认情况,没匹配到的情况 print("The language doesn't matter, what matters is solving problems.")

六、闭包与装饰器

闭包(Closure)

1. 什么是闭包?

闭包是指在一个函数内部定义的另一个函数,并且这个内部函数可以引用外部函数中的变量。即使外部函数已经返回,内部函数仍然可以访问这些变量。这种行为是因为内部函数“闭合”了外部函数的作用域,从而形成了一个闭包。

2. 闭包的条件

要形成闭包,需要满足以下三个条件:
  • 有一个外部函数:这个外部函数包含一个内部函数。
  • 外部函数返回内部函数:外部函数必须返回内部函数的引用。
  • 内部函数引用了外部函数的变量:内部函数使用了外部函数中的变量,并且外部函数已经结束执行。

3. 闭包的示例

def outer_function(text): def inner_function(): print(text) return inner_function closure = outer_function("Hello, World!") closure() # 输出: Hello, World!
  • 解释
    • outer_function 是外部函数,inner_function 是内部函数。
    • inner_function 引用了 outer_function 的局部变量 text
    • 即使 outer_function 执行完毕,inner_function 依然可以访问并使用 text,这就是闭包。

4.使用 nonlocal 修改外部函数的局部变量

def account_create(inittal_amount=0): def atm(num, depost=True): nonlocal inittal_amount if depost: inittal_amount += num print(f'inittal_amount: {inittal_amount}') else: inittal_amount -= num print(f'inittal_amount: {inittal_amount}') return atm atm = account_create() atm(100) atm(100) atm(10, False)

5.闭包的优缺点

优点:
  • 闭包可以捕获并“记住”外部函数的局部变量,即使外部函数已经执行完毕,这使得闭包能够在多次调用时保持状态。这对于需要在多次调用之间共享和维护状态的情况非常有用,如实现计数器、缓存机制等。
  • 闭包可以隐藏外部函数的局部变量,外部代码无法直接访问这些变量。这种封装性提高了代码的安全性和模块化,使得外部代码只能通过内部函数的接口来访问这些数据。
  • 闭包是函数式编程的重要特性之一。它使得函数可以像数据一样传递,并且可以创建更高阶的函数,如装饰器等,这提高了代码的灵活性和可复用性。
  • 闭包可以在需要的时候再计算某些值,而不是立即计算,这对于懒加载或需要节省计算资源的场景非常有用。
缺点:
  • 容易导致内存泄漏:由于闭包会捕获外部函数的局部变量,这可能导致这些变量的生命周期比预期的要长,尤其是在闭包被长时间引用的情况下。这可能会导致内存泄漏,特别是在捕获了大对象或复杂数据结构时。
  • 调试困难:闭包的调试通常比普通函数更复杂,因为变量的作用域变得更加隐蔽,追踪和理解程序的执行流程可能需要更多的时间和精力。

装饰器(Decorator)

1. 什么是装饰器?

装饰器是一种特殊的闭包,用于在不修改函数源代码的情况下,动态地增加或修改函数的行为。装饰器本质上是一个函数,它接受一个函数作为输入,并返回一个新的函数作为输出。

2. 装饰器的使用场景

  • 日志记录:在函数执行前后记录日志。
  • 性能监控:计算函数执行时间。
  • 权限验证:在执行函数前检查用户权限。
  • 缓存:缓存函数的返回结果以提高性能。

3. 装饰器的基本结构

示例1:
def decorator_function(original_function): def wrapper_function(*args, **kwargs): # 在原函数执行前的操作 result = original_function(*args, **kwargs) # 在原函数执行后的操作 return result return wrapper_function
示例2:
import random import time def outer(func): def inner(): print("I am starting sleep...") func() print("I am weaking up...") return inner def sleep(): print("I am sleeping...") time.sleep(random.randint(1,5)) fn = outer(sleep) fn()

4. 装饰器的使用示例

示例1:
def my_decorator(func): def wrapper(): print("Something is happening before the function is called.") func() print("Something is happening after the function is called.") return wrapper @my_decorator def say_hello(): print("Hello!") say_hello() """ Something is happening before the function is called. Hello! Something is happening after the function is called. """
  • my_decorator 是一个装饰器函数,它接收 say_hello 函数作为参数,并返回 wrapper 函数。
  • wrapper 函数在调用 say_hello 之前和之后添加了额外的操作。
  • 使用 @my_decorator 语法糖,将 my_decorator 应用于 say_hello 函数。
示例2:
import random import time def outer(func): def inner(): print("I am starting sleep...") func() print("I am weaking up...") return inner @outer def sleep(): print("I am sleeping...") time.sleep(random.randint(1,5)) sleep()

5. 装饰器与带参数的函数

装饰器不仅可以用于不带参数的函数,还可以用于带参数的函数,甚至是类方法。
def my_decorator(func): def wrapper(*args, **kwargs): print(f"Arguments were: {args}, {kwargs}") return func(*args, **kwargs) return wrapper @my_decorator def display_info(name, age): print(f"display_info received name={name}, age={age}") display_info("John", 25) """ Arguments were: ('John', 25), {} display_info received name=John, age=25 """
  • wrapper 函数使用 args 和 *kwargs 捕获 display_info 函数的所有参数。
  • 装饰器不仅对无参函数有效,对任何带参数的函数都可以灵活使用。

三、闭包与装饰器的关系

装饰器可以看作是闭包的一种特定应用。它们都利用了函数嵌套和作用域规则,允许我们在不修改原函数的前提下,增强或修改函数的行为。闭包为装饰器提供了技术基础,使得装饰器能够在函数调用前后添加逻辑,从而实现代码的复用和扩展。

七、Python更新

Python 3.5

  1. 异步编程 (async 和 await)
      • Python 3.5 引入了 async 和 await 关键字,使得异步编程更加简洁和直观。它们允许编写异步 I/O 操作,更加高效地处理并发任务。
      • 示例:
        • async def fetch_data(): await some_io_operation()
  1. 类型提示 (Type Hints)
      • 类型提示允许在函数签名中指定参数和返回值的类型,为代码提供更好的自文档性和工具支持。
      • 示例:
        • def greeting(name: str) -> str: return 'Hello ' + name import pandas as pd def process_data(df: pd.DataFrame) -> None: # 函数体 print(df.head())
          类型提示本身并不会在运行时强制执行类型检查。也就是说,即使你在定义变量时指定了类型,如果你传递了其他类型的值,Python 解释器也不会报错。
          name: str = "Alice" name = 42 # 这不会报错,但类型提示表明这可能是一个错误
          在函数中,也可以使用类型提示来指定参数和返回值的类型。如果你传递了不匹配的类型,Python 解释器同样不会报错
          def greet(name: str) -> str: return f"Hello, {name}!" greet(42) # 这不会报错,但 mypy 会报告类型错误
          可以使用静态类型检查工具,如 mypymypy 是一个流行的工具,它可以分析你的代码并报告类型不匹配的错误。
          # 安装 mypy pip install mypy # 使用 mypy 检查代码 mypy your_script.py
      • 类型提示(Type Hints)在 Python 中的主要作用是提高代码的可读性、可维护性和可靠性。虽然类型提示本身不会在运行时强制执行类型检查,但它们在以下几个方面非常有用:
        • 类型提示可以让代码的读者更容易理解变量、函数参数和返回值的预期类型。这有助于开发者快速理解代码的意图,尤其是在大型项目或团队合作中。
        • 虽然 Python 是动态类型语言,但通过使用类型提示,你可以借助静态类型检查工具(如 mypy)在编写代码时捕获潜在的类型错误。这有助于在代码运行之前发现问题,减少调试时间。
        • 通过使用类型提示和静态类型检查工具,你可以减少运行时类型错误的发生,从而提高代码的可靠性。这有助于构建更健壮的应用程序。

Python 3.6

  1. 格式化字符串字面值 (f-strings)
      • f-strings 是一种新的字符串格式化方法,允许在字符串中直接嵌入表达式,语法简洁且易读。
      • 示例:
        • name = "World" greeting = f"Hello, {name}!"
  1. secrets 模块
      • 引入了 secrets 模块,用于生成强随机数和安全的密钥,特别适合于生成令牌、密码和其他敏感信息。
      • 示例:
        • import secrets token = secrets.token_hex(16)

Python 3.7

  1. 数据类 (Data Classes)
      • dataclasses 模块提供了 @dataclass 装饰器,用于简化类的定义,特别适合用于表示数据结构。
      • 示例:
        • from dataclasses import dataclass @dataclass class Point: x: int y: int
  1. asyncio.run() 函数
      • asyncio.run() 函数简化了异步程序的启动方式,使异步代码更加易于使用和理解。
      • 示例:
        • import asyncio async def main(): print("Hello, asyncio!") asyncio.run(main())

Python 3.8

  1. 海象运算符 (Walrus Operator, :=)
      • 赋值表达式允许在表达式中进行赋值,从而减少代码重复,提高代码可读性。
      • 示例:
        • if (n := len(some_list)) > 10: print(f"List is too long: {n} elements")
  1. positional-only 参数
      • 可以使用 / 指定函数参数为仅限位置参数,这对于设计 API 更加明确。
      • 示例:
        • def func(a, b, /, c, d): print(a, b, c, d) # 正确的调用方式 func(1, 2, 3, 4) # 输出: 1 2 3 4 func(1, 2, c=3, d=4) # 输出: 1 2 3 4 # 错误的调用方式 func(a=1, b=2, c=3, d=4) # 会引发错误,因为 a 和 b 是 positional-only 参数
        • a, b, /:在 / 之前的参数 a 和 b 是 positional-only 参数。调用函数时,必须通过位置传递它们,不能使用关键字参数。
        • c, d:在 / 之后的参数 c 和 d 是普通参数,可以通过位置或关键字传递。

Python 3.9

  1. 字典合并运算符 (| 和 |=)
      • | 运算符用于合并两个字典,|= 用于更新字典。
      • 示例:
        • dict1 = {'a': 1, 'b': 2} dict2 = {'b': 3, 'c': 4} result = dict1 | dict2 # {'a': 1, 'b': 3, 'c': 4}
  1. 类型提示增强
      • 支持使用 list[int]dict[str, int] 等泛型类型提示,简化类型注解。
      • 示例:
        • def process_items(items: list[int]) -> None: for item in items: print(item)

Python 3.10

  1. 结构化模式匹配 (Pattern Matching)
      • 类似于 switch-case 的模式匹配功能,允许对复杂的数据结构进行解构和匹配。
      • 示例:
        • match command: case "start": print("Starting...") case "stop": print("Stopping...") case _: print("Unknown command")
  1. 类型提示的联合操作符 (|)
      • | 运算符用于表示多个类型的联合,替代 Union
      • 示例:
        • def process(value: int | str) -> None: pass

Python 3.11

  1. 性能改进
      • Python 3.11 对解释器进行了大幅度的性能优化,官方声称整体性能提升了 10-60%。
  1. 异常消息的精确度提升
      • Python 3.11 提供了更精确和详细的异常消息,尤其在链式异常和复杂表达式中的错误定位上。

Python 3.12

  1. Python内存模型的增强
      • 改进了内存分配的方式,进一步提升了 Python 程序的运行速度和内存管理的效率。
  1. 模式匹配中的更多匹配结构
      • 进一步扩展了结构化模式匹配的能力,使其支持更多的匹配结构和自定义模式。
If you have any questions, please contact me.