读后笔记 -- 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 利用率高,占用内存少,因为切换简单 
创建、销毁、切换  运行速度慢,因为要经历复杂的 创建、销毁、切换  运行速度快, 创建、销毁、切换 简单
编程、调试  编程、调试 简单  编程、调试 复杂
可靠性  多进程间不会相互响应  如果某一个线程出问题,影响了整个进程 
分布式  很好地兼容多核、多机等分布式  适用多核分布式 
posted on 2022-01-11 17:24  bruce_he  阅读(54)  评论(0编辑  收藏  举报