在游戏自动化测试领域,行为树由于其强大的描述玩家(Agent)行为逻辑的功能,在很多场景的自动化测试都能得到应用。但是,如果对行为树的作用认识不足,很容易导致整一个自动化测试项目出现难以维护的窘境。因此,这篇文章谈一谈在游戏自动化测试里,用行为树做测试的一些误区。
【Hard Python】【第三章-GC】2、python的GC流程
除了通过引用计数直接销毁对象之外,python
还是拥有内在GC
机制的,并且也有完整的一套流程。
如果只有通过引用计数销毁对象这种机制,那么随便构造一个循环引用就会造成内存泄漏,比如下面的代码:
1 | def _dump_gc(): |
打印出来的结果是:
1 | 13707 |
很显然,当退出_test_internal
作用域时,gc
对象的数量没有变化,这就说明在_test_internal
里创建的a、b
两个对象没有被立即释放掉。如果注释掉两个append
行,就能看到打印结果第三行变成了13707
,说明a、b
在退出函数时就被销毁了。
所以首先,我们从循环引用入手,来研究一下python
的gc
机制(好巧不巧的是,python
的gc
也是专门为循环引用而设置的)。调用gc.collect
触发gc
之后,会跑到gc_collect_main
触发完整的gc
流程。gc_collect_main
是python
整个gc
流程的主入口,我们来看其中的代码:
【Hard Python】【第三章-GC】1、引用计数与内存释放机制
对于编程语言runtime来说,建立起良好运转GC机制是非常必要的,像Java
和Go
,其GC机制都经历了复杂的演化,当然同时也为编程语言带来了更好的性能,这也是为什么这两门语言能成为主流服务端语言的原因之一。
相对于Java
和Go
,python
的GC机制是相对简约的,其中最基础的机制之一就是引用计数。当对象生成时引用计数为1;对象被其它对象引用时引用计数增加1;对象没有被引用,又退出作用域的话,引用计数归0;引用计数归0后,对象被销毁。
我们可以通过一个例子对引用计数机制进行研究:
1 | def test_ref(): |
其反编译的结果是:
【Hard Python】【第二章-异步IO】3、async/await的源码实现
说完了asyncio
事件循环是如何运行异步任务的,接下来back to basic,我们一起看看async
和await
两个原语具体代表了什么含义。
首先是async
,async
通常用来修饰一个函数,表示这个函数会返回一个协程。比如说:
1 | async def _coro_maker(i): |
对_coro_maker
进行反编译,得到这样的结果:
【Hard Python】【第二章-异步IO】2、异步任务在事件循环中的执行
接续第一话的内容,事件循环在创建之后,又是如何运行协程任务以及异步IO任务的?
由asyncio.run
的代码可知,loop.run_until_complete
是运行协程的方法。其定义如下:
【Hard Python】【第二章-异步IO】1、asyncio事件循环的创建
python3中增加的重要特性之一即为asyncio
,其提供了异步编程的原语支持,从而能够让python在事件驱动、协程协同等方面的编程场景大杀四方。
事件循环EventLoop
是异步编程中的核心概念之一。python的异步IO,就从事件循环的实现开始讲起。
首先看一段示例代码:
1 | async def _test_run_main(): |
通过async def
定义的函数,其返回值是一个异步协程coroutine
。协程相当于是事件循环里的一个单位任务,通过asyncio.run
接口就可以将其运行起来。因此我们先来看asyncio.run
的实现:
【Hard Python】【第一章-多进程】3、Pool,多任务并行进程池
前面讲了进程创建与进程通信的内容,接下来讲一下多进程编程最能发挥的地方。对于同时运行多个同质任务来讲,采用multiprocessing.Pool
进程池去管理是最方便的。Pool
的用法如下:
1 | from multiprocessing import Pool, process |
打印出来的结果,可能是这样子的:
【Hard Python】【第一章-多进程】2、Pipe和Queue,进程间通信
第一话详细讲解了Process新进程是如何被创建的,接下来就来讲一下进程之间有什么通信的方法。
要在multiprocessing
中实现进程间通信,最直接的方法是采用Pipe
或者Queue
。其用法如下:
【Hard Python】【第一章-多进程】1、Process,新进程的诞生
在python中,如果要做多任务并行的编程,必须要掌握multiprocessing
库的相关运用。在python的multiprocessing官方文档中,已然详细给出了multiprocessing
库的相关用法。多进程编程其实还是有很多坑存在的,为了进一步探索python多进程的机制,提升对python多进程编程的理解,本篇文章会对多进程模块的实现进行一次详细的剖析。
多进程编程的第一话,首先来聊聊一个新的python子进程是如何诞生的。
首先我们需要了解这么一个事情,python创建的进程之间模块状态是相互隔离的。在多进程的场景下,代码中定义的各种变量,其值并不一定会共享。我们举个例子:
【Hard Python】前言
写完Medium Python之后,不知不觉就有开始继续写Hard Python的冲动。择日不如撞日,心动不如行动,2022年开篇,果断将Hard Python提上日程。
截至2021年底,python依旧是最热门的语言之一,随着3.10、3.11及后续版本的发布,python的runtime在功能及性能上都会有较大的提升。时至今日,提到技术应用最广泛的语言,除了python,还会有另外的吗?排除区块链、游戏、音视频里较为深度的领域,基本上各种技术业务场景,都会有python的影子。
可以说,python是编程界的一把瑞士军刀。如果我们能更加深入的了解python各个重点模块的技术原理,不仅对于我们理解这门语言以及编程语言相关技术有益处,并且以后我们在面对一些python的编程场景时也会更加得心应手。
在先前已经写过两个python系列,分别是Easy Python以及Medium Python。在Easy Python中,对python的各种基础概念以及实用场景介绍了相关案例;而在Medium Python中,通过源码分析的方式,剖析了python内部某些语言特性的实现,讲述了许多python相关的冷知识。这次Hard Python,还是回归本源,将会挑选一些python内部比较重点的基础模块进行深入剖析讲解,争取让每一位阅读本系列文章的同学对python这门语言有更加全新的理解。
话不多说,准备上菜!
目录
- 第一章:多线程
- 1、Process,新进程的诞生
- 2、Pipe和Queue,进程间通信
- 3、Pool,多任务并行进程池
- 第二章:异步IO
- 1、asyncio事件循环的创建
- 2、异步任务在事件循环中的执行
- 3、async/await的源码实现
- 第三章:GC
- 1、引用计数与内存释放机制
- 2、python的GC流程
- 第四章:日志
- 1、Logger与Manager的源码实现
- 2、日志消费者Handler的实现
- 第五章:字符串
- 1、unicode,py3的字符串实现
- 2、re,正则表达式源码详解