读后笔记 -- Python 全栈测试开发 Chapter1 Python 实战实例
1.1 数据类型实战实例
2. (P5)字符串驻留
系统会维护一个 intern 字典类型用于记录已经被驻留的字符串对象。(Java、Python、PHP、Ruby、Julia等都支持 String Interning)
字符串驻留的其他解释:https:#www.w3cschool.cn/article/80177809.html
# -*- coding: utf-8 -*- from sys import intern ''' 1. 字符串编译时时驻留,非运行时不驻留 2. intern 实现强驻留 3. is vs == -- is: 比较两个对象在内存的地址是否一样 => 类似于 Java 的 == -- == : 比较两个值是否相等 => 类似于 Java 的 equals 3. id(),来打印是否指向同一个内存 ''' # ---- test1: 字符串在编译时时驻留,非运行时不驻留 ---- str1 = "wood" + "programming" print(str1 is "woodprogramming") # 组成 str1 的两个字符串拼接完成后,存放到 str1 的内存位置 print(id(str1), id("woodprogramming")) # 2036003228336 2036003228336 str2_1 = "wood" str2_2 = "programming" str2_3 = str2_1 + str2_2 print(str2_3, str2_3 is "woodprogramming") # woodprogramming False, 运行时才拼接,故是一个新地址 print(id(str2_3), id("woodprogramming")) # 2036003029872 2036003228336 # ---- test2: 通过 intern 实现了强制驻留 ---- str4_1 = intern(str2_1 + str2_2) print(str4_1 is "woodprogramming") # True print(id(str2_1 + str2_2), id(str4_1), id("woodprogramming")) # 2036003228400 2036003228336 2036003228336 # ---- test3: 最新的 python 3.9, 无论长度、符号等特殊情况 都实现了驻留 ---- str3_1 = "," str3_2 = "," str3_3 = "wood!" str3_4 = "wood!" print(str3_1 is str3_2, len(str3_1)) # True 1 print(str3_3 is str3_4, len(str3_3)) # True 5 print(id(str3_3), id(str3_4)) # 2036003227696 2036003227696
3. (P5)字典键值对互换
1)键名定义要求:
- 可哈希的类型 / 不可变对象:int, float, bool, string, tuple
- 不可哈希的类型 / 可变对象:list, dict, set 不能作为键名
# -*- coding: utf-8 -*- class Person(object): def __init__(self, name): self.name = name i = 5 f = 5.2 b = True s = 'abc' t = (5, 'a') p = Person('lily') q = Person('xiao') m = {'a': 1, 'b': 2} lst = [1, 2, 3] s_et = {'apple', 'orange'} d = {i: 'five', f: '5.2', b: 'True', s: 'ABC', t: 'five-a', p: 'name:Lily', (p, q): 'tow people: Lily and xiao', (i, s, t, p, q): 'all in the key'} # d[s_et] = 'set' # unhashable type: 'set' # d[lst] = '123' # unhashable type: 'list' # d[m] = 'dict' # unhashable type: 'dict' for key, value in d.items(): print(key, '=>', value)
2)键名重名 及 键值对互换
# ---- test1: 键名重复时,后续的键值覆盖前值 ---- dict1 = {"username": "zhangsan", "password": "1234", "username": "lisi",} print(dict1) # {'username': 'lisi', 'password': '1234'} # ---- test2: 键值对换 ---- dict2 = {"username": "zhangsan", "password": "1234"} dict2_c = {value:key for key, value in dict2.items()} # lambda 表达式 print(dict2_c) # {'zhangsan': 'username', '1234': 'password'}
3)三种推导式对应的语句:
- list:[ expression(i) for i in old_list if condition(i) ]
- dict:{ key:value for key:value in iterable }
- set:{ value for value in iterable }
set2 = {'apple', 'orange', 'pear', 'banana'} set2_c = {value for value in set2} print(set2) # {'orange', 'pear', 'apple', 'banana'},该顺序随机变化 list2 = ['apple', 'orange', 'pear', 'banana'] list2_c = [b for b in list2 if b[0:1]!='a' ] print(list2_c) # ['orange', 'pear', 'banana']
4)字典统计字符串
str2 = "wood programming is the best education" result = {} for i in str2: result[i] = str2.count(i) # result[i] 是字典的键 print(result) # {'w': 1, 'o': 4, 'd': 2, ' ': 5, 'p': 1, 'r': 2, 'g': 2, 'a': 2, 'm': 2, 'i': 3, 'n': 2, 's': 2, 't': 3, 'h': 1, 'e': 3, 'b': 1, 'u': 1, 'c': 1}
1.2 循环逻辑实战
# random.choice() 形成多个随机值的列表 # -------------------------------------------------- random.randint(0, 100) # 从 0-100 区间生成一个随机整数 computer_number = [random.choice(range(0, 101)) for i in range(5)] # 从 1-100 个数字中选5个数组成列表 # dict {key: tuple } 的 使用
# -------------------------------------------------- dict_model = {"easy":(1, 10), "normal":(1, 100), "hard":(1, 1000)} return random.randint(dict_model["easy"][0], dict_model["easy"][1]) # 即 return random.randint(0, 101)
1.3 迭代器、生成器实战
可通过索引访问元素的有:字符串、列表、元组
迭代器:可以处理不存在索引结构的数据类型,如文件、集合、字典
Python中,既可以循环遍历又可以计算的机制,就是 生成器。
迭代器、生成器的 详细介绍 参见:
https:#www.cnblogs.com/bruce-he/p/16384923.html
https:#zhuanlan.zhihu.com/p/341439647
1. 迭代器
- 迭代是Python最强大的功能之一,是访问集合元素的一种方式。
- 迭代器是一个可以记住遍历的位置的对象。
- 迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退。
- 迭代器有两个基本的方法:iter() 和 next()。
- 字符串,列表或元组对象都可用于创建迭代器
# 测试迭代器 list1 = [1, 2, 3, 4, 5, 6] it = iter(list1) # it 是个 list_iterator 对象 print(next(it)) # 输出 1 print(next(it)) # 输出 2 # 下面 for 循环,输出 3 4 5 6 for x in it: print(x, end=" ")
2. 生成器
- 在 Python 中,使用了 yield 的函数被称为生成器(generator);
- 跟普通函数不同的是,生成器是一个返回迭代器的函数,只能用于迭代操作,更简单点理解生成器就是一个迭代器;
- 在调用生成器运行的过程中,每次遇到 yield 时函数会暂停并保存当前所有的运行信息,返回 yield 的值, 并在下一次执行 next() 方法时从当前位置继续运行;
- 调用一个生成器函数,返回的是一个迭代器对象;
import sys def fibonacci(n): a, b, counter = 0, 1, 0 whilecounter <= n: yield a a, b = b, a + b counter += 1 f = fibonacci(10) while True: try: print(next(f), end=" ") except StopIteration: sys.exit() ====== output ====== 0 1 1 2 3 5 8 13 21 34 55
1). (P13)课本代码1
def tt_test(): yield from range(4)
if __name__ == '__main__': g = tt_test() # g 是一个 generator 对象 g1 = (i for i in g) # g1 是一个生成器推导式声明的生成器对象,此时 g1 包含的数据是 [0, 1, 2, 3] g2 = (i for i in g1) print(list(g1)) # list(g1) 执行后,会将生成器 g1 的元素全部取完。实际上,g1 是去生成器 g 取值,将g 的值取空。取完后,g 和 g1 都是空 print(list(g2)) # list(g2)时,需要取值,是去 g1 取值,生成器已空 ==== Output ==== [0, 1, 2, 3] []
2). (P14)课本代码2
def add(n, i): return n + i def t_test(): yield from range(4): g = t_test() for n in [10, 20, 5]: g = (add(n, i) for i in g) print(list(g)) # 解析: n = 1 时, g = (add(n, i) for i in g) n = 10 时, g = (add(n, i) for i in (add(n, i) for i in g)) n = 5 时, g = (add(n, i) for i in (add(n, i) for i in (add(n, i) for i in g))) # 到最后 list 时才取数据进行计算,即,当 n = 5 时才调用生成器 g: g = (add(5, i) for i in (add(5, i) for i in (add(5, i) for i in test()))) => g = (add(5, i) for i in (add(5, i) for i in (add(5, i) for i in [0,1,2,3]))) => g = (add(5, i) for i in (add(5, i) for i in [5,6,7,8])) => g = (add(5, i) for i in [10,11,12,13]) => g = ([15,16,17,18])
3. 匿名函数 lambda 完成 9x9 乘法表
multipl_table = lambda : '\n'.join([' '.join(['%2d *%2d = %2d' %(row, col, row * col) for col in range(1, row+1)]) for row in range(1, 10)]) print(multipl_table()) ============================= 1 * 1 = 1 1 * 2 = 2 2 * 2 = 4 .......................................... (省略) 1 * 9 = 9 2 * 9 = 18 3 * 9 = 27 4 * 9 = 36 5 * 9 = 45 6 * 9 = 54 7 * 9 = 63 8 * 9 = 72 9 * 9 = 81
1.4 装饰器
装饰器的详细介绍可参考:https:#www.runoob.com/w3cnote/python-func-decorators.html
1. 装饰器的属性:
- 实质:函数,应用在 类、方法、函数 上
- 参数:要装饰的函数名(非函数调用)
- 返回:装饰完的函数名(非函数调用)
- 作用:在原有函数实现功能的基础上不改变其任何操作,实现额外的功能扩展
- 特点:基于现有的对象不需要做代码上的变动
- 应用场景:插入日志、性能测试、事务处理、权限校验等
2. 装饰器参数:
- 1)如被装饰的方法或函数存在参数,则需要通过装饰器的内层函数将参数传递
- 2)被装饰的函数调用时是否存在返回值,取决于内层函数是否将值进行返回
- 3)如装饰器需要传参,则需要三层函数,此时第二层函数固定传入被装饰函数的对象,第一层函数传入的参数即为装饰器所传入的参数
def cal_ten_outer(number): # 第一层:传入的参数即为装饰器所传入的参数 def cal_ten_test(func): # 第二层:固定传入被装饰函数的对象 def cal_ten(a, b): # 第三层:被装饰的函数的参数 if number > 10: return func(a, b) - 10 else: return func(a, b) + 10 return cal_ten return cal_ten_test @cal_ten_outer(11) def add(a, b): return a + b if __name__ == '__main__': print(add(1, 6))
3. 装饰器的分类:
3.1 系统装饰器:
1)类装饰器 @classmethod
- 通过类名直接调用其属性和方法(还有种是,通过创建对象调用),但属性必须是类属性,不能调用对象属性;
- 方法必须通过 @classmethod 进行装饰,不能调用对象方法;
- 装饰方法的第一个参数默认是 cls(表示传入当前的类)
类只能调用类属性和类方法,不能调用对象属性和对象方法; 但对象实例 既可以调用类属性、类方法,也可调用 对象属性、对象方法
2)静态方法装饰器 @staticmethod
- 该方法不需要传递任何参数,既可以通过类调用,也可通过对象调用
- 只能够调用类属性和类方法,不能调用实例方法
- 所有对象及类 共享同一份静态属性和方法
3.2 自定义装饰器
from functools import wraps class Decorator(object): def __call__(self, func_name): # 关于 __call__ 的意义参照下面的解释 @wraps(func_name) def wrapped_function(*args, **kwargs): print("Decorator start") func_name() print("Decorator end")return wrapped_function @Decorator() def func(): print("function") if __name__ == '__main__': func()
__call__():Python中,只要在创建类的时候定义了__call__()方法,这个类型就是可调用的。注意哦:可调用意味着这个类实例化之后可以和函数一样使用了。 # 示例: class student(object): def __init__(self,name,age) -> None: self.name = name self.age = age print(self.name) def __call__(self, *args,**kwargs): # *用于收集位置参数;**用于收集关键词参数 print('my friend is',args) print('my friends age is',kwargs) stu1 = student('tim',13) # 实例化为对象stu1 stu1(1,2,2,x = 1, y =2) # stu1这时候可以当作函数来用,输入参数之后,默认执行类中的_call_方法
*args 表示任何多个无名参数, 他本质上是一个 tuple ** kwargs 表示关键字参数, 它本质上是一个 dict 组合使用 args,*args 和 **kwargs 来调用函数。如下面的示例: def print_func(x, *args, **kwargs): print(x) print(args) print(kwargs) print_func(1, 2, 3, 4, y=1, a=2, b=3, c=4) # 输出结果: 1 (2, 3, 4) {'y': 1, 'a': 2, 'b': 3, 'c': 4}
1.5 面向对象实战
1. 示例
class GuessingGame(object): def __init__(self): ...def play(self): ... if __name__ == '__main__': guessing_game = GuessingGame() guessing_game.play()
1.6 多进程、多线程
多进程 | 多线程 | |
数据共享、同步 |
数据通信是需要通过 IPC(进程间通信)完成,所以实现 相对复杂,然而数据是分开的,所以同步相对简单 |
因为共享的是进程数据,所以数据共享相对简单, 同步相对复杂 |
内存、CPU | CPU 利用率低,占用内存多,原因是切换复杂 | CPU 利用率高,占用内存少,因为切换简单 |
创建、销毁、切换 | 运行速度慢,因为要经历复杂的 创建、销毁、切换 | 运行速度快, 创建、销毁、切换 简单 |
编程、调试 | 编程、调试 简单 | 编程、调试 复杂 |
可靠性 | 多进程间不会相互响应 | 如果某一个线程出问题,影响了整个进程 |
分布式 | 很好地兼容多核、多机等分布式 | 适用多核分布式 |