Python导入系统

发布在 编程

本人翻译,转载请注明出处

5. 导入系统

一个模块中的Python代码通过导入的过程可以访问另一个模块的代码。import语句是调用导入机制的最常见的方式,但不是唯一的方式。例如importlib.import_module()和内建函数__import__()也可以用来调用导入机制。

import语句结合了两个操作;搜索指定模块,然后在本地作用域把搜索的结果和名字绑定。import语句的搜索的操作被定义为一次__import__()函数的调用,传入一些合适的参数。__import__()的返回值被用来执行import语句的命名绑定的操作。命名绑定的确切的细节参见import语句。

__import__()的直接调用仅执行模块的搜索,如果有结果,还有模块的创建操作。当( 执行__import__()函数时)某些副作用可能会发生,例如导入父模块,还有更新很多缓存(包括sys.modules),只有import语句执行命名绑定操作。

__import__()作为导入语句被调用的时候,是标准内建函数__import__()被调用。其他调用导入系统的机制(例如importlib.import_module())可能选择推翻__import__()而使用自己的导入语义学实现。

当一个模块第一次被导入的时候,Python搜索这个模块,如果发现,它就会创建一个模块对象[1],初始化之。如果指定模块无法被找到,将会触发一个ImportError。Python实现了多种策略当导入机器被调用时搜索指定模块。这些策略可以被多种下文描述的钩子修改和拓展。

在版本3.3中的改变: 导入系统更新完全实现了PEP 302的第二阶段。不再有任何隐式的导入机制了 - 整个导入系统通过sys.meta_path暴露。此外,本地的命名空间包支持被实现了(参见PEP 420)。

5.1. importlib

importlib模块提供丰富的API和导入系统交互。例如importlib.import_module()提供一个推荐的,比内建函数__import__()更简单的API调用导入机制。参考importlib的库文档获得更多的细节。

5.2. 包

Python只有一种模块对象的类型,所有的模块都是这种类型,无论这个模块是由Python,C还是其他语言实现的。为帮助组织模块提供命名继承,Python有的概念。

你可以把包想成是一个文件系统的目录而模块是目录下的文件,不过不要对这个比喻太咬文嚼字因为包和模块并不需要来自文件系统。就本文档的目的,我们将使用这种目录和文件的方便的比喻。类似文件系统的目录,包依层级组织,并且包它们本身也可能包含子包,或者是常规模块。

重要的是记住所有的包都是模块,但不是所有的模块都是包。或者换种说法,包只是一种特别的模块。特别的,任何含有__path__属性的模块都被认为是包。

所有模块都有一个名字。子包名字和他们的父包的名字用一个点分隔,类似于Python的标准属性的访问语法。因此你或许有一个名为sys的模块和一个名为email的包,其中包含一个名为email.mime的子包和这个子包中的一个名为email.mime.text的模块。

5.2.1. 常规包

Python定义了两种类型的包,常规包命名空间包。常规包是传统的包,他们存在于Python 3.2及更早的版本中。一个常规包通常实现为一个包含一个__init__.py文件的目录。 当导入常规包时,这个__init__.py文件被隐式执行,它定义的对象被绑定到包命名空间中的名称。 __init__.py文件可以包含与任何其他模块可以包含的Python代码相同的Python代码,Python会在导入时为模块添加一些其他属性。

例如,以下文件系统布局定义具有三个子包的顶级父包:

1
2
3
4
5
6
7
8
parent/
__init__.py
one/
__init__.py
two/
__init__.py
three/
__init__.py

导入parent.one将隐式执行parent/__init__.pyparent/one/__init__.py。 后续导入的parent.twoparent.three将分别执行parent/two/__init__.pyparent/three/__init__.py

5.2.2. 命名空间包

命名空间包由各种组分组成,其中每个组分向父包提供子包。组分可以驻留在文件系统上的不同位置。组分也可以在zip文件,网络或Python在导入期间搜索的任何其他位置找到。命名空间包可以或可以不直接对应于文件系统上的对象;它们可以是没有具体代表的虚拟模块。

命名空间包的__path__属性不使用普通的列表。它们改为使用自定义可迭代类型,如果其父包(或顶级包的sys.path)的路径发生更改,则会在该包中的下一次导入尝试时自动执行对包组分的新搜索。

使用命名空间包,没有parent/__init__.py文件。实际上,在导入搜索期间可能会找到多个parent目录,其中每个目录由不同的组分提供。因此,parent/one可能不在物理上位于parent/two旁边。在这种情况下,每当导入顶层父包或其中一个子包时,Python将为顶级父包创建命名空间包。

有关命名空间包规范,请参见PEP 420

5.3. 搜索

要开始搜索,Python需要待导入模块(或包,但为了讨论的目的,差别是无关紧要的)的完全限定名。此名称可能来自import语句的各种参数,或来自importlib.import_module()__import__()函数的参数。

此名称将用于导入搜索的各个阶段,它可以是子模块的点分隔路径,例如foo.bar.baz。 在这种情况下,Python首先尝试导入foo,然后是foo.bar,最后是foo.bar.baz。 如果任何中间导入失败,则会引发ImportError

5.3.1. 模块缓存

在导入搜索期间检查的第一个位置是sys.modules。此映射充当先前导入的所有模块的缓存,包括中间路径。 因此,如果以前导入foo.bar.bazsys.modules将包含foofoo.barfoo.bar.baz的条目。 每个键将具有作为其值的相应模块对象。

在导入期间,模块名称在sys.modules中被查找,如果存在,则关联的值是满足导入的模块,并且导入过程完成。 但是,如果值为None,则会引发ImportError。 如果模块名称丢失,Python将继续搜索模块。

sys.modules是可写的。 删除键可能不会破坏关联的模块(因为其他模块可能保存对它的引用),但它将使指定模块的缓存条目无效,导致Python在下次导入时重新搜索指定的模块。 该键也可以分配为None,强制下一次导入模块导致ImportError

注意,如果你保留对模块对象的引用,使其在sys.modules中的缓存条目无效,然后重新导入指定模块,两个模块对象将不会相同。 相比之下,importlib.reload()将重用相同的模块对象,并且通过重新运行模块的代码来重新初始化模块内容。

5.3.2. 查找器和加载器

如果在sys.modules中找不到指定模块,那么将调用Python的导入协议来查找和加载模块。该协议由两个概念对象查找器加载器组成。查找器的工作是确定是否可以用它已知的无论什么策略找到指定模块。实现这两个接口的对象被称为导入器 - 当他们发现他们可以加载请求的模块时,它们返回自己。

Python包括许多默认查找器和导入器。 第一个知道如何定位内置模块,第二个知道如何定位冻结模块。 第三个默认查找器在导入路径中搜索模块。 导入路径是可以命名文件系统路径或zip文件的位置列表。 它还可以扩展为搜索任何可定位资源,例如由URL标识的资源。

导入机制是可扩展的,因此可以添加新的查找器以扩展模块搜索的范围和范围。

查找器实际上不加载模块。 如果他们可以找到指定模块,他们返回一个模块规范,模块的导入相关信息的封装,导入机制然后在加载模块时使用。

以下部分更详细地介绍查找器和装载程序的协议,包括如何创建和注册新协议以扩展导入机制。

在版本3.4中更改: 在以前的Python版本中,查找器直接返回加载器,而现在它们返回包含装载器的模块规格。 装载机仍在导入期间使用,但责任较少。

5.3.3. 导入钩子

导入机制被设计为可拓展的;这个的主要机制是导入钩子。有两种类型的导入钩子:元钩子和导入路径钩子。

元钩子在导入进行前被调用,在任何其他导入过程开始之前,除了sys.modules缓存查找。这运行元钩子覆盖sys.path处理,冻结模块,甚至内置模块。通过向sys.meta_path添加新的查找器对象来注册元钩子,如下所述。

导入路径钩子作为sys.path(或package.__ path__)处理的一部分,在遇到它们相关的路径项的点被调用。 通过向sys.path_hooks添加新的可调用项来注册导入路径挂接,如下所述。

5.3.4. 元路径

当在sys.modules中无法找到指定模块,Python接下来搜索sys.meta_path, which contains a list of meta path finder objects. These finders are queried in order to see if they know how to handle the named module. Meta path finders must implement a method called find_spec() which takes three arguments: a name, an import path, and (optionally) a target module. The meta path finder can use any strategy it wants to determine whether it can handle the named module or not.

If the meta path finder knows how to handle the named module, it returns a spec object. If it cannot handle the named module, it returns None. If sys.meta_path processing reaches the end of its list without returning a spec, then an ImportError is raised. Any other exceptions raised are simply propagated up, aborting the import process.

The find_spec() method of meta path finders is called with two or three arguments. The first is the fully qualified name of the module being imported, for example foo.bar.baz. The second argument is the path entries to use for the module search. For top-level modules, the second argument is None, but for submodules or subpackages, the second argument is the value of the parent package’s __path__ attribute. If the appropriate __path__ attribute cannot be accessed, an ImportError is raised. The third argument is an existing module object that will be the target of loading later. The import system passes in a target module only during reload.

The meta path may be traversed multiple times for a single import request. For example, assuming none of the modules involved has already been cached, importing foo.bar.baz will first perform a top level import, calling mpf.find_spec("foo", None, None) on each meta path finder (mpf). After foo has been imported, foo.bar will be imported by traversing the meta path a second time, calling mpf.find_spec("foo.bar", foo.__path__, None). Once foo.bar has been imported, the final traversal will call mpf.find_spec("foo.bar.baz", foo.bar.__path__, None).

Some meta path finders only support top level imports. These importers will always return None when anything other than None is passed as the second argument.

Python’s default sys.meta_path has three meta path finders, one that knows how to import built-in modules, one that knows how to import frozen modules, and one that knows how to import modules from an import path (i.e. the path based finder).

在版本3.4中更改: The find_spec() method of meta path finders replaced find_module(), which is now deprecated. While it will continue to work without change, the import machinery will try it only if the finder does not implement find_spec().

脚注

[1] 参见types.ModuleType

注释和共享

当Redis的密码包含`/`

发布在 编程

今天在把Azure Redis Cache集成到Django项目的时候发现一个奇怪的报错,ValueError at /login/ invalid literal for int() with base 10: 'iAITMl7lD127c'

检查一下我的settings.py是这么写的:

1
2
3
4
5
6
7
8
9
CACHES = {
"default": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "rediss://:iAITMl7lD127c/VZEl0OZl8qviaXHIoKekaJjP1J1HI=@#马赛克#:6380/0",
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
}
}
}

然后发现报错信息中的iAITMl7lD127c正好就是/前面的内容,料想应该是解析URL的时候错把这个/当做分隔符了。一路TraceBack发现正是Python的标准库urlparse中抛出的错误,然后义正言辞地去找redis-py,结果发现人家已经修复了,但是django-redis应该不会对此进行修改,因为作者是知道这个BUG的但是也没改,而是给了之前的OPTIONS的解决方案。我目前的解决方案是,刷一个不带/的密码或者这样用:

1
2
3
4
5
6
7
8
9
10
CACHES = {
"default": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "rediss://#马赛克#:6380/0",
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
"PASSWORD": "iAITMl7lD127c/VZEl0OZl8qviaXHIoKekaJjP1J1HI=",
}
}
}

参考阅读:

https://github.com/niwinz/django-redis/issues/114
https://github.com/andymccurdy/redis-py/issues/579

注释和共享

数字转中文

发布在 编程
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
43
44
45
46
47
48
49
50
51
52
53
54
# coding: utf-8
"""\
规则:
1. 中文分为数字(零、一、二、三、四、伍、六、七、八、九),数位[个、十、百、千、万、亿]和符号[负,点]三部分
2. 数位个略去
3. 以一十开头略去首位一
4. 数位为零,保留数字零略去该数位单位
5. 连续数位为零,保留一个零
6. 个,万,亿位为零,略去零
7.
"""
numbers = ["零", "一", "二", "三", "四", "五", "六", "七", "八", "九"]
units = ["亿", "万", "千", "百", "十", ""]
def i2c(i, recursive=False):
if i < 0:
return '负'+i2c(abs(i))
if i == 0:
return "零"
elif i < pow(10, 4):
result = ""
for d, u in zip(str(i).zfill(4), units[2:]):
result += numbers[int(d)]+u if d != "0" else "零"
elif i < pow(10, 8):
wj, rest = divmod(i, pow(10, 4))
result = i2c(wj)+"万"+("零" if rest < pow(10, 3) else "")+i2c(rest, True)
else:
yi, rest = divmod(i, pow(10, 8))
result = i2c(yi)+"亿"+("零" if rest < pow(10, 7) else "")+i2c(rest, True)
result = "零".join(result.replace("零", " ").split())
return result[1:] if result.startswith('一十') and not recursive else result
def f2c(f):
if f == '':
f = '0'
return '点'+''.join(map(lambda d: numbers[int(d)], f))
def n2c(n):
nsplit = n.split('.')
i = int(nsplit[0]) if nsplit[0] else 0
if len(nsplit) == 1:
return i2c(i)
elif len(nsplit) == 2 and n != '.':
return i2c(i)+f2c(nsplit[1])
else:
raise ValueError
if __name__ == '__main__':
import sys
print(n2c(sys.argv[1]))

注释和共享

Hexo是一个比较火的博客框架(主观),然后我使用了其中的Tranquilpeak主题,但是发现写着写着发现,本来应该等宽的代码部分明显不是等宽的,然后就翻文档

Change global style

If you want to change font families, font size, sidebar color, things like that, take a look at source/_css/utils/_variables.scss file. This file contains global variables used in this theme. Build the theme after changes to see changes.

OK,接着翻源码

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
// Global settings
// --------------------------------------------
// Font families
$open-sans: 'Open Sans';
$open-sans-sans-serif: 'Open Sans', sans-serif;
$merriweather-serif: 'Merriweather', serif;
$menlo: Menlo;
$font-family-base: $open-sans-sans-serif;
$font-families: (
// base
'headings': $open-sans-sans-serif,
// components
'code': $menlo,
'caption': $merriweather-serif,
'image-gallery': $open-sans,
'post-header-cover': $merriweather-serif,
'post-meta': $open-sans-sans-serif,
'post-content': $merriweather-serif,
'post-excerpt-link': $open-sans-sans-serif,
'highlight': $menlo,
// layout
'sidebar': $open-sans-sans-serif
);

Menlo这个字体是mono space的呀,怎么显示不对呢,然后我注意到这么一段话:

Menlo是一个无衬线等宽字体,由Jim Lyles设计,首次出现于2009年8月上市的Mac OS X Snow Leopard系统内建字体之一。基于开源字体Bitstream Vera与自由版权字体DejaVu字体改良而来

什么?Mac OS X内建字体?那也就是说本穷用的Windows 10 Home应该是不会有咯。然后本穷就下载,安装,重启浏览器,OK,终于显示正确了。

总结

  • Web开发不得不考虑跨平台的问题,所以一定不能在字体这个地方栽跟头,不能说你用的Mac开发的你就用Menlo不管了,好歹加个备选的monospace。

  • issue给作者之后发现,问题已经被fix,但是还得等下个版本release才能见到。这又教导我,在issue之前最好好好搜一下已有的issue和pull request,已经有的问题不要重复开issue。

注释和共享

PEP 7

发布在 编程

介绍

本文提供Python的C实现的C代码的编码风格。Python代码的风格指南参见相关信息的PEP[1]

注意,规则有时也会被打破。两个打破特定规则的好的理由:

  1. 当遵循这个规则回事代码更少地可读,即使其他人依据此规则去阅读代码。
  2. 未保持和周围也打破规则的代码一致(也许是历史原因)–尽管有一个机会去清理别人的烂摊子(以真正的XP格式)

C方言

  • Python 3.6之前使用的是ANSI/ISO标准C(1989版标准)。这意味着(以及其他许多情况)所有的申明都必须在代码块顶部(不需要再函数的顶部)。
  • Python 3.6及之后的版本使用采用了部分C99特性的C89:
    • 标准整形在<stdint.h><inttypes.h>中。我们要求定宽的整形类型。
    • static inline函数
    • 指定初始化器(designated initializers),对类型定义特别好。
    • 混合申明
    • 布尔值
    • C++风格的行注释
      未来C99的特性可能会被添加到这个列表视编译器支持而定(主要是MSVC)。
  • 不要使用GCC拓展(例如,不要写没有行尾的反斜杠的多行字符串)
  • 所有的函数申明和定义必须使用完全原型(例如,指定所有参数的类型)
  • 永远不要使用C++风格的//一行注释
  • 在几种主要编译器(gcc,VC++,一些其他的)上没有警告

代码布局

  • 使用4个空格缩进并完全禁用tab。
  • 每行不得超过79个字符。如果这一条和前一条一起没有给你的代码足够的空间,那么你的代码就太复杂了–考虑使用子程序。
  • 函数定义格式:函数名在第1列,最外面的花括号在第一列,在本地变量申明之后留空行。
1
2
3
4
5
6
7
8
9
10
static int
extra_ivars(PyTypeObject *type, PyTypeObject *base)
{
int t_size = PyType_BASICSIZE(type);
int b_size = PyType_BASICSIZE(base);
assert(t_size >= b_size); /* type smaller than base! */
...
return 1;
}
  • 代码结构:在诸如iffor等关键词和接下来的左括号直接一个空格;在括号内没有空格;大括号被强烈推荐使用但可在C允许的情况下省略,并且它们应格式化成下面所示:
1
2
3
4
5
6
if (mro != NULL) {
...
}
else {
...
}
  • return语句不应有多余的括号:
    return Py_None; /* correct */
    return(Py_None); /* incorrect */

  • 函数和宏的调用风格:foo(a, b, c)–在左括号之前没有空格,在括号中没有空格,在逗号前没有空格,在每个逗号后一个空格。

  • 总是在赋值符号,布尔值和比较符两边加上括号,在表达式中使用了很多操作符,在最外面的(最低优先级)的两边加上空格。

  • 长行换行:如果可以,在最外层表达式的逗号后折行。总是保持缩进

    1
    2
    3
    PyErr_Format(PyExc_TypeError,
    "cannot create '%.100s' instances",
    type->tp_name);
  • 当你在长表达式的二元操作符处换行,这个操作符跟在前一行的行末,例如:

    1
    2
    3
    4
    if (type->tp_dictoffset != 0 && base->tp_dictoffset == 0 &&
    type->tp_dictoffset == b_size &&
    (size_t)t_size == b_size + sizeof(PyObject *))
    return 0; /* "Forgive" adding a __dict__ only */
  • 在函数、结构定义、函数的主要片段的前后留空行。

  • 注释放在它们描述的函数之前。

  • 所有的函数和全局变量申明为static,除非他们是发布的接口的一部分

  • 对于外部函数和变量,我们总是在“Include”文件夹中合适的头文件中声明,使用PyAPI_FUNC()宏,如:

    PyAPI_FUNC(PyObject *) PyObject_Repr(PyObject *);

命名习惯

  • 公共函数使用Py前缀;永远不要给静态函数(使用Py前缀)。Py_前缀给全局服务例程如Py_FatalError;特定的服务组(例如特定对象类型API)使用长前缀例如字符串使用PyString_。

  • 公共函数和变量使用混合大小写以及下划线,例如这些:PyObject_GetAttrPy_BuildValuePyExc_TypeError

  • 偶尔的一个“内部”函数需要对加载器可见,对此我们使用_Py前缀,例如:_PyObject_Dump

  • 宏应该是混合大小写的前缀跟着大写,例如:PyString_AS_STRINGPy_PRINT_RAW

文档字符串

  • 对文档字符串使用PyDoc_STR()或者PyDoc_STRVAR()宏以获得无文档字符串地编译Python(./configure --without-doc-strings)。

    对于需要支持早于2.3版本Python的C代码,你可以在引入Python.h后包含如下:

    1
    2
    3
    4
    5
    #ifndef PyDoc_STR
    #define PyDoc_VAR(name) static char name[]
    #define PyDoc_STR(str) (str)
    #define PyDoc_STRVAR(name, str) PyDoc_VAR(name) = PyDoc_STR(str)
    #endif
  • 函数文档字符串的第一行应是一个“签名行”–给出一个参数和返回值简短的概要,例如:

    1
    2
    3
    PyDoc_STRVAR(myfunction__doc__,
    "myfunction(name, value) -> bool\n\n\
    Determine whether name and value make a valid pair.");

    总是包含一个空行在签名行和描述的文本之间。

    如果函数的返回值总是为None(因为没有有意义的返回值),不要写返回类型的提示。

  • 当写多行文档字符串时,确保总是像上面例子中使用反斜杠继续,或者字符串字面相连:

    1
    2
    3
    PyDoc_STRVAR(myfunction__doc__,
    "myfunction(name, value) -> bool\n\n"
    "Determine whether name and value make a valid pair.");

    尽管一些C编译器接受没有上述两种规范的字符串:

    1
    2
    3
    4
    /* BAD -- don't do this! */
    PyDoc_STRVAR(myfunction__doc__,
    "myfunction(name, value) -> bool\n\n
    Determine whether name and value make a valid pair.");

    不要这么做;已知MSVC编译器会抱怨这个。

参考

[1] PEP 8 , “Style Guide for Python Code”, van Rossum, Warsaw ( http://www.python.org/dev/peps/pep-0008 )

版权信息

此文档已放在公共领域。

源:https://github.com/python/peps/blob/master/pep-0007.txt

注释和共享

Python源码学习笔记-0

发布在 编程

Python

0. 前言

年初的时候给自己挖了一个坑,计划要在今年过一遍Python的源码。结果这一年零零碎碎总有事情耽搁了。现在已经10月份了,再不填坑就来不及了。于是有了这个系列。

说到看Python源码,陈儒的Python源码剖析可谓经典了。本系列基本也是根据此书的结构的。不过此书出版已近十年(2016-10),Python版本也从2.5到了3.5。不才能力有限,姑妄言之。

运行环境:Bash on Ubuntu on Windows,同样适用其他于UNIX-like环境。

1. 获取Python源码

你可以选择2.7.123.5.2。当然你也可以使用hg clone https://hg.python.org/cpythonhg update 3.5这样的命令来选择你喜欢的版本分支。(hg通过sudo apt install mercurial安装)

值得注意的是,CPython会检测到是否是从工作拷贝(由hg clone的)运行。这意味着如果你在你的拷贝中编辑CPython源码,Python代码(.py文件)的改变会被解释器立即使用和测试。(如果你修改的是C代码,你将需要重新编译受影响的文件)。

2. 编译为调试模式

CPython提供几种编译flag帮助调试很多东西。所有已知的flag都可以在Misc/SpecialBuilds.txt文件中找到,最常用的是Py_DEBUG标志。

暂时还不清楚SpecialBuilds中的编译选项的具体用法,搞明白之后再更新。

构建debug版的Python,可以在源码根目录中运行./configure --with-debug

Python源码剖析中有提到--enable-shared,如果你不知道是干什么的建议不要加上,不然会链接到系统自带的Python的。

2.1. 构建依赖

CPython的核心解释器需要C编译器构建。一些拓展模块也依赖额外的库。
Debian/Ubuntu系的可执行如下命令安装依赖:
sudo apt-get install -y make build-essential libssl-dev zlib1g-dev libbz2-dev \ libreadline-dev libsqlite3-dev wget curl llvm libncurses5-dev libncursesw5-dev xz-utils

Mac OS X:
brew install readline xz
其他发行版本参见参考资料[2]。

3. 目录结构

大部分的子文件夹都有自己的README。大部分的文件都有注释。

文件或文件夹 简介
Demo/ 示例脚本,模块和程序
Doc/ 文档源(reStructuredText)
Grammar/ Python的EBNF语法文件
Include/ 公共头文件
LICENSE 授权信息
Lib/ Python部分标准库,均是Python写的
Mac/ Macintosh特定的资源(例如,用IDLE作为OS X的应用)
Makefile.pre.in Source from which config.status creates the Makefile.pre
Misc/ 其他有用的文件
Modules/ 大部分内置模块的实现
Objects/ 大部分内置对象类型的实现
PC/ PC特定的文件(DOS, Windows, OS/2)
PCbuild/ Microsoft Visual C++的build目录
Parser/ 解析器和分词器以及他们的输入处理AST的节点也在这个文件夹
Python/ 字节编译器和解释器,Python核心部分。
README 读我
RISCOS/ RISC操作系统特定的文件
Tools/ 一些用Python写的有用的程序
pyconfig.h.in Source from which pyconfig.h is created (GNU autoheader output)
configure 配置的shell脚本(GNU autoconf的输出)
configure.ac 配置格式(GNU autoconf的输入)
install-sh 用来安装文件的Shell脚本

4. 贴士

4.1. Py_ssize_t
首先理解什么是size_t,然后是ssize_t,然后你就知道什么是Py_ssize_t了。
4.2. int_repr
在最新的Python代码中没有了int_repr这个函数,甚至没有了intobject。善用搜索引擎,发现了旧版本的intobject.c中有两处int_repr,一处是定义,一处是引用。(reprfunc)int_repr, /* tp_repr */拿注释tp_repr在新版里搜,发现了long_to_decimal_string, /* tp_repr */不出意外这就是改动后的函数,试试再按Python源码剖析的例子操作,成功输出。

未完待续

参考资料:

[1] Python Developer’s Guide — Python Developer’s Guide
[2] Common build problems · yyuu/pyenv Wiki

注释和共享

  • 第 1 页 共 1 页
作者的图片

唐梓涯

Pythoner, Emacser, GNU/Linuxer
目前在微软技术支持掏粪


全栈工程师


中国无锡