测试基础 测试基础-Python 篇 基础③

耿晓 · 2023年02月14日 · 2887 次阅读

再梳理一遍 Python 系列知识点,夯实基础,无他,唯手熟尔

Python 系列总结都是我自己平时的学习笔记,如果有不正确的地方,希望各位佬儿哥指正纠偏🤗

浮点数精度问题

print(0.1+0.2)
----------------------
0.30000000000000004

可以使用 decimal 模块解决浮点数精度问题:

from decimal import Decimal
print(Decimal('0.1') + Decimal('0.2'))

print(type(Decimal('0.1') + Decimal('0.2')))
print(type(Decimal('0.1')))
---------------------------------------------------------
0.3
<class 'decimal.Decimal'>
<class 'decimal.Decimal'>

注意:在使用 Decimal 时要用字符串来表示数字

布尔值其实也是数字

  • 布尔类型其实是整型的子类型
  • True 和 False 这两个布尔值可以直接当做 1 和 0 来使用
  • 通过这个特点,最常用于统计总数
def sum_even(numbers: list[int]):
    """
    返回numbers中偶数的个数
    :param numbers: 整数列表
    """
    return sum(i % 2 == 0 for i in numbers)

不常用但特别好用的字符串方法

  • str.partition(sep) 按照切分符 sep 切分字符串,返回一个包含三个成员的元祖 (part_before, sep, part_after); 若 s 不包括分隔符,则最后一个成员默认是空字符串'';
s = 'TomAndMarry'
s2 = s.partition('And')
print(s2)
--------------------------------
('Tom ', 'And', ' Marry')
  • 2.str.translate(table) 他可以按规则一次性替换多个字符,使用它比调用多次 replace 方法更快也更简单;
s = '明明是中文,却使用了英文标点.'
table = s.maketrans(',.', ',。')
s2 = s.translate(table)
print(s2)
-----------------------------------------------
明明是中文却使用了英文标点

使用枚举类型来替代字面量

  • 更易读:所有人都不需要记忆某个数字代表什么;
  • 更健壮:降低输错数字或字母产生 bug 的可能性;
# 用户每日奖励积分数量
DAILY_POINTS_REWARDS = 100
# VIP用户额外奖励20
VIP_EXTRA_POINTS = 20

from enum import Enum

class UserType(int, Enum):
    # VIP用户
    VIP = 3
    # 小黑屋用户
    BANNED = 13

def add_daily_points(user):
    """用户每天第一次登录增加积分"""
    if user.type == UserType.BANNED:
        return
    if user.type == UserType.VIP:
        user.points += DAILY_POINTS_REWARDS + VIP_EXTRA_POINTS
        return
    user.points += DAILY_POINTS_REWARDS
    return

生成器

  • 定义一个生成器需要生成器函数和 yield 关键字
  • yield 和 return 最大的区别在于,return 会一次性返回结果,使用它会直接中断函数执行,而 yield 可以逐步给调用方生成结果
  • 使用生成器的优点是它们占用的内存比列表要少,因为它们只生成一个元素并在需要时生成下一个元素。这使得生成器特别适合于处理大量数据
  • 每次调用生成器函数都会生成一个新的生成器对象
fruits = {'apple','orange','pineapple'}

def batch(item):
    for _ in item:
        yield _

print(next(batch(fruits)))
print(next(batch(fruits)))
print(next(batch(fruits)))
---------------------------------------
apple
apple
apple

因为每次调用生成器函数都会生成一个新的生成器对象,所以以上程序运行结果会输出三个 apple

fruits = {'apple','orange','pineapple'}

def batch(item):
    for _ in item:
        yield _

g = batch(fruits)

print(next(g))
print(next(g))
print(next(g))
-------------------------------------------------
pineapple
apple
orange

以上代码再次验证了'每次调用生成器函数都会生成一个新的生成器对象'结论

  • 使用生成器的 next() 函数可以在需要时单独生成生成器中的元素,而不是一次性生成整个列表。如果生成器中没有元素,则会引发 StopIteration 异常
  • 用生成器替代列表
def batch_process(item):
    result = []
    for i in item:
        # process_item = ..处理item
        result.append(process_item)
    return result

# 以上方法会有一个问题,当item过大时,会导致函数执行很耗时,并且若调用方想在某个process_item达到条件时中断,以上方法也是做不到的。所以可以使用生成器函数替代。

def batch_process_2(item):
    for i in item:
        # process_item = ..处理item
        yield process_item

# 调用方
for processed_item in batch_process_2(items):
    # 如果某个处理对象过期了,就中断当前的所有处理
    if processed_item.has_expired():
        break

面向对象编程

内置类方法装饰器

  • 类方法
    1.用 def 在类里定义一个函数时,这个函数通常被称作方法。调用这个方法需要先创建一个类实例;
    2.可以使用@classmethod装饰器定义类方法,它属于类,无需实例化也能调用;
    3.作为一种特殊方法,类方法最常见的使用场景,就是定义工厂方法来生成新实例,类方法的主角是类型本身,当发现某个行为不属于实例,而是属于整个类型是,可以考虑使用类方法;

  • 静态方法
    1.如果发现某个方法不需要使用当前实例里的任何内容,那可以使用@staticmethod来定义一个静态方法;
    2.和普通方法相比,静态方法不需要访问实例的任何状态,是一种与状态无关的方法,因此静态方法其实可以写成脱离于类的外部普通函数;
    2.1.如果静态方法特别通用,与类关系不大,那么把他改成普通函数会更好;
    2.2.如果静态方法与类关系密切,那么用静态方法更好;
    2.3.相比函数,静态方法有一些先天优势,比如能被子类继承和重写;

  • 属性装饰器
    1.@property 装饰器模糊了属性和方法间的界限,使用它,可以把方法通过属性的方式暴露出来;
    2.@property 除了可以定义属性的读取逻辑外,还支持自定义写入和删除逻辑;

class FilePath:
    @property
    def basename(self):
        ....
    @property.setter
    def basename(self, name):
        ....
    @property.deleter
    def basename(self):
        ....

经过@property的装饰以后,basename 已经从一个普通的方法变成了 property 对象,所以可以使用 basename.setter 和 basename.deleter 方法;
定义 setter 方法,该方法会在对属性赋值是被调用;
定义 deleter 方法,该方法会在删除属性时被调用;

鸭子类型及其局限性

  • 在鸭子类型编程风格下,如果想操作某个对象,你不会去判断他是否属于某种类型,而会直接判断他是不是有你需要的方法 (或属性)。或者更激进一些。你甚至会直接尝试调用需要的方法,假如失败了,那就让她报错好了;
  • 鸭子类型的优点就是编写简单,使用灵活;
  • 鸭子类型的缺点就是缺乏标准、过于隐式;
  • 可以使用类型注解、静态检查 (mypy)、抽象类来补充鸭子类型;

抽象类

  • isinstance() 函数
    利用 isinstance() 函数,我们可以判断对象是否属于特定类型;
    isinstance() 函数能理解类之间的继承关系,因此子类的实例同样可以通过基类的校验;

  • 校验对象是否是 Iterable 类型
    在 collections.abc 模块中,有许多和容器相关的抽象类,比如代表集合的 Set、代表序列的 Sequence 等,其中有一个最简单的抽象类:Iterable,他表示的是可迭代类型;

  • 鸭子类型和抽象类总结
    鸭子类型是一种编码风格,在这种风格下,代码只关心对象的行为,不关心对象的类型;
    鸭子类型降低了类型校验的成本,让代码变得更灵活;
    传统的鸭子类型里,各种对象接口和协议都是隐式的,没有统一的显示标准;
    传统的 isinstance() 类型检查和鸭子类型的理念是相违背的;
    抽象类是一种特殊的类,他可以通过钩子方法来定制动态的子类检查行为;
    因为抽象类的定制子类化特征,isinstance() 也变得更灵活、更契合鸭子类型了;
    使用@abstractmethod装饰器,抽象类可以强制要求子类在继承时重写特定方法;
    除了抽象方法外,抽象类也可以实现普通的基础方法,供子类继承使用;
    在 collections.abc 模块中,有许多与容器相关的抽象类;

多重继承于 MRO

  • Python 里的一个类可以同时继承多个父类;
  • 调用类的 mro() 方法,可以看到按 MRO 算法排好序的基类列表;
  • 在许多人印象中。super() 是一个用来调用父类方法的工具函数。但这么说并不准确,super() 使用的其实不是当前类的父类,而是当前类在 MRO 链条上的上一个类;
  • 应该避免多重继承;

学习建议

对于 Python 入门及进阶,我推荐两本我认为值得多次阅读的书籍:
《Python 编程从入门到实践(第二版)》- 第一部分为基础语法部分,建议刚接触 Python 的同学多次阅读并实践,夯实基础利器!
《Python 工匠》- 整本无尿点,强烈建议多次阅读并实践,是 Python 进阶的不二之选!

测试基础-Python 篇 琐碎但十分实用的知识点

共收到 0 条回复 时间 点赞
耿晓 测试基础-Python 篇 基础② 中提及了此贴 02月14日 15:59
需要 登录 后方可回复, 如果你还没有账号请点击这里 注册