# 从 numpy._core.defchararray 模块中导入一系列字符串操作函数,用于操作字符数组from numpy._core.defchararray import(
equal as equal,# 别名 equal,用于比较两个字符数组是否相等
not_equal as not_equal,# 别名 not_equal,用于比较两个字符数组是否不相等
greater_equal as greater_equal,# 别名 greater_equal,用于比较字符数组左侧是否大于等于右侧
less_equal as less_equal,# 别名 less_equal,用于比较字符数组左侧是否小于等于右侧
greater as greater,# 别名 greater,用于比较字符数组左侧是否大于右侧
less as less,# 别名 less,用于比较字符数组左侧是否小于右侧
str_len as str_len,# 别名 str_len,返回字符数组中每个字符串的长度
add as add,# 别名 add,用于字符数组的逐元素相加
multiply as multiply,# 别名 multiply,用于字符数组的逐元素相乘
mod as mod,# 别名 mod,用于字符数组的逐元素取模运算
capitalize as capitalize,# 别名 capitalize,将字符数组中每个字符串的首字母大写
center as center,# 别名 center,将字符数组中每个字符串居中对齐
count as count,# 别名 count,统计字符数组中每个字符串出现的次数
decode as decode,# 别名 decode,解码字符数组中的每个字符串
encode as encode,# 别名 encode,编码字符数组中的每个字符串
endswith as endswith,# 别名 endswith,判断字符数组中每个字符串是否以指定后缀结尾
expandtabs as expandtabs,# 别名 expandtabs,将字符数组中每个字符串的制表符扩展为空格
find as find,# 别名 find,查找字符数组中每个字符串第一次出现的位置
index as index,# 别名 index,查找字符数组中每个字符串第一次出现的位置
isalnum as isalnum,# 别名 isalnum,判断字符数组中每个字符串是否由字母和数字组成
isalpha as isalpha,# 别名 isalpha,判断字符数组中每个字符串是否只包含字母
isdigit as isdigit,# 别名 isdigit,判断字符数组中每个字符串是否只包含数字
islower as islower,# 别名 islower,判断字符数组中每个字符串是否全为小写字母
isspace as isspace,# 别名 isspace,判断字符数组中每个字符串是否只包含空白字符
istitle as istitle,# 别名 istitle,判断字符数组中每个字符串是否遵循标题化规则
isupper as isupper,# 别名 isupper,判断字符数组中每个字符串是否全为大写字母
join as join,# 别名 join,将字符数组中每个字符串用指定分隔符连接成一个字符串
ljust as ljust,# 别名 ljust,将字符数组中每个字符串左对齐,并用空格填充至指定长度
lower as lower,# 别名 lower,将字符数组中每个字符串转换为小写
lstrip as lstrip,# 别名 lstrip,将字符数组中每个字符串左侧的空白字符删除
partition as partition,# 别名 partition,将字符数组中每个字符串按照第一次出现的分隔符分为三部分
replace as replace,# 别名 replace,将字符数组中每个字符串的指定子串替换为新的子串
rfind as rfind,# 别名 rfind,查找字符数组中每个字符串最后一次出现的位置
rindex as rindex,# 别名 rindex,查找字符数组中每个字符串最后一次出现的位置
rjust as rjust,# 别名 rjust,将字符数组中每个字符串右对齐,并用空格填充至指定长度
rpartition as rpartition,# 别名 rpartition,将字符数组中每个字符串按照最后一次出现的分隔符分为三部分
rsplit as rsplit,# 别名 rsplit,将字符数组中每个字符串按照指定分隔符从右向左分割
rstrip as rstrip,# 别名 rstrip,将字符数组中每个字符串右侧的空白字符删除
split as split,# 别名 split,将字符数组中每个字符串按照指定分隔符分割
splitlines as splitlines,# 别名 splitlines,将字符数组中每个字符串按照行分割
startswith as startswith,# 别名 startswith,判断字符数组中每个字符串是否以指定前缀开头
strip as strip,# 别名 strip,将字符数组中每个字符串两侧的空白字符删除
swapcase as swapcase,# 别名 swapcase,将字符数组中每个字符串的大小写互换
title as title,# 别名 title,将字符数组中每个字符串转换为标题化形式
translate as translate,# 别名 translate,根据字符映射表对字符数组中每个字符串进行转换
upper as upper,# 别名 upper,将字符数组中每个字符串转换为大写
zfill as zfill,# 别名 zfill,将字符数组中每个字符串左侧填充零到指定宽度
isnumeric as isnumeric,# 别名 isnumeric,判断字符数组中每个字符串是否只包含数字字符
isdecimal as isdecimal,# 别名 isdecimal,判断字符数组中每个字符串是否只包含十进制数字字符
array as array,# 别名 array,创建一个字符数组对象
asarray as asarray,# 别名 asarray,将输入转换为字符数组对象
compare_chararrays as compare_chararrays,# 别名 compare_chararrays,比较两个字符数组对象
chararray as chararray # 别名 chararray,字符数组对象类型)
__all__:list[str]# 定义变量 __all__,包含在 from ... import * 语句中应导入的公共名称列表
.\numpy\numpy\compat\py3k.py
"""
Python 3.X compatibility tools.
While this file was originally intended for Python 2 -> 3 transition,
it is now used to create a compatibility layer between different
minor versions of Python 3.
While the active version of numpy may not support a given version of python, we
allow downstream libraries to continue to use these shims for forward
compatibility with numpy while they transition their code to newer versions of
Python.
"""
__all__ =['bytes','asbytes','isfileobj','getexception','strchar','unicode','asunicode','asbytes_nested','asunicode_nested','asstr','open_latin1','long','basestring','sixu','integer_types','is_pathlib_path','npy_load_module','Path','pickle','contextlib_nullcontext','os_fspath','os_PathLike']import sys # 导入 sys 模块,用于访问系统相关功能import os # 导入 os 模块,用于访问操作系统功能from pathlib import Path # 导入 Path 类,用于处理路径操作import io # 导入 io 模块,用于处理文件流操作try:import pickle5 as pickle # 尝试导入 pickle5 库,若不成功则导入标准的 pickle 库except ImportError:import pickle # 如果导入 pickle5 失败,导入标准的 pickle 库long=int# 定义 long 为 int 类型,用于兼容 Python 3 中移除的 long 类型
integer_types =(int,)# 定义 integer_types 为包含 int 的元组,用于兼容 Python 2/3 整数类型的差异basestring=str# 定义 basestring 为 str 类型,用于兼容 Python 2/3 中字符串类型的差异unicode=str# 定义 unicode 为 str 类型,用于兼容 Python 2/3 中的字符串类型bytes=bytes# 定义 bytes 为 bytes 类型,用于兼容 Python 2/3 中的字节类型defasunicode(s):ifisinstance(s,bytes):return s.decode('latin1')# 如果 s 是 bytes 类型,则解码为 str 类型,使用 Latin-1 编码returnstr(s)# 否则直接转换为 str 类型defasbytes(s):ifisinstance(s,bytes):return s # 如果 s 已经是 bytes 类型,则直接返回returnstr(s).encode('latin1')# 否则将 s 转换为 str 类型后再编码为 bytes 类型,使用 Latin-1 编码defasstr(s):ifisinstance(s,bytes):return s.decode('latin1')# 如果 s 是 bytes 类型,则解码为 str 类型,使用 Latin-1 编码returnstr(s)# 否则直接转换为 str 类型defisfileobj(f):ifnotisinstance(f,(io.FileIO, io.BufferedReader, io.BufferedWriter)):returnFalse# 如果 f 不是文件对象类型,则返回 Falsetry:
f.fileno()# 尝试获取文件描述符,可能会抛出 OSError 异常returnTrue# 如果成功获取文件描述符,则返回 Trueexcept OSError:returnFalse# 获取文件描述符失败,返回 Falsedefopen_latin1(filename, mode='r'):returnopen(filename, mode=mode, encoding='iso-8859-1')# 使用 Latin-1 编码打开指定文件defsixu(s):return s # 返回参数 s,用于兼容性,不进行额外处理
strchar ='U'# 定义 strchar 为 'U'defgetexception():return sys.exc_info()[1]# 返回当前异常信息的第一个元素,即异常对象defasbytes_nested(x):ifhasattr(x,'__iter__')andnotisinstance(x,(bytes,unicode)):return[asbytes_nested(y)for y in x]# 如果 x 是可迭代对象且不是 bytes 或 unicode 类型,则递归处理每个元素else:return asbytes(x)# 否则将 x 转换为 bytes 类型并返回defasunicode_nested(x):ifhasattr(x,'__iter__')andnotisinstance(x,(bytes,unicode)):return[asunicode_nested(y)for y in x]# 如果 x 是可迭代对象且不是 bytes 或 unicode 类型,则递归处理每个元素else:return asunicode(x)# 否则将 x 转换为 unicode 类型并返回defis_pathlib_path(obj):"""
Check whether obj is a `pathlib.Path` object.
Prefer using ``isinstance(obj, os.PathLike)`` instead of this function.
"""returnisinstance(obj, Path)# 检查 obj 是否为 pathlib.Path 对象# from Python 3.7classcontextlib_nullcontext:"""Context manager that does no additional processing.
Used as a stand-in for a normal context manager, when a particular
block of code is only sometimes used with a normal context manager:
cm = optional_cm if condition else nullcontext()
with cm:
# Perform operation, using optional_cm if condition is True
.. note::
Prefer using `contextlib.nullcontext` instead of this context manager.
"""def__init__(self, enter_result=None):
self.enter_result = enter_result # 初始化上下文管理器,保存进入结果# 定义上下文管理器的进入方法,当使用 with 语句时执行def__enter__(self):# 返回上下文管理器的进入结果,通常是为了与 as 关键字后的变量进行绑定return self.enter_result
# 定义上下文管理器的退出方法,当退出 with 语句块时执行def__exit__(self,*excinfo):# 占位符方法体,不做任何实际操作,即使在异常发生时也不处理异常pass# 加载一个模块。使用 ``load_module`` 方法,该方法将在 Python 3.12 中被弃用。# 另外,可以使用 ``exec_module`` 方法,该方法在 numpy.distutils.misc_util.exec_mod_from_location 中定义。# .. versionadded:: 1.11.2# 版本添加说明,从版本 1.11.2 开始可用。# Parameters# ----------# name : str# 完整的模块名称。# fn : str# 模块文件的路径。# info : tuple, optional# 仅用于向后兼容 Python 2.*。# Returns# -------# mod : module# 加载并返回的模块对象。defnpy_load_module(name, fn, info=None):# 显式延迟导入以避免在启动时导入 importlib 的开销from importlib.machinery import SourceFileLoader
return SourceFileLoader(name, fn).load_module()# 将 os.fspath 函数赋值给变量 os_fspath,以便更方便地引用该函数
os_fspath = os.fspath
# 将 os.PathLike 类型赋值给变量 os_PathLike,以便更方便地引用该类型
os_PathLike = os.PathLike
.\numpy\numpy\compat\tests\__init__.py
# 定义一个名为find_duplicate的函数,接收一个参数nums,该参数是一个整数列表deffind_duplicate(nums):# 创建一个空集合dup_set,用于存储出现过的数字
dup_set =set()# 遍历nums列表中的每个元素numfor num in nums:# 如果num已经在dup_set中存在,表示num是重复出现的数字if num in dup_set:# 返回找到的重复数字numreturn num
# 将num加入dup_set中,记录已经出现的数字
dup_set.add(num)# 如果没有找到重复数字,返回NonereturnNone
.\numpy\numpy\compat\__init__.py
"""
python
"""
兼容性模块。
此模块包含从 Python 本身或第三方扩展复制的重复代码,可能包含以下原因:
* 兼容性
* 我们可能只需要复制库/模块的一小部分
此模块自 1.26.0 版本起已被弃用,并将在将来的版本中移除。
"""
# 导入警告模块import warnings
# 从内部工具模块导入 _inspect 模块from.._utils import _inspect
# 从内部工具模块的 _inspect 模块导入 getargspec 和 formatargspec 函数from.._utils._inspect import getargspec, formatargspec
# 从 . 模块导入 py3k 模块from.import py3k
# 从 .py3k 模块导入所有内容from.py3k import*# 引发警告,指示 np.compat 在 Python 2 到 3 的转换期间使用,自 1.26.0 版本起已弃用,并将被移除
warnings.warn("`np.compat`, which was used during the Python 2 to 3 transition,"" is deprecated since 1.26.0, and will be removed",
DeprecationWarning, stacklevel=2)# 将空列表tion,"" is deprecated since 1.26.0, and will be removed",
DeprecationWarning, stacklevel=2)# 初始化模块的公开接口列表
__all__ =[]# 将 _inspect 模块中定义的所有公开名称添加到 __all__ 中
__all__.extend(_inspect.__all__)# 将 py3k 模块中定义的所有公开名称添加到 __all__ 中
__all__.extend(py3k.__all__)
.\numpy\numpy\conftest.py
"""
Pytest configuration and fixtures for the Numpy test suite.
"""# 导入必要的库和模块import os # 导入操作系统接口import tempfile # 导入临时文件和目录创建的模块from contextlib import contextmanager # 导入上下文管理器import warnings # 导入警告模块import hypothesis # 导入假设测试库import pytest # 导入pytest测试框架import numpy # 导入NumPy库from numpy._core._multiarray_tests import get_fpu_mode # 导入获取FPU模式的函数# 尝试导入scipy_doctest.conftest模块,标记是否成功导入try:from scipy_doctest.conftest import dt_config
HAVE_SCPDT =Trueexcept ModuleNotFoundError:
HAVE_SCPDT =False# 初始化旧的FPU模式和结果收集字典
_old_fpu_mode =None
_collect_results ={}# 设置hypothesis缓存的主目录,使用已知且持久的临时目录
hypothesis.configuration.set_hypothesis_home_dir(
os.path.join(tempfile.gettempdir(),".hypothesis"))# 注册两个自定义的Hypothesis配置文件,用于NumPy测试
hypothesis.settings.register_profile(
name="numpy-profile", deadline=None, print_blob=True,)
hypothesis.settings.register_profile(
name="np.test() profile",
deadline=None, print_blob=True, database=None, derandomize=True,
suppress_health_check=list(hypothesis.HealthCheck),)# 根据pytest.ini文件的存在与否加载默认的Hypothesis配置文件
_pytest_ini = os.path.join(os.path.dirname(__file__),"..","pytest.ini")
hypothesis.settings.load_profile("numpy-profile"if os.path.isfile(_pytest_ini)else"np.test() profile")# 设置NUMPY_EXPERIMENTAL_DTYPE_API环境变量为1,用于_umath_tests
os.environ["NUMPY_EXPERIMENTAL_DTYPE_API"]="1"# 定义pytest的配置函数,添加自定义标记defpytest_configure(config):
config.addinivalue_line("markers","valgrind_error: Tests that are known to error under valgrind.")
config.addinivalue_line("markers","leaks_references: Tests that are known to leak references.")
config.addinivalue_line("markers","slow: Tests that are very slow.")
config.addinivalue_line("markers","slow_pypy: Tests that are very slow on pypy.")# 定义pytest的命令行选项,用于设置可用内存量defpytest_addoption(parser):
parser.addoption("--available-memory", action="store", default=None,help=("Set amount of memory available for running the ""test suite. This can result to tests requiring ""especially large amounts of memory to be skipped. ""Equivalent to setting environment variable ""NPY_AVAILABLE_MEM. Default: determined""automatically."))# 在测试会话开始时,根据命令行选项设置环境变量NPY_AVAILABLE_MEMdefpytest_sessionstart(session):
available_mem = session.config.getoption('available_memory')if available_mem isnotNone:
os.environ['NPY_AVAILABLE_MEM']= available_mem
# TODO: 移除yield测试后修复此函数@pytest.hookimpl()defpytest_itemcollected(item):"""
Check FPU precision mode was not changed during test collection.
"""
The clumsy way we do it here is mainly necessary because numpy
still uses yield tests, which can execute code at test collection
time."""
# 声明全局变量 _old_fpu_mode,用于存储旧的浮点数处理单元模式
global _old_fpu_mode
# 获取当前的浮点数处理单元模式
mode = get_fpu_mode()
# 如果 _old_fpu_mode 还未设置,则将当前模式赋给它
if _old_fpu_mode is None:
_old_fpu_mode = mode
# 否则,如果当前模式与旧模式不同,则记录结果到 _collect_results 字典中,并更新 _old_fpu_mode
elif mode != _old_fpu_mode:
_collect_results[item] = (_old_fpu_mode, mode)
_old_fpu_mode = mode
# 如果 HAVE_SCPDT 可用,则定义一个上下文管理器 warnings_errors_and_rng
if HAVE_SCPDT:
@contextmanager
def warnings_errors_and_rng(test=None):
"""Filter out the wall of DeprecationWarnings."""
# 定义需要忽略的 DeprecationWarning 的消息列表
msgs =["The numpy.linalg.linalg","The numpy.fft.helper","dep_util","pkg_resources","numpy.core.umath","msvccompiler","Deprecated call","numpy.core","`np.compat`","Importing from numpy.matlib","This function is deprecated.",# random_integers"Data type alias 'a'",# numpy.rec.fromfile"Arrays of 2-dimensional vectors",# matlib.cross"`in1d` is deprecated",]
msg ="|".join(msgs)# 定义需要忽略的 RuntimeWarning 的消息列表
msgs_r =["invalid value encountered","divide by zero encountered"]
msg_r ="|".join(msgs_r)# 忽略特定类型的警告消息with warnings.catch_warnings():
warnings.filterwarnings('ignore', category=DeprecationWarning, message=msg
)
warnings.filterwarnings('ignore', category=RuntimeWarning, message=msg_r
)yield# 将定义好的上下文管理器应用于用户配置的上下文管理器
dt_config.user_context_mgr = warnings_errors_and_rng
# 为 doctests 添加特定于 numpy 的标记以处理未初始化情况
dt_config.rndm_markers.add('#uninitialized')
dt_config.rndm_markers.add('# uninitialized')# 导入 doctest 模块,用于查找和检查此上下文管理器下的文档测试import doctest
# 设置 doctest 的选项标志,用于规范化空白和省略号处理
dt_config.optionflags = doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS
# 将 'StringDType' 识别为 numpy.dtypes.StringDType 的命名空间检查
dt_config.check_namespace['StringDType']= numpy.dtypes.StringDType
# 设置临时跳过列表,避免在 doctest 中处理以下函数
dt_config.skiplist =set(['numpy.savez',# 文件未关闭'numpy.matlib.savez','numpy.__array_namespace_info__','numpy.matlib.__array_namespace_info__',])# 标记无法通过测试的教程文件为 xfail(预期失败),附加信息为空字符串
dt_config.pytest_extra_xfail ={'how-to-verify-bug.rst':'','c-info.ufunc-tutorial.rst':'','basics.interoperability.rst':'needs pandas',# 需要 pandas'basics.dispatch.rst':'errors out in /testing/overrides.py',# 在 /testing/overrides.py 中出错'basics.subclassing.rst':'.. testcode:: admonitions not understood'# 不理解警告}# 设置额外的忽略列表,用于不希望进行 doctest 集合的内容(例如可选内容)
dt_config.pytest_extra_ignore =['numpy/distutils','numpy/_core/cversions.py','numpy/_pyinstaller','numpy/random/_examples','numpy/compat','numpy/f2py/_backends/_distutils.py',]
.\numpy\numpy\core\arrayprint.py
# 定义一个特殊方法 __getattr__,用于在对象中获取指定属性的值def__getattr__(attr_name):# 从 numpy._core.arrayprint 模块中导入 arrayprint 函数from numpy._core import arrayprint
# 从 ._utils 模块中导入 _raise_warning 函数from._utils import _raise_warning
# 尝试从 arrayprint 模块中获取指定名称的属性值
ret =getattr(arrayprint, attr_name,None)# 如果获取的属性值为 None,则抛出 AttributeError 异常if ret isNone:raise AttributeError(f"module 'numpy.core.arrayprint' has no attribute {attr_name}")# 调用 _raise_warning 函数,提醒获取到的属性名称和其所在模块
_raise_warning(attr_name,"arrayprint")# 返回获取到的属性值return ret
.\numpy\numpy\core\defchararray.py
# 定义一个特殊方法 __getattr__,用于在获取不存在的属性时进行处理def__getattr__(attr_name):# 从 numpy._core 模块中导入 defchararray 对象from numpy._core import defchararray
# 从 ._utils 模块中导入 _raise_warning 函数from._utils import _raise_warning
# 尝试获取 defchararray 对象中的属性 attr_name
ret =getattr(defchararray, attr_name,None)# 如果获取不到该属性,则抛出 AttributeError 异常if ret isNone:raise AttributeError(f"module 'numpy.core.defchararray' has no attribute {attr_name}")# 调用 _raise_warning 函数,向用户发出警告信息
_raise_warning(attr_name,"defchararray")# 返回获取到的属性或方法对象return ret
.\numpy\numpy\core\einsumfunc.py
# 定义一个特殊的属性获取方法,用于获取指定名称的属性值def__getattr__(attr_name):# 从 numpy._core 模块中导入 einsumfunc 函数或属性from numpy._core import einsumfunc
# 从 ._utils 模块中导入 _raise_warning 函数from._utils import _raise_warning
# 尝试获取 einsumfunc 模块中指定名称的属性值
ret =getattr(einsumfunc, attr_name,None)# 如果未找到指定属性,抛出 AttributeError 异常if ret isNone:raise AttributeError(f"module 'numpy.core.einsumfunc' has no attribute {attr_name}")# 调用 _raise_warning 函数,发出警告,提醒 einsumfunc 模块中的属性被访问
_raise_warning(attr_name,"einsumfunc")# 返回获取到的属性值return ret
.\numpy\numpy\core\fromnumeric.py
# 定义一个特殊的方法,用于动态获取属性值def__getattr__(attr_name):# 从 numpy._core 模块中导入 fromnumeric 对象from numpy._core import fromnumeric
# 从当前模块的 _utils 导入 _raise_warning 函数from._utils import _raise_warning
# 尝试获取 fromnumeric 对象的属性 attr_name
ret =getattr(fromnumeric, attr_name,None)# 如果未找到对应属性,则抛出 AttributeError 异常if ret isNone:raise AttributeError(f"module 'numpy.core.fromnumeric' has no attribute {attr_name}")# 调用 _raise_warning 函数,发出警告信息
_raise_warning(attr_name,"fromnumeric")# 返回获取到的属性值return ret
.\numpy\numpy\core\function_base.py
# 定义一个函数 __getattr__,用于获取指定属性名的属性值def__getattr__(attr_name):# 从 numpy._core 中导入 function_base 模块from numpy._core import function_base
# 从 ._utils 模块导入 _raise_warning 函数from._utils import _raise_warning
# 获取 function_base 模块中名称为 attr_name 的属性值,如果不存在则返回 None
ret =getattr(function_base, attr_name,None)# 如果未找到指定的属性值,则抛出 AttributeError 异常if ret isNone:raise AttributeError(f"module 'numpy.core.function_base' has no attribute {attr_name}")# 发出警告,说明找到的属性值来自 function_base 模块
_raise_warning(attr_name,"function_base")# 返回找到的属性值return ret
.\numpy\numpy\core\getlimits.py
# 定义一个特殊的属性访问方法,用于动态获取属性值def__getattr__(attr_name):# 从 numpy._core 模块中导入 getlimits 函数from numpy._core import getlimits
# 从当前模块的 _utils 导入 _raise_warning 函数from._utils import _raise_warning
# 尝试获取 getlimits 模块中名为 attr_name 的属性值
ret =getattr(getlimits, attr_name,None)# 如果获取的属性值为 None,则抛出 AttributeError 异常if ret isNone:raise AttributeError(f"module 'numpy.core.getlimits' has no attribute {attr_name}")# 调用 _raise_warning 函数,发出警告
_raise_warning(attr_name,"getlimits")# 返回获取到的属性值return ret
# 从 numpy._core 模块中导入 _multiarray_umath 对象from numpy._core import _multiarray_umath
# 从 numpy 模块中导入 ufunc 类型from numpy import ufunc
# 遍历 _multiarray_umath 对象的所有属性名称for item in _multiarray_umath.__dir__():# 对于每个属性名称,获取其对应的属性对象
attr =getattr(_multiarray_umath, item)# 如果该属性对象属于 ufunc 类型ifisinstance(attr, ufunc):# 将该属性对象添加到全局命名空间中,属性名称和对象相同globals()[item]= attr
# 定义一个特殊的 __getattr__ 函数def__getattr__(attr_name):# 从 numpy._core 模块中导入 _multiarray_umath 对象from numpy._core import _multiarray_umath
# 从当前模块的 ._utils 子模块中导入 _raise_warning 函数# 如果 attr_name 是 "_ARRAY_API" 或 "_UFUNC_API"if attr_name in{"_ARRAY_API","_UFUNC_API"}:# 从 numpy.version 模块中导入 short_version 变量from numpy.version import short_version
# 导入 textwrap 模块,用于格式化消息文本import textwrap
# 导入 traceback 模块,用于生成调用堆栈信息import traceback
# 导入 sys 模块,用于访问标准错误流import sys
# 创建一条包含详细信息的消息字符串
msg = textwrap.dedent(f"""
A module that was compiled using NumPy 1.x cannot be run in
NumPy {short_version} as it may crash. To support both 1.x and 2.x
versions of NumPy, modules must be compiled with NumPy 2.0.
Some module may need to rebuild instead e.g. with 'pybind11>=2.12'.
If you are a user of the module, the easiest solution will be to
downgrade to 'numpy<2' or try to upgrade the affected module.
We expect that some modules will need time to support NumPy 2.
""")# 创建一个包含调用堆栈的消息字符串
tb_msg ="Traceback (most recent call last):"for line in traceback.format_stack()[:-1]:if"frozen importlib"in line:continue
tb_msg += line
# 将消息和调用堆栈信息写入标准错误流
sys.stderr.write(msg + tb_msg)# 抛出 ImportError 异常,包含详细消息raise ImportError(msg)# 尝试从 _multiarray_umath 对象中获取指定名称的属性对象
ret =getattr(_multiarray_umath, attr_name,None)# 如果获取的属性对象为 None,则抛出 AttributeError 异常if ret isNone:raise AttributeError("module 'numpy.core._multiarray_umath' has no attribute "f"{attr_name}")# 调用 _raise_warning 函数,向用户发出警告
_raise_warning(attr_name,"_multiarray_umath")# 返回获取到的属性对象return ret
# 从全局命名空间中删除 _multiarray_umath 和 ufunc 变量del _multiarray_umath, ufunc
.\numpy\numpy\core\_utils.py
# 导入警告模块,用于生成警告信息import warnings
# 定义一个函数,用于发出警告信息def_raise_warning(attr:str, submodule:str=None)->None:# 定义新旧模块的名称
new_module ="numpy._core"
old_module ="numpy.core"# 如果有子模块,添加到模块名称中if submodule isnotNone:
new_module =f"{new_module}.{submodule}"
old_module =f"{old_module}.{submodule}"# 发出警告,说明旧模块已弃用并重命名
warnings.warn(f"{old_module} is deprecated and has been renamed to {new_module}. ""The numpy._core namespace contains private NumPy internals and its ""use is discouraged, as NumPy internals can change without warning in ""any release. In practice, most real-world usage of numpy.core is to ""access functionality in the public NumPy API. If that is the case, ""use the public NumPy API. If not, you are using NumPy internals. ""If you would still like to access an internal attribute, "f"use {new_module}.{attr}.",
DeprecationWarning,
stacklevel=3# 设置警告的堆栈级别)
.\numpy\numpy\core\__init__.py
"""
The `numpy.core` submodule exists solely for backward compatibility
purposes. The original `core` was renamed to `_core` and made private.
`numpy.core` will be removed in the future.
"""# 从 `numpy` 包中导入 `_core` 子模块,用于向后兼容性目的from numpy import _core
# 从当前包的 `_utils` 模块中导入 `_raise_warning` 函数from._utils import _raise_warning
# We used to use `np.core._ufunc_reconstruct` to unpickle.# This is unnecessary, but old pickles saved before 1.20 will be using it,# and there is no reason to break loading them.# 定义 `_ufunc_reconstruct` 函数,用于反序列化时重建 ufunc 对象def_ufunc_reconstruct(module, name):# 导入指定模块 `module` 并返回其中指定的 `name` 对象# `fromlist` 参数确保当模块名嵌套时,`mod` 指向最内层模块而非父包
mod =__import__(module, fromlist=[name])returngetattr(mod, name)# force lazy-loading of submodules to ensure a warning is printed# 定义 `__all__` 列表,包含公开的子模块名称,用于 `from package import *` 语法
__all__ =["arrayprint","defchararray","_dtype_ctypes","_dtype","einsumfunc","fromnumeric","function_base","getlimits","_internal","multiarray","_multiarray_umath","numeric","numerictypes","overrides","records","shape_base","umath"]# 定义 `__getattr__` 函数,用于动态获取 `numpy.core` 中的属性def__getattr__(attr_name):# 从 `numpy._core` 中获取指定属性 `attr_name`
attr =getattr(_core, attr_name)# 调用 `_raise_warning` 函数,对获取的属性进行警告处理
_raise_warning(attr_name)# 返回获取的属性return attr
.\numpy\numpy\core\__init__.pyi
# 导入所需的模块:re 用于正则表达式操作,os 用于文件路径操作import re
import os
# 定义函数 find_files,接收文件路径和正则表达式作为参数deffind_files(dir, regex):# 初始化一个空列表,用于存储符合条件的文件路径
files =[]# 遍历指定目录及其子目录下的所有文件和文件夹for root, dirs, filenames in os.walk(dir):# 对每个文件名进行正则匹配for filename in filenames:# 如果文件名符合正则表达式条件if re.match(regex, filename):# 构建文件的完整路径并添加到列表中
files.append(os.path.join(root, filename))# 返回符合条件的文件路径列表return files
.\numpy\numpy\ctypeslib.py
"""
============================
``ctypes`` Utility Functions
============================
See Also
--------
load_library : Load a C library.
ndpointer : Array restype/argtype with verification.
as_ctypes : Create a ctypes array from an ndarray.
as_array : Create an ndarray from a ctypes array.
References
----------
.. [1] "SciPy Cookbook: ctypes", https://scipy-cookbook.readthedocs.io/items/Ctypes.html
Examples
--------
Load the C library:
>>> _lib = np.ctypeslib.load_library('libmystuff', '.') #doctest: +SKIP
Our result type, an ndarray that must be of type double, be 1-dimensional
and is C-contiguous in memory:
>>> array_1d_double = np.ctypeslib.ndpointer(
... dtype=np.double,
... ndim=1, flags='CONTIGUOUS') #doctest: +SKIP
Our C-function typically takes an array and updates its values
in-place. For example::
void foo_func(double* x, int length)
{
int i;
for (i = 0; i < length; i++) {
x[i] = i*i;
}
}
We wrap it using:
>>> _lib.foo_func.restype = None #doctest: +SKIP
>>> _lib.foo_func.argtypes = [array_1d_double, c_int] #doctest: +SKIP
Then, we're ready to call ``foo_func``:
>>> out = np.empty(15, dtype=np.double)
>>> _lib.foo_func(out, len(out)) #doctest: +SKIP
"""
__all__ =['load_library','ndpointer','c_intp','as_ctypes','as_array','as_ctypes_type']import os # 导入操作系统接口模块from numpy import(# 从 numpy 库导入以下模块:
integer, ndarray, dtype as _dtype, asarray, frombuffer
)from numpy._core.multiarray import _flagdict, flagsobj # 导入 numpy 的内部多维数组相关模块try:import ctypes # 尝试导入 ctypes 库except ImportError:
ctypes =Noneif ctypes isNone:# 如果 ctypes 库不可用,定义一个 _dummy 函数来抛出 ImportErrordef_dummy(*args,**kwds):"""
Dummy object that raises an ImportError if ctypes is not available.
Raises
------
ImportError
If ctypes is not available.
"""raise ImportError("ctypes is not available.")
load_library = _dummy # 定义 load_library, as_ctypes, as_array 为 _dummy 函数
as_ctypes = _dummy
as_array = _dummy
from numpy import intp as c_intp # 从 numpy 导入 intp 并命名为 c_intp
_ndptr_base =object# _ndptr_base 设为 Python 对象else:import numpy._core._internal as nic # 导入 numpy 内部的 _internal 模块并命名为 nic
c_intp = nic._getintp_ctype()# 获取 ctypes 中的 c_intp 类型del nic # 删除 nic 引用,释放内存
_ndptr_base = ctypes.c_void_p # 设置 _ndptr_base 为 ctypes 的 c_void_p 类型# Adapted from Albert Strasheimdefload_library(libname, loader_path):"""
It is possible to load a library using
>>> lib = ctypes.cdll[<full_path_name>] # doctest: +SKIP
But there are cross-platform considerations, such as library file extensions,
plus the fact Windows will just load the first library it finds with that name.
NumPy supplies the load_library function as a convenience.
.. versionchanged:: 1.20.0
Allow libname and loader_path to take any
:term:`python:path-like object`.
Parameters
----------
libname : path-like
Name of the library, which can have 'lib' as a prefix,
but without an extension.
loader_path : path-like
Where the library can be found.
Returns
-------
ctypes.cdll[libpath] : library object
A ctypes library object
Raises
------
OSError
If there is no library with the expected extension, or the
library is defective and cannot be loaded.
"""# Convert path-like objects into strings
libname = os.fsdecode(libname)# 解码并获取库名称的字符串表示
loader_path = os.fsdecode(loader_path)# 解码并获取加载路径的字符串表示
ext = os.path.splitext(libname)[1]# 获取库名称的文件扩展名ifnot ext:import sys
import sysconfig
# 尝试使用平台特定的库文件名加载库,否则默认为libname.[so|dll|dylib]。# 有时这些文件在非Linux平台上会构建错误。
base_ext =".so"if sys.platform.startswith("darwin"):
base_ext =".dylib"elif sys.platform.startswith("win"):
base_ext =".dll"
libname_ext =[libname + base_ext]
so_ext = sysconfig.get_config_var("EXT_SUFFIX")ifnot so_ext == base_ext:
libname_ext.insert(0, libname + so_ext)else:
libname_ext =[libname]
loader_path = os.path.abspath(loader_path)# 获取加载路径的绝对路径ifnot os.path.isdir(loader_path):
libdir = os.path.dirname(loader_path)# 获取加载路径的父目录else:
libdir = loader_path
for ln in libname_ext:
libpath = os.path.join(libdir, ln)# 组合库文件的完整路径if os.path.exists(libpath):# 检查库文件是否存在try:return ctypes.cdll[libpath]# 尝试加载库文件并返回 ctypes 库对象except OSError:## defective lib fileraise# 抛出异常,说明库文件有问题## if no successful return in the libname_ext loop:raise OSError("no file with expected extension")# 如果在 libname_ext 循环中没有成功返回,则抛出异常def_num_fromflags(flaglist):# 初始化一个计数器
num =0# 遍历传入的标志列表,将每个标志对应的值累加到计数器中for val in flaglist:
num += _flagdict[val]# 返回累加后的结果作为标志的数字表示return num
_flagnames =['C_CONTIGUOUS','F_CONTIGUOUS','ALIGNED','WRITEABLE','OWNDATA','WRITEBACKIFCOPY']def_flags_fromnum(num):# 初始化一个空列表用来存储结果
res =[]# 遍历已定义的所有标志名称for key in _flagnames:# 获取当前标志对应的数值
value = _flagdict[key]# 检查当前标志是否在给定的数字表示中if(num & value):# 如果在其中,则将该标志名称添加到结果列表中
res.append(key)# 返回所有匹配的标志名称列表return res
class_ndptr(_ndptr_base):@classmethoddeffrom_param(cls, obj):# 检查传入的对象是否为 ndarray 类型ifnotisinstance(obj, ndarray):raise TypeError("argument must be an ndarray")# 如果定义了特定的数据类型,检查传入数组是否符合要求if cls._dtype_ isnotNone \
and obj.dtype != cls._dtype_:raise TypeError("array must have data type %s"% cls._dtype_)# 如果定义了特定的维度数,检查传入数组是否符合要求if cls._ndim_ isnotNone \
and obj.ndim != cls._ndim_:raise TypeError("array must have %d dimension(s)"% cls._ndim_)# 如果定义了特定的形状,检查传入数组是否符合要求if cls._shape_ isnotNone \
and obj.shape != cls._shape_:raise TypeError("array must have shape %s"%str(cls._shape_))# 如果定义了特定的标志,检查传入数组是否符合要求if cls._flags_ isnotNone \
and((obj.flags.num & cls._flags_)!= cls._flags_):raise TypeError("array must have flags %s"%
_flags_fromnum(cls._flags_))# 返回传入数组的 ctypes 对象return obj.ctypes
class_concrete_ndptr(_ndptr):"""
Like _ndptr, but with `_shape_` and `_dtype_` specified.
Notably, this means the pointer has enough information to reconstruct
the array, which is not generally true.
"""def_check_retval_(self):"""
This method is called when this class is used as the .restype
attribute for a shared-library function, to automatically wrap the
pointer into an array.
"""# 返回指针指向的数据作为 ndarray 对象return self.contents
@propertydefcontents(self):"""
Get an ndarray viewing the data pointed to by this pointer.
This mirrors the `contents` attribute of a normal ctypes pointer
"""# 构建完整的数据类型描述
full_dtype = _dtype((self._dtype_, self._shape_))# 根据完整的数据类型描述创建对应的 ctypes 类型
full_ctype = ctypes.c_char * full_dtype.itemsize
# 将当前指针对象转换为指向完整 ctypes 类型的指针,并获取其内容buffer= ctypes.cast(self, ctypes.POINTER(full_ctype)).contents
# 将 ctypes 缓冲区转换为 ndarray,并去掉多余的维度return frombuffer(buffer, dtype=full_dtype).squeeze(axis=0)# Factory for an array-checking class with from_param defined for# use with ctypes argtypes mechanism
_pointer_type_cache ={}defndpointer(dtype=None, ndim=None, shape=None, flags=None):"""
Array-checking restype/argtypes.
An ndpointer instance is used to describe an ndarray in restypes
and argtypes specifications. This approach is more flexible than
using, for example, ``POINTER(c_double)``, since several restrictions
can be specified, which are verified upon calling the ctypes function.
These include data type, number of dimensions, shape and flags. If a
given array does not satisfy the specified restrictions,
a ``TypeError`` is raised.
Parameters
----------
"""# 此函数主要用于创建一个描述 ndarray 的类型,检查其数据类型、维度、形状和标志pass# data-type 数据类型,可选参数
dtype : data-type, optional
# int 整数,可选参数
ndim :int, optional
# tuple of ints 整数元组,可选参数
shape :tuple of ints, optional
# str or tuple of str 字符串或字符串元组,数组标志;可以是以下一项或多项:# C_CONTIGUOUS / C / CONTIGUOUS# F_CONTIGUOUS / F / FORTRAN# OWNDATA / O# WRITEABLE / W# ALIGNED / A# WRITEBACKIFCOPY / X# 返回-------# ndpointer 类型对象
klass : ndpointer typeobject# 类型对象,是一个包含 dtype、ndim、shape 和 flags 信息的 `_ndtpr` 实例。# 异常------# TypeError# 如果给定的数组不满足指定的限制条件。# 示例--------# 将 clib.somefunc 的 argtypes 设置为 [np.ctypeslib.ndpointer(dtype=np.float64,# ndim=1,# flags='C_CONTIGUOUS')]...#doctest: +SKIP# 调用 clib.somefunc,传入 np.array([1, 2, 3], dtype=np.float64) 作为参数...#doctest: +SKIP"""
# 将 dtype 标准化为 Optional[dtype]
if dtype is not None:
dtype = _dtype(dtype)
# 将 flags 标准化为 Optional[int]
num = None
if flags is not None:
if isinstance(flags, str):
flags = flags.split(',')
elif isinstance(flags, (int, integer)):
num = flags
flags = _flags_fromnum(num)
elif isinstance(flags, flagsobj):
num = flags.num
flags = _flags_fromnum(num)
if num is None:
try:
flags = [x.strip().upper() for x in flags]
except Exception as e:
raise TypeError("invalid flags specification") from e
num = _num_fromflags(flags)
# 将 shape 标准化为 Optional[tuple]
if shape is not None:
try:
shape = tuple(shape)
except TypeError:
# 单个整数 -> 转为 1 元组
shape = (shape,)
# 缓存键,包含 dtype、ndim、shape 和 num
cache_key = (dtype, ndim, shape, num)
try:
# 尝试从缓存中获取 _pointer_type_cache 中的值
return _pointer_type_cache[cache_key]
except KeyError:
pass
# 为新类型生成一个名称
if dtype is None:
name = 'any'
elif dtype.names is not None:
name = str(id(dtype))
else:
name = dtype.str
if ndim is not None:
name += "_%dd" % ndim
if shape is not None:
name += "_"+"x".join(str(x) for x in shape)
if flags is not None:
name += "_"+"_".join(flags)
# 如果 dtype 和 shape 都不为 None,则基于 _concrete_ndptr
# 否则基于 _ndptr
if dtype is not None and shape is not None:
base = _concrete_ndptr
else:
base = _ndptr
# 创建一个新类型 klass,类型名为 'ndpointer_%s' % name
klass = type("ndpointer_%s"%name, (base,),
{"_dtype_": dtype,
"_shape_" : shape,
"_ndim_" : ndim,
"_flags_" : num})
# 将 klass 存储到 _pointer_type_cache 中,使用 cache_key 作为键
_pointer_type_cache[cache_key] = klass
return klass
if ctypes is not None:
# 定义函数 _ctype_ndarray,用于创建给定元素类型和形状的 ndarray
def _ctype_ndarray(element_type, shape):
""" Create an ndarray of the given element typeand shape """
# 反向遍历形状,逐步构建元素类型
for dim in shape[::-1]:
element_type = dim * element_type
# 防止类型名称包含 np.ctypeslib
element_type.__module__ = None
return element_type
# 定义函数 _get_scalar_type_map,返回将本机字节序标量 dtype 映射到 ctypes 类型的字典
def _get_scalar_type_map():
"""
Return a dictionary mapping native endian scalar dtype to ctypes types
"""
ct = ctypes
# 定义简单的 ctypes 类型列表
simple_types = [
ct.c_byte, ct.c_short, ct.c_int, ct.c_long, ct.c_longlong,
ct.c_ubyte, ct.c_ushort, ct.c_uint, ct.c_ulong, ct.c_ulonglong,
ct.c_float, ct.c_double,
ct.c_bool,
]
# 返回字典,映射 dtype 到对应的 ctypes 类型
return {_dtype(ctype): ctype for ctype in simple_types}
# 获取本机字节序标量 dtype 到 ctypes 类型的映射
_scalar_type_map = _get_scalar_type_map()
# 定义函数 _ctype_from_dtype_scalar,根据 dtype 返回对应的 ctypes 类型
def _ctype_from_dtype_scalar(dtype):
# 确保将 `=` 转换为本机字节序的 <, >, 或 |
dtype_with_endian = dtype.newbyteorder('S').newbyteorder('S')
dtype_native = dtype.newbyteorder('=')
try:
# 根据本机字节序的 dtype 获取对应的 ctypes 类型
ctype = _scalar_type_map[dtype_native]
except KeyError as e:
# 抛出异常,表示无法转换该 dtype 到 ctypes 类型
raise NotImplementedError(
"Converting {!r} to a ctypes type".format(dtype)
) from None
# 根据 dtype 的字节序调整 ctypes 类型
if dtype_with_endian.byteorder == '>':
ctype = ctype.__ctype_be__
elif dtype_with_endian.byteorder == '<':
ctype = ctype.__ctype_le__
return ctype
# 定义函数 _ctype_from_dtype_subarray,根据 dtype 的子数组返回对应的 ctypes 类型
def _ctype_from_dtype_subarray(dtype):
# 获取元素 dtype 和形状
element_dtype, shape = dtype.subdtype
# 根据元素 dtype 获取对应的 ctypes 类型
ctype = _ctype_from_dtype(element_dtype)
# 使用 _ctype_ndarray 创建 ndarray 类型,并返回
return _ctype_ndarray(ctype, shape)
# 根据结构化数据类型(dtype)创建对应的 ctypes 类型,支持嵌套结构和数组
def _ctype_from_dtype_structured(dtype):
# 提取每个字段的偏移量信息
field_data = []
for name in dtype.names:
# 获取字段的数据类型和偏移量
field_dtype, offset = dtype.fields[name][:2]
# 将字段信息存入列表
field_data.append((offset, name, _ctype_from_dtype(field_dtype)))
# ctypes 不关心字段的顺序,按偏移量排序字段信息
field_data = sorted(field_data, key=lambda f: f[0])
# 如果有多个字段且所有字段的偏移量均为 0,则为联合体(union)
if len(field_data) > 1 and all(offset == 0 for offset, name, ctype in field_data):
# 初始化联合体的大小和字段列表
size = 0
_fields_ = []
for offset, name, ctype in field_data:
_fields_.append((name, ctype))
size = max(size, ctypes.sizeof(ctype))
# 如果结构体的总大小与 dtype 中定义的大小不一致,则添加填充字段
if dtype.itemsize != size:
_fields_.append(('', ctypes.c_char * dtype.itemsize))
# 手动插入了填充字段,因此总是设置 `_pack_` 为 1
return type('union', (ctypes.Union,), dict(
_fields_=_fields_,
_pack_=1,
__module__=None,
))
else:
last_offset = 0
_fields_ = []
for offset, name, ctype in field_data:
# 计算字段之间的填充空间
padding = offset - last_offset
if padding < 0:
raise NotImplementedError("Overlapping fields")
if padding > 0:
_fields_.append(('', ctypes.c_char * padding))
_fields_.append((name, ctype))
last_offset = offset + ctypes.sizeof(ctype)
# 计算最后一个字段之后的填充空间
padding = dtype.itemsize - last_offset
if padding > 0:
_fields_.append(('', ctypes.c_char * padding))
# 手动插入了填充字段,因此总是设置 `_pack_` 为 1
return type('struct', (ctypes.Structure,), dict(
_fields_=_fields_,
_pack_=1,
__module__=None,
))
def _ctype_from_dtype(dtype):
# 如果数据类型具有字段信息,则调用 _ctype_from_dtype_structured 处理
if dtype.fields is not None:
return _ctype_from_dtype_structured(dtype)
# 如果数据类型具有子数据类型信息,则调用 _ctype_from_dtype_subarray 处理
elif dtype.subdtype is not None:
return _ctype_from_dtype_subarray(dtype)
# 否则,将数据类型视为标量,调用 _ctype_from_dtype_scalar 处理
else:
return _ctype_from_dtype_scalar(dtype)
def as_ctypes_type(dtype):
r"""
Convert a dtype into a ctypes type.
Parameters
----------
dtype : dtype
The dtype to convert
Returns
-------
ctype
A ctype scalar, union, array,or struct
Raises
------
NotImplementedError
If the conversion isnot possible
Notes
-----
This function does not losslessly round-trip in either direction.
``np.dtype(as_ctypes_type(dt))`` will:- insert padding fields
- reorder fields to be sorted by offset
- discard field titles
``as_ctypes_type(np.dtype(ctype))`` will:- discard the classnames of `ctypes.Structure`\ s and
`ctypes.Union`\ s
- convert single-element `ctypes.Union`\ s into single-element
`ctypes.Structure`\ s
- insert padding fields
"""
# 调用内部函数 _ctype_from_dtype 进行 dtype 到 ctypes 类型的转换
return _ctype_from_dtype(_dtype(dtype))
def as_array(obj, shape=None):
"""
Create a numpy array from a ctypes array or POINTER.
The numpy array shares the memory with the ctypes object.
The shape parameter must be given if converting from a ctypes POINTER.
The shape parameter is ignored if converting from a ctypes array
"""
if isinstance(obj, ctypes._Pointer):
# 如果 obj 是 ctypes._Pointer 类型,则将其转换为指定 shape 的数组
# 如果 shape 为 None,则抛出 TypeError 异常
if shape is None:
raise TypeError(
'as_array() requires a shape argument when called on a '
'pointer')
# 构造指向 obj 的指针类型 p_arr_type
p_arr_type = ctypes.POINTER(_ctype_ndarray(obj._type_, shape))
# 使用 ctypes.cast 将 obj 转换为 p_arr_type 指向的内容(数组)
obj = ctypes.cast(obj, p_arr_type).contents
# 调用 asarray 函数,返回 obj 的 numpy 数组表示
return asarray(obj)
def as_ctypes(obj):
"""Create andreturn a ctypes objectfrom a numpy array. Actually
anything that exposes the __array_interface__ is accepted."""
# 获取 obj 的 __array_interface__
ai = obj.__array_interface__
# 如果数组是 strided arrays,则抛出 TypeError 异常if ai["strides"]:raise TypeError("strided arrays not supported")# 如果 __array_interface__ 的版本不是 3,则抛出 TypeError 异常if ai["version"]!=3:raise TypeError("only __array_interface__ version 3 supported")# 获取数组的数据地址和 readonly 属性
addr, readonly = ai["data"]# 如果数组是只读的,则抛出 TypeError 异常if readonly:raise TypeError("readonly arrays unsupported")# 根据 ai["typestr"] 调用 as_ctypes_type 函数转换为对应的 ctypes 标量类型
ctype_scalar = as_ctypes_type(ai["typestr"])# 构造一个 ctypes 对象,类型是 _ctype_ndarray(ctype_scalar, ai["shape"])
result_type = _ctype_ndarray(ctype_scalar, ai["shape"])# 使用 from_address 方法从地址 addr 创建 result_type 类型的对象 result
result = result_type.from_address(addr)# 将 obj 保存在 result 的 __keep 属性中
result.__keep = obj
return result