victory的博客

长安一片月,万户捣衣声

0%

钩子函数、python中定义钩子函数

钩子函数的概念来源于其工作原理,即在系统或框架的特定阶段“钩住”某一刻以执行自定义代码

钩子函数是一种在特定事件发生时由系统自动调用的自定义函数。这个术语“钩子”(hook)形象地描述了这种机制:它允许开发者“钩住”程序执行过程中的某些点,以便在这些点上插入自己的代码。这些函数通常用于响应系统事件、修改程序行为或执行特定任务。以下是钩子函数由来的几个方面:

  • 系统级钩子函数:在操作系统层面,钩子函数可以监视和处理系统消息,如键盘按键、鼠标动作或窗口消息等。这些函数在消息传递到目标之前截获它们,允许开发者执行自定义操作。
  • 编程框架中的钩子函数:在应用程序框架中,钩子函数用于扩展框架的功能而无需修改框架本身的代码。
  • 函数式编程中的钩子函数:在函数式编程中,钩子函数可以用来实现纯函数,确保函数的输出仅依赖于输入并且不产生副作用。

总的来说,钩子函数提供了一种灵活的方式来处理程序流程中的特定事件,使得开发者能够在不改变原有程序结构的情况下,添加或修改功能。

在Python中,可以使用装饰器来实现钩子函数。以下是一个简单的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
def hook_decorator(func):
def wrapper(*args, **kwargs):
print("在函数执行前执行的钩子函数")
result = func(*args, **kwargs)
print("在函数执行后执行的钩子函数")
return result
return wrapper

@hook_decorator
def example_function():
print("这是一个示例函数")

example_function()

"""
输出结果:
在函数执行前执行的钩子函数
这是一个示例函数
在函数执行后执行的钩子函数
"""

纯函数

纯函数(Pure Function)是一种在函数式编程中非常重要的概念,它指的是那些给定相同的输入,总是返回相同输出,并且在执行过程中不会产生副作用的函数

纯函数的核心特征包括:

  • 相同的输入产生相同的输出:这意味着如果两次调用函数时的参数完全相同,那么这两次调用的结果也应该是相同的。
  • 无副作用:纯函数在执行过程中不会改变任何状态,也不会与系统外部有任何可观察的交互,例如修改全局变量、进行I/O操作等。
  • 结果只依赖于输入参数:函数的返回值只能由它的参数决定,不依赖于任何外部状态或额外的输入。

纯函数的优势在于它们具有可预测性,易于测试和重用。由于它们的输出完全由输入决定,因此可以在不产生副作用的情况下自由地替换和使用。

pytest异常断言

异常断言即测试待测代码段是否会抛出特定的异常。异常断言最常用的两种方式如下:

  1. pytest.raises()
1
2
3
4
5
6
7
8
9
10
import pytest


def test_zero_division():
# pytest.raises(ZeroDivisionError)作为一个上下文管理器(context manageer)断言with后的代码块是否会抛出“ZeroDivisionError”除零异常
with pytest.raises(ZeroDivisionError) as excpinfo:
1 / 0

# 异常信息excpinfo中匹配除零异常信息“division by zero”
assert excpinfo.match("division by zero")
  1. @pytest.mark.xfail
1
2
3
4
5
6
7
def f():
raise IndexError()

# @pytest.mark.xfail(raises=IndexError)是一个python装饰器,用于标记(mark)一个测试用例为预期失败,该例子中若f()函数抛出IndexError异常,则标记该测试为预期失败,测试报告(report)中会报告一个1 xfail,若f()函数未抛出IndexError异常,则测试报告中会报告一个1 xpass
@pytest.mark.xfail(raises=IndexError)
def test_f():
f()

注意:

pytest.mark.xfailraises参数一起使用可能更适合于记录未修复的bug,使用pytest.reises()可能更适合于测试自己的代码故意引发的异常的情况。

GPU加速原理

GPU加速的原理是利用多核处理器进行并行运算来实现程序的加速运行**。与CPU不同,GPU拥有数以千计的核心,专门为同时处理多任务而设计,可以高效地处理并行任务。

CPU几倍很强的通用性,可以处理不同类型的数据,同时擅长处理逻辑判断导致的大量分支跳转和中断处理,CPU相当于一个博学多闻的博士在完成一项工作。而GPU处理的数据类型高度统一,且GPU有数以千计的核心,可以并行计算任务,相当于1000个小学生在完成一个任务,呈现“人多力量大”的优势。

实际使用中,GPU需要CPU的配合来完成任务的计算,并行计算部分会运行在GPU上,串行计算部分运行在CPU上,CPU负责总体的程序流程。

参考资料:GPU加速原理,博主写的非常详细!

如何调用pytest

pytest支持命令行参数选择特定的tests去执行,以下是常见的一些选择方法。

1
2
3
4
5
6
7
# run tests in a module
# 运行测试文件中的所有符合pytest测试发现规则的测试用例(测试文件中符合test_*()的函数、TestXxx类中的测试用例)
pytest test_mod.py

# run tests in a directory
# 运行testing目录下的所有文件名符合test_*.py或*_test.py的测试文件中的测试用例
pytest testing/
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# run tests by keyword expressions
pytest -k "MyClass and not method"
# 通过关键词表达式指定pytest运行的测试用例,pytest会在testpaths或者当前目录下找到类名为TestMyClass的类中的方法名不带method的方法去执行。如下示例,运行pytest -k "MyClass and not method"会执行test_a方法而不会运行test_method_add()方法

# 编写测试文件test_keyword_exp.py
def add(a, b):
return a + b


class TestMyClass:
def test_a(self):
assert "a" == "a"

def test_method_add(self):
assert add(1, 2) == 3

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# run tests by collection arguments

# To run a specific test within a module
# 运行test_mod.py测试文件中的test_func测试用例
pytest tests/test_mod.py::test_func

# To run all tests in a class:
# 运行test_mod.py测试文件中的TestClass类中的测试用例
pytest tests/test_mod.py::TestClass

# Specifying a specific test method:
# 运行test_mod.py测试文件中的TestClass类中的test_method测试用例
pytest tests/test_mod.py::TestClass::test_method

# Specifying a specific parametrization of a test:
# 运行test_mod.py测试文件中的带参数的test_func测试用例
pytest tests/test_mod.py::test_func[x1,y2]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# Run tests by marker expressions
# 运行带有@pytest.mark.slow装饰器的测试
pytest -m slow

# 以下示例中,pytest将运行test_a测试用例,而不会运行test_b测试用例
import pytest

class TestMarkDecoration:
@pytest.mark.slow
def test_a(self):
assert 1 == 1

@pytest.mark.quick
def test_b(self):
assert 2 == 2
1
2
3
# run tests from packages
pytest --pyargs pkg.testing
# 这将导入pkg.testing,并使用其文件系统位置从中查找和运行测试

pytest使用规则及示例

Pytest是一个功能强大且易于使用的python测试框架,支持编写单元测试集成测试功能测试

Pytest使用规则:

  1. 测试文件命名应符合test_*.py*_test.py
  2. 若使用class对测试用例进行分组,测试类名应符合TestXxx
  3. 测试用例函数/方法名称应符合test_*()

另外,值得注意的是,pytest支持在测试类中定义setupteardown方法,这写方法会在每个测试方法运行前后分别调用,用于设置测试环境和清理资源。setup_classteardown_class方法在测试类中的所有测试方法之前和之后分别只运行一次

下面编写一个计算器类Calculator,并使用pytest进行测试。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# test_calculator.py 测试文件名称符合test_*.py
import pytest

# 定义计算器类
class Calculator:
@staticmethod
def add(a, b):
return a + b

@staticmethod
def sub(a, b):
return a - b

@staticmethod
def mul(a, b):
return a * b

@staticmethod
def div(a, b):
try:
return a / b
except ZeroDivisionError:
print("zero Division Error Exception")

# 定义测试类
class TestCalc: # 测试类名符合TestXxx
def test_calc_add(self): # 测试用例方法名符合test_*()
assert Calculator.add(1, 2) == 3

def test_calc_sub(self):
assert Calculator.sub(3, 1) == 2

def test_calc_mul(self):
assert Calculator.mul(2, 4) == 8

def test_calc_div(self):
assert Calculator.div(8, 4) == 2

def test_calc_div_zero_div_exp(self):
with pytest.raises(ZeroDivisionError):
Calculator.div(3, 0)

CI/CD

CI/CD:持续集成和持续交付/持续部署,是DevOps实践中的关键组成部分,包括代码的自动构建、测试和部署,以确保软件的快速和健壮开发。

DevOps是软件开发中的一种文化和实践,强调开发(Development),测试,运维(Operations)三个领域的合并,通过自动化软件交付和架构变更的流程,实现构建、测试、发布软件的快捷、频繁和可靠。

参考资料1:什么是 CI/CD?

参考资料2:使用gitlab实现CI/CD

迭代器与生成器的区别

  1. python迭代器

    Python中的迭代器是一个可以记住遍历位置的对象,用于访问集合元素的一种方式。迭代器是Python中处理数据集合的一种高效机制,它们允许开发者逐个访问集合中的元素,而不需要一次性将所有数据加载到内存中

    迭代器通过实现__iter__()__next__()方法来定义其行为。__iter__()方法返回迭代器对象本身,而__next__()方法负责返回容器中的下一个值。当没有更多元素可以返回时,__next__()方法会抛出StopIteration异常,通知调用者所有元素已经被遍历完毕。

阅读全文 »

GCC/Clang的头文件搜索路径

  • GCC/Clang编译器在对C++源代码文件进行预处理时,会对C++源代码头部包含的头文件进行搜索,搜索时根据环境变量CPLUS_INCLUDE_PATH中的路径进行搜索,如果搜索不到某头文件会报错。若CPLUS_INCLUDE_PATH中没有包含所使用头文件所在路径,也可以通过-I参数制定使用到的头文件的路径,例如,gcc -I /your/header/file/ test.cpp。

  • 添加GCC头文件搜索路径

    1
    export CPLUS_INCLUDE_PATH=$CPLUS_INCLUDE_PATH:/xxx/include
  • 查看C++编译器(GCC)的头文件搜索路径命令

    1
    2
    3
    4
    5
    6
    gcc -x c++ -v -E -
    # gcc:表示使用GCC编译器进行编译。
    # -x c++:表示指定编译的语言为C++。
    # -v:表示显示详细的编译过程信息。
    # -E:表示仅执行预处理阶段,不进行编译和链接。
    # -:表示从标准输入读取源代码。
  • 查看C++编译器(Clang)的头文件搜索路径命令

    1
    2
    3
    4
    5
    6
    clang -x c++ -v -E -
    # clang:表示使用GCC编译器进行编译。
    # -x c++:表示指定编译的语言为C++。
    # -v:表示显示详细的编译过程信息。
    # -E:表示仅执行预处理阶段,不进行编译和链接。
    # -:表示从标准输入读取源代码。