0。前言浙江省新版高中技术教材将以Python3作为信息技术教学语言。作为高一新生,笔者开始温习自己的Python知识。除了复习,我特地设立了这个系列来记录我的复习。这篇笔记中提到的问题是笔者的一位同学提出的,与Python中的int对象池有关。1、问题描述代码片段如下:var_a=1var_b=1print("Address:var_a:{0}var_b:{1}".format(id(var_a),id(var_b)))print("var_aisvar_b?{0}".format(var_aisvar_b))var_c=300var_d=300print("地址:var_c:{0}var_d:{1}".format(id(var_c),id(var_d)))print("var_cisvar_d?{0}".format(var_cisvar_d))在Pythonshell中产生以下结果:>>>>>>var_a=1>>>var_b=1>>>print("Address:var_a:{0}var_b:{1}".format(id(var_a),id(var_b)))地址:var_a:9310336var_b:9310336>>>print("var_aisvar_b?{0}".format(var_a是var_b))var_a是var_b?True>>>>>>var_c=300>>>var_d=300>>>print("Address:var_c:{0}var_d:{1}".format(id(var_c),id(var_d)))地址:var_c:140399450822160var_d:140399450823472>>>print("var_cisvar_d?{0}".format(var_cisvar_d))var_cisvar_d?假>>>为什么也是int类型对象地址和is判断会有区别吗?2.解决方案与讨论在Python3(也称为CPython,是python.org上可以下载的版本)的C语言实现中,有一个int对象缓存池,即int对象池。为了提高解释器的运行效率,CPython会默认在解释器初始化的时候创建少量的(最常用的)int对象存储在数组中。当引用这些int时,解释器直接返回缓存对象的地址,无需重新分配。这可以在CPython代码中清楚地看到。//Python-3.8.5/Objects/longobject.c,L18-L23#ifndefNSMALLPOSINTS#defineNSMALLPOSINTS257#endif#ifndefNSMALLNEGINTS#defineNSMALLNEGINTS5#endif上面代码中,CPython预定义了两个宏,分别为正边界(NSMALLPOSINTS)为“较小的数字”和负边界(NSMALLNEGINTS)为“较小的数字”定义。也就是说,这里定义了CPython中的int对象池边界。//Python-3.8.5/Objects/longobject.c,L37-L43#ifNSMALLNEGINTS+NSMALLPOSINTS>0/*小整数预先分配在这个数组中,以便它们可以共享。预分配的整数在-NSMALLNEGINTS(含)到NSMALLPOSINTS(不含)范围内。*/staticPyLongObjectsmall_ints[NSMALLNEGINTS+NSMALLPOSINTS];此代码创建一个PyLongObject数组。[NSMALLNEGINTS,NSMALLPOSINTS)(左闭右开)内的数字可以放在这个数组中。//Python-3.8.5/Objects/longobject.c,L48-L79staticPyObject*get_small_int(sdigitival){PyObject*v;断言(-NSMALLNEGINTS<=ival&&ival=0)_Py_quick_int_allocs++;否则_Py_quick_neg_int_allocs++;#endifreturnv;}#defineCHECK_SMALL_INT(ival)\doif(-NSMALLSMALLNEGINTS&){\returnget_small_int((sdigit)ival);\}while(0)staticPyLongObject*maybe_small_long(PyLongObject*v){if(v&&Py_ABS(Py_SIZE(v))<=1){sdigitival=MEDIUM_VALUE(v);if(-NSMALLNEGINTS<=ival&&ival)10.0000.0000.0220.022{built-inmethodbuiltins.exec}10.0000.0000.0000.000{method'disable'of'_lsprof.Profiler'objects}$可以看到输出很详细,包括有多少个functioncallsncalls,函数体总运行时间(不含子函数调用)tottime,函数(含子函数调用)总运行时间cumtime,具体文件名,行号和函数名。但对于较大的脚本,cProfile过于详细和冗余:$python-mcProfilegen_pattern.py--help...省略脚本本身的输出...0.024秒内进行24601次函数调用(23801次原语调用)排序方式:标准名称ncallstottimepercallcumtimepercallfilename:lineno(function)370.0000.0000.0000.000:1009(_handle_fromlist)280.0000.0000.0000.000:1000.00(release)000.00000.0.:143(__init__)280.0000.0000.0000.000:147(__enter__)280.0000.0000.0000.000:151(__exit__)280.0000.0000.0000.000:157(_get_module_lock)280.0000.0000.0000.000:176(cb)36/20.0000.0000.0190.009:211(_call_with_frames_removed)3190.0000.0000.0000.000:222(_verbose_message)80.0000.0000.0000.000:232(_requires_builtin_wrapper)250.0000.0000.0000.000:307(__INT__)250.0000.0000.0000.0000.000:311(__ENTER__)250.0000.0000.0000.0000.0000.0000.0000.000:318(____exit__exit__exit__exit__exit__exit__)冻结的importlib._bootstrap>:321()160.0000.0000.0000.000<冻结的importlib._bootstrap>:35(_new_module)260.0000.0000.0000.000<冻结的importlib._bootstrap>:369(__init__)<0.0.0000.0000冻结的importlib._boots陷阱>:403(缓存)250.0000.0000.0000.000<冻结导入库._bootstrap>:416(父级)250.0000.0000.0000.000<冻结导入库._bootstrap>:424(has_location)80.0000.0000.000导入:433(spec_from_loader)250.0000.0000.0000.0000.000:504(_init_module_attrs)250.0000.0000.0000.0000.0000.0000.0000.000:576(Module_from_spec)(__init__)25/20.0000.0000.0200.010:663(_load_unlocked)260.0000.0000.0000.000:719(find_spec)80.0000.0000.0000.000:740(create_module)...此处省略529行...10.0000.0000.0000.000{'_io.TextIOWrapper'对象的方法'write'}$这里的gen_pattern.py来自开源计算机视觉库(opencv/opencv),作者是Jaycee(yassiezar)、VladislavSovrasov(sovrasov)、S.Garrido(sergarrido),NicholasNadeau(nnadeau),Rong"Mantle"Bao(CSharperMantle),anddebjan(debjan)Runtimelibrarystubbingwiththeldlinker使用运行时库存根机制,我们可以将任意函数重定向到我们自己的优势功能:高度可定制;自由度高缺点:只能在有GCC的Linux设备上运行;使用复杂的例子:这里我们堆内存分配的标准库函数malloc()。//代码/mymalloc.cpp#include#include#include#defineLOG_INFOstd::printf#ifdef__cplusplusextern"C"{#endif/*__cplusplus*/void*malloc(size_tsize){autosymbol_addr=dlsym(RTLD_NEXT,"malloc");如果(!symbol_addr){抛出std::runtime_error(dlerror());}typedefvoid*(*__malloc)(size_t);autolibc_malloc=reinterpret_cast<__malloc>(symbol_addr);autoptr=libc_malloc(size);静态thread_local布尔调用=false;如果(!被称为){被称为=真;LOG_INFO("malloc(%ld)=%p\n",size,ptr);称为=假;}returnptr;}voidfree(void*ptr){autosymbol_addr=dlsym(RTLD_NEXT,"free");如果(!symbol_addr){抛出std::runtime_error(dlerror());}typedefvoid(*__free)(void*);autolibc_free=reinterpret_cast<__free>(symbol_addr);libc_free(ptr);LOG_INFO("free()=%p\n",ptr);}#ifdef__cplusplus}#endif/*__cplusplus*/编译运行:$g++-omymalloc.somymalloc.cpp-shared-fPIC-ldl-D_GNU_SOURCE-g-Wall-Wextra$LD_PRELOAD="./mymalloc.so"python./python_int_pool_a.pymalloc(72704)=0x138a2d0malloc(32)=0x139c2f0malloc(2)=0x139c320malloc(5)=0x139c340free()=0x139c340malloc(120)=0x139c360...$THEE