
有用的宏头文件里头, 定义了好些有用的宏, 好多宏是, 在靠近它们被运用的地方定义的, 比如说, 其他更为普遍通用一些的宏, 则是定义在这儿, 这儿所展示呈现出来的这些, 并非是一组完整毫无遗漏的列表。()当你存在一个设计方面没办法抵达的代码路径之际就能使用这个, 比如说, 一旦一个语句里所有有可能的值都已经被case子句涵盖了, 那么就能够把它运用在:子句当中, 当你极其想要在某个位置放置一个(0)或者abort()调用的时候同样可以使用这个。处在特定一种模式情形下时, 此类这个宏专门起到协助工具编译器针对代码施行优化的作用, 并且还能够规避发出那种显现为不可到达状况的代代码所引发的警告, 举例来说, 于处在GCC的特定一种模式状态下时, 这般宏借助e()来达成实现。在使用一种特定用法之后, 该用法在于调用这样一个函数, 这个函数不会返回, 并且此函数尚未被声明。要是存在一个代码路径, 它不太具备成为正常代码的可能性, 然而在特殊情形之下能够抵达, 那就不可以运用该宏。举例来讲, 在低内存状况时, 或者有一个系统调用返回超出预期范围的值, 像这类情况, 最好把错误汇报给调用者。要是没办法将错误汇报给调用者, 可以加以使用。(x)返回 x 的绝对值。(x, y)返回 x 和 y 当中的最小值。(x, y)返回 x 和 y 当中的最大值。(x)使x转变成为C字符串, 举例来说(123)得出123。(type, )返回结构 (type) 的大小以字节表示。(c)参数必须为-128, 127或者, 处于一定范围之内的字符类型或者整数类型哩。这个宏的作为, 先是把c针对加以使劲强行转换, 最终才向着 char 返回。(s)与(s)相似之情形下, 然而当于命令行之上进行传递之时, 却反而会返回NULL, 若是设置了Flag。(arg)针对函数定义环节的那些未被采用 的相关的参数, 依托于此这之后去消弭 编译器所反馈回来的相关的警告, 像这样 举例来说 , 有int func(int a, int (b));这种形式 , 其中已然是表达为a , 其构成实际作用了。()弃用声明。该宏必须放置在符号名称前。示例:Py_DEPRECATED(3.8) PyAPI_FUNC(int) Py_OldFunction(void);在 3.8 版更改: 添加了 MSVC 支持。(name, str)制作一个能够于文档字符串里运用的, 称作 name 的变量, 倘若并非跟文档字符串一块儿创构, 这个值就会是空白的。按照PEP 7中所讲的那样, 采用 用来当作文档字符串, 以此去支撑不与文档字符串一并构建的情形。示例:PyDoc_STRVAR(pop_doc, Remove and return the rightmost element.); static PyMethodDef deque_methods[] { // ... {pop, (PyCFunction)deque_pop, METH_NOARGS, pop_doc}, // ... }(str)根据给定的字符串输入, 去创建一个文档字符串, 或者在文档字符串已被禁用的情况下, 去创建一个空字符串。按照PEP 7当中所讲述的那样, 运用指定的文档字符串, 以此来支撑不与文档字符串一同进行构建的情形。示例:static PyMethodDef pysqlite_row_methods[] { {keys, (PyCFunction)pysqlite_row_keys, METH_NOARGS, PyDoc_STR(Returns the keys of the row.)}, {NULL, NULL} };对象、类型和引用计数大多数 /C API 函数存在一个或多个参数, 还有一个 * 类型的返回值, 这种类型是指向任意对象的不透明数据类型的指针, 由于所有对象类型, 在绝大多数情形下, 被语言以相同方式处理。比如赋值、作用域规则以及参数传递等, 所以用单个 C 类型去表示它们很适宜, 几乎所有对象存在于堆中, 你无法声明一个类型为 的自动或静态变量, 只能声明类型为 * 的指针变量。存在着唯一的例外情况, 那就是type对象, 由于这种对象始终都不可能被释放掉, 所以它们一般而言都是静态的对象。一切对象就连整数也在内都具备一个type以及一个count。对象的类型用以明确它是何种类型的对象像是整数、列表或者用户定义函数另外还有很多, 如同所阐述的那样。针对每一种广为人知的类型, 均存在一个宏用于核查对象是否归属于该类型举例而言, 当并且只有当a所指向的对象为列表时(a)才是真的。引用计数现有计算机内存大小有限, 且往往限制得很严格, 这使得引用计数变得重要 , 它会计算有多少不同地方对一个对象进行了引用 , 这些地方可以是另一个对象 , 也可以是全局或静态C变量 , 或是某个C函数中的局部变量 , 当某个对象的最后一个引用被释放时 , 也就是其引用计数变为零的时候 , 该对象就会被取消分配 , 如果该对象包含对其他对象的引用 , 那么也会释放这些引用。当不存在对别的对象的引用时, 这些对象会以相同方式被取消分配, 如此类推。此处对象间相互引用明显是个问题当下解决办法是“别这么做”对于引用计数而言, 总是会去显式地开展操作。一般的做法是借助宏来获取对象的新引用, 也就是使得引用计数加一, 并且利用宏来释放引用, 也就是让引用计数减一。宏相较于宏要复杂许多, 这是由于它必须查验引用计数是否为零, 随后才会去调用对象的释放器。释放器是一个函数指针, 它被包含在对象的类型结构体之中。要是对象属于复合对象类型, 像列表那样, 特定于这种类型的释放器会负责去释放对象中所含的其他对象的引用, 并执行所需的其他终结化操作。引用计数不会出现溢出的情况, 用于保存引用计数的位数, 至少会和虚拟内存里不同内存位置的位数一样多假设 () 大于等于 (void*), 所以呢, 引用计数的增添可算得上是一个简单范畴之内的操作。不必要为各含指向对象指针的局部变量持有, 也就是增加引用计数。理论来讲, 变量指向对象时对象引用计数会加一, 变量离开作用域时引用计数会减一。然而, 这两种情形会相互抵消, 所以最终引用计数未改变。使用引用计数的唯一切实原因在于只要变量指向对象就能防止对象被释放。假若是这样一种情形, 就是只要我们清楚地晓得, 至少存在着一个指向某对象的引用, 并且这个引用是与我们的变量共同存在着的, 那么就根本没有必要去临时获取一个新的引用(所谓新的用在这里就是增加引用计数啦),另外, 有这样一种具体真实情况会使引用计数发生增加情形呢, 那就是当对象被当作参数传递给扩展模块里的C函数的时候, 而且这些C函数是在特定环境中被调用的, 在该调用机制的作用下, 会确保在调用的那段期间, 对于每个参数都持有一个引用。可是, 一个平常会出现的圈套是从列表里提取对象, 并且在没有获取新引用的状况下将其留置一阵子。某一个别的操作说不定会在不经意间从列表里剔除该对象, 释放这个引用, 还可能撤销分配其资源。实际的风险在于看起来没什么问题的操作或许会引发随意的代码去做这件事存在一条代码路径能够让控制权从特定流程回流到用户那里, 所以差不多任何操作都潜藏着风险。泛型操作名称以, _, 或_始的函数视为安全做法, 始终予以采用。这些操作总会按其返回对象, 创建全新_也就是加大引用计数_。这致使调用者于获取结果后, 调用_负有责任此种做法不久便能顺应成自然。引用计数细节以引用所有权来解说 /C API 里函数 的引用计数为宜, 要晓得所有权关联对象不当盖因对象无从成为隶属: 往往是被分享的形势, 乃是与引用挂钩的。所谓“拥有一经受有”指的应该是当那个引用不再被需要之际, 就得针对这引用加以调告。并且所有权能够进行移转, 这表明收受得过来该一属质对象名引用的代码, 在自己不再需留它时分必须通过调号 或者为调号 来做最终放弃要不然就持续地把这份责任给传递下去一般是转交给调用这操作方即调指拨方吧。当有一个函数, 此函数把引用所有权转让给调用自己的那一方的时候, 那就表明调用的这一方接收到了一个从原本那儿新得来的引用。当没有将所有权进行转移时, 那就表明调用的那一方并非转让得到引用而是以借用的方式得到这个引用。对于上述所描述的一种情况而言, 并不需要进行任何额外的操作。与之相反, 在调用方函数传入一个对象引用之际, 存在着两种情形可以出现: 其一, 该函数获取了一个对象的引用其二, 该函数并未获取。所谓获取引用, 指的是当你向一个函数传入引用之时, 该函数会认定它对该引用具有所有权, 而你对此便不再承担责任了。极为少见函数会去窃取引用, 存在两个关键的例外, 那便是和, 它们会去窃取对条目的引用, 不过并非条目所处的元组或者列表。这些函数被设计成会窃取引用, 原因在于在运用新创建的对象去填充元组或者列表时存在一个惯常的惯例, 比如创建元组(1, 2three)的代码看上去大概是如此这般的, 暂且先别去管错误处理, 后续会呈现更为妥当的代码编写方式。PyObject *t; t PyTuple_New(3); PyTuple_SetItem(t, 0, PyLong_FromLong(1L)); PyTuple_SetItem(t, 1, PyLong_FromLong(2L)); PyTuple_SetItem(t, 2, PyUnicode_FromString(three));在这个地方, 返回了一个全新的引用, 并且此引用马上就会被进行窃取的那个东西给盗取。当处于你想到要持续使用一个对象, 然而它的引用将会遭到窃取的那种状况时, 可在调用会窃取引用的函数以前, 采用去抓取另外一个引用。顺便提及一下, 存在的一种方式是用于设置元组条目的仅有途径, 且另外的两种情况会拒绝如此操作, 原因在于元组属于不可变数据类型, 而你理应仅仅针对由你自身所创建的元组来运用该方式。等价于填充一个列表的代码可以使用 和 来编写。然而, 于实践当中, 你极少会运用这些用以创建以及填充元组或者列表的方式。存在一个通用的函数, 其能够依据C值来创建多数常用对象, 此由一个格式字符串予以指明。比如说, 上面那两个代码块能够用下面的代码来替代还会承担错误检测之责:。PyObject *tuple, *list; tuple Py_BuildValue((iis), 1, 2, three); list Py_BuildValue([iis], 1, 2, three);对条目使用等操作时, 更常见做法是只借入引用, 像把参数传递给正在编写的函数那样。这种情形下, 它们在引用方面行为更清晰。因为不必为转走引用获取新引用“让它被偷取”。比方说, 这个函数把列表实际是任何可变序列中所有条目设为给定条目:。int set_all(PyObject *target, PyObject *item) { Py_ssize_t i, n; n PyObject_Length(target); if (n 0) return -1; for (i 0; i n; i) { PyObject *index PyLong_FromSsize_t(i); if (!index) return -1; if (PyObject_SetItem(target, index, item) 0) { Py_DECREF(index); return -1; } Py_DECREF(index); } return 0; }在函数返回值这方面, 有着略微不一样的情形。多数函数传递引用时, 不会变更你对该引用的所有权责任。然而, 不少返回引用的函数, 会给予你该引用的所有权。所以会这样, 原因很单纯: 好多情形下, 返回的对象是临时创建的, 而你所获取的引用是针对该对象的唯一引用。故而, 像某些会返回对象引用的通用函数, 会始终返回一个全新的引用调用方会成为此引用的所有者。重点之一是, 能否拥有函数返回的引用, 仅取决于所调用的函数, 作为参数传给函数的对象类型这一附带物, 不会产生额外影响。所以, 若以从列表提取条目这种方式, 便不会拥有其引用。然而, 若用恰好接受相同参数的方式, 从同一列表获取同样的条目, 就会拥有对返回对象的引用。下面说一说, 你得怎样去编写一个能对整数列表里若干条目进行计算的函数示例, 其中一种是采用此处以逗号分隔开的方式, 而另一种则是采用彼处以逗号分隔开的方式。long sum_list(PyObject *list) { Py_ssize_t i, n; long total 0, value; PyObject *item; n PyList_Size(list); if (n 0) return -1; /* Not a list */ for (i 0; i n; i) { item PyList_GetItem(list, i); /* Cant fail */ if (!PyLong_Check(item)) continue; /* Skip non-integers */ value PyLong_AsLong(item); if (value -1 PyErr_Occurred()) /* Integer too big to fit in a C long, bail out */ return -1; total value; } return total; }long sum_sequence(PyObject *sequence) { Py_ssize_t i, n; long total 0, value; PyObject *item; n PySequence_Length(sequence); if (n 0) return -1; /* Has no length */ for (i 0; i n; i) { item PySequence_GetItem(sequence, i); if (item NULL) return -1; /* Not a sequence, or other failure */ if (PyLong_Check(item)) { value PyLong_AsLong(item); Py_DECREF(item); if (value -1 PyErr_Occurred()) /* Integer too big to fit in a C long, bail out */ return -1; total value; } else { Py_DECREF(item); /* Discard reference ownership */ } } return total; }类型在/C API里, 扮演关键作用的其他数据类型数量稀少多数是像int、long以及char*这类简单的C类型等。存在一些结构类型, 其用途是燃烧液体在列出模块所导出的函数其中的一个, 或者是某个新对象类型的一个, 另外还有一个结构类型, 用于描述复数的值。这些结构类型会跟使用它们的函数一同展开讨论。typePart of the .有一种有符号整数类型, 它能让 () 等于 ()。C99 并未直接定义这样的事物这里的 是一种无符号整数类型。要是想了解详细情况, 去查阅一下 PEP 353。当中代表 类型的最大正整数值。异常程序员仅需应对特定得去处理的错误异常, 未处理的异常会自动传进调用者那里, 接着传递给调用者的调用者, 如此这般, 一直到抵达顶级解释器, 在那儿把它们汇报给用户并伴有堆栈回溯。然而, 对于C程序员而言, 错误检查必然始终要显式去做。/C API里的全部函数皆有可能引发异常, 除非在函数的文档当中另外专门显式声明。通常来讲, 当一个函数碰到错误时, 它会去设置一个异常, 抛弃掉它所具备的任何对象引用, 并且返回一个错误标示。要是没有关于例外的文档说明, 这个标示将会是NULL或者-1, 具体是依据函数的返回类型来定。有一小部分函数会返回一个布尔真/假结果值, 其中假值意味着错误。有极端少的函数, 不存在显式的错误标示, 亦或是有着不明确的返回值, 要用一种方式来进行显式的检测。这些属于被归入例外的状况总是会得以明白清晰地记入文档之中。各个线程的存储里维护着异常状态这等同于在没有线程的应用当中运用全局存储, 一个线程能够处于两种状态里的其中一种状态: 异常已然发生, 或者并未发生, 函数能够被用于检查这种状态: 当异常发生的情况下它会返回一个被借入的异常类型对象的引用, 在另外的情形下就返回NULL, 有多个函数能够设置异常状态, 是最为常见的尽管并非最通用的设置异常状态的函数, 而能够清除异常状态。由三个对象构成完整的异常状态, (这三个对象都有可能是 NULL): 异常类型、对应的异常值、还有回溯信息。这些对象的含义跟, 中 sys.() 的结果是一样一样的捏但是, 它们又不完全相同: 对象代表着由...语句 的最后一个异常, 而 C 层级的异常状态嘛仅仅在异常被传入到啊 C 函数或者在它们之间传递的时候才存在, 一直到它抵达字节码解释器的主事件循环, 这个主事件循环会负责把它转移到 sys.() 等地方。请留意, 自1.5起, 从代码去访问异常状态, 其首选的、线程安全的方式乃是调用函数, 该函数会返回代码的分线程异常状态。另外, 这两种访问异常状态的方式, 其语义都有变化, 所以捕获到异常的函数会保存并恢复其线程的异常状态, 藉以保留其调用方的异常状态。这能够防止异常处理代码里, 因一个看似无害的函数覆盖了正在处理的异常而导致的常见错误它还能减少在回溯由栈帧所引用对象时, 那种往往并不必要的生命期延长。作为一项用于普遍遵循的原则, 存在这样一个函数, 该函数会调用另外某个从事特定任务执行的功能, 此期间, 要对被调用的那个功能是否引发了出乎预料的出错状况加以检验查证, 假如正好引发了异常状态, 那么就要使这种异常形势传递给调用它的那一方。它需要抛弃丢弃自身所拥有的任何对象引用, 并且返回一个用于体现错误性质的标示标志, 然而它绝对不可以设置另外一个与之相关的异常情况 --- 这个若一设置, 就会把刚刚引发的异常给覆盖掉, 进而导致丢失有关错误确切缘由机制道理的至关重要的信息内容。一个进行检测异常并传递它们的简单例子, 在上面的()示例里进行了演示, 这个例子恰好处在检测到错误时, 不需要清理所拥有的任何引用, 下面的示例函数演示了一些错误清理操作, 首先为了向你提示的优势我们展示了等效的代码:。def incr_item(dict, key): try: item dict[key] except KeyError: item 0 dict[key] item 1对应的 C 代码如下int incr_item(PyObject *dict, PyObject *key) { /* Objects all initialized to NULL for Py_XDECREF */ PyObject *item NULL, *const_one NULL, *incremented_item NULL; int rv -1; /* Return value initialized to -1 (failure) */ item PyObject_GetItem(dict, key); if (item NULL) { /* Handle KeyError only: */ if (!PyErr_ExceptionMatches(PyExc_KeyError)) goto error; /* Clear the error and use zero: */ PyErr_Clear(); item PyLong_FromLong(0L); if (item NULL) goto error; } const_one PyLong_FromLong(1L); if (const_one NULL) goto error; incremented_item PyNumber_Add(item, const_one); if (incremented_item NULL) goto error; if (PyObject_SetItem(dict, key, incremented_item) 0) goto error; rv 0; /* Success */ /* Continue with cleanup code */ error: /* Cleanup code, shared by success and failure path */ /* Use Py_XDECREF() to ignore NULL references */ Py_XDECREF(item); Py_XDECREF(const_one); Py_XDECREF(incremented_item); return rv; /* -1 for error, 0 for success */ }这个例证展现了C语言里头, goto语句一种得到认许的运用范例, 其中指明了如何借某某措施去处置特定类别异常状况, 以及怎样借助某某手段去应对或许为NULL的自有引用情况, 留意名称里头有着‘X’字样, 每当遇上NULL引用之际便会出现崩溃后果, 具备关键意义之处乃在于, 用来留存自有引用的变量应为NULL方可发挥相应功效, 与之相类似, 所建议的返回值亦需被初始化为-1表示失败, 唯独于最终执行的那个调用成功之后才能够被设置为对应成功状态。嵌入关于它的初始化, 以及可能存在的最终化, 这是一项只有解释器的嵌入方相对于扩展编写者来讲才需忧心的重要任务。在解释器被初始化完成之后, 解释器的多数功能方才能够被投入使用。基础的初始化函数存在着。那个函数会对已加载的模块表予以初始化 , 并且去创建基础模块 , 以及。它同样会对模块搜索路径 sys.path进行初始化。没法进行“脚本参数列表”sys.argv的设置。要是后续即将执行的代码用到这个变量, 那就得在调用之后, 借助调用argc, argv来明确地设置它。在多数系统当中, 尤其是Unix和, 尽管在细节之处存在差异, 会按照对标准解释器可执行文件位置的最佳推测来算出模块搜索路径, 并且设定库能在相较于解释器可执行文件的固定位置被找到。特别而言, 它会于基于在shell命令搜索路径环境变量PATH上查到的名为的可执行文件所在父目录里搜寻名为lib/.Y的目录。以举例来讲, 要是可执行文件处在 /usr/local/bin/ 这个位置, 那它就会认定库是在 /usr/local/lib/.Y 这儿。事实上, 这个特定的路径还会变成“回退”的地方, 在没办法在 PATH 当中找到名字是的可执行文件的时候就会被用上用户能够借着去设置环境变量, 或者借着去设置在标准路径之前放进额外的目录来把这种行为给覆盖掉。被嵌入的该应用程序, 能够借助于在进行调用之举的先前时段动用相应的(file)方式予以实施调用操作, 进而达成对于搜索时序予以变更行为。需要留意此点, 仍然会将这样的设置予以覆盖, 并且仍然会被安插至标准路径的前方之地。那些需求拥有完全化控制权的应用程序, 必须自行供给它独一无二的, , 以及实现举措举措这些相关函数全都是在/.c这个具体场域之中进行定义的。有时, 还得针对进行“反初始化”。比如说, 应用程序有可能想要重新启动再度调用, 或者应用程序对于的使用已然完成且想要释放所分配的内存。这能够借助调用得以达成。要是当前处于已初始化状态, 那么函数将会返回真值。关于这些函数的更多信息会在后续的章节里予以呈现。需要留意的是, 不会释放所有由解释器所分配的内存, 像是由扩展模块所分配的内存当前是不会被释放的。调试构建可以附带某些宏去进行编译, 以此来启用对解释器以及扩展模块的额外检查, 这些检查会给运行时增添大量的额外开销, 鉴于此, 它们在默认情况下是未被启用的。完整列表涵盖各种调试构建版, 其所在之处为源代码颁发包中的Misc/.txt。存在支持追踪引用计数的构建版, 有调试内存分配器的构建版, 还有针对主解释器事件循环的低层级性能分析方面支撑的构建版等以供使用。本节剩余部分仅会论述最常用的几种构建版。附带定义, 宏来编译解释器会产生通常所说的那种。在Unix编译里启用, 是借助添加到./命令来达成的。它还能够通过提供非专属的宏来予以启用。当在Unix编译中启用这个的时候, 编译器优化将会被禁用。除了下文描述的引用计数调试还会执行额外检查请参阅 。将引用追踪启用进行定义, 参见相关内容。当此宏被定义之时, 会通过在每个之上增添两个额外字段, 以此种方式来维护一个活动对象的循环双链列表。总的分配量也会处于被追踪的状态。在退出之际, 所有现存的引用将会被打印出来。在交互模式之下, 这将会在解释器运行每条语句以后发生。有关更多详细信息请参阅源代码中的 Misc/.txt 。