python标准库阅读系列—OS库(一)

源自大佬的python编程水平提升秘诀之一,阅读标准库源码。我预期将OS库的源码分成三篇来,这里是第一篇。加油!(做梦去吧,八篇能解决就不错了,赣)

import abc
import sys
import stat as st
from _collections_abc import _check_methods
GenericAlias = type(list[int])
_names = sys.builtin_module_names
__all__ = ["altsep", "curdir", "pardir", "sep", "pathsep", "linesep",
           "defpath", "name", "path", "devnull", "SEEK_SET", "SEEK_CUR",
           "SEEK_END", "fsencode", "fsdecode", "get_exec_path", "fdopen",
           "popen", "extsep"]
def _exists(name):
    return name in globals()
def _get_exports_list(module):
    try:
        return list(module.__all__)
    except AttributeError:
        return [n for n in dir(module) if n[0] != '_']

开头先导入了三个模块一个方法,分别是abc、sys、stat、_collections_abc._check_methods。

sys模块:“sys”即“system”,“系统”之意。该模块提供了一些接口,用于访问 Python 解释器自身使用和维护的变量,同时模块中还提供了一部分函数,可以与解释器进行比较深度的交互。

abc模块:由于python中并没有内置提供抽象类与抽象方法,所以设置了该模块来模拟实现抽象类。

stat模块:系统调用时用来返回相关文件的系统状态信息。这个模块包含了一些 “os.stat“ 函数中可用的常量和测试函数。

_check_methods方法:目前没有找到相关的介绍说明,只找到了对应的源码。猜测定义了许多的符号错误码。用于替代之前版本导入的errno模块。

GenericAlias = type(list[int]):存储list[int]的类型(只在后续中调用一次,影响不大),list[int]暂时没有查清楚是什么意思,后续查到了我会进行相应补充。

sys.builtin_module_names:返回一个包含内建模块名字的元组,包含所有已经编译到Python解释器的模块名字。

__all__ :在这之前有句注释,表明这里只有部分,后续还会添加一些。该属性,可用于模块导入时限制,如:from module import * 。此时被导入模块若定义了__all__属性,则只有__all__内指定的属性、方法、类可被导入。若没定义,则导入模块内的所有公有属性,方法和类 。但它只对import *起作用,对from XXX import XXX不起作用。

_exists(name):判断name是否属于全局变量。返回一个bool型变量(c++的习惯还没改过来,就是返回True或者False)。globals() 会以字典类型返回当前位置的全部全局变量(更详细的请自行查阅官方文档)。

_get_exports_list(module):通过传入module名,获取module.__all__ 定义的属性或方法,如果该module没有定义__all__,则获取所有不是以下划线_开头的属性、方法。(私有的属性、方法一般以下划线开头)

[n for n in dir(module) if n[0] != ‘_’]:列表推导式,dir() 函数不带参数时,返回当前范围内的变量、方法和定义的类型列表;带参数时,返回参数的属性、方法列表。如果参数包含方法__dir__(),该方法将被调用。如果参数不包含__dir__(),该方法将最大限度地收集参数信息。

if 'posix' in _names:
    name = 'posix'
    linesep = '\n'
    from posix import *
    try:
        from posix import _exit
        __all__.append('_exit')
    except ImportError:
        pass
    import posixpath as path
    try:
        from posix import _have_functions
    except ImportError:
        pass
    import posix
    __all__.extend(_get_exports_list(posix))
    del posix
elif 'nt' in _names:
    name = 'nt'
    linesep = '\r\n'
    from nt import *
    try:
        from nt import _exit
        __all__.append('_exit')
    except ImportError:
        pass
    import ntpath as path
    import nt
    __all__.extend(_get_exports_list(nt))
    del nt
    try:
        from nt import _have_functions
    except ImportError:
        pass
else:
    raise ImportError('no os specific module found')
sys.modules['os.path'] = path
from os.path import (curdir, pardir, sep, pathsep, defpath, extsep, altsep,devnull)
del _names

这部分代码是判断当前环境的操作系统。_names是之前定义的内建模块。判断posix(Unix系统)或者nt(Windows系统)是否在元组里来判断操作系统。

原理就是不同操作系统在安装Python环境时,windows有nt模块而没有posix模块,linux中则相反。

接着根据不同操作系统类型,定义不同的变量。name表示操作系统类型。linesep定义了当前平台使用的行终止符。例如,Windows使用’\r\n’,Linux使用’\n’而Mac使用’\r’。

from nt import */from posix import *:从这里可以看出OS模块其实是对nt模块或者posix模块的二次封装。这样完成对不同操作系统的适配。

通过 import posixpath as path 和 sys.modules[‘os.path’] = path 两语句可以发现我们常用的os.path其实就是ntpath或者posixpath模块。

sys.modules是一个全局字典,Python启动后就加载在内存中,记录新导入的模块。

if _exists("_have_functions"):
    _globals = globals()
    def _add(str, fn):
        if (fn in _globals) and (str in _have_functions):
            _set.add(_globals[fn])
    _set = set()
    _add("HAVE_FACCESSAT",  "access")
    _add("HAVE_FCHMODAT",   "chmod")
    _add("HAVE_FCHOWNAT",   "chown")
    _add("HAVE_FSTATAT",    "stat")
    _add("HAVE_FUTIMESAT",  "utime")
    _add("HAVE_LINKAT",     "link")
    _add("HAVE_MKDIRAT",    "mkdir")
    _add("HAVE_MKFIFOAT",   "mkfifo")
    _add("HAVE_MKNODAT",    "mknod")
    _add("HAVE_OPENAT",     "open")
    _add("HAVE_READLINKAT", "readlink")
    _add("HAVE_RENAMEAT",   "rename")
    _add("HAVE_SYMLINKAT",  "symlink")
    _add("HAVE_UNLINKAT",   "unlink")
    _add("HAVE_UNLINKAT",   "rmdir")
    _add("HAVE_UTIMENSAT",  "utime")
    supports_dir_fd = _set
    _set = set()
    _add("HAVE_FACCESSAT",  "access")
    supports_effective_ids = _set
    _set = set()
    _add("HAVE_FCHDIR",     "chdir")
    _add("HAVE_FCHMOD",     "chmod")
    _add("HAVE_FCHOWN",     "chown")
    _add("HAVE_FDOPENDIR",  "listdir")
    _add("HAVE_FDOPENDIR",  "scandir")
    _add("HAVE_FEXECVE",    "execve")
    _set.add(stat) # fstat always works
    _add("HAVE_FTRUNCATE",  "truncate")
    _add("HAVE_FUTIMENS",   "utime")
    _add("HAVE_FUTIMES",    "utime")
    _add("HAVE_FPATHCONF",  "pathconf")
    if _exists("statvfs") and _exists("fstatvfs"): # mac os x10.3
        _add("HAVE_FSTATVFS", "statvfs")
    supports_fd = _set
    _set = set()
    _add("HAVE_FACCESSAT",  "access")
    _add("HAVE_FCHOWNAT",   "chown")
    _add("HAVE_FSTATAT",    "stat")
    _add("HAVE_LCHFLAGS",   "chflags")
    _add("HAVE_LCHMOD",     "chmod")
    if _exists("lchown"): # mac os x10.3
        _add("HAVE_LCHOWN", "chown")
    _add("HAVE_LINKAT",     "link")
    _add("HAVE_LUTIMES",    "utime")
    _add("HAVE_LSTAT",      "stat")
    _add("HAVE_FSTATAT",    "stat")
    _add("HAVE_UTIMENSAT",  "utime")
    _add("MS_WINDOWS",      "stat")
    supports_follow_symlinks = _set
    del _set
    del _have_functions
    del _globals
    del _add
SEEK_SET = 0
SEEK_CUR = 1
SEEK_END = 2

这段代码调用之前的_exists函数。由于不同平台提供不同的功能,所以根据不同平台,判断支持的函数。

_set是一个集合,通过回溯代码,我们可以发现 _have_functions 是一个列表,且它在不同的系统中是不一样的。

然后通过_add函数向集合中添加不同系统支持的函数。多次向_add传参之后将集合赋值给了新的变量。比如同样的os.supports_dir_fd,在windows中就是一个空集,而在linux中则非常多。

最后定义了三个SEEK常量,根据注释的意思是必要时他们被映射到posixmoudle.c中使用本机常量。

————————————————————————————————————————————————————————————————————————————————————————

总结:和大佬的博客内容还是有蛮多的相似,等后面全部读完了,再来改吧。现在我就是跟着大佬的思路读的,暂时还走不出新路。

留下评论