NumPy-源码解析-八十一-

104 阅读58分钟

NumPy 源码解析(八十一)

.\numpy\numpy\_core\tests\test_defchararray.py

import pytest  # 导入 pytest 测试框架

import numpy as np  # 导入 NumPy 库
from numpy._core.multiarray import _vec_string  # 导入 NumPy 内部模块 _vec_string
from numpy.testing import (  # 导入 NumPy 测试模块中的多个函数
    assert_, assert_equal, assert_array_equal, assert_raises,
    assert_raises_regex
    )

kw_unicode_true = {'unicode': True}  # 定义关键字参数字典,用于启用 Unicode 支持,以便 2to3 正常工作
kw_unicode_false = {'unicode': False}

class TestBasic:
    def test_from_object_array(self):
        A = np.array([['abc', 2],  # 创建一个 NumPy 对象数组 A,包含字符串和整数
                      ['long   ', '0123456789']], dtype='O')
        B = np.char.array(A)  # 使用 np.char.array 将 A 转换为字符串数组 B
        assert_equal(B.dtype.itemsize, 10)  # 断言 B 的元素的字节大小为 10
        assert_array_equal(B, [[b'abc', b'2'],  # 断言 B 与预期的二维数组相等
                               [b'long', b'0123456789']])

    def test_from_object_array_unicode(self):
        A = np.array([['abc', 'Sigma \u03a3'],  # 创建一个包含 Unicode 字符串的 NumPy 对象数组 A
                      ['long   ', '0123456789']], dtype='O')
        assert_raises(ValueError, np.char.array, (A,))  # 断言调用 np.char.array(A) 抛出 ValueError 异常
        B = np.char.array(A, **kw_unicode_true)  # 使用关键字参数启用 Unicode 支持创建字符串数组 B
        assert_equal(B.dtype.itemsize, 10 * np.array('a', 'U').dtype.itemsize)  # 断言 B 中元素的字节大小与 Unicode 字符 'a' 的字节大小乘以 10 相等
        assert_array_equal(B, [['abc', 'Sigma \u03a3'],  # 断言 B 与预期的二维数组相等
                               ['long', '0123456789']])

    def test_from_string_array(self):
        A = np.array([[b'abc', b'foo'],  # 创建一个字节字符串数组 A
                      [b'long   ', b'0123456789']])
        assert_equal(A.dtype.type, np.bytes_)  # 断言 A 的数据类型是 np.bytes_
        B = np.char.array(A)  # 使用 np.char.array 将 A 转换为字符串数组 B
        assert_array_equal(B, A)  # 断言 B 与 A 相等
        assert_equal(B.dtype, A.dtype)  # 断言 B 的数据类型与 A 的数据类型相等
        assert_equal(B.shape, A.shape)  # 断言 B 的形状与 A 的形状相等
        B[0, 0] = 'changed'  # 修改 B 中的元素
        assert_(B[0, 0] != A[0, 0])  # 断言修改后 B 的元素不等于 A 的原始元素
        C = np.char.asarray(A)  # 使用 np.char.asarray 将 A 转换为字符串数组 C
        assert_array_equal(C, A)  # 断言 C 与 A 相等
        assert_equal(C.dtype, A.dtype)  # 断言 C 的数据类型与 A 的数据类型相等
        C[0, 0] = 'changed again'  # 修改 C 中的元素
        assert_(C[0, 0] != B[0, 0])  # 断言修改后 C 的元素不等于 B 的元素
        assert_(C[0, 0] == A[0, 0])  # 断言修改后 C 的元素等于 A 的原始元素

    def test_from_unicode_array(self):
        A = np.array([['abc', 'Sigma \u03a3'],  # 创建一个包含 Unicode 字符串的 NumPy 对象数组 A
                      ['long   ', '0123456789']])
        assert_equal(A.dtype.type, np.str_)  # 断言 A 的数据类型是 np.str_
        B = np.char.array(A)  # 使用 np.char.array 将 A 转换为字符串数组 B
        assert_array_equal(B, A)  # 断言 B 与 A 相等
        assert_equal(B.dtype, A.dtype)  # 断言 B 的数据类型与 A 的数据类型相等
        assert_equal(B.shape, A.shape)  # 断言 B 的形状与 A 的形状相等
        B = np.char.array(A, **kw_unicode_true)  # 使用关键字参数启用 Unicode 支持创建字符串数组 B
        assert_array_equal(B, A)  # 断言 B 与 A 相等
        assert_equal(B.dtype, A.dtype)  # 断言 B 的数据类型与 A 的数据类型相等
        assert_equal(B.shape, A.shape)  # 断言 B 的形状与 A 的形状相等

        def fail():
            np.char.array(A, **kw_unicode_false)  # 尝试使用禁用 Unicode 支持的关键字参数创建字符串数组

        assert_raises(UnicodeEncodeError, fail)  # 断言调用 fail() 抛出 UnicodeEncodeError 异常

    def test_unicode_upconvert(self):
        A = np.char.array(['abc'])  # 创建一个包含字节字符串的字符串数组 A
        B = np.char.array(['\u03a3'])  # 创建一个包含 Unicode 字符串的字符串数组 B
        assert_(issubclass((A + B).dtype.type, np.str_))  # 断言 (A + B) 的数据类型是 np.str_

    def test_from_string(self):
        A = np.char.array(b'abc')  # 创建一个字节字符串的字符串数组 A
        assert_equal(len(A), 1)  # 断言 A 的长度为 1
        assert_equal(len(A[0]), 3)  # 断言 A 的第一个元素的长度为 3
        assert_(issubclass(A.dtype.type, np.bytes_))  # 断言 A 的数据类型是 np.bytes_

    def test_from_unicode(self):
        A = np.char.array('\u03a3')  # 创建一个包含 Unicode 字符串的字符串数组 A
        assert_equal(len(A), 1)  # 断言 A 的长度为 1
        assert_equal(len(A[0]), 1)  # 断言 A 的第一个元素的长度为 1
        assert_equal(A.itemsize, 4)  # 断言 A 中元素的字节大小为 4
        assert_(issubclass(A.dtype.type, np.str_))  # 断言 A 的数据类型是 np.str_

class TestVecString:
    # 定义测试方法,用于测试不存在的方法
    def test_non_existent_method(self):

        # 定义一个会失败的函数,尝试调用 _vec_string 方法,但提供的方法名不存在
        def fail():
            _vec_string('a', np.bytes_, 'bogus')

        # 断言调用 fail 函数会抛出 AttributeError 异常
        assert_raises(AttributeError, fail)

    # 定义测试方法,用于测试非字符串数组作为输入时的情况
    def test_non_string_array(self):

        # 定义一个会失败的函数,尝试将整数作为输入传递给 _vec_string 方法
        def fail():
            _vec_string(1, np.bytes_, 'strip')

        # 断言调用 fail 函数会抛出 TypeError 异常
        assert_raises(TypeError, fail)

    # 定义测试方法,用于测试传递无效参数元组时的情况
    def test_invalid_args_tuple(self):

        # 定义一个会失败的函数,尝试传递包含非字符串元素的列表作为参数元组给 _vec_string 方法
        def fail():
            _vec_string(['a'], np.bytes_, 'strip', 1)

        # 断言调用 fail 函数会抛出 TypeError 异常
        assert_raises(TypeError, fail)

    # 定义测试方法,用于测试传递无效的类型描述符时的情况
    def test_invalid_type_descr(self):

        # 定义一个会失败的函数,尝试将无效的类型描述符 'BOGUS' 传递给 _vec_string 方法
        def fail():
            _vec_string(['a'], 'BOGUS', 'strip')

        # 断言调用 fail 函数会抛出 TypeError 异常
        assert_raises(TypeError, fail)

    # 定义测试方法,用于测试传递无效函数参数时的情况
    def test_invalid_function_args(self):

        # 定义一个会失败的函数,尝试将包含非单一元素元组作为函数参数传递给 _vec_string 方法
        def fail():
            _vec_string(['a'], np.bytes_, 'strip', (1,))

        # 断言调用 fail 函数会抛出 TypeError 异常
        assert_raises(TypeError, fail)

    # 定义测试方法,用于测试返回类型不正确时的情况
    def test_invalid_result_type(self):

        # 定义一个会失败的函数,尝试调用 _vec_string 方法,但期望返回类型为 np.int_ 而不是字符串
        def fail():
            _vec_string(['a'], np.int_, 'strip')

        # 断言调用 fail 函数会抛出 TypeError 异常
        assert_raises(TypeError, fail)

    # 定义测试方法,用于测试广播错误时的情况
    def test_broadcast_error(self):

        # 定义一个会失败的函数,尝试将包含不同长度子列表的列表作为参数广播给 _vec_string 方法
        def fail():
            _vec_string([['abc', 'def']], np.int_, 'find', (['a', 'd', 'j'],))

        # 断言调用 fail 函数会抛出 ValueError 异常
        assert_raises(ValueError, fail)
class TestWhitespace:
    # 设置测试前的准备工作,初始化两个二维字符数组 A 和 B
    def setup_method(self):
        self.A = np.array([['abc ', '123  '],
                           ['789 ', 'xyz ']]).view(np.char.chararray)
        self.B = np.array([['abc', '123'],
                           ['789', 'xyz']]).view(np.char.chararray)

    # 测试相等性的方法
    def test1(self):
        assert_(np.all(self.A == self.B))  # 断言 A 等于 B 的所有元素
        assert_(np.all(self.A >= self.B))  # 断言 A 大于等于 B 的所有元素
        assert_(np.all(self.A <= self.B))  # 断言 A 小于等于 B 的所有元素
        assert_(not np.any(self.A > self.B))  # 断言 A 没有比 B 大的元素
        assert_(not np.any(self.A < self.B))  # 断言 A 没有比 B 小的元素
        assert_(not np.any(self.A != self.B))  # 断言 A 和 B 的所有元素都相等

class TestChar:
    # 设置测试前的准备工作,初始化一个字符数组 A
    def setup_method(self):
        self.A = np.array('abc1', dtype='c').view(np.char.chararray)

    # 测试字符数组的方法
    def test_it(self):
        assert_equal(self.A.shape, (4,))  # 断言字符数组 A 的形状为 (4,)
        assert_equal(self.A.upper()[:2].tobytes(), b'AB')  # 断言字符数组 A 大写后前两个字符为 'AB'

class TestComparisons:
    # 设置测试前的准备工作,初始化两个二维字符数组 A 和 B
    def setup_method(self):
        self.A = np.array([['abc', 'abcc', '123'],
                           ['789', 'abc', 'xyz']]).view(np.char.chararray)
        self.B = np.array([['efg', 'efg', '123  '],
                           ['051', 'efgg', 'tuv']]).view(np.char.chararray)

    # 测试不等于的方法
    def test_not_equal(self):
        assert_array_equal((self.A != self.B),  # 断言 A 和 B 的不等性
                           [[True, True, False], [True, True, True]])

    # 测试等于的方法
    def test_equal(self):
        assert_array_equal((self.A == self.B),  # 断言 A 和 B 的等性
                           [[False, False, True], [False, False, False]])

    # 测试大于等于的方法
    def test_greater_equal(self):
        assert_array_equal((self.A >= self.B),  # 断言 A 大于等于 B
                           [[False, False, True], [True, False, True]])

    # 测试小于等于的方法
    def test_less_equal(self):
        assert_array_equal((self.A <= self.B),  # 断言 A 小于等于 B
                           [[True, True, True], [False, True, False]])

    # 测试大于的方法
    def test_greater(self):
        assert_array_equal((self.A > self.B),  # 断言 A 大于 B
                           [[False, False, False], [True, False, True]])

    # 测试小于的方法
    def test_less(self):
        assert_array_equal((self.A < self.B),  # 断言 A 小于 B
                           [[True, True, False], [False, True, False]])

    # 测试类型比较的方法
    def test_type(self):
        out1 = np.char.equal(self.A, self.B)  # 比较 A 和 B 的元素是否相等,返回布尔数组
        out2 = np.char.equal('a', 'a')  # 比较单个字符 'a' 是否等于 'a',返回布尔值
        assert_(isinstance(out1, np.ndarray))  # 断言 out1 是 NumPy 数组类型
        assert_(isinstance(out2, np.ndarray))  # 断言 out2 是 NumPy 数组类型

class TestComparisonsMixed1(TestComparisons):
    """Ticket #1276"""

    # 继承自 TestComparisons,重写 setup_method 方法
    def setup_method(self):
        TestComparisons.setup_method(self)
        self.B = np.array(
            [['efg', 'efg', '123  '],
             ['051', 'efgg', 'tuv']], np.str_).view(np.char.chararray)

class TestComparisonsMixed2(TestComparisons):
    """Ticket #1276"""

    # 继承自 TestComparisons,重写 setup_method 方法
    def setup_method(self):
        TestComparisons.setup_method(self)
        self.A = np.array(
            [['abc', 'abcc', '123'],
             ['789', 'abc', 'xyz']], np.str_).view(np.char.chararray)
    # 设置测试方法的初始化操作
    def setup_method(self):
        # 创建包含字符串数组的 numpy 数组 A,每个元素都是 chararray 类型
        self.A = np.array([[' abc ', ''],
                           ['12345', 'MixedCase'],
                           ['123 \t 345 \0 ', 'UPPER']]) \
                            .view(np.char.chararray)
        # 创建包含字符串数组的 numpy 数组 B,每个元素都是 chararray 类型,包含 Unicode 字符
        self.B = np.array([[' \u03a3 ', ''],
                           ['12345', 'MixedCase'],
                           ['123 \t 345 \0 ', 'UPPER']]) \
                            .view(np.char.chararray)
        # 创建包含长字符串的 numpy 数组 C,每个元素都是 chararray 类型
        # 这些字符串的长度超过了代码中的 MEMCHR_CUT_OFF
        self.C = (np.array(['ABCDEFGHIJKLMNOPQRSTUVWXYZ',
                            '01234567890123456789012345'])
                  .view(np.char.chararray))

    # 测试字符串长度的方法
    def test_len(self):
        # 断言返回的字符串长度数组的 dtype 是整数类型的子类
        assert_(issubclass(np.char.str_len(self.A).dtype.type, np.integer))
        # 断言计算得到的字符串长度数组与预期结果相等
        assert_array_equal(np.char.str_len(self.A), [[5, 0], [5, 9], [12, 5]])
        assert_array_equal(np.char.str_len(self.B), [[3, 0], [5, 9], [12, 5]])

    # 测试字符串计数的方法
    def test_count(self):
        # 断言返回的空字符计数数组的 dtype 是整数类型的子类
        assert_(issubclass(self.A.count('').dtype.type, np.integer))
        # 断言计算得到的字符计数数组与预期结果相等
        assert_array_equal(self.A.count('a'), [[1, 0], [0, 1], [0, 0]])
        assert_array_equal(self.A.count('123'), [[0, 0], [1, 0], [1, 0]])
        # Python 似乎不支持计算空字符('\0')的数量
        # assert_array_equal(self.A.count('\0'), [[0, 0], [0, 0], [1, 0]])
        assert_array_equal(self.A.count('a', 0, 2), [[1, 0], [0, 0], [0, 0]])
        assert_array_equal(self.B.count('a'), [[0, 0], [0, 1], [0, 0]])
        assert_array_equal(self.B.count('123'), [[0, 0], [1, 0], [1, 0]])
        # assert_array_equal(self.B.count('\0'), [[0, 0], [0, 0], [1, 0]])

    # 测试字符串后缀匹配的方法
    def test_endswith(self):
        # 断言返回的空字符串后缀匹配数组的 dtype 是布尔类型的子类
        assert_(issubclass(self.A.endswith('').dtype.type, np.bool))
        # 断言字符串是否以指定的后缀结束,结果与预期相等
        assert_array_equal(self.A.endswith(' '), [[1, 0], [0, 0], [1, 0]])
        assert_array_equal(self.A.endswith('3', 0, 3), [[0, 0], [1, 0], [1, 0]])

        # 定义一个失败的函数,用于测试 TypeError 异常
        def fail():
            self.A.endswith('3', 'fdjk')

        # 断言调用失败函数会抛出 TypeError 异常
        assert_raises(TypeError, fail)

    # 参数化测试字符串查找的方法
    @pytest.mark.parametrize(
        "dtype, encode",
        [("U", str),  # 使用 Unicode 编码类型
         ("S", lambda x: x.encode('ascii')),  # 使用 ASCII 编码类型的 lambda 函数
         ])
    def test_find(self, dtype, encode):
        # 将数组 A 转换为指定 dtype 的数组
        A = self.A.astype(dtype)
        # 断言返回的查找结果数组的 dtype 是整数类型的子类
        assert_(issubclass(A.find(encode('a')).dtype.type, np.integer))
        # 断言查找的结果数组与预期结果相等
        assert_array_equal(A.find(encode('a')),
                           [[1, -1], [-1, 6], [-1, -1]])
        assert_array_equal(A.find(encode('3')),
                           [[-1, -1], [2, -1], [2, -1]])
        assert_array_equal(A.find(encode('a'), 0, 2),
                           [[1, -1], [-1, -1], [-1, -1]])
        assert_array_equal(A.find([encode('1'), encode('P')]),
                           [[-1, -1], [0, -1], [0, 1]])
        # 将数组 C 转换为指定 dtype 的数组
        C = self.C.astype(dtype)
        # 断言查找的结果数组与预期结果相等
        assert_array_equal(C.find(encode('M')), [12, -1])
    # 定义测试方法 test_index,用于测试索引相关的方法

        # 定义内部方法 fail,用于测试在 self.A 中查找 'a' 是否会引发 ValueError 异常
        def fail():
            self.A.index('a')

        # 使用 assert_raises 断言,验证调用 fail 方法会抛出 ValueError 异常
        assert_raises(ValueError, fail)
        
        # 使用 assert_ 断言,验证 np.char.index 方法在字符串 'abcba' 中查找 'b' 的索引为 1
        assert_(np.char.index('abcba', 'b') == 1)
        
        # 使用 assert_ 断言,验证 np.char.index 方法返回的结果的 dtype 是 np.integer 类型的子类
        assert_(issubclass(np.char.index('abcba', 'b').dtype.type, np.integer))

    # 定义测试方法 test_isalnum,用于测试字符串是否由字母和数字组成
    def test_isalnum(self):
        # 使用 assert_ 断言,验证 self.A.isalnum() 返回的结果的 dtype 是 np.bool 类型的子类
        assert_(issubclass(self.A.isalnum().dtype.type, np.bool))
        
        # 使用 assert_array_equal 断言,验证 self.A.isalnum() 对象的返回结果与预期的 [[False, False], [True, True], [False, True]] 相等
        assert_array_equal(self.A.isalnum(), [[False, False], [True, True], [False, True]])

    # 定义测试方法 test_isalpha,用于测试字符串是否只包含字母
    def test_isalpha(self):
        # 使用 assert_ 断言,验证 self.A.isalpha() 返回的结果的 dtype 是 np.bool 类型的子类
        assert_(issubclass(self.A.isalpha().dtype.type, np.bool))
        
        # 使用 assert_array_equal 断言,验证 self.A.isalpha() 对象的返回结果与预期的 [[False, False], [False, True], [False, True]] 相等
        assert_array_equal(self.A.isalpha(), [[False, False], [False, True], [False, True]])

    # 定义测试方法 test_isdigit,用于测试字符串是否只包含数字
    def test_isdigit(self):
        # 使用 assert_ 断言,验证 self.A.isdigit() 返回的结果的 dtype 是 np.bool 类型的子类
        assert_(issubclass(self.A.isdigit().dtype.type, np.bool))
        
        # 使用 assert_array_equal 断言,验证 self.A.isdigit() 对象的返回结果与预期的 [[False, False], [True, False], [False, False]] 相等
        assert_array_equal(self.A.isdigit(), [[False, False], [True, False], [False, False]])

    # 定义测试方法 test_islower,用于测试字符串是否只包含小写字母
    def test_islower(self):
        # 使用 assert_ 断言,验证 self.A.islower() 返回的结果的 dtype 是 np.bool 类型的子类
        assert_(issubclass(self.A.islower().dtype.type, np.bool))
        
        # 使用 assert_array_equal 断言,验证 self.A.islower() 对象的返回结果与预期的 [[True, False], [False, False], [False, False]] 相等
        assert_array_equal(self.A.islower(), [[True, False], [False, False], [False, False]])

    # 定义测试方法 test_isspace,用于测试字符串是否只包含空格
    def test_isspace(self):
        # 使用 assert_ 断言,验证 self.A.isspace() 返回的结果的 dtype 是 np.bool 类型的子类
        assert_(issubclass(self.A.isspace().dtype.type, np.bool))
        
        # 使用 assert_array_equal 断言,验证 self.A.isspace() 对象的返回结果与预期的 [[False, False], [False, False], [False, False]] 相等
        assert_array_equal(self.A.isspace(), [[False, False], [False, False], [False, False]])

    # 定义测试方法 test_istitle,用于测试字符串是否符合 title 格式(每个单词首字母大写)
    def test_istitle(self):
        # 使用 assert_ 断言,验证 self.A.istitle() 返回的结果的 dtype 是 np.bool 类型的子类
        assert_(issubclass(self.A.istitle().dtype.type, np.bool))
        
        # 使用 assert_array_equal 断言,验证 self.A.istitle() 对象的返回结果与预期的 [[False, False], [False, False], [False, False]] 相等
        assert_array_equal(self.A.istitle(), [[False, False], [False, False], [False, False]])

    # 定义测试方法 test_isupper,用于测试字符串是否只包含大写字母
    def test_isupper(self):
        # 使用 assert_ 断言,验证 self.A.isupper() 返回的结果的 dtype 是 np.bool 类型的子类
        assert_(issubclass(self.A.isupper().dtype.type, np.bool))
        
        # 使用 assert_array_equal 断言,验证 self.A.isupper() 对象的返回结果与预期的 [[False, False], [False, False], [False, True]] 相等
        assert_array_equal(self.A.isupper(), [[False, False], [False, False], [False, True]])

    # 定义测试方法 test_rfind,用于测试字符串从右往左查找子字符串的索引
    def test_rfind(self):
        # 使用 assert_ 断言,验证 self.A.rfind('a') 返回的结果的 dtype 是 np.integer 类型的子类
        assert_(issubclass(self.A.rfind('a').dtype.type, np.integer))
        
        # 使用 assert_array_equal 断言,验证 self.A.rfind('a') 对象的返回结果与预期的 [[1, -1], [-1, 6], [-1, -1]] 相等
        assert_array_equal(self.A.rfind('a'), [[1, -1], [-1, 6], [-1, -1]])
        
        # 使用 assert_array_equal 断言,验证 self.A.rfind('3') 对象的返回结果与预期的 [[-1, -1], [2, -1], [6, -1]] 相等
        assert_array_equal(self.A.rfind('3'), [[-1, -1], [2, -1], [6, -1]])
        
        # 使用 assert_array_equal 断言,验证 self.A.rfind('a', 0, 2) 对象的返回结果与预期的 [[1, -1], [-1, -1], [-1, -1]] 相等
        assert_array_equal(self.A.rfind('a', 0, 2), [[1, -1], [-1, -1], [-1, -1]])
        
        # 使用 assert_array_equal 断言,验证 self.A.rfind(['1', 'P']) 对象的返回结果与预期的 [[-1, -1], [0, -1], [0, 2]] 相等
        assert_array_equal(self.A.rfind(['1', 'P']), [[-1, -1], [0, -1], [0, 2]])

    # 定义测试方法 test_rindex,用于测试字符串从右往左查找子字符串的索引
    def test_rindex(self):
        
        # 定义内部方法 fail,用于测试在 self.A 中查找 'a' 是否会引发 ValueError 异常
        def fail():
            self.A.rindex('a')

        # 使用 assert_raises 断言,验证调用 fail 方法会抛出 ValueError 异常
        assert_raises(ValueError, fail)
        
        # 使用 assert_ 断言,验证 np.char.rindex 方法在字符串 'abcba' 中查找 'b' 的索引为 3
        assert_(np.char.rindex('abcba', 'b') == 3)
        
        # 使用 assert_ 断言,验证 np.char.rindex 方法返回的结果的 dtype 是 np.integer 类型的子类
        assert_(issubclass(np.char.rindex('abcba', 'b').dtype.type, np.integer))

    # 定义测试方法 test_startswith,用于测试字符串是否以指定前缀开头
    def test_startswith(self):
        # 使用 assert_ 断言,验证 self.A.startswith('') 返回的结果的 dtype 是 np.bool 类型的子类
        assert_(issubclass(self.A.startswith('').dtype.type, np.bool))
        
        # 使用 assert_array_equal 断言,验证 self.A.startswith(' ') 对象的返回结果与预期的 [[1, 0], [0, 0], [0, 0]] 相等
        assert_array_equal(self.A.startswith(' '), [[1, 0], [0, 0], [0, 0]])
        
        # 使用 assert_array_equal 断言,验证 self.A.startswith('1', 0, 3) 对象的返回结果与
class TestMethods:
    # 设置测试方法的初始化函数
    def setup_method(self):
        # 创建字符串数组 A,使用 np.char.chararray 将其视为字符数组
        self.A = np.array([[' abc ', ''],
                           ['12345', 'MixedCase'],
                           ['123 \t 345 \0 ', 'UPPER']],
                          dtype='S').view(np.char.chararray)
        # 创建字符串数组 B,使用 np.char.chararray 将其视为字符数组
        self.B = np.array([[' \u03a3 ', ''],
                           ['12345', 'MixedCase'],
                           ['123 \t 345 \0 ', 'UPPER']]).view(
                                                            np.char.chararray)

    # 测试 capitalize 方法
    def test_capitalize(self):
        # 期望的结果 tgt,使用 np.bytes_ 类型检查结果的数据类型
        tgt = [[b' abc ', b''],
               [b'12345', b'Mixedcase'],
               [b'123 \t 345 \0 ', b'Upper']]
        assert_(issubclass(self.A.capitalize().dtype.type, np.bytes_))
        # 检查 capitalize 方法的输出是否与期望的结果 tgt 一致
        assert_array_equal(self.A.capitalize(), tgt)

        # 期望的结果 tgt,使用 np.str_ 类型检查结果的数据类型
        tgt = [[' \u03c3 ', ''],
               ['12345', 'Mixedcase'],
               ['123 \t 345 \0 ', 'Upper']]
        assert_(issubclass(self.B.capitalize().dtype.type, np.str_))
        # 检查 capitalize 方法的输出是否与期望的结果 tgt 一致
        assert_array_equal(self.B.capitalize(), tgt)

    # 测试 center 方法
    def test_center(self):
        # 使用 np.bytes_ 类型检查 center 方法的输出的数据类型
        assert_(issubclass(self.A.center(10).dtype.type, np.bytes_))
        # 创建数组 C,检查 center 方法的输出是否符合预期的长度
        C = self.A.center([10, 20])
        assert_array_equal(np.char.str_len(C), [[10, 20], [10, 20], [12, 20]])

        # 使用指定填充符号进行 center 方法测试
        C = self.A.center(20, b'#')
        # 检查填充后的字符串是否以指定字符开始和结束
        assert_(np.all(C.startswith(b'#')))
        assert_(np.all(C.endswith(b'#')))

        # 使用 np.char.center 函数测试指定字符串的居中效果
        C = np.char.center(b'FOO', [[10, 20], [15, 8]])
        # 期望的结果 tgt,使用 np.bytes_ 类型检查结果的数据类型
        tgt = [[b'   FOO    ', b'        FOO         '],
               [b'      FOO      ', b'  FOO   ']]
        assert_(issubclass(C.dtype.type, np.bytes_))
        # 检查 np.char.center 方法的输出是否与期望的结果 tgt 一致
        assert_array_equal(C, tgt)

    # 测试 decode 方法
    def test_decode(self):
        # 创建数组 A,使用 decode 方法解码 Unicode 转义字符
        A = np.char.array([b'\\u03a3'])
        assert_(A.decode('unicode-escape')[0] == '\u03a3')

    # 测试 encode 方法
    def test_encode(self):
        # 使用 encode 方法对数组 B 执行 Unicode 转义编码
        B = self.B.encode('unicode_escape')
        assert_(B[0][0] == str(' \\u03a3 ').encode('latin1'))

    # 测试 expandtabs 方法
    def test_expandtabs(self):
        # 使用 expandtabs 方法展开数组 A 中的制表符
        T = self.A.expandtabs()
        assert_(T[2, 0] == b'123      345 \0')

    # 测试 join 方法
    def test_join(self):
        # 注意:list(b'123') == [49, 50, 51]
        # b','.join(b'123') 在 Python 3 上会导致错误
        A0 = self.A.decode('ascii')

        # 使用 join 方法连接数组 A0 中的字符串,指定连接符为 ',' 和 '#'
        A = np.char.join([',', '#'], A0)
        assert_(issubclass(A.dtype.type, np.str_))
        # 期望的结果 tgt,使用 np.array 创建连接后的字符串数组
        tgt = np.array([[' ,a,b,c, ', ''],
                        ['1,2,3,4,5', 'M#i#x#e#d#C#a#s#e'],
                        ['1,2,3, ,\t, ,3,4,5, ,\x00, ', 'U#P#P#E#R']])
        # 检查 np.char.join 方法的输出是否与期望的结果 tgt 一致
        assert_array_equal(np.char.join([',', '#'], A0), tgt)
    def test_ljust(self):
        # 断言:确保 self.A.ljust(10) 返回的类型是 np.bytes_ 的子类
        assert_(issubclass(self.A.ljust(10).dtype.type, np.bytes_))

        # 将 self.A 分别左对齐到长度为 10 和 20 的数组 C,并检查字符长度
        C = self.A.ljust([10, 20])
        assert_array_equal(np.char.str_len(C), [[10, 20], [10, 20], [12, 20]])

        # 使用字符 '#' 将 self.A 左对齐到长度为 20 的数组 C,并检查开头和结尾
        C = self.A.ljust(20, b'#')
        assert_array_equal(C.startswith(b'#'), [
                [False, True], [False, False], [False, False]])
        assert_(np.all(C.endswith(b'#')))

        # 使用 np.char.ljust 将字节串 b'FOO' 左对齐到不同长度的数组,并断言结果是否正确
        C = np.char.ljust(b'FOO', [[10, 20], [15, 8]])
        tgt = [[b'FOO       ', b'FOO                 '],
               [b'FOO            ', b'FOO     ']]
        assert_(issubclass(C.dtype.type, np.bytes_))
        assert_array_equal(C, tgt)

    def test_lower(self):
        # 定义预期的小写转换结果并断言 self.A.lower() 的返回类型
        tgt = [[b' abc ', b''],
               [b'12345', b'mixedcase'],
               [b'123 \t 345 \0 ', b'upper']]
        assert_(issubclass(self.A.lower().dtype.type, np.bytes_))
        assert_array_equal(self.A.lower(), tgt)

        # 定义另一组预期的小写转换结果并断言 self.B.lower() 的返回类型
        tgt = [[' \u03c3 ', ''],
               ['12345', 'mixedcase'],
               ['123 \t 345 \0 ', 'upper']]
        assert_(issubclass(self.B.lower().dtype.type, np.str_))
        assert_array_equal(self.B.lower(), tgt)

    def test_lstrip(self):
        # 定义预期的去除左空格结果并断言 self.A.lstrip() 的返回类型
        tgt = [[b'abc ', b''],
               [b'12345', b'MixedCase'],
               [b'123 \t 345 \0 ', b'UPPER']]
        assert_(issubclass(self.A.lstrip().dtype.type, np.bytes_))
        assert_array_equal(self.A.lstrip(), tgt)

        # 定义另一组预期的去除左字符 '1' 和 'M' 结果并断言 self.A.lstrip([b'1', b'M']) 的返回类型
        tgt = [[b' abc', b''],
               [b'2345', b'ixedCase'],
               [b'23 \t 345 \x00', b'UPPER']]
        assert_array_equal(self.A.lstrip([b'1', b'M']), tgt)

        # 定义另一组预期的去除左空格结果并断言 self.B.lstrip() 的返回类型
        tgt = [['\u03a3 ', ''],
               ['12345', 'MixedCase'],
               ['123 \t 345 \0 ', 'UPPER']]
        assert_(issubclass(self.B.lstrip().dtype.type, np.str_))
        assert_array_equal(self.B.lstrip(), tgt)

    def test_partition(self):
        # 使用 [b'3', b'M'] 将 self.A 分区并断言返回结果的类型
        P = self.A.partition([b'3', b'M'])
        tgt = [[(b' abc ', b'', b''), (b'', b'', b'')],
               [(b'12', b'3', b'45'), (b'', b'M', b'ixedCase')],
               [(b'12', b'3', b' \t 345 \0 '), (b'UPPER', b'', b'')]]
        assert_(issubclass(P.dtype.type, np.bytes_))
        assert_array_equal(P, tgt)
    # 定义一个测试方法,用于测试字符串数组的替换操作
    def test_replace(self):
        # 调用数组对象的替换方法,将指定的子串替换为新的子串
        R = self.A.replace([b'3', b'a'],
                           [b'##########', b'@'])
        # 定义预期结果数组,用于验证替换操作的正确性
        tgt = [[b' abc ', b''],
               [b'12##########45', b'MixedC@se'],
               [b'12########## \t ##########45 \x00 ', b'UPPER']]
        # 断言替换后的数组的数据类型是 np.bytes_ 的子类
        assert_(issubclass(R.dtype.type, np.bytes_))
        # 断言替换后的数组与预期结果数组相等
        assert_array_equal(R, tgt)
        
        # 测试一些特殊情况,这些情况下应返回输入数组,因为替换是不可能或没有效果的
        # 测试替换长字节串时应返回原始数组
        S1 = self.A.replace(b'A very long byte string, longer than A', b'')
        assert_array_equal(S1, self.A)
        # 测试空串替换为空串应返回原始数组
        S2 = self.A.replace(b'', b'')
        assert_array_equal(S2, self.A)
        # 测试不做任何替换的情况下应返回原始数组
        S3 = self.A.replace(b'3', b'3')
        assert_array_equal(S3, self.A)
        # 测试不做任何替换的情况下应返回原始数组
        S4 = self.A.replace(b'3', b'', count=0)
        assert_array_equal(S4, self.A)

    # 定义一个测试方法,用于测试替换操作的计数和大小
    def test_replace_count_and_size(self):
        # 创建一个字符串数组对象 a,包含四个不同长度的字符串
        a = np.array(['0123456789' * i for i in range(4)]).view(np.char.chararray)
        # 对数组进行替换操作,将指定的子串替换为新的子串
        r1 = a.replace('5', 'ABCDE')
        # 断言替换后的数组元素大小符合预期
        assert r1.dtype.itemsize == (3*10 + 3*4) * 4
        # 断言替换后的数组与预期结果数组相等
        assert_array_equal(r1, np.array(['01234ABCDE6789' * i for i in range(4)]))
        
        # 对数组进行替换操作,指定替换的次数
        r2 = a.replace('5', 'ABCDE', count=1)
        # 断言替换后的数组元素大小符合预期
        assert r2.dtype.itemsize == (3*10 + 4) * 4
        
        # 对数组进行替换操作,不进行任何替换
        r3 = a.replace('5', 'ABCDE', count=0)
        # 断言替换后的数组元素大小与原始数组相同
        assert r3.dtype.itemsize == a.dtype.itemsize
        # 断言替换后的数组与原始数组相等
        assert_array_equal(r3, a)
        
        # 使用负数值表示替换所有匹配项
        r4 = a.replace('5', 'ABCDE', count=-1)
        # 断言替换后的数组元素大小符合预期
        assert r4.dtype.itemsize == (3*10 + 3*4) * 4
        # 断言替换后的数组与 r1 相等
        assert_array_equal(r4, r1)
        
        # 可以对每个元素进行独立的替换计数
        r5 = a.replace('5', 'ABCDE', count=[-1, -1, -1, 1])
        # 断言替换后的数组元素大小符合预期
        assert r5.dtype.itemsize == (3*10 + 4) * 4
        # 断言替换后的数组与预期结果数组相等
        assert_array_equal(r5, np.array(['01234ABCDE6789' * i for i in range(3)] + ['01234ABCDE6789' + '0123456789' * 2]))

    # 定义一个测试方法,用于测试替换操作的广播特性
    def test_replace_broadcasting(self):
        # 创建一个字符串数组对象 a
        a = np.array('0,0,0').view(np.char.chararray)
        # 对数组进行替换操作,替换指定子串为新的子串,并指定每个替换操作的次数
        r1 = a.replace('0', '1', count=np.arange(3))
        # 断言替换后的数组数据类型与原始数组相同
        assert r1.dtype == a.dtype
        # 断言替换后的数组与预期结果数组相等
        assert_array_equal(r1, np.array(['0,0,0', '1,0,0', '1,1,0']))
        
        # 对数组进行替换操作,使用二维数组作为新的子串进行替换,并指定每个替换操作的次数
        r2 = a.replace('0', [['1'], ['2']], count=np.arange(1, 4))
        # 断言替换后的数组与预期结果数组相等
        assert_array_equal(r2, np.array([['1,0,0', '1,1,0', '1,1,1'], ['2,0,0', '2,2,0', '2,2,2']]))
        
        # 对数组进行替换操作,替换多个子串为新的子串
        r3 = a.replace(['0', '0,0', '0,0,0'], 'X')
        # 断言替换后的数组与预期结果数组相等
        assert_array_equal(r3, np.array(['X,X,X', 'X,0', 'X']))
    def test_rjust(self):
        # 断言:self.A.rjust(10) 的返回值的类型是 np.bytes_
        assert_(issubclass(self.A.rjust(10).dtype.type, np.bytes_))

        # 将 self.A.rjust([10, 20]) 的结果赋给 C
        C = self.A.rjust([10, 20])
        # 断言:np.char.str_len(C) 的结果应与 [[10, 20], [10, 20], [12, 20]] 相等
        assert_array_equal(np.char.str_len(C), [[10, 20], [10, 20], [12, 20]])

        # 将 self.A.rjust(20, b'#') 的结果赋给 C
        C = self.A.rjust(20, b'#')
        # 断言:C 中所有元素均以 b'#' 开头
        assert_(np.all(C.startswith(b'#')))
        # 断言:C 的元素以 [[False, True], [False, False], [False, False]] 结尾
        assert_array_equal(C.endswith(b'#'),
                           [[False, True], [False, False], [False, False]])

        # 将 np.char.rjust(b'FOO', [[10, 20], [15, 8]]) 的结果赋给 C
        C = np.char.rjust(b'FOO', [[10, 20], [15, 8]])
        # 预期的结果 tgt
        tgt = [[b'       FOO', b'                 FOO'],
               [b'            FOO', b'     FOO']]
        # 断言:C 的类型是 np.bytes_
        assert_(issubclass(C.dtype.type, np.bytes_))
        # 断言:C 应与 tgt 相等
        assert_array_equal(C, tgt)

    def test_rpartition(self):
        # 将 self.A.rpartition([b'3', b'M']) 的结果赋给 P
        P = self.A.rpartition([b'3', b'M'])
        # 预期的结果 tgt
        tgt = [[(b'', b'', b' abc '), (b'', b'', b'')],
               [(b'12', b'3', b'45'), (b'', b'M', b'ixedCase')],
               [(b'123 \t ', b'3', b'45 \0 '), (b'', b'', b'UPPER')]]
        # 断言:P 的类型是 np.bytes_
        assert_(issubclass(P.dtype.type, np.bytes_))
        # 断言:P 应与 tgt 相等
        assert_array_equal(P, tgt)

    def test_rsplit(self):
        # 将 self.A.rsplit(b'3') 的结果赋给 A
        A = self.A.rsplit(b'3')
        # 预期的结果 tgt
        tgt = [[[b' abc '], [b'']],
               [[b'12', b'45'], [b'MixedCase']],
               [[b'12', b' \t ', b'45 \x00 '], [b'UPPER']]]
        # 断言:A 的类型是 np.object_
        assert_(issubclass(A.dtype.type, np.object_))
        # 断言:A 应与 tgt 相等
        assert_equal(A.tolist(), tgt)

    def test_rstrip(self):
        # 断言:self.A.rstrip() 的返回值的类型是 np.bytes_
        assert_(issubclass(self.A.rstrip().dtype.type, np.bytes_))

        # 预期的结果 tgt
        tgt = [[b' abc', b''],
               [b'12345', b'MixedCase'],
               [b'123 \t 345', b'UPPER']]
        # 断言:self.A.rstrip() 应与 tgt 相等
        assert_array_equal(self.A.rstrip(), tgt)

        # 预期的结果 tgt
        tgt = [[b' abc ', b''],
               [b'1234', b'MixedCase'],
               [b'123 \t 345 \x00', b'UPP']
               ]
        # 断言:self.A.rstrip([b'5', b'ER']) 应与 tgt 相等
        assert_array_equal(self.A.rstrip([b'5', b'ER']), tgt)

        # 预期的结果 tgt
        tgt = [[' \u03a3', ''],
               ['12345', 'MixedCase'],
               ['123 \t 345', 'UPPER']]
        # 断言:self.B.rstrip() 的返回值的类型是 np.str_
        assert_(issubclass(self.B.rstrip().dtype.type, np.str_))
        # 断言:self.B.rstrip() 应与 tgt 相等
        assert_array_equal(self.B.rstrip(), tgt)

    def test_strip(self):
        # 预期的结果 tgt
        tgt = [[b'abc', b''],
               [b'12345', b'MixedCase'],
               [b'123 \t 345', b'UPPER']]
        # 断言:self.A.strip() 的返回值的类型是 np.bytes_
        assert_(issubclass(self.A.strip().dtype.type, np.bytes_))
        # 断言:self.A.strip() 应与 tgt 相等
        assert_array_equal(self.A.strip(), tgt)

        # 预期的结果 tgt
        tgt = [[b' abc ', b''],
               [b'234', b'ixedCas'],
               [b'23 \t 345 \x00', b'UPP']]
        # 断言:self.A.strip([b'15', b'EReM']) 应与 tgt 相等
        assert_array_equal(self.A.strip([b'15', b'EReM']), tgt)

        # 预期的结果 tgt
        tgt = [['\u03a3', ''],
               ['12345', 'MixedCase'],
               ['123 \t 345', 'UPPER']]
        # 断言:self.B.strip() 的返回值的类型是 np.str_
        assert_(issubclass(self.B.strip().dtype.type, np.str_))
        # 断言:self.B.strip() 应与 tgt 相等
        assert_array_equal(self.B.strip(), tgt)

    def test_split(self):
        # 将 self.A.split(b'3') 的结果赋给 A
        A = self.A.split(b'3')
        # 预期的结果 tgt
        tgt = [
               [[b' abc '], [b'']],
               [[b'12', b'45'], [b'MixedCase']],
               [[b'12', b' \t ', b'45 \x00 '], [b'UPPER']]]
        # 断言:A 的类型是 np.object_
        assert_(issubclass(A.dtype.type, np.object_))
        # 断言:A 应与 tgt 相等
        assert_equal(A.tolist(), tgt)
    # 定义一个测试方法,测试 np.char.array 的 splitlines() 方法
    def test_splitlines(self):
        # 创建一个包含单个字符串 'abc\nfds\nwer' 的 np.char.array 对象,并对其调用 splitlines() 方法
        A = np.char.array(['abc\nfds\nwer']).splitlines()
        # 断言 A.dtype.type 是 np.object_ 的子类
        assert_(issubclass(A.dtype.type, np.object_))
        # 断言 A 的形状为 (1,)
        assert_(A.shape == (1,))
        # 断言 A[0] 的长度为 3
        assert_(len(A[0]) == 3)

    # 定义一个测试方法,测试 self.A 的 swapcase() 方法
    def test_swapcase(self):
        # 预期的目标结果列表,包含两个子列表,每个子列表包含两个 bytes 对象
        tgt = [[b' ABC ', b''],
               [b'12345', b'mIXEDcASE'],
               [b'123 \t 345 \0 ', b'upper']]
        # 断言 self.A.swapcase() 返回的数组的 dtype.type 是 np.bytes_ 的子类
        assert_(issubclass(self.A.swapcase().dtype.type, np.bytes_))
        # 断言 self.A.swapcase() 返回的数组与预期的 tgt 相等
        assert_array_equal(self.A.swapcase(), tgt)

        # 预期的目标结果列表,包含两个子列表,每个子列表包含两个字符串对象
        tgt = [[' \u03c3 ', ''],
               ['12345', 'mIXEDcASE'],
               ['123 \t 345 \0 ', 'upper']]
        # 断言 self.B.swapcase() 返回的数组的 dtype.type 是 np.str_ 的子类
        assert_(issubclass(self.B.swapcase().dtype.type, np.str_))
        # 断言 self.B.swapcase() 返回的数组与预期的 tgt 相等
        assert_array_equal(self.B.swapcase(), tgt)

    # 定义一个测试方法,测试 self.A 的 title() 方法
    def test_title(self):
        # 预期的目标结果列表,包含两个子列表,每个子列表包含两个 bytes 对象
        tgt = [[b' Abc ', b''],
               [b'12345', b'Mixedcase'],
               [b'123 \t 345 \0 ', b'Upper']]
        # 断言 self.A.title() 返回的数组的 dtype.type 是 np.bytes_ 的子类
        assert_(issubclass(self.A.title().dtype.type, np.bytes_))
        # 断言 self.A.title() 返回的数组与预期的 tgt 相等
        assert_array_equal(self.A.title(), tgt)

        # 预期的目标结果列表,包含两个子列表,每个子列表包含两个字符串对象
        tgt = [[' \u03a3 ', ''],
               ['12345', 'Mixedcase'],
               ['123 \t 345 \0 ', 'Upper']]
        # 断言 self.B.title() 返回的数组的 dtype.type 是 np.str_ 的子类
        assert_(issubclass(self.B.title().dtype.type, np.str_))
        # 断言 self.B.title() 返回的数组与预期的 tgt 相等
        assert_array_equal(self.B.title(), tgt)

    # 定义一个测试方法,测试 self.A 的 upper() 方法
    def test_upper(self):
        # 预期的目标结果列表,包含两个子列表,每个子列表包含两个 bytes 对象
        tgt = [[b' ABC ', b''],
               [b'12345', b'MIXEDCASE'],
               [b'123 \t 345 \0 ', b'UPPER']]
        # 断言 self.A.upper() 返回的数组的 dtype.type 是 np.bytes_ 的子类
        assert_(issubclass(self.A.upper().dtype.type, np.bytes_))
        # 断言 self.A.upper() 返回的数组与预期的 tgt 相等
        assert_array_equal(self.A.upper(), tgt)

        # 预期的目标结果列表,包含两个子列表,每个子列表包含两个字符串对象
        tgt = [[' \u03a3 ', ''],
               ['12345', 'MIXEDCASE'],
               ['123 \t 345 \0 ', 'UPPER']]
        # 断言 self.B.upper() 返回的数组的 dtype.type 是 np.str_ 的子类
        assert_(issubclass(self.B.upper().dtype.type, np.str_))
        # 断言 self.B.upper() 返回的数组与预期的 tgt 相等
        assert_array_equal(self.B.upper(), tgt)

    # 定义一个测试方法,测试 self.B 的 isnumeric() 方法
    def test_isnumeric(self):

        # 定义一个内部函数 fail(),尝试调用 self.A 的 isnumeric() 方法,预期会抛出 TypeError 异常
        def fail():
            self.A.isnumeric()

        # 断言调用 fail() 会抛出 TypeError 异常
        assert_raises(TypeError, fail)
        # 断言 self.B.isnumeric() 返回的数组的 dtype.type 是 np.bool 的子类
        assert_(issubclass(self.B.isnumeric().dtype.type, np.bool))
        # 断言 self.B.isnumeric() 返回的数组与预期的二维 bool 数组相等
        assert_array_equal(self.B.isnumeric(), [
                [False, False], [True, False], [False, False]])

    # 定义一个测试方法,测试 self.B 的 isdecimal() 方法
    def test_isdecimal(self):

        # 定义一个内部函数 fail(),尝试调用 self.A 的 isdecimal() 方法,预期会抛出 TypeError 异常
        def fail():
            self.A.isdecimal()

        # 断言调用 fail() 会抛出 TypeError 异常
        assert_raises(TypeError, fail)
        # 断言 self.B.isdecimal() 返回的数组的 dtype.type 是 np.bool 的子类
        assert_(issubclass(self.B.isdecimal().dtype.type, np.bool))
        # 断言 self.B.isdecimal() 返回的数组与预期的二维 bool 数组相等
        assert_array_equal(self.B.isdecimal(), [
                [False, False], [True, False], [False, False]])
# 定义一个测试类 TestOperations
class TestOperations:
    
    # 在每个测试方法执行前调用,初始化数组 A 和 B
    def setup_method(self):
        self.A = np.array([['abc', '123'],
                           ['789', 'xyz']]).view(np.char.chararray)
        self.B = np.array([['efg', '456'],
                           ['051', 'tuv']]).view(np.char.chararray)

    # 测试字符串数组的加法操作
    def test_add(self):
        # 预期的加法结果 AB
        AB = np.array([['abcefg', '123456'],
                       ['789051', 'xyztuv']]).view(np.char.chararray)
        # 断言两个数组是否相等
        assert_array_equal(AB, (self.A + self.B))
        # 断言加法结果的第一个元素的第一个字符的长度是否为6
        assert_(len((self.A + self.B)[0][0]) == 6)

    # 测试字符串数组的右加操作
    def test_radd(self):
        # 预期的右加结果 QA
        QA = np.array([['qabc', 'q123'],
                       ['q789', 'qxyz']]).view(np.char.chararray)
        # 断言两个数组是否相等
        assert_array_equal(QA, ('q' + self.A))

    # 测试字符串数组的乘法操作
    def test_mul(self):
        A = self.A
        # 遍历不同的乘数 r 进行测试
        for r in (2, 3, 5, 7, 197):
            # 预期的乘法结果 Ar
            Ar = np.array([[A[0, 0]*r, A[0, 1]*r],
                           [A[1, 0]*r, A[1, 1]*r]]).view(np.char.chararray)
            # 断言两个数组是否相等
            assert_array_equal(Ar, (self.A * r))

        # 对于不支持的对象类型进行异常处理
        for ob in [object(), 'qrs']:
            # 断言抛出 ValueError 异常
            with assert_raises_regex(ValueError,
                                     'Can only multiply by integers'):
                A*ob

    # 测试字符串数组的右乘操作
    def test_rmul(self):
        A = self.A
        # 遍历不同的乘数 r 进行测试
        for r in (2, 3, 5, 7, 197):
            # 预期的右乘结果 Ar
            Ar = np.array([[A[0, 0]*r, A[0, 1]*r],
                           [A[1, 0]*r, A[1, 1]*r]]).view(np.char.chararray)
            # 断言两个数组是否相等
            assert_array_equal(Ar, (r * self.A))

        # 对于不支持的对象类型进行异常处理
        for ob in [object(), 'qrs']:
            # 断言抛出 ValueError 异常
            with assert_raises_regex(ValueError,
                                     'Can only multiply by integers'):
                ob * A

    # 测试字符串数组的模运算操作
    def test_mod(self):
        """Ticket #856"""
        # 定义格式化字符串数组 F 和整数数组 C
        F = np.array([['%d', '%f'], ['%s', '%r']]).view(np.char.chararray)
        C = np.array([[3, 7], [19, 1]], dtype=np.int64)
        # 预期的模运算结果 FC
        FC = np.array([['3', '7.000000'],
                       ['19', 'np.int64(1)']]).view(np.char.chararray)
        # 断言两个数组是否相等
        assert_array_equal(FC, F % C)

        # 定义格式化字符串数组 A 和整数 1
        A = np.array([['%.3f', '%d'], ['%s', '%r']]).view(np.char.chararray)
        # 预期的模运算结果 A1
        A1 = np.array([['1.000', '1'],
                       ['1', repr(np.array(1)[()])]]).view(np.char.chararray)
        # 断言两个数组是否相等
        assert_array_equal(A1, (A % 1))

        # 预期的模运算结果 A2
        A2 = np.array([['1.000', '2'],
                       ['3', repr(np.array(4)[()])]]).view(np.char.chararray)
        # 断言两个数组是否相等
        assert_array_equal(A2, (A % [[1, 2], [3, 4]]))

    # 测试字符串数组的右模运算操作
    def test_rmod(self):
        # 断言字符串数组 A 的字符串格式化结果是否等于其字符串表示形式
        assert_(("%s" % self.A) == str(self.A))
        # 断言字符串数组 A 的原始表示形式是否等于其 repr 表示形式
        assert_(("%r" % self.A) == repr(self.A))

        # 对于不支持的操作数类型进行异常处理
        for ob in [42, object()]:
            # 断言抛出 TypeError 异常
            with assert_raises_regex(
                    TypeError, "unsupported operand type.* and 'chararray'"):
                ob % self.A
    def test_slice(self):
        """Regression test for https://github.com/numpy/numpy/issues/5982"""
        
        # 创建一个二维的字符数组 arr,每个元素长度为 4,视图转换为 chararray 类型
        arr = np.array([['abc ', 'def '], ['geh ', 'ijk ']],
                       dtype='S4').view(np.char.chararray)
        
        # 对整个数组进行切片,sl1 是 arr 的一个视图
        sl1 = arr[:]
        # 断言 sl1 与 arr 相等
        assert_array_equal(sl1, arr)
        # 断言 sl1 的基础对象是 arr
        assert_(sl1.base is arr)
        # 断言 sl1 的基础对象的基础对象是 arr 的基础对象
        assert_(sl1.base.base is arr.base)

        # 对整个数组进行完整切片,sl2 是 arr 的一个视图
        sl2 = arr[:, :]
        # 断言 sl2 与 arr 相等
        assert_array_equal(sl2, arr)
        # 断言 sl2 的基础对象是 arr
        assert_(sl2.base is arr)
        # 断言 sl2 的基础对象的基础对象是 arr 的基础对象
        assert_(sl2.base.base is arr.base)

        # 断言 arr 的第一个元素的值是 b'abc'
        assert_(arr[0, 0] == b'abc')

    @pytest.mark.parametrize('data', [['plate', '   ', 'shrimp'],
                                      [b'retro', b'  ', b'encabulator']])
    def test_getitem_length_zero_item(self, data):
        # Regression test for gh-26375.
        
        # 使用 data 创建一个字符数组 a
        a = np.char.array(data)
        
        # a.dtype.type() 将是一个空字符串或者 bytes 实例。
        # 如果 a[1] 类型不正确或长度不为 0,相等性测试将失败。
        # 断言 a[1] 等于 a 的 dtype 的类型
        assert_equal(a[1], a.dtype.type())
class TestMethodsEmptyArray:
    # 设置每个测试方法的初始化方法
    def setup_method(self):
        # 创建一个空的 Unicode 类型的 NumPy 数组
        self.U = np.array([], dtype='U')
        # 创建一个空的字节串(Bytes)类型的 NumPy 数组
        self.S = np.array([], dtype='S')

    # 测试 np.char.encode 方法
    def test_encode(self):
        # 对空的 Unicode 数组进行编码
        res = np.char.encode(self.U)
        # 断言编码后结果与空列表相等
        assert_array_equal(res, [])
        # 断言编码后结果的数据类型为字节串
        assert_(res.dtype.char == 'S')

    # 测试 np.char.decode 方法
    def test_decode(self):
        # 对空的字节串数组进行解码
        res = np.char.decode(self.S)
        # 断言解码后结果与空列表相等
        assert_array_equal(res, [])
        # 断言解码后结果的数据类型为 Unicode
        assert_(res.dtype.char == 'U')

    # 测试带有 reshape 的 np.char.decode 方法
    def test_decode_with_reshape(self):
        # 对空的字节串数组进行 reshape 并解码
        res = np.char.decode(self.S.reshape((1, 0, 1)))
        # 断言结果数组的形状为 (1, 0, 1)
        assert_(res.shape == (1, 0, 1))


class TestMethodsScalarValues:
    # 测试 np.char.mod 方法
    def test_mod(self):
        # 创建一个字节串类型的 NumPy 数组 A
        A = np.array([[' abc ', ''],
                      ['12345', 'MixedCase'],
                      ['123 \t 345 \0 ', 'UPPER']], dtype='S')
        # 预期的目标数组 tgt
        tgt = [[b'123 abc ', b'123'],
               [b'12312345', b'123MixedCase'],
               [b'123123 \t 345 \0 ', b'123UPPER']]
        # 断言使用 np.char.mod 方法后得到的结果与预期的目标数组相等
        assert_array_equal(np.char.mod(b"123%s", A), tgt)

    # 测试 np.char.decode 方法
    def test_decode(self):
        # 给定一个字节串
        bytestring = b'\x81\xc1\x81\xc1\x81\xc1'
        # 使用 cp037 编码解码字节串,预期结果为 'aAaAaA'
        assert_equal(np.char.decode(bytestring, encoding='cp037'),
                     'aAaAaA')

    # 测试 np.char.encode 方法
    def test_encode(self):
        # 给定一个 Unicode 字符串
        unicode = 'aAaAaA'
        # 使用 cp037 编码 Unicode 字符串,预期结果为对应的字节串
        assert_equal(np.char.encode(unicode, encoding='cp037'),
                     b'\x81\xc1\x81\xc1\x81\xc1')

    # 测试 np.char.expandtabs 方法
    def test_expandtabs(self):
        # 给定一个带有制表符的字符串 s
        s = "\tone level of indentation\n\t\ttwo levels of indentation"
        # 断言使用 np.char.expandtabs 方法后的结果与预期的字符串相等
        assert_equal(
            np.char.expandtabs(s, tabsize=2),
            "  one level of indentation\n    two levels of indentation"
        )

    # 测试 np.char.join 方法
    def test_join(self):
        # 给定分隔符数组 seps 和字符串 'hello'
        seps = np.array(['-', '_'])
        # 断言使用 np.char.join 方法后的结果与预期的列表相等
        assert_array_equal(np.char.join(seps, 'hello'),
                           ['h-e-l-l-o', 'h_e_l_l_o'])

    # 测试 np.char.partition 方法
    def test_partition(self):
        # 断言使用 np.char.partition 方法后的结果与预期的列表相等
        assert_equal(np.char.partition('This string', ' '),
                     ['This', ' ', 'string'])

    # 测试 np.char.rpartition 方法
    def test_rpartition(self):
        # 断言使用 np.char.rpartition 方法后的结果与预期的列表相等
        assert_equal(np.char.rpartition('This string here', ' '),
                     ['This string', ' ', 'here'])

    # 测试 np.char.replace 方法
    def test_replace(self):
        # 断言使用 np.char.replace 方法后的结果与预期的字符串相等
        assert_equal(np.char.replace('Python is good', 'good', 'great'),
                     'Python is great')


def test_empty_indexing():
    """Regression test for ticket 1948."""
    # 检查使用空列表索引 chararray 是否返回空的 chararray 而不是包含一个空字符串的 chararray
    s = np.char.chararray((4,))
    assert_(s[[]].size == 0)

.\numpy\numpy\_core\tests\test_deprecations.py

"""
Tests related to deprecation warnings. Also a convenient place
to document how deprecations should eventually be turned into errors.

"""
# 导入必要的库和模块
import datetime  # 导入日期时间模块
import operator  # 导入操作符模块
import warnings  # 导入警告模块
import pytest  # 导入 pytest 测试框架
import tempfile  # 导入临时文件模块
import re  # 导入正则表达式模块
import sys  # 导入系统相关模块

# 导入 numpy 库及其测试相关的模块和函数
import numpy as np
from numpy.testing import (
    assert_raises, assert_warns, assert_, assert_array_equal, SkipTest,
    KnownFailureException, break_cycles, temppath
    )

# 导入 C API 相关测试模块
from numpy._core._multiarray_tests import fromstring_null_term_c_api

# 尝试导入 pytz 库,标记是否成功
try:
    import pytz
    _has_pytz = True
except ImportError:
    _has_pytz = False


class _DeprecationTestCase:
    # 警告信息的起始部分必须匹配,因为 warnings 使用了 re.match
    message = ''
    warning_cls = DeprecationWarning

    def setup_method(self):
        # 捕获警告信息并记录
        self.warn_ctx = warnings.catch_warnings(record=True)
        self.log = self.warn_ctx.__enter__()

        # 不要忽略其它 DeprecationWarnings,因为忽略可能导致混乱
        warnings.filterwarnings("always", category=self.warning_cls)
        warnings.filterwarnings("always", message=self.message,
                                category=self.warning_cls)

    def teardown_method(self):
        # 退出警告记录环境
        self.warn_ctx.__exit__()

    def assert_not_deprecated(self, function, args=(), kwargs={}):
        """Test that warnings are not raised.

        This is just a shorthand for:

        self.assert_deprecated(function, num=0, ignore_others=True,
                        exceptions=tuple(), args=args, kwargs=kwargs)
        """
        # 断言不会出现警告
        self.assert_deprecated(function, num=0, ignore_others=True,
                        exceptions=tuple(), args=args, kwargs=kwargs)


class _VisibleDeprecationTestCase(_DeprecationTestCase):
    # 使用可见性 DeprecationWarning 类
    warning_cls = np.exceptions.VisibleDeprecationWarning


class TestDTypeAttributeIsDTypeDeprecation(_DeprecationTestCase):
    # 警告消息指定 `dtype` 属性已废弃,从 NumPy 1.21 开始
    message = r".*`.dtype` attribute"

    def test_deprecation_dtype_attribute_is_dtype(self):
        # 定义两个测试类,验证其 dtype 属性是否会触发警告
        class dt:
            dtype = "f8"

        class vdt(np.void):
            dtype = "f,f"

        # 断言各种情况下会触发 dtype 相关警告
        self.assert_deprecated(lambda: np.dtype(dt))
        self.assert_deprecated(lambda: np.dtype(dt()))
        self.assert_deprecated(lambda: np.dtype(vdt))
        self.assert_deprecated(lambda: np.dtype(vdt(1)))


class TestTestDeprecated:
    # 这里测试的部分被省略,需要继续添加注释
    # 定义一个测试方法 test_assert_deprecated,用于测试断言是否捕获了弃用警告
    def test_assert_deprecated(self):
        # 创建 _DeprecationTestCase 的实例
        test_case_instance = _DeprecationTestCase()
        # 调用 setup_method 方法来设置测试环境
        test_case_instance.setup_method()
        # 断言捕获 Assertion 错误,期望 test_case_instance.assert_deprecated 调用时会引发 AssertionError
        assert_raises(AssertionError,
                      test_case_instance.assert_deprecated,
                      lambda: None)

        # 定义一个函数 foo,该函数会发出一个 DeprecationWarning 警告
        def foo():
            warnings.warn("foo", category=DeprecationWarning, stacklevel=2)

        # 调用 test_case_instance.assert_deprecated 来检查是否捕获 foo 函数发出的弃用警告
        test_case_instance.assert_deprecated(foo)
        # 调用 teardown_method 方法来清理测试环境
        test_case_instance.teardown_method()
# _DeprecationTestCase 的子类,用于测试 ndarray.conjugate 在非数值数据类型上的行为
class TestNonNumericConjugate(_DeprecationTestCase):

    """
    Deprecate no-op behavior of ndarray.conjugate on non-numeric dtypes,
    which conflicts with the error behavior of np.conjugate.
    """

    # 测试 ndarray.conjugate 方法的行为
    def test_conjugate(self):
        # 对于 np.array(5) 和 np.array(5j),检查其不应该被弃用
        for a in np.array(5), np.array(5j):
            self.assert_not_deprecated(a.conjugate)
        # 对于 np.array('s'), np.array('2016', 'M'), np.array((1, 2), [('a', int), ('b', int)]),
        # 检查其应该被弃用
        for a in (np.array('s'), np.array('2016', 'M'),
                np.array((1, 2), [('a', int), ('b', int)])):
            self.assert_deprecated(a.conjugate)


# _DeprecationTestCase 的子类,测试 datetime 和 timedelta 的事件
class TestDatetimeEvent(_DeprecationTestCase):

    # 2017-08-11, 1.14.0
    # 测试 3 元组的情况
    def test_3_tuple(self):
        # 对于 np.datetime64 和 np.timedelta64,检查其应该不被弃用
        for cls in (np.datetime64, np.timedelta64):
            # 两种有效用法 - (unit, num) 和 (unit, num, den, None)
            self.assert_not_deprecated(cls, args=(1, ('ms', 2)))
            self.assert_not_deprecated(cls, args=(1, ('ms', 2, 1, None)))

            # 尝试使用事件参数,在 1.7.0 版本中被移除,因此应该被弃用
            # 事件参数曾经是 uint8 类型
            self.assert_deprecated(cls, args=(1, ('ms', 2, 'event')))
            self.assert_deprecated(cls, args=(1, ('ms', 2, 63)))
            self.assert_deprecated(cls, args=(1, ('ms', 2, 1, 'event')))
            self.assert_deprecated(cls, args=(1, ('ms', 2, 1, 63)))


# _DeprecationTestCase 的子类,测试空数组的真值测试
class TestTruthTestingEmptyArrays(_DeprecationTestCase):

    # 2017-09-25, 1.14.0
    message = '.*truth value of an empty array is ambiguous.*'

    # 测试 1 维数组的情况
    def test_1d(self):
        # 检查空数组的布尔值测试应该被弃用
        self.assert_deprecated(bool, args=(np.array([]),))

    # 测试 2 维数组的情况
    def test_2d(self):
        # 检查不同形状的空数组的布尔值测试应该被弃用
        self.assert_deprecated(bool, args=(np.zeros((1, 0)),))
        self.assert_deprecated(bool, args=(np.zeros((0, 1)),))
        self.assert_deprecated(bool, args=(np.zeros((0, 0)),))


# _DeprecationTestCase 的子类,测试 np.bincount 方法的 minlength 参数
class TestBincount(_DeprecationTestCase):

    # 2017-06-01, 1.14.0
    # 测试 bincount 方法的 minlength 参数
    def test_bincount_minlength(self):
        # 检查使用 None 作为 minlength 参数的情况应该被弃用
        self.assert_deprecated(lambda: np.bincount([1, 2, 3], minlength=None))


# _DeprecationTestCase 的子类,测试生成器作为 np.sum 的输入
class TestGeneratorSum(_DeprecationTestCase):

    # 2018-02-25, 1.15.0
    # 测试生成器作为 np.sum 的输入
    def test_generator_sum(self):
        # 检查使用生成器作为输入的情况应该被弃用
        self.assert_deprecated(np.sum, args=((i for i in range(5)),))


# _DeprecationTestCase 的子类,测试 np.fromstring 方法的使用
class TestFromstring(_DeprecationTestCase):

    # 2017-10-19, 1.14
    # 测试 fromstring 方法的使用
    def test_fromstring(self):
        # 检查使用 fromstring 方法的情况应该被弃用
        self.assert_deprecated(np.fromstring, args=('\x00'*80,))


# _DeprecationTestCase 的子类,测试无效数据作为字符串或文件输入的情况
class TestFromStringAndFileInvalidData(_DeprecationTestCase):

    # 2019-06-08, 1.17.0
    # 测试字符串或文件输入无法完全读取的情况
    # 当弃用完成后,这些测试应该被移到实际测试中
    message = "string or file could not be read to its end"

    @pytest.mark.parametrize("invalid_str", [",invalid_data", "invalid_sep"])
    # 定义一个测试方法,用于测试处理不可解析数据文件时的行为
    def test_deprecate_unparsable_data_file(self, invalid_str):
        # 创建一个包含浮点数的 NumPy 数组
        x = np.array([1.51, 2, 3.51, 4], dtype=float)

        # 使用临时文件来操作数据
        with tempfile.TemporaryFile(mode="w") as f:
            # 将数组 x 的数据以指定格式写入到临时文件 f 中,数据项之间用逗号分隔,保留两位小数
            x.tofile(f, sep=',', format='%.2f')
            # 向临时文件 f 中写入不可解析的字符串 invalid_str
            f.write(invalid_str)

            # 将文件指针移动到文件开头
            f.seek(0)
            # 使用 assert_deprecated 方法断言调用 np.fromfile 时会发出 DeprecationWarning
            self.assert_deprecated(lambda: np.fromfile(f, sep=","))
            
            # 将文件指针移动到文件开头
            f.seek(0)
            # 使用 assert_deprecated 方法断言调用 np.fromfile 时会发出 DeprecationWarning,并指定读取的数据项数目为 5
            self.assert_deprecated(lambda: np.fromfile(f, sep=",", count=5))
            
            # 不应该触发警告:
            with warnings.catch_warnings():
                warnings.simplefilter("error", DeprecationWarning)
                # 将文件指针移动到文件开头
                f.seek(0)
                # 读取临时文件 f 中的数据,数据项之间用逗号分隔,期望的数据项数目为 4,将结果与数组 x 进行比较
                res = np.fromfile(f, sep=",", count=4)
                assert_array_equal(res, x)

    # 使用参数化测试来测试处理不可解析的字符串时的行为
    @pytest.mark.parametrize("invalid_str", [",invalid_data", "invalid_sep"])
    def test_deprecate_unparsable_string(self, invalid_str):
        # 创建一个包含浮点数的 NumPy 数组
        x = np.array([1.51, 2, 3.51, 4], dtype=float)
        # 创建一个包含不可解析字符串的字符串 x_str
        x_str = "1.51,2,3.51,4{}".format(invalid_str)

        # 使用 assert_deprecated 方法断言调用 np.fromstring 时会发出 DeprecationWarning
        self.assert_deprecated(lambda: np.fromstring(x_str, sep=","))

        # 使用 assert_deprecated 方法断言调用 np.fromstring 时会发出 DeprecationWarning,并指定读取的数据项数目为 5
        self.assert_deprecated(lambda: np.fromstring(x_str, sep=",", count=5))

        # 测试 C 语言级别的 API,使用 0 结尾的字符串来创建数组
        bytestr = x_str.encode("ascii")
        self.assert_deprecated(lambda: fromstring_null_term_c_api(bytestr))

        # 使用 assert_warns 方法断言调用 np.fromstring 时会产生警告,警告类型为 DeprecationWarning
        with assert_warns(DeprecationWarning):
            # 调用 np.fromstring 从字符串 x_str 中读取数据,数据项之间用逗号分隔,期望的数据项数目为 5,
            # 将结果与数组 x 进行比较,排除最后一个元素
            res = np.fromstring(x_str, sep=",", count=5)
            assert_array_equal(res[:-1], x)

        # 关闭 DeprecationWarning 警告,并确保不会触发其它警告
        with warnings.catch_warnings():
            warnings.simplefilter("error", DeprecationWarning)

            # 不应该触发警告:
            # 调用 np.fromstring 从字符串 x_str 中读取数据,数据项之间用逗号分隔,期望的数据项数目为 4,
            # 将结果与数组 x 进行比较
            res = np.fromstring(x_str, sep=",", count=4)
            assert_array_equal(res, x)
# 继承自 _DeprecationTestCase 的测试类,用于测试 tostring() 方法的废弃情况
class TestToString(_DeprecationTestCase):
    # 设置错误消息,使用 re.escape 转义特殊字符
    message = re.escape("tostring() is deprecated. Use tobytes() instead.")

    # 测试 tostring() 方法的废弃情况
    def test_tostring(self):
        # 创建一个包含特定字节序列的 numpy 数组
        arr = np.array(list(b"test\xFF"), dtype=np.uint8)
        # 断言使用了废弃的 tostring() 方法
        self.assert_deprecated(arr.tostring)

    # 测试 tostring() 方法与 tobytes() 方法是否匹配
    def test_tostring_matches_tobytes(self):
        # 创建一个包含特定字节序列的 numpy 数组
        arr = np.array(list(b"test\xFF"), dtype=np.uint8)
        # 将数组转换为字节流
        b = arr.tobytes()
        # 使用 assert_warns 检测 DeprecationWarning
        with assert_warns(DeprecationWarning):
            # 使用废弃的 tostring() 方法
            s = arr.tostring()
        # 断言 tostring() 方法的结果与 tobytes() 方法的结果相同
        assert s == b


# 继承自 _DeprecationTestCase 的测试类,用于测试类型强制转换的废弃情况
class TestDTypeCoercion(_DeprecationTestCase):
    # 设置错误消息,指出正在废弃的类型转换情况
    message = "Converting .* to a dtype .*is deprecated"
    # 包含所有废弃类型的列表
    deprecated_types = [
        # 内置的标量超类型:
        np.generic, np.flexible, np.number,
        np.inexact, np.floating, np.complexfloating,
        np.integer, np.unsignedinteger, np.signedinteger,
        # 字符串类型是一个特殊的 S1 废弃案例:
        np.character,
    ]

    # 测试各种类型的强制转换是否废弃
    def test_dtype_coercion(self):
        for scalar_type in self.deprecated_types:
            # 断言废弃类型转换
            self.assert_deprecated(np.dtype, args=(scalar_type,))

    # 测试数组构造中的类型强制转换是否废弃
    def test_array_construction(self):
        for scalar_type in self.deprecated_types:
            # 断言数组构造中的废弃类型转换
            self.assert_deprecated(np.array, args=([], scalar_type,))

    # 测试未废弃的类型转换
    def test_not_deprecated(self):
        # 所有特定类型都未废弃:
        for group in np._core.sctypes.values():
            for scalar_type in group:
                # 断言未废弃的类型转换
                self.assert_not_deprecated(np.dtype, args=(scalar_type,))

        # 对于如 type、dict、list、tuple 等典型的 Python 类型,当前被强制转换为对象:
        for scalar_type in [type, dict, list, tuple]:
            # 断言未废弃的类型转换
            self.assert_not_deprecated(np.dtype, args=(scalar_type,))


# 继承自 _DeprecationTestCase 的测试类,用于测试复数类型的 round() 方法的废弃情况
class BuiltInRoundComplexDType(_DeprecationTestCase):
    # 包含废弃复数类型的列表
    deprecated_types = [np.csingle, np.cdouble, np.clongdouble]
    # 包含未废弃的数值类型的列表
    not_deprecated_types = [
        np.int8, np.int16, np.int32, np.int64,
        np.uint8, np.uint16, np.uint32, np.uint64,
        np.float16, np.float32, np.float64,
    ]

    # 测试废弃的复数类型的 round() 方法
    def test_deprecated(self):
        for scalar_type in self.deprecated_types:
            scalar = scalar_type(0)
            # 断言废弃的 round() 方法
            self.assert_deprecated(round, args=(scalar,))
            self.assert_deprecated(round, args=(scalar, 0))
            self.assert_deprecated(round, args=(scalar,), kwargs={'ndigits': 0})

    # 测试未废弃的数值类型的 round() 方法
    def test_not_deprecated(self):
        for scalar_type in self.not_deprecated_types:
            scalar = scalar_type(0)
            # 断言未废弃的 round() 方法
            self.assert_not_deprecated(round, args=(scalar,))
            self.assert_not_deprecated(round, args=(scalar, 0))
            self.assert_not_deprecated(round, args=(scalar,), kwargs={'ndigits': 0})


# 继承自 _DeprecationTestCase 的测试类,用于测试在高级索引中空结果的错误情况
class TestIncorrectAdvancedIndexWithEmptyResult(_DeprecationTestCase):
    # 错误消息,指出已经不再忽略的越界索引
    message = "Out of bound index found. This was previously ignored.*"

    # 使用 pytest.mark.parametrize 注入参数化的测试索引
    @pytest.mark.parametrize("index", [([3, 0],), ([0, 0], [3, 0])])
    # 定义一个测试方法,用于测试空子空间情况
    def test_empty_subspace(self, index):
        # 测试单个和多个高级索引的情况
        # 这些情况在未来可能会引发 IndexError
        arr = np.ones((2, 2, 0))
        # 断言 arr.__getitem__ 方法会被弃用
        self.assert_deprecated(arr.__getitem__, args=(index,))
        # 断言 arr.__setitem__ 方法会被弃用,将 index 作为参数
        self.assert_deprecated(arr.__setitem__, args=(index, 0.))

        # 对于这个数组,子空间只有在应用切片后才为空
        arr2 = np.ones((2, 2, 1))
        # 创建一个新的索引 index2,通过在开头插入 slice(0, 0) 来扩展 index
        index2 = (slice(0, 0),) + index
        # 断言 arr2.__getitem__ 方法会被弃用,将 index2 作为参数
        self.assert_deprecated(arr2.__getitem__, args=(index2,))
        # 断言 arr2.__setitem__ 方法会被弃用,将 index2 和 0 作为参数
        self.assert_deprecated(arr2.__setitem__, args=(index2, 0.))

    # 定义一个测试方法,用于测试空索引广播不会被弃用的情况
    def test_empty_index_broadcast_not_deprecated(self):
        arr = np.ones((2, 2, 2))

        # 定义一个广播索引 index,将会产生一个空的结果
        index = ([[3], [2]], [])  # broadcast to an empty result.
        # 断言 arr.__getitem__ 方法不会被弃用,将 index 作为参数
        self.assert_not_deprecated(arr.__getitem__, args=(index,))
        # 断言 arr.__setitem__ 方法不会被弃用,将 index 和空数组作为参数
        self.assert_not_deprecated(arr.__setitem__,
                                   args=(index, np.empty((2, 0, 2))))
class TestNonExactMatchDeprecation(_DeprecationTestCase):
    # 继承自 _DeprecationTestCase 的测试类,测试非精确匹配的警告信息
    def test_non_exact_match(self):
        # 创建一个二维 NumPy 数组
        arr = np.array([[3, 6, 6], [4, 5, 1]])
        # 断言检查:期望在使用 np.ravel_multi_index 函数时产生警告,参数 mode 的值拼写错误
        self.assert_deprecated(lambda: np.ravel_multi_index(arr, (7, 6), mode='Cilp'))
        # 断言检查:期望在使用 np.searchsorted 函数时产生警告,参数 side 的值以 'R' 开头完全不同
        self.assert_deprecated(lambda: np.searchsorted(arr[0], 4, side='Random'))


class TestMatrixInOuter(_DeprecationTestCase):
    # 继承自 _DeprecationTestCase 的测试类,测试 np.matrix 在 np.add.outer 中的警告
    # 2020-05-13 NumPy 1.20.0
    message = (r"add.outer\(\) was passed a numpy matrix as "
               r"(first|second) argument.")

    def test_deprecated(self):
        # 创建一个一维 NumPy 数组
        arr = np.array([1, 2, 3])
        # 创建一个 np.matrix 对象
        m = np.array([1, 2, 3]).view(np.matrix)
        # 断言检查:期望在使用 np.add.outer 函数时产生警告,传递 np.matrix 对象作为参数
        self.assert_deprecated(np.add.outer, args=(m, m), num=2)
        self.assert_deprecated(np.add.outer, args=(arr, m))
        self.assert_deprecated(np.add.outer, args=(m, arr))
        # 断言检查:期望在使用 np.add.outer 函数时不会产生警告,传递两个一维数组作为参数
        self.assert_not_deprecated(np.add.outer, args=(arr, arr))


class FlatteningConcatenateUnsafeCast(_DeprecationTestCase):
    # 继承自 _DeprecationTestCase 的测试类,测试使用 axis=None 的 np.concatenate 的警告
    # NumPy 1.20, 2020-09-03
    message = "concatenate with `axis=None` will use same-kind casting"

    def test_deprecated(self):
        # 断言检查:期望在使用 np.concatenate 函数时产生警告,设置 axis=None 并使用不安全的类型转换
        self.assert_deprecated(np.concatenate,
                args=(([0.], [1.]),),
                kwargs=dict(axis=None, out=np.empty(2, dtype=np.int64)))

    def test_not_deprecated(self):
        # 断言检查:期望在使用 np.concatenate 函数时不会产生警告,设置 axis=None 并使用安全的类型转换
        self.assert_not_deprecated(np.concatenate,
                args=(([0.], [1.]),),
                kwargs={'axis': None, 'out': np.empty(2, dtype=np.int64),
                        'casting': "unsafe"})

        # 使用 assert_raises 检查是否会抛出 TypeError 异常
        with assert_raises(TypeError):
            # 断言检查:确保在传递参数时首先会注意到警告
            np.concatenate(([0.], [1.]), out=np.empty(2, dtype=np.int64),
                           casting="same_kind")


class TestDeprecatedUnpickleObjectScalar(_DeprecationTestCase):
    # 继承自 _DeprecationTestCase 的测试类,测试反序列化 numpy 对象标量的警告
    # Deprecated 2020-11-24, NumPy 1.20
    """
    技术上,应该不可能创建 numpy 对象标量,但存在一个理论上允许的反序列化路径。该路径无效且必须导致警告。
    """
    message = "Unpickling a scalar with object dtype is deprecated."

    def test_deprecated(self):
        # lambda 函数测试:使用 np._core.multiarray.scalar 创建一个具有对象类型的标量,期望产生警告
        ctor = np._core.multiarray.scalar
        self.assert_deprecated(lambda: ctor(np.dtype("O"), 1))


class TestSingleElementSignature(_DeprecationTestCase):
    # 继承自 _DeprecationTestCase 的测试类,测试使用长度为1的签名的警告
    # Deprecated 2021-04-01, NumPy 1.21
    message = r"The use of a length 1"

    def test_deprecated(self):
        # lambda 函数测试:使用 np.add 函数时,使用长度为 1 的签名,期望产生警告
        self.assert_deprecated(lambda: np.add(1, 2, signature="d"))
        self.assert_deprecated(lambda: np.add(1, 2, sig=(np.dtype("l"),)))


class TestCtypesGetter(_DeprecationTestCase):
    # 继承自 _DeprecationTestCase 的测试类,测试使用 ctypes 获取属性的警告
    # Deprecated 2021-05-18, Numpy 1.21.0
    warning_cls = DeprecationWarning
    ctypes = np.array([1]).ctypes

    @pytest.mark.parametrize(
        "name", ["get_data", "get_shape", "get_strides", "get_as_parameter"]
    )
    # 定义一个测试方法,用于测试废弃的功能是否触发了警告
    def test_deprecated(self, name: str) -> None:
        # 获取self.ctypes对象中名为name的属性或方法,并赋值给func变量
        func = getattr(self.ctypes, name)
        # 断言调用func()函数会触发废弃警告
        self.assert_deprecated(lambda: func())
    
    # 使用pytest的参数化装饰器,定义多个参数化测试用例,每个用例测试一个属性名
    @pytest.mark.parametrize(
        "name", ["data", "shape", "strides", "_as_parameter_"]
    )
    # 定义一个测试方法,用于测试未废弃的功能是否没有触发警告
    def test_not_deprecated(self, name: str) -> None:
        # 断言调用self.ctypes对象中名为name的属性或方法不会触发废弃警告
        self.assert_not_deprecated(lambda: getattr(self.ctypes, name))
# 定义一个字典,包含了不同的分区方法和函数作为值,用于测试
PARTITION_DICT = {
    "partition method": np.arange(10).partition,
    "argpartition method": np.arange(10).argpartition,
    "partition function": lambda kth: np.partition(np.arange(10), kth),
    "argpartition function": lambda kth: np.argpartition(np.arange(10), kth),
}

# 使用 pytest 的参数化装饰器,对 PARTITION_DICT 中的每个值进行单独的测试
@pytest.mark.parametrize("func", PARTITION_DICT.values(), ids=PARTITION_DICT)
class TestPartitionBoolIndex(_DeprecationTestCase):
    # Deprecated 2021-09-29, NumPy 1.22
    # 使用 DeprecationWarning 来标记测试中的弃用警告
    warning_cls = DeprecationWarning
    message = "Passing booleans as partition index is deprecated"

    # 测试用例,检查传递布尔值作为分区索引是否会触发弃用警告
    def test_deprecated(self, func):
        self.assert_deprecated(lambda: func(True))
        self.assert_deprecated(lambda: func([False, True]))

    # 测试用例,检查传递其他类型参数是否不会触发弃用警告
    def test_not_deprecated(self, func):
        self.assert_not_deprecated(lambda: func(1))
        self.assert_not_deprecated(lambda: func([0, 1]))


class TestMachAr(_DeprecationTestCase):
    # Deprecated 2022-11-22, NumPy 1.25
    # 使用 DeprecationWarning 来标记测试中的弃用警告
    warning_cls = DeprecationWarning

    # 测试用例,检查模块中的 MachAr 是否会触发弃用警告
    def test_deprecated_module(self):
        self.assert_deprecated(lambda: getattr(np._core, "MachAr"))


class TestQuantileInterpolationDeprecation(_DeprecationTestCase):
    # Deprecated 2021-11-08, NumPy 1.22
    # 使用 pytest 的参数化装饰器,对多个函数进行测试
    @pytest.mark.parametrize("func",
        [np.percentile, np.quantile, np.nanpercentile, np.nanquantile])
    def test_deprecated(self, func):
        # 检查使用线性插值时是否会触发弃用警告
        self.assert_deprecated(
            lambda: func([0., 1.], 0., interpolation="linear"))
        # 检查使用最近邻插值时是否会触发弃用警告
        self.assert_deprecated(
            lambda: func([0., 1.], 0., interpolation="nearest"))

    # 测试用例,检查在使用两个特定参数时是否会引发 TypeError 异常
    @pytest.mark.parametrize("func",
            [np.percentile, np.quantile, np.nanpercentile, np.nanquantile])
    def test_both_passed(self, func):
        with warnings.catch_warnings():
            # 设置警告过滤器以捕获 DeprecationWarning
            warnings.simplefilter("always", DeprecationWarning)
            # 检查是否会因 method 参数而触发 TypeError 异常
            with pytest.raises(TypeError):
                func([0., 1.], 0., interpolation="nearest", method="nearest")


class TestArrayFinalizeNone(_DeprecationTestCase):
    # 使用 DeprecationWarning 来标记测试中的弃用警告
    message = "Setting __array_finalize__ = None"

    # 测试用例,检查设置 __array_finalize__ = None 是否会触发弃用警告
    def test_use_none_is_deprecated(self):
        # 定义一个没有 __array_finalize__ 方法的 ndarray 子类
        class NoFinalize(np.ndarray):
            __array_finalize__ = None

        # 检查创建视图时是否会触发弃用警告
        self.assert_deprecated(lambda: np.array(1).view(NoFinalize))


class TestLoadtxtParseIntsViaFloat(_DeprecationTestCase):
    # Deprecated 2022-07-03, NumPy 1.23
    # 本测试在弃用后可以移除而不需要替换。
    # 此处的消息文本指定了一个需要移除的警告过滤器。
    message = r"loadtxt\(\): Parsing an integer via a float is deprecated.*"

    # 使用 pytest 的参数化装饰器,对多种整数数据类型进行测试
    @pytest.mark.parametrize("dtype", np.typecodes["AllInteger"])
    # 在单元测试中,测试特定数据类型的 `np.loadtxt` 函数是否会触发 DeprecationWarning
    def test_deprecated_warning(self, dtype):
        # 使用 pytest 的 warn 断言来检查是否会触发 DeprecationWarning,并且匹配特定的警告信息
        with pytest.warns(DeprecationWarning, match=self.message):
            # 调用 np.loadtxt 以特定的数据类型读取文件 ["10.5"]
            np.loadtxt(["10.5"], dtype=dtype)

    # 使用 pytest 的参数化标记,测试所有整数类型的 dtype
    @pytest.mark.parametrize("dtype", np.typecodes["AllInteger"])
    def test_deprecated_raised(self, dtype):
        # DeprecationWarning 在抛出时会被链接,因此需要手动测试:
        with warnings.catch_warnings():
            # 设置警告过滤器,将 DeprecationWarning 设置为错误,这样会抛出异常
            warnings.simplefilter("error", DeprecationWarning)
            try:
                # 尝试调用 np.loadtxt 以特定的数据类型读取文件 ["10.5"]
                np.loadtxt(["10.5"], dtype=dtype)
            except ValueError as e:
                # 检查异常是否是 DeprecationWarning 引起的
                assert isinstance(e.__cause__, DeprecationWarning)
class TestScalarConversion(_DeprecationTestCase):
    # 2023-01-02, 1.25.0
    # 此类测试标量类型转换的过时行为

    def test_float_conversion(self):
        # 断言 float 的行为已被弃用,传入参数为 numpy 数组 [3.14]
        self.assert_deprecated(float, args=(np.array([3.14]),))

    def test_behaviour(self):
        # 测试特定行为
        b = np.array([[3.14]])
        c = np.zeros(5)
        with pytest.warns(DeprecationWarning):
            # 通过向 c[0] 赋值 b 的值来触发警告
            c[0] = b

class TestPyIntConversion(_DeprecationTestCase):
    message = r".*stop allowing conversion of out-of-bound.*"
    # 此类测试 Python 整数转换的过时行为,消息模式指示出界转换的停止

    @pytest.mark.parametrize("dtype", np.typecodes["AllInteger"])
    def test_deprecated_scalar(self, dtype):
        # 测试特定类型的标量行为
        dtype = np.dtype(dtype)
        info = np.iinfo(dtype)

        # Cover the most common creation paths (all end up in the
        # same place):
        # 覆盖最常见的创建路径(都会最终到达相同的地方)
        def scalar(value, dtype):
            # 创建标量值
            dtype.type(value)

        def assign(value, dtype):
            # 分配值给数组元素
            arr = np.array([0, 0, 0], dtype=dtype)
            arr[2] = value

        def create(value, dtype):
            # 创建包含值的数组
            np.array([value], dtype=dtype)

        for creation_func in [scalar, assign, create]:
            try:
                self.assert_deprecated(
                        lambda: creation_func(info.min - 1, dtype))
            except OverflowError:
                pass  # OverflowErrors always happened also before and are OK.

            try:
                self.assert_deprecated(
                        lambda: creation_func(info.max + 1, dtype))
            except OverflowError:
                pass  # OverflowErrors always happened also before and are OK.


@pytest.mark.parametrize("name", ["str", "bytes", "object"])
def test_future_scalar_attributes(name):
    # FutureWarning added 2022-11-17, NumPy 1.24,
    # 测试未来标量属性的警告,这些属性在未来版本中可能会被移除
    assert name not in dir(np)  # we may want to not add them
    with pytest.warns(FutureWarning,
            match=f"In the future .*{name}"):
        assert not hasattr(np, name)

    # Unfortunately, they are currently still valid via `np.dtype()`
    # 不幸的是,它们目前仍然通过 `np.dtype()` 是有效的
    np.dtype(name)
    name in np._core.sctypeDict


# Ignore the above future attribute warning for this test.
# 忽略上述未来属性警告以进行此测试
@pytest.mark.filterwarnings("ignore:In the future:FutureWarning")
class TestRemovedGlobals:
    # Removed 2023-01-12, NumPy 1.24.0
    # Not a deprecation, but the large error was added to aid those who missed
    # the previous deprecation, and should be removed similarly to one
    # (or faster).
    # 此类测试移除的全局变量,该变更不是废弃,但为了帮助错过先前废弃的人们,添加了大的错误信息,并应该类似废弃移除。

    @pytest.mark.parametrize("name",
            ["object", "float", "complex", "str", "int"])
    def test_attributeerror_includes_info(self, name):
        # 测试 AttributeError 是否包含特定信息
        msg = f".*\n`np.{name}` was a deprecated alias for the builtin"
        with pytest.raises(AttributeError, match=msg):
            getattr(np, name)


class TestDeprecatedFinfo(_DeprecationTestCase):
    # Deprecated in NumPy 1.25, 2023-01-16
    # 此类测试 NumPy 1.25 中废弃的 np.finfo

    def test_deprecated_none(self):
        # 断言 np.finfo(None) 的行为已被废弃
        self.assert_deprecated(np.finfo, args=(None,))


class TestMathAlias(_DeprecationTestCase):
    # 此类测试 np.lib.math 的过时别名
    def test_deprecated_np_lib_math(self):
        self.assert_deprecated(lambda: np.lib.math)


class TestLibImports(_DeprecationTestCase):
    # 空类,用于测试库导入的过时行为
    # 在Numpy 1.26.0中弃用,预计在2023年9月不再支持使用
    def test_lib_functions_deprecation_call(self):
        # 导入必要的函数和类
        from numpy.lib._utils_impl import safe_eval
        from numpy.lib._npyio_impl import recfromcsv, recfromtxt
        from numpy.lib._function_base_impl import disp
        from numpy.lib._shape_base_impl import get_array_wrap
        from numpy._core.numerictypes import maximum_sctype
        from numpy.lib.tests.test_io import TextIO
        from numpy import in1d, row_stack, trapz
        
        # 断言函数safe_eval("None")已经被弃用
        self.assert_deprecated(lambda: safe_eval("None"))
        
        # 定义一个生成数据的lambda函数,用于测试recfromcsv函数
        data_gen = lambda: TextIO('A,B\n0,1\n2,3')
        kwargs = dict(delimiter=",", missing_values="N/A", names=True)
        # 断言函数recfromcsv(data_gen())已经被弃用
        self.assert_deprecated(lambda: recfromcsv(data_gen()))
        # 断言函数recfromtxt(data_gen(), **kwargs)已经被弃用
        self.assert_deprecated(lambda: recfromtxt(data_gen(), **kwargs))
        
        # 断言函数disp("test")已经被弃用
        self.assert_deprecated(lambda: disp("test"))
        # 断言函数get_array_wrap()已经被弃用
        self.assert_deprecated(lambda: get_array_wrap())
        # 断言函数maximum_sctype(int)已经被弃用
        self.assert_deprecated(lambda: maximum_sctype(int))
        
        # 断言函数in1d([1], [1])已经被弃用
        self.assert_deprecated(lambda: in1d([1], [1]))
        # 断言函数row_stack([[]])已经被弃用
        self.assert_deprecated(lambda: row_stack([[]]))
        # 断言函数trapz([1], [1])已经被弃用
        self.assert_deprecated(lambda: trapz([1], [1]))
        # 断言np.chararray已经被弃用
        self.assert_deprecated(lambda: np.chararray)
# 创建一个测试类 TestDeprecatedDTypeAliases,继承自 _DeprecationTestCase
class TestDeprecatedDTypeAliases(_DeprecationTestCase):

    # 定义一个辅助方法 _check_for_warning,用于检查是否发出了警告
    def _check_for_warning(self, func):
        # 使用 warnings 模块捕获警告
        with warnings.catch_warnings(record=True) as caught_warnings:
            func()  # 执行传入的函数
        # 断言捕获的警告数量为1
        assert len(caught_warnings) == 1
        # 获取第一个捕获的警告对象
        w = caught_warnings[0]
        # 断言警告类别为 DeprecationWarning
        assert w.category is DeprecationWarning
        # 断言警告消息包含特定文本
        assert "alias 'a' was deprecated in NumPy 2.0" in str(w.message)

    # 定义测试方法 test_a_dtype_alias
    def test_a_dtype_alias(self):
        # 遍历测试的数据类型列表
        for dtype in ["a", "a10"]:
            # 定义匿名函数 f,用于创建指定 dtype 的 numpy 数据类型对象
            f = lambda: np.dtype(dtype)
            # 检查警告
            self._check_for_warning(f)
            # 断言该函数调用已被弃用
            self.assert_deprecated(f)
            # 使用匿名函数 f 创建 numpy 数组,并将其类型转换为指定 dtype
            f = lambda: np.array(["hello", "world"]).astype("a10")
            # 检查警告
            self._check_for_warning(f)
            # 断言该函数调用已被弃用
            self.assert_deprecated(f)



# 创建一个测试类 TestDeprecatedArrayWrap,继承自 _DeprecationTestCase
class TestDeprecatedArrayWrap(_DeprecationTestCase):
    # 定义类变量 message,表示警告消息包含的正则表达式模式
    message = "__array_wrap__.*"

    # 定义测试方法 test_deprecated
    def test_deprecated(self):
        # 定义 Test1 类
        class Test1:
            # 定义 __array__ 方法
            def __array__(self, dtype=None, copy=None):
                return np.arange(4)

            # 定义 __array_wrap__ 方法
            def __array_wrap__(self, arr, context=None):
                self.called = True
                return 'pass context'

        # 定义 Test2 类,继承自 Test1
        class Test2(Test1):
            # 重写 __array_wrap__ 方法
            def __array_wrap__(self, arr):
                self.called = True
                return 'pass'

        # 创建 Test1 的实例 test1
        test1 = Test1()
        # 创建 Test2 的实例 test2
        test2 = Test2()
        # 断言对 test1 执行 np.negative 函数时已被弃用
        self.assert_deprecated(lambda: np.negative(test1))
        # 断言 test1 实例中的 called 属性为 True
        assert test1.called
        # 断言对 test2 执行 np.negative 函数时已被弃用
        self.assert_deprecated(lambda: np.negative(test2))
        # 断言 test2 实例中的 called 属性为 True
        assert test2.called



# 创建一个测试类 TestDeprecatedDTypeParenthesizedRepeatCount,继承自 _DeprecationTestCase
class TestDeprecatedDTypeParenthesizedRepeatCount(_DeprecationTestCase):
    # 定义类变量 message,表示警告消息包含的文本
    message = "Passing in a parenthesized single number"

    # 使用 pytest 的参数化装饰器标记测试方法 test_parenthesized_repeat_count
    @pytest.mark.parametrize("string", ["(2)i,", "(3)3S,", "f,(2)f"])
    def test_parenthesized_repeat_count(self, string):
        # 断言调用 np.dtype 函数时已被弃用,传入参数 string
        self.assert_deprecated(np.dtype, args=(string,))



# 创建一个测试类 TestDeprecatedSaveFixImports,继承自 _DeprecationTestCase
class TestDeprecatedSaveFixImports(_DeprecationTestCase):
    # 类变量 message 指示警告消息内容,指出 'fix_imports' 标志已弃用
    message = "The 'fix_imports' flag is deprecated and has no effect."

    # 定义测试方法 test_deprecated
    def test_deprecated(self):
        # 使用 temppath 上下文管理器创建临时路径,文件后缀为 .npy
        with temppath(suffix='.npy') as path:
            # 准备样本参数
            sample_args = (path, np.array(np.zeros((1024, 10))))
            # 断言对 np.save 函数的调用未被弃用,传入样本参数
            self.assert_not_deprecated(np.save, args=sample_args)
            # 断言对 np.save 函数的调用已被弃用,传入样本参数和 fix_imports=True 的关键字参数
            self.assert_deprecated(np.save, args=sample_args,
                                kwargs={'fix_imports': True})
            # 断言对 np.save 函数的调用已被弃用,传入样本参数和 fix_imports=False 的关键字参数
            self.assert_deprecated(np.save, args=sample_args,
                                kwargs={'fix_imports': False})
            # 遍历 allow_pickle 的取值 [True, False]
            for allow_pickle in [True, False]:
                # 断言对 np.save 函数的调用未被弃用,传入样本参数和 allow_pickle 的关键字参数
                self.assert_not_deprecated(np.save, args=sample_args,
                                        kwargs={'allow_pickle': allow_pickle})
                # 断言对 np.save 函数的调用已被弃用,传入样本参数、allow_pickle 和 fix_imports=True 的关键字参数
                self.assert_deprecated(np.save, args=sample_args,
                                    kwargs={'allow_pickle': allow_pickle,
                                            'fix_imports': True})
                # 断言对 np.save 函数的调用已被弃用,传入样本参数、allow_pickle 和 fix_imports=False 的关键字参数
                self.assert_deprecated(np.save, args=sample_args,
                                    kwargs={'allow_pickle': allow_pickle,
                                            'fix_imports': False})

.\numpy\numpy\_core\tests\test_dlpack.py

# 导入系统相关模块和 pytest 模块
import sys
import pytest

# 导入 NumPy 库并从中导入所需的函数和变量
import numpy as np
from numpy.testing import assert_array_equal, IS_PYPY

# 定义一个生成器函数,生成包含新旧 DLPack 对象的迭代器
def new_and_old_dlpack():
    # 生成一个包含从 0 到 4 的整数数组的 NumPy 对象
    yield np.arange(5)

    # 定义一个旧版本的 DLPack 类,继承自 np.ndarray
    class OldDLPack(np.ndarray):
        # 只支持“旧”版本的 __dlpack__ 方法
        def __dlpack__(self, stream=None):
            return super().__dlpack__(stream=None)

    # 生成一个将整数数组视图转换为 OldDLPack 类型的对象
    yield np.arange(5).view(OldDLPack)

# 定义一个测试类 TestDLPack
class TestDLPack:
    # 标记测试为跳过状态,如果是在 PyPy 环境下运行则跳过
    @pytest.mark.skipif(IS_PYPY, reason="PyPy can't get refcounts.")
    # 参数化测试,参数为 max_version,包括 (0, 0), None, (1, 0), (100, 3)
    @pytest.mark.parametrize("max_version", [(0, 0), None, (1, 0), (100, 3)])
    # 测试 __dlpack__ 方法的引用计数
    def test_dunder_dlpack_refcount(self, max_version):
        x = np.arange(5)
        # 调用 x 对象的 __dlpack__ 方法
        y = x.__dlpack__(max_version=max_version)
        # 断言 x 对象的引用计数为 3
        assert sys.getrefcount(x) == 3
        del y
        # 删除 y 对象后,再次断言 x 对象的引用计数为 2
        assert sys.getrefcount(x) == 2

    # 测试 __dlpack__ 方法的 stream 参数
    def test_dunder_dlpack_stream(self):
        x = np.arange(5)
        # 调用 x 对象的 __dlpack__ 方法,stream 参数为 None
        x.__dlpack__(stream=None)

        # 使用 pytest 断言捕获 RuntimeError 异常
        with pytest.raises(RuntimeError):
            # 再次调用 x 对象的 __dlpack__ 方法,此时 stream 参数为 1
            x.__dlpack__(stream=1)

    # 测试 __dlpack__ 方法的 copy 参数
    def test_dunder_dlpack_copy(self):
        # 显式检查 __dlpack__ 方法的参数解析
        x = np.arange(5)
        x.__dlpack__(copy=True)
        x.__dlpack__(copy=None)
        x.__dlpack__(copy=False)

        # 使用 pytest 断言捕获 ValueError 异常
        with pytest.raises(ValueError):
            # __dlpack__ 方法的 copy 参数传入一个 NumPy 数组
            x.__dlpack__(copy=np.array([1, 2, 3]))

    # 测试 strides 不是 itemsize 的倍数时的情况
    def test_strides_not_multiple_of_itemsize(self):
        # 创建一个复合数据类型的零数组
        dt = np.dtype([('int', np.int32), ('char', np.int8)])
        y = np.zeros((5,), dtype=dt)
        # 获取复合数组的 'int' 字段作为 z
        z = y['int']

        # 使用 pytest 断言捕获 BufferError 异常
        with pytest.raises(BufferError):
            # 尝试从 DLPack 对象 z 中导入数据
            np.from_dlpack(z)

    # 标记测试为跳过状态,如果是在 PyPy 环境下运行则跳过
    @pytest.mark.skipif(IS_PYPY, reason="PyPy can't get refcounts.")
    # 参数化测试,参数为 new_and_old_dlpack() 生成器的迭代结果
    @pytest.mark.parametrize("arr", new_and_old_dlpack())
    # 测试从 DLPack 对象到 NumPy 对象的引用计数
    def test_from_dlpack_refcount(self, arr):
        arr = arr.copy()
        y = np.from_dlpack(arr)
        # 断言 arr 对象的引用计数为 3
        assert sys.getrefcount(arr) == 3
        del y
        # 删除 y 对象后,再次断言 arr 对象的引用计数为 2
        assert sys.getrefcount(arr) == 2

    # 参数化测试,参数为 dtype 和 new_and_old_dlpack() 生成器的迭代结果
    @pytest.mark.parametrize("dtype", [
        np.bool,
        np.int8, np.int16, np.int32, np.int64,
        np.uint8, np.uint16, np.uint32, np.uint64,
        np.float16, np.float32, np.float64,
        np.complex64, np.complex128
    ])
    @pytest.mark.parametrize("arr", new_and_old_dlpack())
    # 测试 dtype 在 from_dlpack 调用中的传递
    def test_dtype_passthrough(self, arr, dtype):
        x = arr.astype(dtype)
        y = np.from_dlpack(x)

        # 断言 y 的数据类型与 x 相同
        assert y.dtype == x.dtype
        # 断言 y 与 x 相等
        assert_array_equal(x, y)

    # 测试传入无效 dtype 参数时的情况
    def test_invalid_dtype(self):
        # 创建一个包含 np.datetime64 对象的 NumPy 数组
        x = np.asarray(np.datetime64('2021-05-27'))

        # 使用 pytest 断言捕获 BufferError 异常
        with pytest.raises(BufferError):
            # 尝试从 x 中导入数据
            np.from_dlpack(x)

    # 测试传入无效的字节交换方式时的情况
    def test_invalid_byte_swapping(self):
        # 创建一个使用 newbyteorder() 方法改变字节顺序的数组
        dt = np.dtype('=i8').newbyteorder()
        x = np.arange(5, dtype=dt)

        # 使用 pytest 断言捕获 BufferError 异常
        with pytest.raises(BufferError):
            # 尝试从 x 中导入数据
            np.from_dlpack(x)
    def test_non_contiguous(self):
        x = np.arange(25).reshape((5, 5))

        y1 = x[0]
        assert_array_equal(y1, np.from_dlpack(y1))
        # 获取数组 x 的第一行,转换为 DLPack 格式,与从 DLPack 转换回来的结果进行比较

        y2 = x[:, 0]
        assert_array_equal(y2, np.from_dlpack(y2))
        # 获取数组 x 的第一列,转换为 DLPack 格式,与从 DLPack 转换回来的结果进行比较

        y3 = x[1, :]
        assert_array_equal(y3, np.from_dlpack(y3))
        # 获取数组 x 的第二行,转换为 DLPack 格式,与从 DLPack 转换回来的结果进行比较

        y4 = x[1]
        assert_array_equal(y4, np.from_dlpack(y4))
        # 获取数组 x 的第二行,转换为 DLPack 格式,与从 DLPack 转换回来的结果进行比较

        y5 = np.diagonal(x).copy()
        assert_array_equal(y5, np.from_dlpack(y5))
        # 获取数组 x 的对角线元素,复制为新数组,转换为 DLPack 格式,与从 DLPack 转换回来的结果进行比较

    @pytest.mark.parametrize("ndim", range(33))
    def test_higher_dims(self, ndim):
        shape = (1,) * ndim
        x = np.zeros(shape, dtype=np.float64)

        assert shape == np.from_dlpack(x).shape
        # 创建指定维度的零数组 x,将其转换为 DLPack 格式后,比较其形状是否与原始形状一致

    def test_dlpack_device(self):
        x = np.arange(5)
        assert x.__dlpack_device__() == (1, 0)
        # 检查数组 x 的设备信息是否为 (1, 0)
        y = np.from_dlpack(x)
        assert y.__dlpack_device__() == (1, 0)
        # 将数组 x 转换为 DLPack 格式后,检查转换后数组 y 的设备信息是否为 (1, 0)
        z = y[::2]
        assert z.__dlpack_device__() == (1, 0)
        # 对从 DLPack 转换回来的数组 y 进行切片操作,检查切片后数组 z 的设备信息是否为 (1, 0)

    def dlpack_deleter_exception(self, max_version):
        x = np.arange(5)
        _ = x.__dlpack__(max_version=max_version)
        raise RuntimeError
        # 尝试使用指定的 max_version 将数组 x 转换为 DLPack 格式,并抛出 RuntimeError 异常

    @pytest.mark.parametrize("max_version", [None, (1, 0)])
    def test_dlpack_destructor_exception(self, max_version):
        with pytest.raises(RuntimeError):
            self.dlpack_deleter_exception(max_version=max_version)
        # 测试在使用不同的 max_version 参数时,调用 dlpack_deleter_exception 方法是否会抛出 RuntimeError 异常

    def test_readonly(self):
        x = np.arange(5)
        x.flags.writeable = False
        # 设置数组 x 为不可写
        # 没有指定 max_version 时应该引发异常
        with pytest.raises(BufferError):
            x.__dlpack__()

        # 但是如果我们尝试指定版本,应该正常工作
        y = np.from_dlpack(x)
        assert not y.flags.writeable
        # 将不可写的数组 x 转换为 DLPack 格式后,检查转换后数组 y 是否为不可写

    def test_ndim0(self):
        x = np.array(1.0)
        y = np.from_dlpack(x)
        assert_array_equal(x, y)
        # 将标量数组 x 转换为 DLPack 格式后,与从 DLPack 转换回来的结果进行比较

    def test_size1dims_arrays(self):
        x = np.ndarray(dtype='f8', shape=(10, 5, 1), strides=(8, 80, 4),
                       buffer=np.ones(1000, dtype=np.uint8), order='F')
        y = np.from_dlpack(x)
        assert_array_equal(x, y)
        # 创建一个特定属性的多维数组 x,将其转换为 DLPack 格式后,与从 DLPack 转换回来的结果进行比较

    def test_copy(self):
        x = np.arange(5)

        y = np.from_dlpack(x)
        assert np.may_share_memory(x, y)
        # 将数组 x 转换为 DLPack 格式后,检查转换后数组 y 是否与原数组共享内存
        y = np.from_dlpack(x, copy=False)
        assert np.may_share_memory(x, y)
        # 使用 copy=False 将数组 x 转换为 DLPack 格式后,检查转换后数组 y 是否与原数组共享内存
        y = np.from_dlpack(x, copy=True)
        assert not np.may_share_memory(x, y)
        # 使用 copy=True 将数组 x 转换为 DLPack 格式后,检查转换后数组 y 是否与原数组共享内存

    def test_device(self):
        x = np.arange(5)
        # 请求 (1, 0),即 CPU 设备,在两次调用中均有效:
        x.__dlpack__(dl_device=(1, 0))
        np.from_dlpack(x, device="cpu")
        np.from_dlpack(x, device=None)
        # 将数组 x 转换为 DLPack 格式时,指定不同的设备参数,验证其是否符合预期

        with pytest.raises(ValueError):
            x.__dlpack__(dl_device=(10, 0))
        with pytest.raises(ValueError):
            np.from_dlpack(x, device="gpu")
        # 尝试使用无效的设备参数时,应该引发 ValueError 异常

.\numpy\numpy\_core\tests\test_dtype.py

# 导入系统相关模块
import sys
# 导入运算符模块,用于操作符的函数集合
import operator
# 导入 pytest 测试框架模块
import pytest
# ctypes 是一个提供 C 语言数据类型的 Python 扩展库
import ctypes
# gc 是 Python 的垃圾回收模块
import gc
# types 模块包含用于操作类型和创建新类型的函数
import types
# 导入 pickle 模块,用于序列化和反序列化 Python 对象
import pickle

# 导入 numpy 库,并使用 np 别名
import numpy as np
# 导入 numpy 的数据类型模块
import numpy.dtypes
# 导入 numpy 的有理数模块
from numpy._core._rational_tests import rational
# 导入 numpy 的自定义字段数据类型创建模块
from numpy._core._multiarray_tests import create_custom_field_dtype
# 导入 numpy 的测试函数集
from numpy.testing import (
    assert_, assert_equal, assert_array_equal, assert_raises, HAS_REFCOUNT,
    IS_PYSTON, _OLD_PROMOTION)
# 导入 permutations 函数,用于生成迭代器的所有可能排列
from itertools import permutations
# 导入 random 模块,用于生成随机数
import random

# 导入 hypothesis 库,用于基于假设的快速测试
import hypothesis
# 导入 hypothesis.extra.numpy 模块,提供了对 numpy 数组的假设支持
from hypothesis.extra import numpy as hynp


# 定义一个函数,用于断言两个数据类型相等
def assert_dtype_equal(a, b):
    # 使用 assert_equal 函数断言 a 和 b 相等
    assert_equal(a, b)
    # 断言两个等价类型的哈希值也相等
    assert_equal(hash(a), hash(b),
                 "two equivalent types do not hash to the same value !")

# 定义一个函数,用于断言两个数据类型不相等
def assert_dtype_not_equal(a, b):
    # 断言 a 和 b 不相等
    assert_(a != b)
    # 断言两个不同类型的哈希值不相等
    assert_(hash(a) != hash(b),
            "two different types hash to the same value !")

# 定义一个测试类 TestBuiltin
class TestBuiltin:
    # 使用 pytest 的参数化装饰器,测试函数接受多个参数化的 t
    @pytest.mark.parametrize('t', [int, float, complex, np.int32, str, object])
    def test_run(self, t):
        """Only test hash runs at all."""
        # 创建一个 numpy 数据类型对象 dt
        dt = np.dtype(t)
        # 对 dt 进行哈希运算
        hash(dt)

    # 使用 pytest 的参数化装饰器,测试函数接受多个参数化的 t
    @pytest.mark.parametrize('t', [int, float])
    def test_dtype(self, t):
        # 创建一个 numpy 数据类型对象 dt
        dt = np.dtype(t)
        # 创建新的字节顺序为小端的数据类型对象 dt2
        dt2 = dt.newbyteorder("<")
        # 创建新的字节顺序为大端的数据类型对象 dt3
        dt3 = dt.newbyteorder(">")
        # 如果 dt 等于 dt2
        if dt == dt2:
            # 断言 dt 的字节顺序不等于 dt2 的字节顺序,验证测试是否无效
            assert_(dt.byteorder != dt2.byteorder, "bogus test")
            # 断言 dt 和 dt2 的数据类型相等
            assert_dtype_equal(dt, dt2)
        else:
            # 断言 dt 的字节顺序不等于 dt3 的字节顺序,验证测试是否无效
            assert_(dt.byteorder != dt3.byteorder, "bogus test")
            # 断言 dt 和 dt3 的数据类型相等
            assert_dtype_equal(dt, dt3)

    # 测试等效数据类型的哈希值
    def test_equivalent_dtype_hashing(self):
        # 创建一个无符号整数指针类型的 numpy 数据类型对象 uintp
        uintp = np.dtype(np.uintp)
        # 如果 uintp 的字节大小为 4
        if uintp.itemsize == 4:
            # 将 left 设为 uintp
            left = uintp
            # 将 right 设为 32 位无符号整数的数据类型对象
            right = np.dtype(np.uint32)
        else:
            # 将 left 设为 uintp
            left = uintp
            # 将 right 设为 64 位无符号长整数的数据类型对象
            right = np.dtype(np.ulonglong)
        # 断言 left 等于 right
        assert_(left == right)
        # 断言 left 和 right 的哈希值相等
        assert_(hash(left) == hash(right))
    def test_invalid_types(self):
        # 确保对于无效的类型字符串会引发错误

        # 检查特定类型字符串是否会引发 TypeError 异常
        assert_raises(TypeError, np.dtype, 'O3')
        assert_raises(TypeError, np.dtype, 'O5')
        assert_raises(TypeError, np.dtype, 'O7')
        assert_raises(TypeError, np.dtype, 'b3')
        assert_raises(TypeError, np.dtype, 'h4')
        assert_raises(TypeError, np.dtype, 'I5')
        assert_raises(TypeError, np.dtype, 'e3')
        assert_raises(TypeError, np.dtype, 'f5')

        # 根据 'g' 的 itemsize 属性确定条件
        if np.dtype('g').itemsize == 8 or np.dtype('g').itemsize == 16:
            # 当 'g' 的 itemsize 为 8 或 16 时,检查 'g12' 是否引发 TypeError 异常
            assert_raises(TypeError, np.dtype, 'g12')
        elif np.dtype('g').itemsize == 12:
            # 当 'g' 的 itemsize 为 12 时,检查 'g16' 是否引发 TypeError 异常
            assert_raises(TypeError, np.dtype, 'g16')

        # 根据 'l' 的 itemsize 属性确定条件
        if np.dtype('l').itemsize == 8:
            # 当 'l' 的 itemsize 为 8 时,检查 'l4' 和 'L4' 是否引发 TypeError 异常
            assert_raises(TypeError, np.dtype, 'l4')
            assert_raises(TypeError, np.dtype, 'L4')
        else:
            # 当 'l' 的 itemsize 不为 8 时,检查 'l8' 和 'L8' 是否引发 TypeError 异常
            assert_raises(TypeError, np.dtype, 'l8')
            assert_raises(TypeError, np.dtype, 'L8')

        # 根据 'q' 的 itemsize 属性确定条件
        if np.dtype('q').itemsize == 8:
            # 当 'q' 的 itemsize 为 8 时,检查 'q4' 和 'Q4' 是否引发 TypeError 异常
            assert_raises(TypeError, np.dtype, 'q4')
            assert_raises(TypeError, np.dtype, 'Q4')
        else:
            # 当 'q' 的 itemsize 不为 8 时,检查 'q8' 和 'Q8' 是否引发 TypeError 异常
            assert_raises(TypeError, np.dtype, 'q8')
            assert_raises(TypeError, np.dtype, 'Q8')

        # 确保负大小的 dtype 引发 TypeError 异常
        assert_raises(TypeError, np.dtype, 'S-1')
        assert_raises(TypeError, np.dtype, 'U-1')
        assert_raises(TypeError, np.dtype, 'V-1')

    def test_richcompare_invalid_dtype_equality(self):
        # 确保无法转换为有效 dtype 的对象在与有效 dtype 比较时返回 False/True
        # 这里的 7 无法转换为 dtype,因此比较时不应引发异常

        # 检查 np.int32 类型的 dtype 是否等于 7,应返回 False
        assert not np.dtype(np.int32) == 7, "dtype richcompare failed for =="
        # 检查 np.int32 类型的 dtype 是否不等于 7,应返回 True
        assert np.dtype(np.int32) != 7, "dtype richcompare failed for !="

    @pytest.mark.parametrize(
        'operation',
        [operator.le, operator.lt, operator.ge, operator.gt])
    def test_richcompare_invalid_dtype_comparison(self, operation):
        # 确保对于无效 dtype,比较操作符会引发 TypeError 异常
        # 这里的 7 是一个无效的 dtype。

        # 使用 pytest 的 raises 来检查在比较操作中是否会引发 TypeError 异常
        with pytest.raises(TypeError):
            operation(np.dtype(np.int32), 7)

    @pytest.mark.parametrize("dtype",
             ['Bool', 'Bytes0', 'Complex32', 'Complex64',
              'Datetime64', 'Float16', 'Float32', 'Float64',
              'Int8', 'Int16', 'Int32', 'Int64',
              'Object0', 'Str0', 'Timedelta64',
              'UInt8', 'UInt16', 'Uint32', 'UInt32',
              'Uint64', 'UInt64', 'Void0',
              "Float128", "Complex128"])
    def test_numeric_style_types_are_invalid(self, dtype):
        # 确保数值风格的类型字符串会引发 TypeError 异常

        # 使用 assert_raises 来检查是否会引发 TypeError 异常
        with assert_raises(TypeError):
            np.dtype(dtype)
    def test_expired_dtypes_with_bad_bytesize(self):
        # 定义正则表达式匹配模式,用于捕获 NumPy 2.0 中移除的数据类型错误
        match: str = r".*removed in NumPy 2.0.*"

        # 使用 pytest 检查以下数据类型定义是否抛出 TypeError,并匹配预定义的错误消息模式
        with pytest.raises(TypeError, match=match):
            np.dtype("int0")
        with pytest.raises(TypeError, match=match):
            np.dtype("uint0")
        with pytest.raises(TypeError, match=match):
            np.dtype("bool8")
        with pytest.raises(TypeError, match=match):
            np.dtype("bytes0")
        with pytest.raises(TypeError, match=match):
            np.dtype("str0")
        with pytest.raises(TypeError, match=match):
            np.dtype("object0")
        with pytest.raises(TypeError, match=match):
            np.dtype("void0")

    @pytest.mark.parametrize(
        'value',
        ['m8', 'M8', 'datetime64', 'timedelta64',
         'i4, (2,3)f8, f4', 'S3, 3u8, (3,4)S10',
         '>f', '<f', '=f', '|f',
        ])
    def test_dtype_bytes_str_equivalence(self, value):
        # 将字符串编码为 ASCII 字节表示
        bytes_value = value.encode('ascii')
        # 使用 ASCII 字节表示创建 NumPy dtype 对象
        from_bytes = np.dtype(bytes_value)
        # 直接使用字符串创建 NumPy dtype 对象
        from_str = np.dtype(value)
        # 断言两种创建方式得到的 dtype 对象相等
        assert_dtype_equal(from_bytes, from_str)

    def test_dtype_from_bytes(self):
        # 检查空的字节对象是否会抛出 TypeError
        assert_raises(TypeError, np.dtype, b'')
        # 检查带有字节顺序指示符但没有类型信息的字节对象是否会抛出 TypeError
        assert_raises(TypeError, np.dtype, b'|')

        # 使用单个字节字符 < NPY_NTYPES_LEGACY 的值作为索引,返回对应的类型
        assert_dtype_equal(np.dtype(bytes([0])), np.dtype('bool'))
        assert_dtype_equal(np.dtype(bytes([17])), np.dtype(object))

        # 使用单个字节表示有效的类型码 'f',验证是否正确解析为 'float32'
        assert_dtype_equal(np.dtype(b'f'), np.dtype('float32'))

        # 检查带有非 ASCII 值的字节对象是否会抛出 TypeError
        assert_raises(TypeError, np.dtype, b'\xff')
        assert_raises(TypeError, np.dtype, b's\xff')

    def test_bad_param(self):
        # 检查给定过小的 itemsize 是否会抛出 ValueError
        assert_raises(ValueError, np.dtype,
                        {'names':['f0', 'f1'],
                         'formats':['i4', 'i1'],
                         'offsets':[0, 4],
                         'itemsize':4})
        # 如果启用了对齐,itemsize 必须是对齐值(4)的倍数
        assert_raises(ValueError, np.dtype,
                        {'names':['f0', 'f1'],
                         'formats':['i4', 'i1'],
                         'offsets':[0, 4],
                         'itemsize':9}, align=True)
        # 如果启用了对齐,各个字段必须按照对齐要求进行对齐
        assert_raises(ValueError, np.dtype,
                        {'names':['f0', 'f1'],
                         'formats':['i1', 'f4'],
                         'offsets':[0, 2]}, align=True)
    # 定义一个测试方法,用于测试字段顺序的相等性
    def test_field_order_equality(self):
        # 创建一个 NumPy 数据类型 x,包含字段名 'A' 和 'B',对应格式为整数和浮点数,偏移量分别为 0 和 4
        x = np.dtype({'names': ['A', 'B'],
                      'formats': ['i4', 'f4'],
                      'offsets': [0, 4]})
        # 创建另一个 NumPy 数据类型 y,字段名 'B' 和 'A',对应格式为整数和浮点数,偏移量分别为 4 和 0
        y = np.dtype({'names': ['B', 'A'],
                      'formats': ['i4', 'f4'],
                      'offsets': [4, 0]})
        # 断言 x 和 y 不相等
        assert_equal(x == y, False)
        # 使用 "safe" 类型转换,验证 x 是否可以安全地转换为 y
        assert np.can_cast(x, y, casting="safe")

    # 使用参数化测试装饰器,测试直接创建字符串类型数据类型
    @pytest.mark.parametrize(
        ["type_char", "char_size", "scalar_type"],
        [["U", 4, np.str_],
         ["S", 1, np.bytes_]])
    def test_create_string_dtypes_directly(
            self, type_char, char_size, scalar_type):
        # 获取相应的数据类型类对象
        dtype_class = type(np.dtype(type_char))

        # 创建指定大小的数据类型
        dtype = dtype_class(8)
        # 断言数据类型是期望的标量类型
        assert dtype.type is scalar_type
        # 断言数据类型的字节大小符合预期
        assert dtype.itemsize == 8*char_size

    # 测试创建无效字符串类型数据类型时的错误情况
    def test_create_invalid_string_errors(self):
        # 计算一个超出整数容量范围的值
        one_too_big = np.iinfo(np.intc).max + 1
        # 应该抛出 TypeError 异常
        with pytest.raises(TypeError):
            type(np.dtype("U"))(one_too_big // 4)

        # 应该抛出 TypeError 异常,用于非常大的数值
        with pytest.raises(TypeError):
            type(np.dtype("U"))(np.iinfo(np.intp).max // 4 + 1)

        # 如果超出整数容量范围,则应该抛出 TypeError 异常
        if one_too_big < sys.maxsize:
            with pytest.raises(TypeError):
                type(np.dtype("S"))(one_too_big)

        # 应该抛出 ValueError 异常,因为大小为负数
        with pytest.raises(ValueError):
            type(np.dtype("U"))(-1)

        # 在 32 位系统上可能会出现 OverflowError
        with pytest.raises((TypeError, OverflowError)):
            # 测试一个非常大的数值
            type(np.dtype("S"))(2**61)

        # 应该抛出 TypeError 异常,因为数据类型名中包含无效字符
        with pytest.raises(TypeError):
            np.dtype("S1234hello")

    # 测试解析时忽略前导零
    def test_leading_zero_parsing(self):
        # 创建数据类型 dt1 和 dt2,都表示长度为 10 的字符串,但忽略了 dt1 中的前导零
        dt1 = np.dtype('S010')
        dt2 = np.dtype('S10')

        # 断言 dt1 和 dt2 是相等的
        assert dt1 == dt2
        # 断言 dt1 的字符串表示为 "dtype('S10')"
        assert repr(dt1) == "dtype('S10')"
        # 断言 dt1 的字节大小为 10
        assert dt1.itemsize == 10
class TestRecord:
    def test_equivalent_record(self):
        """Test whether equivalent record dtypes hash the same."""
        # 定义包含单个字段 'yo' 的 dtype
        a = np.dtype([('yo', int)])
        # 定义另一个与前一个相同的 dtype
        b = np.dtype([('yo', int)])
        # 断言这两个 dtype 相等
        assert_dtype_equal(a, b)

    def test_different_names(self):
        # 在理论上,它们可能会哈希相同(碰撞)?
        # 定义包含 'yo' 和 'ye' 两个不同字段的 dtype
        a = np.dtype([('yo', int)])
        b = np.dtype([('ye', int)])
        # 断言这两个 dtype 不相等
        assert_dtype_not_equal(a, b)

    def test_different_titles(self):
        # 在理论上,它们可能会哈希相同(碰撞)?
        # 定义两个结构化 dtype,每个都包含 'r' 和 'b' 两个字段,但标题略有不同
        a = np.dtype({'names': ['r', 'b'],
                      'formats': ['u1', 'u1'],
                      'titles': ['Red pixel', 'Blue pixel']})
        b = np.dtype({'names': ['r', 'b'],
                      'formats': ['u1', 'u1'],
                      'titles': ['RRed pixel', 'Blue pixel']})
        # 断言这两个 dtype 不相等
        assert_dtype_not_equal(a, b)

    @pytest.mark.skipif(not HAS_REFCOUNT, reason="Python lacks refcounts")
    def test_refcount_dictionary_setting(self):
        # 测试 dtype 对象的引用计数设置
        names = ["name1"]
        formats = ["f8"]
        titles = ["t1"]
        offsets = [0]
        # 创建一个包含字段名、格式、标题和偏移量的字典
        d = dict(names=names, formats=formats, titles=titles, offsets=offsets)
        # 记录字典中每个值的引用计数
        refcounts = {k: sys.getrefcount(i) for k, i in d.items()}
        # 创建一个新的 dtype 对象并再次记录引用计数
        np.dtype(d)
        refcounts_new = {k: sys.getrefcount(i) for k, i in d.items()}
        # 断言新旧两组引用计数相同
        assert refcounts == refcounts_new

    def test_mutate(self):
        # 改变 dtype 应该重置其缓存的哈希值。
        # 注意:改变操作应该被弃用,但新 API 已添加以替代它。
        # 定义初始的 dtype
        a = np.dtype([('yo', int)])
        b = np.dtype([('yo', int)])
        c = np.dtype([('ye', int)])
        # 断言 a 和 b 相等,a 和 c 不相等
        assert_dtype_equal(a, b)
        assert_dtype_not_equal(a, c)
        # 修改字段名称并再次断言 a 和 c 相等,a 和 b 不相等
        a.names = ['ye']
        assert_dtype_equal(a, c)
        assert_dtype_not_equal(a, b)
        # 获取 b 的状态并恢复 a 的状态,断言 a 和 b 再次相等,a 和 c 不相等
        state = b.__reduce__()[2]
        a.__setstate__(state)
        assert_dtype_equal(a, b)
        assert_dtype_not_equal(a, c)

    def test_init_simple_structured(self):
        # 初始化简单结构化 dtype,检查字段名称
        dt1 = np.dtype("i, i")
        assert dt1.names == ("f0", "f1")

        dt2 = np.dtype("i,")
        assert dt2.names == ("f0",)

    def test_mutate_error(self):
        # 注意:改变操作应该被弃用,但新 API 已添加以替代它。
        # 定义初始 dtype 包含多个字段
        a = np.dtype("i,i")

        # 使用 pytest 断言应该触发 ValueError,因为逐个更改字段名已被弃用
        with pytest.raises(ValueError, match="must replace all names at once"):
            a.names = ["f0"]

        # 使用 pytest 断言应该触发 ValueError,因为包含非 Unicode 名称
        with pytest.raises(ValueError, match=".*and not string"):
            a.names = ["f0", b"not a unicode name"]

    def test_not_lists(self):
        """Test if an appropriate exception is raised when passing bad values to
        the dtype constructor.
        """
        # 使用 assert_raises 检查当向 dtype 构造函数传递错误值时是否引发了 TypeError
        assert_raises(TypeError, np.dtype,
                      dict(names={'A', 'B'}, formats=['f8', 'i4']))
        assert_raises(TypeError, np.dtype,
                      dict(names=['A', 'B'], formats={'f8', 'i4'}))
    def test_aligned_size(self):
        # 检查结构化数据类型是否被填充到对齐的大小
        dt = np.dtype('i4, i1', align=True)
        # 断言第一个数据类型的大小为8字节
        assert_equal(dt.itemsize, 8)
        
        dt = np.dtype([('f0', 'i4'), ('f1', 'i1')], align=True)
        # 断言第二个数据类型的大小为8字节
        assert_equal(dt.itemsize, 8)
        
        dt = np.dtype({'names':['f0', 'f1'],
                       'formats':['i4', 'u1'],
                       'offsets':[0, 4]}, align=True)
        # 断言第三个数据类型的大小为8字节
        assert_equal(dt.itemsize, 8)
        
        dt = np.dtype({'f0': ('i4', 0), 'f1':('u1', 4)}, align=True)
        # 断言第四个数据类型的大小为8字节
        assert_equal(dt.itemsize, 8)
        
        # 嵌套应保持相同的对齐
        dt1 = np.dtype([('f0', 'i4'),
                        ('f1', [('f1', 'i1'), ('f2', 'i4'), ('f3', 'i1')]),
                        ('f2', 'i1')], align=True)
        # 断言第一个嵌套数据类型的大小为20字节
        assert_equal(dt1.itemsize, 20)
        
        dt2 = np.dtype({'names':['f0', 'f1', 'f2'],
                        'formats':['i4',
                                   [('f1', 'i1'), ('f2', 'i4'), ('f3', 'i1')],
                                   'i1'],
                        'offsets':[0, 4, 16]}, align=True)
        # 断言第二个嵌套数据类型的大小为20字节
        assert_equal(dt2.itemsize, 20)
        
        dt3 = np.dtype({'f0': ('i4', 0),
                        'f1': ([('f1', 'i1'), ('f2', 'i4'), ('f3', 'i1')], 4),
                        'f2': ('i1', 16)}, align=True)
        # 断言第三个嵌套数据类型的大小为20字节
        assert_equal(dt3.itemsize, 20)
        
        # 断言三个嵌套数据类型相等
        assert_equal(dt1, dt2)
        assert_equal(dt2, dt3)
        
        # 嵌套应保持打包
        dt1 = np.dtype([('f0', 'i4'),
                        ('f1', [('f1', 'i1'), ('f2', 'i4'), ('f3', 'i1')]),
                        ('f2', 'i1')], align=False)
        # 断言第一个非对齐的嵌套数据类型的大小为11字节
        assert_equal(dt1.itemsize, 11)
        
        dt2 = np.dtype({'names':['f0', 'f1', 'f2'],
                        'formats':['i4',
                                   [('f1', 'i1'), ('f2', 'i4'), ('f3', 'i1')],
                                   'i1'],
                        'offsets':[0, 4, 10]}, align=False)
        # 断言第二个非对齐的嵌套数据类型的大小为11字节
        assert_equal(dt2.itemsize, 11)
        
        dt3 = np.dtype({'f0': ('i4', 0),
                        'f1': ([('f1', 'i1'), ('f2', 'i4'), ('f3', 'i1')], 4),
                        'f2': ('i1', 10)}, align=False)
        # 断言第三个非对齐的嵌套数据类型的大小为11字节
        assert_equal(dt3.itemsize, 11)
        
        # 断言三个非对齐的嵌套数据类型相等
        assert_equal(dt1, dt2)
        assert_equal(dt2, dt3)
        
        # 子类型的数组应保持对齐
        dt1 = np.dtype([('a', '|i1'),
                        ('b', [('f0', '<i2'),
                              ('f1', '<f4')], 2)], align=True)
        # 断言子类型的数组的描述符
        assert_equal(dt1.descr, [('a', '|i1'), ('', '|V3'),
                                 ('b', [('f0', '<i2'), ('', '|V2'),
                                       ('f1', '<f4')], (2,))])
    def test_empty_struct_alignment(self):
        # 测试空结构的对齐方式
        # 创建一个空的 dtype,指定对齐方式为 True,默认对齐到 1
        dt = np.dtype([], align=True)
        # 断言该 dtype 的对齐方式为 1
        assert_equal(dt.alignment, 1)

        # 创建一个包含一个空字段 'f0' 的 dtype,指定对齐方式为 True
        dt = np.dtype([('f0', [])], align=True)
        # 断言该 dtype 的对齐方式为 1
        assert_equal(dt.alignment, 1)

        # 创建一个空的结构字典 dtype,指定对齐方式为 True
        dt = np.dtype({'names': [],
                       'formats': [],
                       'offsets': []}, align=True)
        # 断言该 dtype 的对齐方式为 1
        assert_equal(dt.alignment, 1)

        # 创建一个包含字段 'f0' 的 dtype,其中该字段为空列表,指定对齐方式为 True
        dt = np.dtype({'names': ['f0'],
                       'formats': [[]],
                       'offsets': [0]}, align=True)
        # 断言该 dtype 的对齐方式为 1
        assert_equal(dt.alignment, 1)

    def test_union_struct(self):
        # 测试创建联合结构的 dtype
        # 创建一个包含三个字段 'f0', 'f1', 'f2' 的 dtype,各自指定不同的格式和偏移量,指定对齐方式为 True
        dt = np.dtype({'names':['f0', 'f1', 'f2'], 'formats':['<u4', '<u2', '<u2'],
                        'offsets':[0, 0, 2]}, align=True)
        # 断言该 dtype 的总字节大小为 4
        assert_equal(dt.itemsize, 4)

        # 创建一个 uint32 类型的数组 a,视图使用上面定义的 dtype
        a = np.array([3], dtype='<u4').view(dt)
        # 对字段 'f1' 和 'f2' 赋值
        a['f1'] = 10
        a['f2'] = 36
        # 断言字段 'f0' 的值应为 10 + 36 * 256 * 256
        assert_equal(a['f0'], 10 + 36*256*256)

        # 创建一个包含字段 'f0', 'f1', 'f2' 的 dtype,但指定的偏移量不按顺序排列,指定对齐方式为 True
        dt = np.dtype({'names':['f0', 'f1', 'f2'], 'formats':['<u4', '<u2', '<u2'],
                        'offsets':[4, 0, 2]}, align=True)
        # 断言该 dtype 的总字节大小为 8
        assert_equal(dt.itemsize, 8)

        # 创建另一个与上述 dtype 结构等效的 dtype,但字段名顺序不同,指定对齐方式为 True
        dt2 = np.dtype({'names':['f2', 'f0', 'f1'],
                        'formats':['<u4', '<u2', '<u2'],
                        'offsets':[4, 0, 2]}, align=True)
        # 创建数据数组 vals 和 vals2
        vals = [(0, 1, 2), (3, 2**15-1, 4)]
        vals2 = [(0, 1, 2), (3, 2**15-1, 4)]
        # 创建数组 a 和 b,分别使用 dt 和 dt2
        a = np.array(vals, dt)
        b = np.array(vals2, dt2)
        # 断言 a 转换为 dt2 结构与 b 相等
        assert_equal(a.astype(dt2), b)
        # 断言 b 转换为 dt 结构与 a 相等
        assert_equal(b.astype(dt), a)
        # 断言 a 的视图使用 dt2 结构与 b 相等
        assert_equal(a.view(dt2), b)
        # 断言 b 的视图使用 dt 结构与 a 相等
        assert_equal(b.view(dt), a)

        # 尝试创建与其他类型重叠的对象,预期会抛出 TypeError 异常
        assert_raises(TypeError, np.dtype,
                {'names':['f0', 'f1'],
                 'formats':['O', 'i1'],
                 'offsets':[0, 2]})
        assert_raises(TypeError, np.dtype,
                {'names':['f0', 'f1'],
                 'formats':['i4', 'O'],
                 'offsets':[0, 3]})
        assert_raises(TypeError, np.dtype,
                {'names':['f0', 'f1'],
                 'formats':[[('a', 'O')], 'i1'],
                 'offsets':[0, 2]})
        assert_raises(TypeError, np.dtype,
                {'names':['f0', 'f1'],
                 'formats':['i4', [('a', 'O')]],
                 'offsets':[0, 3]})

        # 创建一个字段顺序不同但结构相同的 dtype,指定对齐方式为 True
        dt = np.dtype({'names':['f0', 'f1'],
                       'formats':['i1', 'O'],
                       'offsets':[np.dtype('intp').itemsize, 0]})
    def test_subarray_list(self, obj, dtype, expected):
        # 将 dtype 转换为 NumPy 的数据类型对象
        dtype = np.dtype(dtype)
        # 使用指定的 dtype 创建 NumPy 数组
        res = np.array(obj, dtype=dtype)

        if expected is None:
            # 如果期望结果为空,则迭代 1 维列表以填充数组
            expected = np.empty(len(obj), dtype=dtype)
            for i in range(len(expected)):
                expected[i] = obj[i]

        # 使用 assert_array_equal 函数比较 res 和 expected 数组是否相等
        assert_array_equal(res, expected)

    def test_parenthesized_single_number(self):
        # 使用 pytest 的断言检查是否引发了 TypeError 异常,并匹配指定的错误消息
        with pytest.raises(TypeError, match="not understood"):
            np.dtype("(2)f4")

        # 下面这段代码用来测试 DeprecationWarning,已经被移动到 test_deprecations.py::TestDeprecatedDTypeParenthesizedRepeatCount 中
        # 保留在这里以便于将来转换为异常检查
        with pytest.warns(DeprecationWarning,
                          match="parenthesized single number"):
            np.dtype("(2)f4,")

    def test_comma_datetime(self):
        # 创建一个复合数据类型 dt,包含三个字段
        dt = np.dtype('M8[D],datetime64[Y],i8')
        # 使用 assert_equal 函数比较 dt 和指定的数据类型是否相等
        assert_equal(dt, np.dtype([('f0', 'M8[D]'),
                                   ('f1', 'datetime64[Y]'),
                                   ('f2', 'i8')]))

    def test_from_dictproxy(self):
        # 用于测试 PR #5920 的功能
        dt = np.dtype({'names': ['a', 'b'], 'formats': ['i4', 'f4']})
        # 使用 assert_dtype_equal 函数比较两个数据类型是否相等
        assert_dtype_equal(dt, np.dtype(dt.fields))
        # 创建一个新的数据类型 dt2,与 dt 相同
        dt2 = np.dtype((np.void, dt.fields))
        # 使用 assert_equal 函数比较两个数据类型的字段是否相等
        assert_equal(dt2.fields, dt.fields)

    def test_from_dict_with_zero_width_field(self):
        # 用于测试问题 #6430 / #2196 的回归测试
        dt = np.dtype([('val1', np.float32, (0,)), ('val2', int)])
        # 创建一个相同结构的数据类型 dt2
        dt2 = np.dtype({'names': ['val1', 'val2'],
                        'formats': [(np.float32, (0,)), int]})
        # 使用 assert_dtype_equal 函数比较两个数据类型是否相等
        assert_dtype_equal(dt, dt2)
        # 使用 assert_equal 函数检查字段 'val1' 的字节大小是否为 0
        assert_equal(dt.fields['val1'][0].itemsize, 0)
        # 使用 assert_equal 函数检查整个数据类型的字节大小是否正确
        assert_equal(dt.itemsize, dt.fields['val2'][0].itemsize)

    def test_bool_commastring(self):
        # 创建一个复合数据类型 d,包含三个字段,每个字段为布尔类型
        d = np.dtype('?,?,?')  # raises?
        # 使用 assert_equal 函数检查字段数量是否为 3
        assert_equal(len(d.names), 3)
        # 遍历字段名,使用 assert_equal 函数检查每个字段的数据类型是否为布尔类型
        for n in d.names:
            assert_equal(d.fields[n][0], np.dtype('?'))

    def test_nonint_offsets(self):
        # gh-8059
        # 定义一个函数 make_dtype,用于创建指定偏移量的数据类型
        def make_dtype(off):
            return np.dtype({'names': ['A'], 'formats': ['i4'],
                             'offsets': [off]})

        # 使用 assert_raises 函数检查 make_dtype 函数在输入错误类型时是否会引发异常
        assert_raises(TypeError, make_dtype, 'ASD')
        # 使用 assert_raises 函数检查 make_dtype 函数在输入超出范围的偏移量时是否会引发异常
        assert_raises(OverflowError, make_dtype, 2**70)
        # 使用 assert_raises 函数检查 make_dtype 函数在输入错误类型的偏移量时是否会引发异常
        assert_raises(TypeError, make_dtype, 2.3)
        # 使用 assert_raises 函数检查 make_dtype 函数在输入负数偏移量时是否会引发异常
        assert_raises(ValueError, make_dtype, -10)

        # 创建一个数据类型 dt,偏移量为 0,不应引发异常
        dt = make_dtype(np.uint32(0))
        # 调用 np.zeros 函数创建一个数组,使用指定的数据类型 dt
        np.zeros(1, dtype=dt)[0].item()

    def test_fields_by_index(self):
        # 创建一个复合数据类型 dt,包含两个字段
        dt = np.dtype([('a', np.int8), ('b', np.float32, 3)])
        # 使用 assert_dtype_equal 函数比较索引 0 处的字段数据类型是否为 np.int8
        assert_dtype_equal(dt[0], np.dtype(np.int8))
        # 使用 assert_dtype_equal 函数比较索引 1 处的字段数据类型是否为 (np.float32, 3)
        assert_dtype_equal(dt[1], np.dtype((np.float32, 3)))
        # 使用 assert_dtype_equal 函数比较负索引 -1 处的字段数据类型是否与索引 1 处相同
        assert_dtype_equal(dt[-1], dt[1])
        # 使用 assert_dtype_equal 函数比较负索引 -2 处的字段数据类型是否与索引 0 处相同
        assert_dtype_equal(dt[-2], dt[0])
        # 使用 assert_raises 函数检查索引 -3 是否引发 IndexError 异常
        assert_raises(IndexError, lambda: dt[-3])

        # 使用 assert_raises 函数检查传入非整数索引是否引发 TypeError 异常
        assert_raises(TypeError, operator.getitem, dt, 3.0)

        # 使用 assert_equal 函数比较索引 1 处的字段数据类型是否与索引 np.int8(1) 处相同
        assert_equal(dt[1], dt[np.int8(1)])
    @pytest.mark.parametrize('align_flag',[False, True])
    def test_multifield_index(self, align_flag):
        # 使用 pytest 的参数化标记来定义测试用例,测试多字段索引功能
        # align_flag 表示是否对齐的标志,在不同的测试参数下进行测试

        # 定义一个结构化数据类型 dt,包含字段 'title' 和 'col1',以及 'A' 和 'B' 两个字段
        dt = np.dtype([
            (('title', 'col1'), '<U20'), ('A', '<f8'), ('B', '<f8')
        ], align=align_flag)

        # 通过列表索引获取子数据类型 dt_sub,包含 'B' 和 'col1' 两个字段
        dt_sub = dt[['B', 'col1']]
        assert_equal(
            dt_sub,
            np.dtype({
                'names': ['B', 'col1'],
                'formats': ['<f8', '<U20'],
                'offsets': [88, 0],
                'titles': [None, 'title'],
                'itemsize': 96
            })
        )
        assert_equal(dt_sub.isalignedstruct, align_flag)

        # 通过列表索引获取子数据类型 dt_sub,只包含 'B' 字段
        dt_sub = dt[['B']]
        assert_equal(
            dt_sub,
            np.dtype({
                'names': ['B'],
                'formats': ['<f8'],
                'offsets': [88],
                'itemsize': 96
            })
        )
        assert_equal(dt_sub.isalignedstruct, align_flag)

        # 通过空列表索引获取子数据类型 dt_sub,不包含任何字段
        dt_sub = dt[[]]
        assert_equal(
            dt_sub,
            np.dtype({
                'names': [],
                'formats': [],
                'offsets': [],
                'itemsize': 96
            })
        )
        assert_equal(dt_sub.isalignedstruct, align_flag)

        # 测试异常情况:尝试使用不支持的索引方式
        assert_raises(TypeError, operator.getitem, dt, ())
        assert_raises(TypeError, operator.getitem, dt, [1, 2, 3])
        assert_raises(TypeError, operator.getitem, dt, ['col1', 2])
        assert_raises(KeyError, operator.getitem, dt, ['fake'])
        assert_raises(KeyError, operator.getitem, dt, ['title'])
        assert_raises(ValueError, operator.getitem, dt, ['col1', 'col1'])

    def test_partial_dict(self):
        # 测试异常情况:创建结构化数据类型时缺少 'names' 字段
        assert_raises(ValueError, np.dtype,
                {'formats': ['i4', 'i4'], 'f0': ('i4', 0), 'f1':('i4', 4)})

    def test_fieldless_views(self):
        # 创建一个没有字段的数组 a
        a = np.zeros(2, dtype={'names':[], 'formats':[], 'offsets':[],
                               'itemsize':8})
        # 测试异常情况:尝试视图转换为没有字段的数据类型
        assert_raises(ValueError, a.view, np.dtype([]))

        # 创建一个没有基础数据类型的数据类型 d
        d = np.dtype((np.dtype([]), 10))
        assert_equal(d.shape, (10,))
        assert_equal(d.itemsize, 0)
        assert_equal(d.base, np.dtype([]))

        # 创建一个空的数组 arr,数据类型为没有字段的数据类型 []
        arr = np.fromiter((() for i in range(10)), [])
        assert_equal(arr.dtype, np.dtype([]))
        assert_raises(ValueError, np.frombuffer, b'', dtype=[])
        assert_equal(np.frombuffer(b'', dtype=[], count=2),
                     np.empty(2, dtype=[]))

        # 测试异常情况:尝试创建不支持的数据类型 ([], 'f8')
        assert_raises(ValueError, np.dtype, ([], 'f8'))
        assert_raises(ValueError, np.zeros(1, dtype='i4').view, [])

        # 对比两个没有字段的数组是否相等
        assert_equal(np.zeros(2, dtype=[]) == np.zeros(2, dtype=[]),
                     np.ones(2, dtype=bool))

        # 对比两个没有字段的数组是否相等
        assert_equal(np.zeros((1, 2), dtype=[]) == a,
                     np.ones((1, 2), dtype=bool))
    def test_nonstructured_with_object(self):
        # 使用说明:这是一个测试函数,用于测试非结构化的包含对象的情况
        # 在这里,如果下面关于 dtype 的断言失败,测试将变得毫无意义(这是可以接受的)
        
        # 创建一个空的记录数组,dtype 设置为对象类型
        arr = np.recarray((0,), dtype="O")
        
        # 断言数组的 dtype 中没有命名字段
        assert arr.dtype.names is None  # no fields
        
        # 断言数组的 dtype 声明包含对象
        assert arr.dtype.hasobject  # but claims to contain objects
        
        # 删除数组以释放资源,此操作之前曾经失败过
        del arr  # the deletion failed previously.
class TestSubarray:
    # 定义测试类 TestSubarray

    def test_single_subarray(self):
        # 定义测试方法 test_single_subarray
        a = np.dtype((int, (2)))
        # 创建一个 NumPy 数据类型 a,元素类型为 int,形状为 (2)
        b = np.dtype((int, (2,)))
        # 创建一个 NumPy 数据类型 b,元素类型为 int,形状为 (2,)
        assert_dtype_equal(a, b)
        # 断言 a 和 b 的数据类型相等

        assert_equal(type(a.subdtype[1]), tuple)
        # 断言 a 的第二个子数据类型为元组类型
        assert_equal(type(b.subdtype[1]), tuple)
        # 断言 b 的第二个子数据类型为元组类型

    def test_equivalent_record(self):
        """Test whether equivalent subarray dtypes hash the same."""
        # 测试等价子数组数据类型是否具有相同的哈希值
        a = np.dtype((int, (2, 3)))
        # 创建一个形状为 (2, 3) 的 NumPy 数据类型 a,元素类型为 int
        b = np.dtype((int, (2, 3)))
        # 创建一个形状为 (2, 3) 的 NumPy 数据类型 b,元素类型为 int
        assert_dtype_equal(a, b)
        # 断言 a 和 b 的数据类型相等

    def test_nonequivalent_record(self):
        """Test whether different subarray dtypes hash differently."""
        # 测试不同的子数组数据类型是否具有不同的哈希值
        a = np.dtype((int, (2, 3)))
        # 创建一个形状为 (2, 3) 的 NumPy 数据类型 a,元素类型为 int
        b = np.dtype((int, (3, 2)))
        # 创建一个形状为 (3, 2) 的 NumPy 数据类型 b,元素类型为 int
        assert_dtype_not_equal(a, b)
        # 断言 a 和 b 的数据类型不相等

        a = np.dtype((int, (2, 3)))
        # 创建一个形状为 (2, 3) 的 NumPy 数据类型 a,元素类型为 int
        b = np.dtype((int, (2, 2)))
        # 创建一个形状为 (2, 2) 的 NumPy 数据类型 b,元素类型为 int
        assert_dtype_not_equal(a, b)
        # 断言 a 和 b 的数据类型不相等

        a = np.dtype((int, (1, 2, 3)))
        # 创建一个形状为 (1, 2, 3) 的 NumPy 数据类型 a,元素类型为 int
        b = np.dtype((int, (1, 2)))
        # 创建一个形状为 (1, 2) 的 NumPy 数据类型 b,元素类型为 int
        assert_dtype_not_equal(a, b)
        # 断言 a 和 b 的数据类型不相等

    def test_shape_equal(self):
        """Test some data types that are equal"""
        # 测试一些相等的数据类型
        assert_dtype_equal(np.dtype('f8'), np.dtype(('f8', tuple())))
        # 断言两个数据类型 'f8' 和 ('f8', ()) 相等
        assert_dtype_equal(np.dtype('(1,)f8'), np.dtype(('f8', 1)))
        # 断言两个数据类型 '(1,)f8' 和 ('f8', 1) 相等
        assert np.dtype(('f8', 1)).shape == (1,)
        # 断言数据类型 ('f8', 1) 的形状为 (1,)
        assert_dtype_equal(np.dtype((int, 2)), np.dtype((int, (2,))))
        # 断言两个数据类型 (int, 2) 和 (int, (2,)) 相等
        assert_dtype_equal(np.dtype(('<f4', (3, 2))), np.dtype(('<f4', (3, 2))))
        # 断言两个数据类型 ('<f4', (3, 2)) 相等
        d = ([('a', 'f4', (1, 2)), ('b', 'f8', (3, 1))], (3, 2))
        assert_dtype_equal(np.dtype(d), np.dtype(d))
        # 断言自定义结构化数据类型 d 相等

    def test_shape_simple(self):
        """Test some simple cases that shouldn't be equal"""
        # 测试一些不应相等的简单情况
        assert_dtype_not_equal(np.dtype('f8'), np.dtype(('f8', (1,))))
        # 断言数据类型 'f8' 和 ('f8', (1,)) 不相等
        assert_dtype_not_equal(np.dtype(('f8', (1,))), np.dtype(('f8', (1, 1))))
        # 断言数据类型 ('f8', (1,)) 和 ('f8', (1, 1)) 不相等
        assert_dtype_not_equal(np.dtype(('f4', (3, 2))), np.dtype(('f4', (2, 3))))
        # 断言数据类型 ('f4', (3, 2)) 和 ('f4', (2, 3)) 不相等

    def test_shape_monster(self):
        """Test some more complicated cases that shouldn't be equal"""
        # 测试一些更复杂的情况,它们不应该相等
        assert_dtype_not_equal(
            np.dtype(([('a', 'f4', (2, 1)), ('b', 'f8', (1, 3))], (2, 2))),
            np.dtype(([('a', 'f4', (1, 2)), ('b', 'f8', (1, 3))], (2, 2))))
        # 断言两个复杂的数据类型不相等
        assert_dtype_not_equal(
            np.dtype(([('a', 'f4', (2, 1)), ('b', 'f8', (1, 3))], (2, 2))),
            np.dtype(([('a', 'f4', (2, 1)), ('b', 'i8', (1, 3))], (2, 2))))
        # 断言两个复杂的数据类型不相等
        assert_dtype_not_equal(
            np.dtype(([('a', 'f4', (2, 1)), ('b', 'f8', (1, 3))], (2, 2))),
            np.dtype(([('e', 'f8', (1, 3)), ('d', 'f4', (2, 1))], (2, 2))))
        # 断言两个复杂的数据类型不相等
        assert_dtype_not_equal(
            np.dtype(([('a', [('a', 'i4', 6)], (2, 1)), ('b', 'f8', (1, 3))], (2, 2))),
            np.dtype(([('a', [('a', 'u4', 6)], (2, 1)), ('b', 'f8', (1, 3))], (2, 2))))
        # 断言两个复杂的数据类型不相等
    def test_shape_sequence(self):
        # 测试形状序列的函数
        # 任何整数序列都可以作为形状,但结果应该是一个包含基本类型整数的元组(不可变的)。

        # 创建一个包含整数的numpy数组,指定数据类型为int16
        a = np.array([1, 2, 3], dtype=np.int16)
        
        # 创建一个普通的Python列表
        l = [1, 2, 3]

        # 创建一个自定义的数据类型,其中'a'字段包含一个float32类型的数组
        dt = np.dtype([('a', 'f4', a)])
        
        # 断言'a'字段的形状是一个元组
        assert_(isinstance(dt['a'].shape, tuple))
        
        # 断言'a'字段形状元组的第一个元素是整数类型
        assert_(isinstance(dt['a'].shape[0], int))

        # 创建一个自定义的数据类型,其中'a'字段包含一个列表
        dt = np.dtype([('a', 'f4', l)])
        
        # 断言'a'字段的形状是一个元组
        assert_(isinstance(dt['a'].shape, tuple))
        
        # 未提供具体的断言信息,保留空注释行

        # 定义一个模拟整数的类
        class IntLike:
            def __index__(self):
                return 3

            def __int__(self):
                # (a PyNumber_Check fails without __int__)
                return 3

        # 创建一个自定义的数据类型,其中'a'字段包含一个IntLike类的实例
        dt = np.dtype([('a', 'f4', IntLike())])
        
        # 断言'a'字段的形状是一个元组
        assert_(isinstance(dt['a'].shape, tuple))
        
        # 断言'a'字段形状元组的第一个元素是整数类型
        assert_(isinstance(dt['a'].shape[0], int))

        # 创建一个自定义的数据类型,其中'a'字段包含一个元组(IntLike的实例)
        dt = np.dtype([('a', 'f4', (IntLike(),))])
        
        # 断言'a'字段的形状是一个元组
        assert_(isinstance(dt['a'].shape, tuple))
        
        # 断言'a'字段形状元组的第一个元素是整数类型
        assert_(isinstance(dt['a'].shape[0], int))

    def test_shape_matches_ndim(self):
        # 测试形状匹配维数的函数

        # 创建一个自定义的数据类型,其中'a'字段是一个空元组
        dt = np.dtype([('a', 'f4', ())])
        
        # 断言'a'字段的形状是一个空元组
        assert_equal(dt['a'].shape, ())
        
        # 断言'a'字段的维数是0
        assert_equal(dt['a'].ndim, 0)

        # 创建一个自定义的数据类型,其中'a'字段是一个float32类型
        dt = np.dtype([('a', 'f4')])
        
        # 断言'a'字段的形状是一个空元组
        assert_equal(dt['a'].shape, ())
        
        # 断言'a'字段的维数是0
        assert_equal(dt['a'].ndim, 0)

        # 创建一个自定义的数据类型,其中'a'字段是一个长度为4的float32类型数组
        dt = np.dtype([('a', 'f4', 4)])
        
        # 断言'a'字段的形状是一个包含一个元素4的元组
        assert_equal(dt['a'].shape, (4,))
        
        # 断言'a'字段的维数是1
        assert_equal(dt['a'].ndim, 1)

        # 创建一个自定义的数据类型,其中'a'字段是一个形状为(1, 2, 3)的float32类型数组
        dt = np.dtype([('a', 'f4', (1, 2, 3))])
        
        # 断言'a'字段的形状是一个包含(1, 2, 3)的元组
        assert_equal(dt['a'].shape, (1, 2, 3))
        
        # 断言'a'字段的维数是3
        assert_equal(dt['a'].ndim, 3)

    def test_shape_invalid(self):
        # 测试无效形状的函数
        # 检查形状是否有效。

        # 获取np.intc类型的最大整数
        max_int = np.iinfo(np.intc).max
        
        # 获取np.intp类型的最大整数
        max_intp = np.iinfo(np.intp).max
        
        # 过大的值(数据类型的一部分)
        assert_raises(ValueError, np.dtype, [('a', 'f4', max_int // 4 + 1)])
        assert_raises(ValueError, np.dtype, [('a', 'f4', max_int + 1)])
        assert_raises(ValueError, np.dtype, [('a', 'f4', (max_int, 2))])
        
        # 采用不同的代码路径(更早失败:
        assert_raises(ValueError, np.dtype, [('a', 'f4', max_intp + 1)])
        
        # 负值
        assert_raises(ValueError, np.dtype, [('a', 'f4', -1)])
        assert_raises(ValueError, np.dtype, [('a', 'f4', (-1, -1))])

    def test_alignment(self):
        # 检查子数组是否对齐的函数
        t1 = np.dtype('(1,)i4', align=True)
        t2 = np.dtype('2i4', align=True)
        
        # 断言t1和t2的对齐方式相同
        assert_equal(t1.alignment, t2.alignment)

    def test_aligned_empty(self):
        # 主要是gh-19696的回归测试:完全构造失败
        # 测试对齐空数据类型的函数

        # 创建一个空的数据类型,指定对齐方式为True
        dt = np.dtype([], align=True)
        
        # 断言dt等于空的数据类型
        assert dt == np.dtype([])
        
        # 创建一个空的数据类型,使用字典形式指定结构
        dt = np.dtype({"names": [], "formats": [], "itemsize": 0}, align=True)
        
        # 断言dt等于空的数据类型
        assert dt == np.dtype([])
    # 定义一个测试方法,验证子数组的基本项
    def test_subarray_base_item(self):
        # 创建一个包含三个元素的全一数组,每个元素为一个结构化数组的字段 "f",包含一个长度为 3 的整数子数组
        arr = np.ones(3, dtype=[("f", "i", 3)])
        
        # 通过提取字段 "f",将子数组转换为视图
        assert arr["f"].base is arr
        
        # 提取结构化数组的项,然后检查元组组成部分
        item = arr.item(0)
        assert type(item) is tuple and len(item) == 1
        assert item[0].base is arr

    # 定义一个测试方法,验证子数组的强制类型转换为对象时的复制行为
    def test_subarray_cast_copies(self):
        # 在旧版本的 NumPy 中,强制类型转换为对象时不会复制,但它们会出现所有权问题
        # 在 1.21 版本之后(我认为),这些问题基本上会导致崩溃。这里定义正确的行为为复制。
        arr = np.ones(3, dtype=[("f", "i", 3)])
        
        # 将数组强制类型转换为对象类型
        cast = arr.astype(object)
        
        # 遍历转换后的对象数组
        for fields in cast:
            # 每个元素应为一个元组,长度为 1
            assert type(fields) == tuple and len(fields) == 1
            
            # 获取子数组
            subarr = fields[0]
            
            # 子数组的基应为 None,即它不共享内存
            assert subarr.base is None
            
            # 检查子数组是否拥有自己的数据
            assert subarr.flags.owndata
def iter_struct_object_dtypes():
    """
    Iterates over a few complex dtypes and object pattern which
    fill the array with a given object (defaults to a singleton).

    Yields
    ------
    dtype : dtype
        Data type for NumPy arrays.
    pattern : tuple
        Structured tuple for use with `np.array`.
    count : int
        Number of objects stored in the dtype.
    singleton : object
        A singleton object. The returned pattern is constructed so that
        all objects inside the datatype are set to the singleton.
    """
    # 创建一个普通的 Python 对象
    obj = object()

    # 定义一个包含子数组的结构化数据类型
    dt = np.dtype([('b', 'O', (2, 3))])
    # 创建填充数据为单例对象的模式
    p = ([[obj] * 3] * 2,)
    # 使用 pytest 的 parametrize 生成器返回参数化测试的参数
    yield pytest.param(dt, p, 6, obj, id="<subarray>")

    # 定义包含字段和子数组的结构化数据类型
    dt = np.dtype([('a', 'i4'), ('b', 'O', (2, 3))])
    # 创建填充数据为单例对象的模式
    p = (0, [[obj] * 3] * 2)
    yield pytest.param(dt, p, 6, obj, id="<subarray in field>")

    # 定义包含结构化子数组的复杂结构化数据类型
    dt = np.dtype([('a', 'i4'),
                   ('b', [('ba', 'O'), ('bb', 'i1')], (2, 3))])
    # 创建填充数据为单例对象的模式
    p = (0, [[(obj, 0)] * 3] * 2)
    yield pytest.param(dt, p, 6, obj, id="<structured subarray 1>")

    # 定义包含两个对象字段的结构化数据类型
    dt = np.dtype([('a', 'i4'),
                   ('b', [('ba', 'O'), ('bb', 'O')], (2, 3))])
    # 创建填充数据为单例对象的模式
    p = (0, [[(obj, obj)] * 3] * 2)
    yield pytest.param(dt, p, 12, obj, id="<structured subarray 2>")


@pytest.mark.skipif(
    sys.version_info >= (3, 12),
    reason="Python 3.12 has immortal refcounts, this test will no longer "
           "work. See gh-23986"
)
@pytest.mark.skipif(not HAS_REFCOUNT, reason="Python lacks refcounts")
class TestStructuredObjectRefcounting:
    """These tests cover various uses of complicated structured types which
    include objects and thus require reference counting.
    """
    @pytest.mark.parametrize(['dt', 'pat', 'count', 'singleton'],
                             iter_struct_object_dtypes())
    @pytest.mark.parametrize(["creation_func", "creation_obj"], [
        pytest.param(np.empty, None,
             # None is probably used for too many things
             marks=pytest.mark.skip("unreliable due to python's behaviour")),
        (np.ones, 1),
        (np.zeros, 0)])
    def test_structured_object_create_delete(self, dt, pat, count, singleton,
                                             creation_func, creation_obj):
        """Structured object reference counting in creation and deletion"""
        # 测试假设 0, 1 和 None 是单例对象
        gc.collect()
        # 获取创建对象前的引用计数
        before = sys.getrefcount(creation_obj)
        # 创建指定结构化数据类型的数组
        arr = creation_func(3, dt)

        # 获取创建对象后的引用计数
        now = sys.getrefcount(creation_obj)
        # 断言创建后引用计数增加了特定数量的对象
        assert now - before == count * 3
        # 删除数组对象
        del arr
        # 获取删除对象后的引用计数
        now = sys.getrefcount(creation_obj)
        # 断言删除后引用计数与创建前相同
        assert now == before

    @pytest.mark.parametrize(['dt', 'pat', 'count', 'singleton'],
                             iter_struct_object_dtypes())
    def test_structured_object_item_setting(self, dt, pat, count, singleton):
        """Structured object reference counting for simple item setting"""
        # 定义变量 `one` 并赋值为整数 `1`
        one = 1

        # 执行垃圾回收,获取设置前 `singleton` 的引用计数
        gc.collect()
        before = sys.getrefcount(singleton)
        # 创建一个包含多个 `pat` 副本的数组,并检查引用计数的变化
        arr = np.array([pat] * 3, dt)
        assert sys.getrefcount(singleton) - before == count * 3
        # 用 `1` 填充数组,并检查其是否正确替换:
        before2 = sys.getrefcount(one)
        arr[...] = one
        after2 = sys.getrefcount(one)
        assert after2 - before2 == count * 3
        # 删除数组 `arr`,执行垃圾回收
        del arr
        gc.collect()
        # 检查 `one` 的引用计数是否恢复到操作前的状态
        assert sys.getrefcount(one) == before2
        # 检查 `singleton` 的引用计数是否恢复到操作前的状态
        assert sys.getrefcount(singleton) == before

    @pytest.mark.parametrize(['dt', 'pat', 'count', 'singleton'],
                             iter_struct_object_dtypes())
    @pytest.mark.parametrize(
        ['shape', 'index', 'items_changed'],
        [((3,), ([0, 2],), 2),
         ((3, 2), ([0, 2], slice(None)), 4),
         ((3, 2), ([0, 2], [1]), 2),
         ((3,), ([True, False, True]), 2)])
    def test_structured_object_indexing(self, shape, index, items_changed,
                                        dt, pat, count, singleton):
        """Structured object reference counting for advanced indexing."""
        # 使用两个较小的负值(应为单例,但更不容易遇到竞争条件)。
        # 在某些线程环境中使用 0 和 1 可能会失败。如果再次失败,应删除所有显式检查,
        # 依赖于 `pytest-leaks` 的引用计数检查器。
        val0 = -4
        val1 = -5

        # 创建一个填充了 `val0` 的数组
        arr = np.full(shape, val0, dt)

        # 执行垃圾回收,获取设置前 `val0` 和 `val1` 的引用计数
        gc.collect()
        before_val0 = sys.getrefcount(val0)
        before_val1 = sys.getrefcount(val1)
        # 测试获取索引项
        part = arr[index]
        after_val0 = sys.getrefcount(val0)
        assert after_val0 - before_val0 == count * items_changed
        # 删除 `part`
        del part
        # 测试设置索引项
        arr[index] = val1
        gc.collect()
        after_val0 = sys.getrefcount(val0)
        after_val1 = sys.getrefcount(val1)
        assert before_val0 - after_val0 == count * items_changed
        assert after_val1 - before_val1 == count * items_changed

    @pytest.mark.parametrize(['dt', 'pat', 'count', 'singleton'],
                             iter_struct_object_dtypes())
    # 定义一个测试方法,用于测试结构化对象的取值和重复操作
    def test_structured_object_take_and_repeat(self, dt, pat, count, singleton):
        """Structured object reference counting for specialized functions.
        The older functions such as take and repeat use different code paths
        then item setting (when writing this).
        """
        # 定义一个索引列表
        indices = [0, 1]

        # 创建一个包含多个重复项的数组,并指定数据类型为 dt
        arr = np.array([pat] * 3, dt)
        # 手动执行垃圾回收
        gc.collect()
        # 获取操作前单例对象的引用计数
        before = sys.getrefcount(singleton)
        # 执行 take 操作,并获取结果
        res = arr.take(indices)
        # 获取操作后单例对象的引用计数
        after = sys.getrefcount(singleton)
        # 断言 take 操作后引用计数的变化是否符合预期
        assert after - before == count * 2
        # 对结果数组进行重复操作
        new = res.repeat(10)
        # 再次手动执行垃圾回收
        gc.collect()
        # 获取重复操作后单例对象的引用计数
        after_repeat = sys.getrefcount(singleton)
        # 断言重复操作后引用计数的变化是否符合预期
        assert after_repeat - after == count * 2 * 10
class TestStructuredDtypeSparseFields:
    """Tests subarray fields which contain sparse dtypes so that
    not all memory is used by the dtype work. Such dtype's should
    leave the underlying memory unchanged.
    """

    # 定义一个结构化的 NumPy 数据类型,包含一个名为 'a' 的字段,字段包含稀疏数据类型
    dtype = np.dtype([('a', {'names':['aa', 'ab'], 'formats':['f', 'f'],
                             'offsets':[0, 4]}, (2, 3))])

    # 定义一个稀疏的数据类型,是上述 dtype 中 'a' 字段的一部分,只包含名为 'ab' 的字段
    sparse_dtype = np.dtype([('a', {'names':['ab'], 'formats':['f'],
                                    'offsets':[4]}, (2, 3))])

    def test_sparse_field_assignment(self):
        # 创建一个形状为 (3,) 的零数组,使用定义好的 dtype
        arr = np.zeros(3, self.dtype)
        
        # 将 arr 视图为稀疏数据类型 sparse_dtype
        sparse_arr = arr.view(self.sparse_dtype)

        # 将 sparse_arr 的所有元素设置为 np.float32 类型的最大值
        sparse_arr[...] = np.finfo(np.float32).max
        
        # 断言通过索引 'a' 字段 'aa' 的值为形状为 (3, 2, 3) 的零数组
        assert_array_equal(arr["a"]["aa"], np.zeros((3, 2, 3)))

    def test_sparse_field_assignment_fancy(self):
        # 创建一个形状为 (3,) 的零数组,使用定义好的 dtype
        arr = np.zeros(3, self.dtype)
        
        # 将 arr 视图为稀疏数据类型 sparse_dtype
        sparse_arr = arr.view(self.sparse_dtype)

        # 对 sparse_arr 使用高级索引,将第 [0, 1, 2] 行设置为 np.float32 类型的最大值
        sparse_arr[[0, 1, 2]] = np.finfo(np.float32).max
        
        # 断言通过索引 'a' 字段 'aa' 的值为形状为 (3, 2, 3) 的零数组
        assert_array_equal(arr["a"]["aa"], np.zeros((3, 2, 3)))


class TestMonsterType:
    """Test deeply nested subtypes."""

    def test1(self):
        # 定义一个简单的结构化数据类型 simple1
        simple1 = np.dtype({'names': ['r', 'b'], 'formats': ['u1', 'u1'],
                            'titles': ['Red pixel', 'Blue pixel']})
        
        # 定义一个结构化数据类型 a,包含 'yo'、'ye' 和 'yi' 三个字段
        a = np.dtype([('yo', int), ('ye', simple1),
                      ('yi', np.dtype((int, (3, 2))))])
        
        # 定义一个结构化数据类型 b,与 a 相同
        b = np.dtype([('yo', int), ('ye', simple1),
                      ('yi', np.dtype((int, (3, 2))))])
        
        # 断言 a 和 b 结构化数据类型相等
        assert_dtype_equal(a, b)
        
        # 定义一个结构化数据类型 c,与 a 相同,但 'yi' 字段的类型为 a
        c = np.dtype([('yo', int), ('ye', simple1),
                      ('yi', np.dtype((a, (3, 2))))])
        
        # 定义一个结构化数据类型 d,与 c 相同
        d = np.dtype([('yo', int), ('ye', simple1),
                      ('yi', np.dtype((a, (3, 2))))])
        
        # 断言 c 和 d 结构化数据类型相等
        assert_dtype_equal(c, d)

    @pytest.mark.skipif(IS_PYSTON, reason="Pyston disables recursion checking")
    def test_list_recursion(self):
        # 创建一个空列表 l
        l = list()
        
        # 向列表 l 添加一个包含 'f' 和列表 l 自身的元组
        l.append(('f', l))
        
        # 使用 pytest 的断言检查是否会引发 RecursionError
        with pytest.raises(RecursionError):
            np.dtype(l)

    @pytest.mark.skipif(IS_PYSTON, reason="Pyston disables recursion checking")
    def test_tuple_recursion(self):
        # 定义变量 d 为 np.int32
        d = np.int32
        
        # 将 d 变量迭代增加到 100000 次,每次迭代 d 变量为一个包含 d 和 (1,) 的元组
        for i in range(100000):
            d = (d, (1,))
        
        # 使用 pytest 的断言检查是否会引发 RecursionError
        with pytest.raises(RecursionError):
            np.dtype(d)

    @pytest.mark.skipif(IS_PYSTON, reason="Pyston disables recursion checking")
    def test_dict_recursion(self):
        # 定义一个字典 d,包含 'names' 键和值为列表 ['self'],'formats' 键和值为 [None],'offsets' 键和值为 [0]
        d = dict(names=['self'], formats=[None], offsets=[0])
        
        # 将 d 字典中 'formats' 键的值设为 d 字典自身
        d['formats'][0] = d
        
        # 使用 pytest 的断言检查是否会引发 RecursionError
        with pytest.raises(RecursionError):
            np.dtype(d)


class TestMetadata:
    def test_no_metadata(self):
        # 创建一个简单的数据类型 d,为 np.int32
        d = np.dtype(int)
        
        # 使用 assert_ 函数检查 d 的 metadata 是否为 None
        assert_(d.metadata is None)

    def test_metadata_takes_dict(self):
        # 创建一个简单的数据类型 d,为 np.int32,同时定义 metadata 为 {'datum': 1}
        d = np.dtype(int, metadata={'datum': 1})
        
        # 使用 assert_ 函数检查 d 的 metadata 是否为 {'datum': 1}
        assert_(d.metadata == {'datum': 1})
    # 测试函数:验证 metadata 参数拒绝非字典类型的输入
    def test_metadata_rejects_nondict(self):
        # 断言抛出 TypeError 异常,当 metadata 参数为 'datum' 字符串时
        assert_raises(TypeError, np.dtype, int, metadata='datum')
        # 断言抛出 TypeError 异常,当 metadata 参数为整数 1 时
        assert_raises(TypeError, np.dtype, int, metadata=1)
        # 断言抛出 TypeError 异常,当 metadata 参数为 None 时
        assert_raises(TypeError, np.dtype, int, metadata=None)

    # 测试函数:验证嵌套 metadata 的情况
    def test_nested_metadata(self):
        # 创建一个结构化数据类型 d,包含字段 'a',其数据类型为 int,同时具有 metadata {'datum': 1}
        d = np.dtype([('a', np.dtype(int, metadata={'datum': 1}))])
        # 断言字段 'a' 的 metadata 等于 {'datum': 1}
        assert_(d['a'].metadata == {'datum': 1})

    # 测试函数:验证基础数据类型的 metadata 被正确复制
    def test_base_metadata_copied(self):
        # 创建一个 void 类型的数据结构,包含两个 i4 类型字段,并且其 metadata 为 {'datum': 1}
        d = np.dtype((np.void, np.dtype('i4,i4', metadata={'datum': 1})))
        # 断言该数据结构的 metadata 等于 {'datum': 1}
        assert_(d.metadata == {'datum': 1})
# 定义一个名为 TestString 的类
class TestString:
    # 测试函数 test_repr_structured
    def test_repr_structured(self):
        # 创建结构化数据类型 dt
        dt = np.dtype([('top', [('tiles', ('>f4', (64, 64)), (1,)),
                                ('rtile', '>f4', (64, 36))], (3,)),
                       ('bottom', [('bleft', ('>f4', (8, 64)), (1,)),
                                   ('bright', '>f4', (8, 36))])])
        # 断言函数 repr(dt) 的返回值是否符合预期
        assert_equal(repr(dt),
                     "dtype([('top', [('tiles', ('>f4', (64, 64)), (1,)), "
                     "('rtile', '>f4', (64, 36))], (3,)), "
                     "('bottom', [('bleft', ('>f4', (8, 64)), (1,)), "
                     "('bright', '>f4', (8, 36))])])")

        # 重新赋值结构化数据类型 dt
        dt = np.dtype({'names': ['r', 'g', 'b'], 'formats': ['u1', 'u1', 'u1'],
                        'offsets': [0, 1, 2],
                        'titles': ['Red pixel', 'Green pixel', 'Blue pixel']},
                        align=True)
        # 断言函数 repr(dt) 的返回值是否符合预期
        assert_equal(repr(dt),
                    "dtype([(('Red pixel', 'r'), 'u1'), "
                    "(('Green pixel', 'g'), 'u1'), "
                    "(('Blue pixel', 'b'), 'u1')], align=True)")

    # 测试函数 test_repr_structured_not_packed
    def test_repr_structured_not_packed(self):
        dt = np.dtype({'names': ['rgba', 'r', 'g', 'b'],
                       'formats': ['<u4', 'u1', 'u1', 'u1'],
                       'offsets': [0, 0, 1, 2],
                       'titles': ['Color', 'Red pixel',
                                  'Green pixel', 'Blue pixel']}, align=True)
        # 断言函数 repr(dt) 的返回值是否符合预期
        assert_equal(repr(dt),
                    "dtype({'names': ['rgba', 'r', 'g', 'b'],"
                    " 'formats': ['<u4', 'u1', 'u1', 'u1'],"
                    " 'offsets': [0, 0, 1, 2],"
                    " 'titles': ['Color', 'Red pixel', "
                                "'Green pixel', 'Blue pixel'],"
                    " 'itemsize': 4}, align=True)")

        # 重新赋值结构化数据类型 dt
        dt = np.dtype({'names': ['r', 'b'], 'formats': ['u1', 'u1'],
                        'offsets': [0, 2],
                        'titles': ['Red pixel', 'Blue pixel'],
                        'itemsize': 4})
        # 断言函数 repr(dt) 的返回值是否符合预期
        assert_equal(repr(dt),
                    "dtype({'names': ['r', 'b'], "
                    "'formats': ['u1', 'u1'], "
                    "'offsets': [0, 2], "
                    "'titles': ['Red pixel', 'Blue pixel'], "
                    "'itemsize': 4})")

    # 测试函数 test_repr_structured_datetime
    def test_repr_structured_datetime(self):
        dt = np.dtype([('a', '<M8[D]'), ('b', '<m8[us]')])
        # 断言函数 repr(dt) 的返回值是否符合预期
        assert_equal(repr(dt),
                    "dtype([('a', '<M8[D]'), ('b', '<m8[us]')])")

    # 测试函数 test_repr_str_subarray
    def test_repr_str_subarray(self):
        dt = np.dtype(('<i2', (1,)))
        # 断言函数 repr(dt) 的返回值是否符合预期
        assert_equal(repr(dt), "dtype(('<i2', (1,)))")
        # 断言函数 str(dt) 的返回值是否符合预期
        assert_equal(str(dt), "('<i2', (1,))")

    # 测试函数 test_base_dtype_with_object_type
    def test_base_dtype_with_object_type(self):
        # 验证 Issue gh-2798,应该不报错
        np.array(['a'], dtype="O").astype(("O", [("name", "O")]))

    # 测试函数 test_empty_string_to_object
    def test_empty_string_to_object(self):
        # 验证 Pull request #4722
        np.array(["", ""]).astype(object)
    # 定义测试方法,用于测试 np.record 类型的 dtype

    def test_void_subclass_unsized(self):
        # 创建一个无大小限制的 np.record 类型的 dtype
        dt = np.dtype(np.record)
        # 断言返回值的表达式,应为 "dtype('V')"
        assert_equal(repr(dt), "dtype('V')")
        # 断言返回值的字符串表示,应为 '|V0'
        assert_equal(str(dt), '|V0')
        # 断言 dtype 的名称属性,应为 'record'
        assert_equal(dt.name, 'record')

    def test_void_subclass_sized(self):
        # 创建一个大小为 2 的 np.record 类型的 dtype
        dt = np.dtype((np.record, 2))
        # 断言返回值的表达式,应为 "dtype('V2')"
        assert_equal(repr(dt), "dtype('V2')")
        # 断言返回值的字符串表示,应为 '|V2'
        assert_equal(str(dt), '|V2')
        # 断言 dtype 的名称属性,应为 'record16'
        assert_equal(dt.name, 'record16')

    def test_void_subclass_fields(self):
        # 创建一个包含字段的 np.record 类型的 dtype,字段为 ('a', '<u2')
        dt = np.dtype((np.record, [('a', '<u2')]))
        # 断言返回值的表达式,应为 "dtype((numpy.record, [('a', '<u2')]))"
        assert_equal(repr(dt), "dtype((numpy.record, [('a', '<u2')]))")
        # 断言返回值的字符串表示,应为 "(numpy.record, [('a', '<u2')])"
        assert_equal(str(dt), "(numpy.record, [('a', '<u2')])")
        # 断言 dtype 的名称属性,应为 'record16'
        assert_equal(dt.name, 'record16')
class TestDtypeAttributeDeletion:

    def test_dtype_non_writable_attributes_deletion(self):
        dt = np.dtype(np.double)
        attr = ["subdtype", "descr", "str", "name", "base", "shape",
                "isbuiltin", "isnative", "isalignedstruct", "fields",
                "metadata", "hasobject"]

        # 循环遍历不可写属性列表
        for s in attr:
            # 断言删除不可写属性时抛出 AttributeError 异常
            assert_raises(AttributeError, delattr, dt, s)

    def test_dtype_writable_attributes_deletion(self):
        dt = np.dtype(np.double)
        attr = ["names"]
        
        # 循环遍历可写属性列表
        for s in attr:
            # 断言删除可写属性时抛出 AttributeError 异常
            assert_raises(AttributeError, delattr, dt, s)


class TestDtypeAttributes:
    def test_descr_has_trailing_void(self):
        # 见 issue gh-6359
        # 创建一个结构化 dtype
        dtype = np.dtype({
            'names': ['A', 'B'],
            'formats': ['f4', 'f4'],
            'offsets': [0, 8],
            'itemsize': 16})
        # 通过现有描述符创建新的 dtype
        new_dtype = np.dtype(dtype.descr)
        # 断言新 dtype 的 itemsize 属性为 16
        assert_equal(new_dtype.itemsize, 16)

    def test_name_dtype_subclass(self):
        # Ticket #4357
        # 定义一个继承自 np.void 的用户自定义子类
        class user_def_subcls(np.void):
            pass
        # 断言用户定义子类的 dtype 名称为 'user_def_subcls'
        assert_equal(np.dtype(user_def_subcls).name, 'user_def_subcls')

    def test_zero_stride(self):
        # 创建一个 int64 类型的数组
        arr = np.ones(1, dtype="i8")
        # 广播数组,使其大小为 10
        arr = np.broadcast_to(arr, 10)
        # 断言数组的 strides 属性为 (0,)
        assert arr.strides == (0,)
        # 使用 pytest 断言设置非法 dtype 时抛出 ValueError 异常
        with pytest.raises(ValueError):
            arr.dtype = "i1"

class TestDTypeMakeCanonical:
    def check_canonical(self, dtype, canonical):
        """
        Check most properties relevant to "canonical" versions of a dtype,
        which is mainly native byte order for datatypes supporting this.

        The main work is checking structured dtypes with fields, where we
        reproduce most the actual logic used in the C-code.
        """
        # 断言dtype和canonical的类型相同
        assert type(dtype) is type(canonical)

        # 断言可以在等价转换的情况下从dtype转换为canonical,反之亦然
        assert np.can_cast(dtype, canonical, casting="equiv")
        assert np.can_cast(canonical, dtype, casting="equiv")

        # 断言canonical的字节顺序是本机的
        assert canonical.isnative

        # 断言canonical的结果类型与自身相同(没有转换)
        assert np.result_type(canonical) == canonical

        if not dtype.names:
            # 对于非结构化dtype,标志位不会改变
            assert dtype.flags == canonical.flags
            return

        # 对于结构化dtype,必须设置所有需要的API标志位
        assert dtype.flags & 0b10000

        # 断言字段名相同,包括顺序和标题
        assert dtype.fields.keys() == canonical.fields.keys()

        def aligned_offset(offset, alignment):
            # 向上取整对齐偏移量
            return -(-offset // alignment) * alignment

        totalsize = 0
        max_alignment = 1
        for name in dtype.names:
            # 每个字段也必须是canonical的
            new_field_descr = canonical.fields[name][0]
            self.check_canonical(dtype.fields[name][0], new_field_descr)

            # 必须具有继承的对象相关标志位
            expected = 0b11011 & new_field_descr.flags
            assert (canonical.flags & expected) == expected

            if canonical.isalignedstruct:
                totalsize = aligned_offset(totalsize, new_field_descr.alignment)
                max_alignment = max(new_field_descr.alignment, max_alignment)

            # 断言字段的偏移量与totalsize相同
            assert canonical.fields[name][1] == totalsize
            # 如果存在标题,它们必须匹配(否则为空元组)
            assert dtype.fields[name][2:] == canonical.fields[name][2:]

            totalsize += new_field_descr.itemsize

        if canonical.isalignedstruct:
            totalsize = aligned_offset(totalsize, max_alignment)

        # 断言canonical的总大小和对齐方式与计算结果相同
        assert canonical.itemsize == totalsize
        assert canonical.alignment == max_alignment
    # 定义一个测试方法,测试简单的数据类型操作
    def test_simple(self):
        # 创建一个大端字节顺序的32位整数数据类型对象
        dt = np.dtype(">i4")
        # 断言该数据类型对象的结果类型是本机字节顺序
        assert np.result_type(dt).isnative
        # 断言该数据类型对象的结果类型的数字类型与原始类型相同
        assert np.result_type(dt).num == dt.num

        # 带有空间的结构化数据类型:
        struct_dt = np.dtype(">i4,<i1,i8,V3")[["f0", "f2"]]
        # 获取结构化数据类型的规范结果类型
        canonical = np.result_type(struct_dt)
        # 断言规范结果类型的字节大小等于子类型的总和
        assert canonical.itemsize == 4+8
        # 断言规范结果类型是本机字节顺序
        assert canonical.isnative

        # 带有对齐的结构化数据类型:
        struct_dt = np.dtype(">i1,<i4,i8,V3", align=True)[["f0", "f2"]]
        # 获取结构化数据类型的规范结果类型
        canonical = np.result_type(struct_dt)
        # 断言规范结果类型是对齐的结构化数据类型
        assert canonical.isalignedstruct
        # 断言规范结果类型的字节大小等于i8数据类型的对齐数加上8
        assert canonical.itemsize == np.dtype("i8").alignment + 8
        # 断言规范结果类型是本机字节顺序
        assert canonical.isnative

    # 测试对象标志未继承的情况
    def test_object_flag_not_inherited(self):
        # 创建一个包含整数、对象和整数的数组
        arr = np.ones(3, "i,O,i")[["f0", "f2"]]
        # 断言数组的数据类型具有对象类型
        assert arr.dtype.hasobject
        # 获取数组数据类型的规范结果类型
        canonical_dt = np.result_type(arr.dtype)
        # 断言规范结果类型不具有对象类型
        assert not canonical_dt.hasobject

    # 使用假设测试生成规范结果类型的方法
    @pytest.mark.slow
    @hypothesis.given(dtype=hynp.nested_dtypes())
    def test_make_canonical_hypothesis(self, dtype):
        # 获取数据类型的规范结果类型
        canonical = np.result_type(dtype)
        # 检查生成的规范结果类型是否正确
        self.check_canonical(dtype, canonical)
        # 使用两个相同的数据类型参数调用result_type应该得到相同的结果
        two_arg_result = np.result_type(dtype, dtype)
        # 断言两个规范结果类型可以进行无类型转换的转换
        assert np.can_cast(two_arg_result, canonical, casting="no")

    # 使用假设测试数组数据类型生成规范结果类型的方法
    @pytest.mark.slow
    @hypothesis.given(
            dtype=hypothesis.extra.numpy.array_dtypes(
                subtype_strategy=hypothesis.extra.numpy.array_dtypes(),
                min_size=5, max_size=10, allow_subarrays=True))
    # 定义一个测试方法,用于结构化数据类型的测试
    def test_structured(self, dtype):
        # 从给定的 dtype 中随机选择 4 个字段,这会在 dtype 中留下空白空间(因为此处不做规范化处理)
        field_subset = random.sample(dtype.names, k=4)
        # 从 dtype 中提取带有空白空间的子 dtype
        dtype_with_empty_space = dtype[field_subset]
        # 断言带有空白空间的 dtype 的字节大小与原始 dtype 的相同
        assert dtype_with_empty_space.itemsize == dtype.itemsize
        # 获取带有空白空间的 dtype 的规范化结果
        canonicalized = np.result_type(dtype_with_empty_space)
        # 调用方法检查带有空白空间的 dtype 是否与规范化结果一致
        self.check_canonical(dtype_with_empty_space, canonicalized)
        # 使用两个相同的参数进行类型提升应始终给出相同的结果:
        two_arg_result = np.promote_types(
                dtype_with_empty_space, dtype_with_empty_space)
        # 断言两个结果可以互相转换,且不进行类型转换
        assert np.can_cast(two_arg_result, canonicalized, casting="no")

        # 确保我们还检查对齐结构(检查相反情况,以防假设增加对 `align` 的支持)。然后重复测试:
        # 根据 dtype 的描述和对齐情况创建一个新的 dtype
        dtype_aligned = np.dtype(dtype.descr, align=not dtype.isalignedstruct)
        # 从新的对齐 dtype 中提取带有空白空间的子 dtype
        dtype_with_empty_space = dtype_aligned[field_subset]
        # 断言带有空白空间的 dtype 的字节大小与新的对齐 dtype 的相同
        assert dtype_with_empty_space.itemsize == dtype_aligned.itemsize
        # 获取带有空白空间的 dtype 的规范化结果
        canonicalized = np.result_type(dtype_with_empty_space)
        # 调用方法检查带有空白空间的 dtype 是否与规范化结果一致
        self.check_canonical(dtype_with_empty_space, canonicalized)
        # 使用两个相同的参数进行类型提升应始终给出相同的结果:
        two_arg_result = np.promote_types(
            dtype_with_empty_space, dtype_with_empty_space)
        # 断言两个结果可以互相转换,且不进行类型转换
        assert np.can_cast(two_arg_result, canonicalized, casting="no")
class TestPickling:

    def check_pickling(self, dtype):
        # 遍历所有协议版本进行序列化和反序列化测试
        for proto in range(pickle.HIGHEST_PROTOCOL + 1):
            # 使用指定协议版本将 dtype 序列化为字节流
            buf = pickle.dumps(dtype, proto)
            # 检查序列化后的字节流中不包含 "_DType_reconstruct" 字符串
            assert b"_DType_reconstruct" not in buf
            # 检查序列化后的字节流中包含 "dtype" 字符串
            assert b"dtype" in buf
            # 反序列化得到的对象应该与原始 dtype 相等
            pickled = pickle.loads(buf)
            assert_equal(pickled, dtype)
            # 检查反序列化后的对象的描述符与原始 dtype 的描述符相等
            assert_equal(pickled.descr, dtype.descr)
            # 如果原始 dtype 有元数据,则检查反序列化后的对象的元数据也相等
            if dtype.metadata is not None:
                assert_equal(pickled.metadata, dtype.metadata)
            # 检查重建的 dtype 是否能正常使用
            x = np.zeros(3, dtype=dtype)
            y = np.zeros(3, dtype=pickled)
            assert_equal(x, y)
            assert_equal(x[0], y[0])

    @pytest.mark.parametrize('t', [int, float, complex, np.int32, str, object,
                                   bool])
    def test_builtin(self, t):
        # 对于内置数据类型,进行序列化和反序列化测试
        self.check_pickling(np.dtype(t))

    def test_structured(self):
        # 测试结构化 dtype 的序列化和反序列化
        dt = np.dtype(([('a', '>f4', (2, 1)), ('b', '<f8', (1, 3))], (2, 2)))
        self.check_pickling(dt)

    def test_structured_aligned(self):
        # 测试结构化 dtype 的序列化和反序列化(对齐)
        dt = np.dtype('i4, i1', align=True)
        self.check_pickling(dt)

    def test_structured_unaligned(self):
        # 测试结构化 dtype 的序列化和反序列化(不对齐)
        dt = np.dtype('i4, i1', align=False)
        self.check_pickling(dt)

    def test_structured_padded(self):
        # 测试带填充的结构化 dtype 的序列化和反序列化
        dt = np.dtype({
            'names': ['A', 'B'],
            'formats': ['f4', 'f4'],
            'offsets': [0, 8],
            'itemsize': 16})
        self.check_pickling(dt)

    def test_structured_titles(self):
        # 测试带标题的结构化 dtype 的序列化和反序列化
        dt = np.dtype({'names': ['r', 'b'],
                       'formats': ['u1', 'u1'],
                       'titles': ['Red pixel', 'Blue pixel']})
        self.check_pickling(dt)

    @pytest.mark.parametrize('base', ['m8', 'M8'])
    @pytest.mark.parametrize('unit', ['', 'Y', 'M', 'W', 'D', 'h', 'm', 's',
                                      'ms', 'us', 'ns', 'ps', 'fs', 'as'])
    def test_datetime(self, base, unit):
        # 测试日期时间 dtype 的序列化和反序列化
        dt = np.dtype('%s[%s]' % (base, unit) if unit else base)
        self.check_pickling(dt)
        if unit:
            dt = np.dtype('%s[7%s]' % (base, unit))
            self.check_pickling(dt)

    def test_metadata(self):
        # 测试包含元数据的 dtype 的序列化和反序列化
        dt = np.dtype(int, metadata={'datum': 1})
        self.check_pickling(dt)

    @pytest.mark.parametrize("DType",
        [type(np.dtype(t)) for t in np.typecodes['All']] +
        [type(np.dtype(rational)), np.dtype])
    def test_pickle_dtype_class(self, DType):
        # 测试 dtype 类的序列化和反序列化
        for proto in range(pickle.HIGHEST_PROTOCOL + 1):
            roundtrip_DType = pickle.loads(pickle.dumps(DType, proto))
            assert roundtrip_DType is DType

    @pytest.mark.parametrize("dt",
        [np.dtype(t) for t in np.typecodes['All']] +
        [np.dtype(rational)])
    def test_pickle_dtype_instance(self, dt):
        # 测试 dtype 实例的序列化和反序列化
        for proto in range(pickle.HIGHEST_PROTOCOL + 1):
            roundtrip_dt = pickle.loads(pickle.dumps(dt, proto))
            assert roundtrip_dt == dt
    def test_pickle_dtype(self, dt):
        # 测试数据类型在通过 pickle 进行序列化和反序列化后能否保持一致,同时保持哈希值不变
        # 记录序列化前的哈希值
        pre_pickle_hash = hash(dt)
        # 遍历所有 pickle 协议版本
        for proto in range(pickle.HIGHEST_PROTOCOL + 1):
            # 使用指定协议版本进行序列化和反序列化
            roundtrip_dt = pickle.loads(pickle.dumps(dt, proto))
            # 断言反序列化后的对象与原始对象相等
            assert roundtrip_dt == dt
            # 断言反序列化后的对象的哈希值与原始对象的哈希值相等
            assert hash(dt) == pre_pickle_hash
# 定义一个测试类,用于测试复杂数据类型的提升(promotion)逻辑
class TestPromotion:
    """Test cases related to more complex DType promotions.  Further promotion
    tests are defined in `test_numeric.py`
    """

    # 使用 np._no_nep50_warning() 装饰器,标记不显示 NEP 50 的警告信息
    @np._no_nep50_warning()
    # 使用 pytest.mark.parametrize 装饰器,参数化测试方法的输入参数
    @pytest.mark.parametrize(["other", "expected", "expected_weak"],
            [(2**16-1, np.complex64, None),
             (2**32-1, np.complex128, np.complex64),
             (np.float16(2), np.complex64, None),
             (np.float32(2), np.complex64, None),
             (np.longdouble(2), np.complex64, np.clongdouble),
             # Base of the double value to sidestep any rounding issues:
             (np.longdouble(np.nextafter(1.7e308, 0.)),
                  np.complex128, np.clongdouble),
             # Additionally use "nextafter" so the cast can't round down:
             (np.longdouble(np.nextafter(1.7e308, np.inf)),
                  np.clongdouble, None),
             # repeat for complex scalars:
             (np.complex64(2), np.complex64, None),
             (np.clongdouble(2), np.complex64, np.clongdouble),
             # Base of the double value to sidestep any rounding issues:
             (np.clongdouble(np.nextafter(1.7e308, 0.) * 1j),
                  np.complex128, np.clongdouble),
             # Additionally use "nextafter" so the cast can't round down:
             (np.clongdouble(np.nextafter(1.7e308, np.inf)),
                  np.clongdouble, None),
             ])
    # 定义测试方法,测试根据其他值进行复杂类型推断的逻辑
    def test_complex_other_value_based(self,
            weak_promotion, other, expected, expected_weak):
        # 如果存在弱类型推断并且期望的弱类型不为空,则更新期望值
        if weak_promotion and expected_weak is not None:
            expected = expected_weak

        # 定义最小的复杂类型为 np.complex64
        min_complex = np.dtype(np.complex64)

        # 进行类型推断,比较结果是否符合预期
        res = np.result_type(other, min_complex)
        assert res == expected
        # 检查简单的 ufunc 调用是否使用相同的逻辑:
        res = np.minimum(other, np.ones(3, dtype=min_complex)).dtype
        assert res == expected

    # 使用 pytest.mark.parametrize 装饰器,参数化测试方法的输入参数
    @pytest.mark.parametrize(["other", "expected"],
                 [(np.bool, np.complex128),
                  (np.int64, np.complex128),
                  (np.float16, np.complex64),
                  (np.float32, np.complex64),
                  (np.float64, np.complex128),
                  (np.longdouble, np.clongdouble),
                  (np.complex64, np.complex64),
                  (np.complex128, np.complex128),
                  (np.clongdouble, np.clongdouble),
                  ])
    # 定义测试方法,测试根据标量复杂值进行推断的逻辑
    def test_complex_scalar_value_based(self, other, expected):
        # 定义复杂标量为虚数单位 1j
        complex_scalar = 1j

        # 进行类型推断,比较结果是否符合预期
        res = np.result_type(other, complex_scalar)
        assert res == expected
        # 检查简单的 ufunc 调用是否使用相同的逻辑:
        res = np.minimum(np.ones(3, dtype=other), complex_scalar).dtype
        assert res == expected
    # 定义一个测试方法,用于测试复杂的数值类型提升到有理数的情况
    def test_complex_pyscalar_promote_rational(self):
        # 使用 pytest.raises 检查是否会抛出 TypeError 异常
        # 匹配错误信息,确保包含指定的错误信息提示
        with pytest.raises(TypeError,
                match=r".* no common DType exists for the given inputs"):
            # 调用 np.result_type 函数,测试 1j 和 rational 类型的组合
            np.result_type(1j, rational)

        with pytest.raises(TypeError,
                match=r".* no common DType exists for the given inputs"):
            # 调用 np.result_type 函数,测试 1j 和 rational(1, 2) 类型的组合
            np.result_type(1j, rational(1, 2))

    # 使用 pytest 的参数化标记,定义一个测试方法,测试 Python 整数的提升
    @pytest.mark.parametrize("val", [2, 2**32, 2**63, 2**64, 2*100])
    def test_python_integer_promotion(self, val):
        # 如果只传递标量(主要是 Python 标量),根据 NEP 50 规定,会得到默认整数类型
        expected_dtype = np.dtype(int)  # 默认整数类型
        # 断言调用 np.result_type 函数后返回的类型与期望的默认整数类型相同
        assert np.result_type(val, 0) == expected_dtype
        # 根据 NEP 50 规定,NumPy 标量将胜出:
        # 断言调用 np.result_type 函数后返回的类型与 np.int8 相同
        assert np.result_type(val, np.int8(0)) == np.int8

    # 使用 pytest 的参数化标记,定义一个测试方法,测试浮点数和整数提升到有理数的情况
    @pytest.mark.parametrize(["other", "expected"],
            [(1, rational), (1., np.float64)])
    @np._no_nep50_warning()
    def test_float_int_pyscalar_promote_rational(
            self, weak_promotion, other, expected):
        # 注意,有理数在与 float64 提升时有点尴尬,会与默认整数类型提升,但不与 float16 或 uint8/int8 提升(看起来不一致)
        # 新的提升修复了这个问题(部分修复?)
        if not weak_promotion and type(other) == float:
            # 如果不是弱提升且 other 是 float 类型,则检查在旧路径中是否会抛出 TypeError 异常
            with pytest.raises(TypeError,
                    match=r".* do not have a common DType"):
                np.result_type(other, rational)
        else:
            # 否则,断言调用 np.result_type 函数后返回的类型与期望的类型相同
            assert np.result_type(other, rational) == expected

        # 断言调用 np.result_type 函数后返回的类型与期望的类型相同
        assert np.result_type(other, rational(1, 2)) == expected

    # 使用 pytest 的参数化标记,定义一个测试方法,测试多种数据类型组合的提升
    @pytest.mark.parametrize(["dtypes", "expected"], [
             # 这些提升不是可交换的:
             ([np.uint16, np.int16, np.float16], np.float32),
             ([np.uint16, np.int8, np.float16], np.float32),
             ([np.uint8, np.int16, np.float16], np.float32),
             # 以下提升不是模糊的,但涵盖了抽象提升的代码路径(没有特定逻辑被测试)
             ([1, 1, np.float64], np.float64),
             ([1, 1., np.complex128], np.complex128),
             ([1, 1j, np.float64], np.complex128),
             ([1., 1., np.int64], np.float64),
             ([1., 1j, np.float64], np.complex128),
             ([1j, 1j, np.float64], np.complex128),
             ([1, True, np.bool], np.int_),
            ])
    # 定义一个测试方法,验证排列顺序对结果无影响
    def test_permutations_do_not_influence_result(self, dtypes, expected):
        # 对于给定的数据类型的排列进行迭代测试
        for perm in permutations(dtypes):
            # 断言:使用 numpy 计算结果类型,检查是否等于期望的结果类型
            assert np.result_type(*perm) == expected
def test_rational_dtype():
    # test for bug gh-5719
    # 创建一个包含单个元素 1111 的 NumPy 数组,并使用自定义的 rational 数据类型
    a = np.array([1111], dtype=rational).astype
    # 断言尝试将该数组转换为 'int8' 类型时会引发 OverflowError 异常
    assert_raises(OverflowError, a, 'int8')

    # test that dtype detection finds user-defined types
    # 创建一个 rational 类型的对象 x
    x = rational(1)
    # 断言创建包含两个 x 元素的 NumPy 数组的数据类型为 rational
    assert_equal(np.array([x,x]).dtype, np.dtype(rational))


def test_dtypes_are_true():
    # test for gh-6294
    # 断言 'f8' 数据类型的布尔值为真
    assert bool(np.dtype('f8'))
    # 断言 'i8' 数据类型的布尔值为真
    assert bool(np.dtype('i8'))
    # 断言包含两个字段 ('a', 'i8') 和 ('b', 'f4') 的结构化数据类型的布尔值为真
    assert bool(np.dtype([('a', 'i8'), ('b', 'f4')]))


def test_invalid_dtype_string():
    # test for gh-10440
    # 断言尝试使用无效的 dtype 字符串 'f8,i8,[f8,i8]' 会引发 TypeError 异常
    assert_raises(TypeError, np.dtype, 'f8,i8,[f8,i8]')
    # 断言尝试使用无效的 dtype 字符串 'Fl\xfcgel' 会引发 TypeError 异常
    assert_raises(TypeError, np.dtype, 'Fl\xfcgel')


def test_keyword_argument():
    # test for https://github.com/numpy/numpy/pull/16574#issuecomment-642660971
    # 断言传递 dtype=np.float64 参数时返回的数据类型为 np.float64
    assert np.dtype(dtype=np.float64) == np.dtype(np.float64)


class TestFromDTypeAttribute:
    def test_simple(self):
        class dt:
            dtype = np.dtype("f8")

        # 断言使用类 dt 的 dtype 属性创建的数据类型为 np.float64
        assert np.dtype(dt) == np.float64
        # 断言使用 dt 类的实例创建的数据类型为 np.float64
        assert np.dtype(dt()) == np.float64

    @pytest.mark.skipif(IS_PYSTON, reason="Pyston disables recursion checking")
    def test_recursion(self):
        class dt:
            pass

        dt.dtype = dt
        # 断言尝试创建循环引用的数据类型 dt 会引发 RecursionError 异常
        with pytest.raises(RecursionError):
            np.dtype(dt)

        dt_instance = dt()
        dt_instance.dtype = dt
        # 断言尝试创建循环引用的数据类型 dt_instance 会引发 RecursionError 异常
        with pytest.raises(RecursionError):
            np.dtype(dt_instance)

    def test_void_subtype(self):
        class dt(np.void):
            # This code path is fully untested before, so it is unclear
            # what this should be useful for. Note that if np.void is used
            # numpy will think we are deallocating a base type [1.17, 2019-02].
            dtype = np.dtype("f,f")

        # 创建 void 子类型 dt 的数据类型
        np.dtype(dt)
        np.dtype(dt(1))

    @pytest.mark.skipif(IS_PYSTON, reason="Pyston disables recursion checking")
    def test_void_subtype_recursion(self):
        class vdt(np.void):
            pass

        vdt.dtype = vdt

        # 断言尝试创建循环引用的 void 子类型 vdt 会引发 RecursionError 异常
        with pytest.raises(RecursionError):
            np.dtype(vdt)

        with pytest.raises(RecursionError):
            np.dtype(vdt(1))


class TestDTypeClasses:
    @pytest.mark.parametrize("dtype", list(np.typecodes['All']) + [rational])
    # 测试基本数据类型的子类属性
    def test_basic_dtypes_subclass_properties(self, dtype):
        # 注意:除了 isinstance 和 type 检查外,这些属性当前被视为私有且可能会改变。
        
        # 将输入的 dtype 转换为 numpy 的 dtype 对象
        dtype = np.dtype(dtype)
        
        # 断言 dtype 是 np.dtype 的实例
        assert isinstance(dtype, np.dtype)
        
        # 断言 dtype 的类型不是 np.dtype 类型本身
        assert type(dtype) is not np.dtype
        
        # 如果 dtype 的类型名称不是 "rational"
        if dtype.type.__name__ != "rational":
            # 获取 dtype 的类型名称,并去除 "dtype" 后缀
            dt_name = type(dtype).__name__.lower().removesuffix("dtype")
            
            # 如果 dt_name 是 "uint" 或 "int"
            if dt_name == "uint" or dt_name == "int":
                # 在标量名称上添加 'c',因为 "int" 是 Python 中的 int,而那是长整型...
                dt_name += "c"
            
            # 获取 dtype 的类型名称
            sc_name = dtype.type.__name__
            
            # 断言 dt_name 和 sc_name(去除下划线后)相同
            assert dt_name == sc_name.strip("_")
            
            # 断言 dtype 的类型模块是 "numpy.dtypes"
            assert type(dtype).__module__ == "numpy.dtypes"
            
            # 断言 numpy.dtypes 下的类型名称与 dtype 的类型相同
            assert getattr(numpy.dtypes, type(dtype).__name__) is type(dtype)
        
        else:
            # 如果 dtype 的类型名称是 "dtype[rational]"
            assert type(dtype).__name__ == "dtype[rational]"
            
            # 断言 dtype 的类型模块是 "numpy"
            assert type(dtype).__module__ == "numpy"
        
        # 断言 dtype 的类型不是抽象类型
        assert not type(dtype)._abstract
        
        # 对于灵活的 dtype 和 datetime/timedelta,它们有额外的参数
        # 这些参数不仅仅是存储信息,创建 dtype 时需要考虑这些参数
        parametric = (np.void, np.str_, np.bytes_, np.datetime64, np.timedelta64)
        
        # 如果 dtype 的类型不在 parametric 中
        if dtype.type not in parametric:
            # 断言 dtype 不是参数化的类型
            assert not type(dtype)._parametric
            
            # 断言 type(dtype)() 是 dtype 自身
            assert type(dtype)() is dtype
        
        else:
            # 如果 dtype 是参数化的类型
            assert type(dtype)._parametric
            
            # 使用 assert_raises 确保创建 type(dtype)() 会抛出 TypeError 异常
            with assert_raises(TypeError):
                type(dtype)()

    # 测试 dtype 的超类
    def test_dtype_superclass(self):
        # 断言 np.dtype 不是 type 类型本身
        assert type(np.dtype) is not type
        
        # 断言 np.dtype 是 type 类的实例
        assert isinstance(np.dtype, type)
        
        # 断言 np.dtype 的类型名称是 "_DTypeMeta"
        assert type(np.dtype).__name__ == "_DTypeMeta"
        
        # 断言 np.dtype 的类型模块是 "numpy"
        assert type(np.dtype).__module__ == "numpy"
        
        # 断言 np.dtype 是抽象类型
        assert np.dtype._abstract

    # 测试是否为数值类型
    def test_is_numeric(self):
        # 获取所有类型码和数值类型码的集合
        all_codes = set(np.typecodes['All'])
        numeric_codes = set(np.typecodes['AllInteger'] +
                            np.typecodes['AllFloat'] + '?')
        
        # 非数值类型码的集合
        non_numeric_codes = all_codes - numeric_codes
        
        # 对于数值类型码中的每个类型码
        for code in numeric_codes:
            # 断言 type(np.dtype(code))._is_numeric 是 True
            assert type(np.dtype(code))._is_numeric
        
        # 对于非数值类型码中的每个类型码
        for code in non_numeric_codes:
            # 断言 type(np.dtype(code))._is_numeric 是 False
            assert not type(np.dtype(code))._is_numeric

    # 测试整数别名名称
    @pytest.mark.parametrize("int_", ["UInt", "Int"])
    @pytest.mark.parametrize("size", [8, 16, 32, 64])
    def test_integer_alias_names(self, int_, size):
        # 获取 numpy.dtypes 中的 DType 类型
        DType = getattr(numpy.dtypes, f"{int_}{size}DType")
        
        # 获取 numpy 中的相应的数据类型
        sctype = getattr(numpy, f"{int_.lower()}{size}")
        
        # 断言 DType 的类型是 sctype
        assert DType.type is sctype
        
        # 断言 DType 的名称去除后缀 "dtype" 与 sctype 的名称相同
        assert DType.__name__.lower().removesuffix("dtype") == sctype.__name__

    # 测试浮点数别名名称
    @pytest.mark.parametrize("name",
            ["Half", "Float", "Double", "CFloat", "CDouble"])
    def test_float_alias_names(self, name):
        # 使用 pytest.raises 确保获取不存在的浮点数别名名称会引发 AttributeError 异常
        with pytest.raises(AttributeError):
            getattr(numpy.dtypes, name + "DType") is numpy.dtypes.Float16DType
class TestFromCTypes:

    @staticmethod
    def check(ctype, dtype):
        # 将 dtype 转换为 NumPy 数据类型对象
        dtype = np.dtype(dtype)
        # 断言 ctype 的 NumPy 数据类型与 dtype 相同
        assert np.dtype(ctype) == dtype
        # 断言 ctype 的实例化对象的 NumPy 数据类型与 dtype 相同
        assert np.dtype(ctype()) == dtype
        # 断言 ctype 的大小与 dtype 的字节大小相同
        assert ctypes.sizeof(ctype) == dtype.itemsize

    def test_array(self):
        # 定义一个 ctypes 类型 c_uint8
        c8 = ctypes.c_uint8
        # 调用 check 方法,验证 3 * c8 的类型和形状是否符合预期
        self.check(3 * c8, (np.uint8, (3,)))
        # 调用 check 方法,验证 1 * c8 的类型和形状是否符合预期
        self.check(1 * c8, (np.uint8, (1,)))
        # 调用 check 方法,验证 0 * c8 的类型和形状是否符合预期
        self.check(0 * c8, (np.uint8, (0,)))
        # 调用 check 方法,验证 1 * (3 * c8) 的类型和形状是否符合预期
        self.check(1 * (3 * c8), ((np.uint8, (3,)), (1,)))
        # 调用 check 方法,验证 3 * (1 * c8) 的类型和形状是否符合预期
        self.check(3 * (1 * c8), ((np.uint8, (1,)), (3,)))

    def test_padded_structure(self):
        # 定义一个包含字节对齐的结构体 PaddedStruct
        class PaddedStruct(ctypes.Structure):
            _fields_ = [
                ('a', ctypes.c_uint8),
                ('b', ctypes.c_uint16)
            ]
        # 定义预期的 NumPy 数据类型对象 expected
        expected = np.dtype([
            ('a', np.uint8),
            ('b', np.uint16)
        ], align=True)
        # 调用 check 方法,验证 PaddedStruct 的类型和形状是否符合预期
        self.check(PaddedStruct, expected)

    def test_bit_fields(self):
        # 定义一个包含位域的结构体 BitfieldStruct
        class BitfieldStruct(ctypes.Structure):
            _fields_ = [
                ('a', ctypes.c_uint8, 7),
                ('b', ctypes.c_uint8, 1)
            ]
        # 断言抛出 TypeError 异常,因为无法将 BitfieldStruct 转换为 NumPy 数据类型
        assert_raises(TypeError, np.dtype, BitfieldStruct)
        # 断言抛出 TypeError 异常,因为无法将 BitfieldStruct 的实例化对象转换为 NumPy 数据类型
        assert_raises(TypeError, np.dtype, BitfieldStruct())

    def test_pointer(self):
        # 定义一个指向 ctypes.c_uint8 类型的指针 p_uint8
        p_uint8 = ctypes.POINTER(ctypes.c_uint8)
        # 断言抛出 TypeError 异常,因为无法将 p_uint8 转换为 NumPy 数据类型
        assert_raises(TypeError, np.dtype, p_uint8)

    def test_size_t(self):
        # 断言 np.uintp 的 NumPy 数据类型等同于 "N"
        assert np.dtype(np.uintp) is np.dtype("N")
        # 调用 check 方法,验证 ctypes.c_size_t 和 np.uintp 的类型和形状是否符合预期
        self.check(ctypes.c_size_t, np.uintp)

    def test_void_pointer(self):
        # 调用 check 方法,验证 ctypes.c_void_p 和 "P" 的类型和形状是否符合预期
        self.check(ctypes.c_void_p, "P")

    def test_union(self):
        # 定义一个联合体 Union
        class Union(ctypes.Union):
            _fields_ = [
                ('a', ctypes.c_uint8),
                ('b', ctypes.c_uint16),
            ]
        # 定义预期的 NumPy 数据类型对象 expected
        expected = np.dtype(dict(
            names=['a', 'b'],
            formats=[np.uint8, np.uint16],
            offsets=[0, 0],
            itemsize=2
        ))
        # 调用 check 方法,验证 Union 的类型和形状是否符合预期
        self.check(Union, expected)

    def test_union_with_struct_packed(self):
        # 定义一个紧凑结构体 Struct
        class Struct(ctypes.Structure):
            _pack_ = 1
            _fields_ = [
                ('one', ctypes.c_uint8),
                ('two', ctypes.c_uint32)
            ]

        # 定义一个包含结构体的联合体 Union
        class Union(ctypes.Union):
            _fields_ = [
                ('a', ctypes.c_uint8),
                ('b', ctypes.c_uint16),
                ('c', ctypes.c_uint32),
                ('d', Struct),
            ]
        # 定义预期的 NumPy 数据类型对象 expected
        expected = np.dtype(dict(
            names=['a', 'b', 'c', 'd'],
            formats=['u1', np.uint16, np.uint32, [('one', 'u1'), ('two', np.uint32)]],
            offsets=[0, 0, 0, 0],
            itemsize=ctypes.sizeof(Union)
        ))
        # 调用 check 方法,验证 Union 的类型和形状是否符合预期
        self.check(Union, expected)
    # 定义一个测试方法,用于测试 Union 类型的打包方式
    def test_union_packed(self):
        # 定义一个嵌套的结构体类 Struct,包含两个字段:一个是 8 位无符号整数,一个是 32 位无符号整数
        class Struct(ctypes.Structure):
            _fields_ = [
                ('one', ctypes.c_uint8),
                ('two', ctypes.c_uint32)
            ]
            _pack_ = 1  # 设置结构体的打包对齐方式为 1 字节

        # 定义一个 Union 类,包含四个字段:一个是 8 位无符号整数,一个是 16 位无符号整数,一个是 32 位无符号整数,一个是结构体 Struct
        class Union(ctypes.Union):
            _pack_ = 1  # 设置联合体的打包对齐方式为 1 字节
            _fields_ = [
                ('a', ctypes.c_uint8),
                ('b', ctypes.c_uint16),
                ('c', ctypes.c_uint32),
                ('d', Struct),
            ]

        # 构造一个预期的 NumPy dtype,包含字段名、格式、偏移量、结构体大小等信息
        expected = np.dtype(dict(
            names=['a', 'b', 'c', 'd'],
            formats=['u1', np.uint16, np.uint32, [('one', 'u1'), ('two', np.uint32)]],
            offsets=[0, 0, 0, 0],
            itemsize=ctypes.sizeof(Union)
        ))

        # 调用自定义的检查方法,验证 Union 类型和预期的 dtype 是否一致
        self.check(Union, expected)

    # 定义一个测试方法,用于测试带打包方式的普通结构体 PackedStructure
    def test_packed_structure(self):
        # 定义一个带打包方式的结构体 PackedStructure,包含一个 8 位无符号整数和一个 16 位无符号整数
        class PackedStructure(ctypes.Structure):
            _pack_ = 1  # 设置结构体的打包对齐方式为 1 字节
            _fields_ = [
                ('a', ctypes.c_uint8),
                ('b', ctypes.c_uint16)
            ]

        # 构造一个预期的 NumPy dtype,包含字段名和对应的数据类型
        expected = np.dtype([
            ('a', np.uint8),
            ('b', np.uint16)
        ])

        # 调用自定义的检查方法,验证 PackedStructure 类型和预期的 dtype 是否一致
        self.check(PackedStructure, expected)

    # 定义一个测试方法,用于测试更大的带打包方式的结构体 PackedStructure
    def test_large_packed_structure(self):
        # 定义一个更大的带打包方式的结构体 PackedStructure,包含多个字段:8 位、16 位、32 位无符号整数
        class PackedStructure(ctypes.Structure):
            _pack_ = 2  # 设置结构体的打包对齐方式为 2 字节
            _fields_ = [
                ('a', ctypes.c_uint8),
                ('b', ctypes.c_uint16),
                ('c', ctypes.c_uint8),
                ('d', ctypes.c_uint16),
                ('e', ctypes.c_uint32),
                ('f', ctypes.c_uint32),
                ('g', ctypes.c_uint8)
            ]

        # 构造一个预期的 NumPy dtype,包含字段名、数据类型、偏移量、结构体大小等信息
        expected = np.dtype(dict(
            formats=[np.uint8, np.uint16, np.uint8, np.uint16, np.uint32, np.uint32, np.uint8],
            offsets=[0, 2, 4, 6, 8, 12, 16],
            names=['a', 'b', 'c', 'd', 'e', 'f', 'g'],
            itemsize=18
        ))

        # 调用自定义的检查方法,验证 PackedStructure 类型和预期的 dtype 是否一致
        self.check(PackedStructure, expected)

    # 定义一个测试方法,用于测试带打包方式的大端字节序结构体 BigEndStruct
    def test_big_endian_structure_packed(self):
        # 定义一个带打包方式的大端字节序结构体 BigEndStruct,包含一个 8 位无符号整数和一个 32 位无符号整数
        class BigEndStruct(ctypes.BigEndianStructure):
            _fields_ = [
                ('one', ctypes.c_uint8),
                ('two', ctypes.c_uint32)
            ]
            _pack_ = 1  # 设置结构体的打包对齐方式为 1 字节

        # 构造一个预期的 NumPy dtype,包含字段名和对应的数据类型
        expected = np.dtype([('one', 'u1'), ('two', '>u4')])

        # 调用自定义的检查方法,验证 BigEndStruct 类型和预期的 dtype 是否一致
        self.check(BigEndStruct, expected)

    # 定义一个测试方法,用于测试带打包方式的小端字节序结构体 LittleEndStruct
    def test_little_endian_structure_packed(self):
        # 定义一个带打包方式的小端字节序结构体 LittleEndStruct,包含一个 8 位无符号整数和一个 32 位无符号整数
        class LittleEndStruct(ctypes.LittleEndianStructure):
            _fields_ = [
                ('one', ctypes.c_uint8),
                ('two', ctypes.c_uint32)
            ]
            _pack_ = 1  # 设置结构体的打包对齐方式为 1 字节

        # 构造一个预期的 NumPy dtype,包含字段名和对应的数据类型
        expected = np.dtype([('one', 'u1'), ('two', '<u4')])

        # 调用自定义的检查方法,验证 LittleEndStruct 类型和预期的 dtype 是否一致
        self.check(LittleEndStruct, expected)

    # 定义一个测试方法,用于测试不带打包方式的小端字节序结构体 PaddedStruct
    def test_little_endian_structure(self):
        # 定义一个不带打包方式的小端字节序结构体 PaddedStruct,包含一个 8 位无符号整数和一个 16 位无符号整数
        class PaddedStruct(ctypes.LittleEndianStructure):
            _fields_ = [
                ('a', ctypes.c_uint8),
                ('b', ctypes.c_uint16)
            ]

        # 构造一个预期的 NumPy dtype,包含字段名、数据类型和是否需要对齐等信息
        expected = np.dtype([
            ('a', '<B'),
            ('b', '<H')
        ], align=True)

        # 调用自定义的检查方法,验证 PaddedStruct 类型和预期的 dtype 是否一致
        self.check(PaddedStruct, expected)
    # 定义一个测试类方法,用于测试大端结构的情况
    def test_big_endian_structure(self):
        # 定义一个继承自 ctypes.BigEndianStructure 的内部类 PaddedStruct
        class PaddedStruct(ctypes.BigEndianStructure):
            # 定义结构体的字段
            _fields_ = [
                ('a', ctypes.c_uint8),   # 字段 a,无符号 8 位整数
                ('b', ctypes.c_uint16)   # 字段 b,无符号 16 位整数
            ]
        # 创建一个预期的 NumPy 数据类型,描述了字段 a 和 b 的大端字节顺序
        expected = np.dtype([
            ('a', '>B'),   # 字段 a,大端无符号 8 位整数
            ('b', '>H')    # 字段 b,大端无符号 16 位整数
        ], align=True)
        # 调用 self.check 方法,验证 PaddedStruct 类型与预期的数据类型 expected 是否匹配
        self.check(PaddedStruct, expected)

    # 定义一个测试方法,用于测试简单的大小端类型
    def test_simple_endian_types(self):
        # 验证 ctypes.c_uint16 的小端数据类型与 np.dtype('<u2') 是否匹配
        self.check(ctypes.c_uint16.__ctype_le__, np.dtype('<u2'))
        # 验证 ctypes.c_uint16 的大端数据类型与 np.dtype('>u2') 是否匹配
        self.check(ctypes.c_uint16.__ctype_be__, np.dtype('>u2'))
        # 验证 ctypes.c_uint8 的小端数据类型与 np.dtype('u1') 是否匹配
        self.check(ctypes.c_uint8.__ctype_le__, np.dtype('u1'))
        # 验证 ctypes.c_uint8 的大端数据类型与 np.dtype('u1') 是否匹配
        self.check(ctypes.c_uint8.__ctype_be__, np.dtype('u1'))

    # 使用 itertools 的 permutations 函数生成所有类型码的两两排列组合
    all_types = set(np.typecodes['All'])
    all_pairs = permutations(all_types, 2)

    # 使用 pytest 的 parametrize 装饰器,对 all_pairs 中的每对元素执行测试
    @pytest.mark.parametrize("pair", all_pairs)
    def test_pairs(self, pair):
        """
        Check that np.dtype('x,y') matches [np.dtype('x'), np.dtype('y')]
        Example: np.dtype('d,I') -> dtype([('f0', '<f8'), ('f1', '<u4')])
        """
        # gh-5645: check that np.dtype('i,L') can be used
        # 根据 pair 的两个类型码,创建一个复合数据类型 np.dtype('x,y')
        pair_type = np.dtype('{},{}'.format(*pair))
        # 创建一个预期的复合数据类型,其中包含两个字段 f0 和 f1,分别对应 pair 的两个类型
        expected = np.dtype([('f0', pair[0]), ('f1', pair[1])])
        # 断言 pair_type 是否与预期的 expected 相等
        assert_equal(pair_type, expected)
class TestUserDType:
    @pytest.mark.leaks_references(reason="dynamically creates custom dtype.")
    def test_custom_structured_dtype(self):
        # 定义一个空的类 mytype
        class mytype:
            pass

        # 创建一个包含单个字段的自定义 dtype 蓝图
        blueprint = np.dtype([("field", object)])
        # 使用 create_custom_field_dtype 函数创建自定义字段类型 dt
        dt = create_custom_field_dtype(blueprint, mytype, 0)
        # 断言自定义字段类型的类型是 mytype
        assert dt.type == mytype
        # 断言 np.dtype(mytype) 等于 np.dtype("O")
        # 这里是因为当前不能直接用 np.dtype 创建 mytype,因为 mytype 没有继承自 `np.generic` 类型,
        # 这看起来是一个不必要的限制,但这个限制已经存在很久了。
        assert np.dtype(mytype) == np.dtype("O")

        if HAS_REFCOUNT:
            # 创建一个数组并测试内存是否正确清理(gh-25949)
            o = object()
            a = np.array([o], dtype=dt)
            del a
            # 断言对象 o 的引用计数为 2
            assert sys.getrefcount(o) == 2

    def test_custom_structured_dtype_errors(self):
        # 定义一个空的类 mytype
        class mytype:
            pass

        # 创建一个包含单个字段的自定义 dtype 蓝图
        blueprint = np.dtype([("field", object)])

        with pytest.raises(ValueError):
            # 测试在创建过程中字段未设置会发生什么情况,这种情况当前会被拒绝,因为包含的对象未设置字段(见 PyArray_RegisterDataType)。
            create_custom_field_dtype(blueprint, mytype, 1)

        with pytest.raises(RuntimeError):
            # 测试 dtype 必须将其类型字段设置为 np.dtype 或内置实例。
            create_custom_field_dtype(blueprint, mytype, 2)


class TestClassGetItem:
    def test_dtype(self) -> None:
        # 创建一个 np.dtype[Any] 的别名
        alias = np.dtype[Any]
        # 断言别名是 types.GenericAlias 的实例
        assert isinstance(alias, types.GenericAlias)
        # 断言别名的原始类型是 np.dtype
        assert alias.__origin__ is np.dtype

    @pytest.mark.parametrize("code", np.typecodes["All"])
    def test_dtype_subclass(self, code: str) -> None:
        # 获取对应 typecode 的类
        cls = type(np.dtype(code))
        # 创建一个 cls[Any] 的别名
        alias = cls[Any]
        # 断言别名是 types.GenericAlias 的实例
        assert isinstance(alias, types.GenericAlias)
        # 断言别名的原始类型是 cls
        assert alias.__origin__ is cls

    @pytest.mark.parametrize("arg_len", range(4))
    def test_subscript_tuple(self, arg_len: int) -> None:
        # 创建一个由 Any 组成的元组 arg_tup
        arg_tup = (Any,) * arg_len
        if arg_len == 1:
            # 断言 np.dtype[arg_tup] 是有效的
            assert np.dtype[arg_tup]
        else:
            # 测试当元组长度大于 1 时会抛出 TypeError
            with pytest.raises(TypeError):
                np.dtype[arg_tup]

    def test_subscript_scalar(self) -> None:
        # 断言 np.dtype[Any] 是有效的
        assert np.dtype[Any]


def test_result_type_integers_and_unitless_timedelta64():
    # 回归测试 gh-20077。以下调用 `result_type` 会导致段错误。
    td = np.timedelta64(4)
    # 调用 np.result_type 函数
    result = np.result_type(0, td)
    # 断言结果的 dtype 与 td 的 dtype 相等
    assert_dtype_equal(result, td.dtype)


def test_creating_dtype_with_dtype_class_errors():
    # 回归测试 #25031,用 np.dtype 自身调用会导致段错误。
    with pytest.raises(TypeError, match="Cannot convert np.dtype into a"):
        np.array(np.ones(10), dtype=np.dtype)

.\numpy\numpy\_core\tests\test_einsum.py

# 导入 itertools 库,用于生成迭代器的工具函数
import itertools
# 导入 sys 库,提供对 Python 解释器的访问和操作功能
import sys
# 导入 platform 库,提供访问平台特定属性的功能
import platform

# 导入 pytest 库,用于编写和运行测试用例
import pytest

# 导入 numpy 库,并从中导入一系列用于测试的函数和类
import numpy as np
from numpy.testing import (
    assert_, assert_equal, assert_array_equal, assert_almost_equal,
    assert_raises, suppress_warnings, assert_raises_regex, assert_allclose
    )

# 为优化 einsum 函数设置参数
# 字符串 chars 表示用于 einsum 函数的标记
chars = 'abcdefghij'
# 大小数组 sizes 表示与每个标记相关联的尺寸
sizes = np.array([2, 3, 4, 5, 4, 3, 2, 6, 5, 4, 3])
# 全局大小字典 global_size_dict 将每个标记映射到其对应的大小
global_size_dict = dict(zip(chars, sizes))

# 定义测试类 TestEinsum
class TestEinsum:
    # 使用 pytest.mark.parametrize 装饰器,为测试函数参数化
    @pytest.mark.parametrize("do_opt", [True, False])
    @pytest.mark.parametrize("einsum_fn", [np.einsum, np.einsum_path])
   python
    def test_einsum_errors(self, do_opt, einsum_fn):
        # 检查参数数量是否足够
        assert_raises(ValueError, einsum_fn, optimize=do_opt)
        assert_raises(ValueError, einsum_fn, "", optimize=do_opt)

        # 子脚本必须是字符串
        assert_raises(TypeError, einsum_fn, 0, 0, optimize=do_opt)

        # 问题 4528 显示此调用会导致段错误
        assert_raises(TypeError, einsum_fn, *(None,)*63, optimize=do_opt)

        # 操作数数量必须与子脚本字符串中的计数匹配
        assert_raises(ValueError, einsum_fn, "", 0, 0, optimize=do_opt)
        assert_raises(ValueError, einsum_fn, ",", 0, [0], [0],
                      optimize=do_opt)
        assert_raises(ValueError, einsum_fn, ",", [0], optimize=do_opt)

        # 子脚本不能比操作数的维度更多
        assert_raises(ValueError, einsum_fn, "i", 0, optimize=do_opt)
        assert_raises(ValueError, einsum_fn, "ij", [0, 0], optimize=do_opt)
        assert_raises(ValueError, einsum_fn, "...i", 0, optimize=do_opt)
        assert_raises(ValueError, einsum_fn, "i...j", [0, 0], optimize=do_opt)
        assert_raises(ValueError, einsum_fn, "i...", 0, optimize=do_opt)
        assert_raises(ValueError, einsum_fn, "ij...", [0, 0], optimize=do_opt)

        # 无效的省略号
        assert_raises(ValueError, einsum_fn, "i..", [0, 0], optimize=do_opt)
        assert_raises(ValueError, einsum_fn, ".i...", [0, 0], optimize=do_opt)
        assert_raises(ValueError, einsum_fn, "j->..j", [0, 0], optimize=do_opt)
        assert_raises(ValueError, einsum_fn, "j->.j...", [0, 0],
                      optimize=do_opt)

        # 无效的子脚本字符
        assert_raises(ValueError, einsum_fn, "i%...", [0, 0], optimize=do_opt)
        assert_raises(ValueError, einsum_fn, "...j$", [0, 0], optimize=do_opt)
        assert_raises(ValueError, einsum_fn, "i->&", [0, 0], optimize=do_opt)

        # 输出子脚本必须在输入中出现
        assert_raises(ValueError, einsum_fn, "i->ij", [0, 0], optimize=do_opt)

        # 输出子脚本只能指定一次
        assert_raises(ValueError, einsum_fn, "ij->jij", [[0, 0], [0, 0]],
                      optimize=do_opt)

        # 当被折叠时,维度必须匹配
        assert_raises(ValueError, einsum_fn, "ii",
                      np.arange(6).reshape(2, 3), optimize=do_opt)
        assert_raises(ValueError, einsum_fn, "ii->i",
                      np.arange(6).reshape(2, 3), optimize=do_opt)

        with assert_raises_regex(ValueError, "'b'"):
            # gh-11221 - 错误消息中错误地出现了 'c'
            a = np.ones((3, 3, 4, 5, 6))
            b = np.ones((3, 4, 5))
            einsum_fn('aabcb,abc', a, b)

    @pytest.mark.parametrize("do_opt", [True, False])
    # 定义测试函数,用于检查 np.einsum 函数在特定错误情况下的行为
    def test_einsum_specific_errors(self, do_opt):
        # 断言:out 参数必须是一个数组类型
        assert_raises(TypeError, np.einsum, "", 0, out='test',
                      optimize=do_opt)

        # 断言:order 参数必须是有效的顺序字符串
        assert_raises(ValueError, np.einsum, "", 0, order='W',
                      optimize=do_opt)

        # 断言:casting 参数必须是有效的转换规则
        assert_raises(ValueError, np.einsum, "", 0, casting='blah',
                      optimize=do_opt)

        # 断言:dtype 参数必须是有效的数据类型
        assert_raises(TypeError, np.einsum, "", 0, dtype='bad_data_type',
                      optimize=do_opt)

        # 断言:不接受其他关键字参数
        assert_raises(TypeError, np.einsum, "", 0, bad_arg=0, optimize=do_opt)

        # 断言:必须显式启用对新维度的广播
        assert_raises(ValueError, np.einsum, "i", np.arange(6).reshape(2, 3),
                      optimize=do_opt)
        assert_raises(ValueError, np.einsum, "i->i", [[0, 1], [0, 1]],
                      out=np.arange(4).reshape(2, 2), optimize=do_opt)

        # 检查 order 关键字参数,asanyarray 允许 1 维数组通过
        assert_raises(ValueError, np.einsum, "i->i",
                      np.arange(6).reshape(-1, 1), optimize=do_opt, order='d')
    # 测试 einsum 方法对对象错误的处理能力
    def test_einsum_object_errors(self):
        # 定义自定义异常类,用于处理对象算术引发的异常
        class CustomException(Exception):
            pass

        # 定义一个特殊的盒子类,支持加法和乘法运算,当值超过特定阈值时抛出自定义异常
        class DestructoBox:

            def __init__(self, value, destruct):
                self._val = value
                self._destruct = destruct

            def __add__(self, other):
                # 定义加法运算,如果结果值超过设定的破坏阈值,则抛出自定义异常
                tmp = self._val + other._val
                if tmp >= self._destruct:
                    raise CustomException
                else:
                    self._val = tmp
                    return self

            def __radd__(self, other):
                # 定义反向加法运算,处理特殊情况下的加法
                if other == 0:
                    return self
                else:
                    return self.__add__(other)

            def __mul__(self, other):
                # 定义乘法运算,如果结果值超过设定的破坏阈值,则抛出自定义异常
                tmp = self._val * other._val
                if tmp >= self._destruct:
                    raise CustomException
                else:
                    self._val = tmp
                    return self

            def __rmul__(self, other):
                # 定义反向乘法运算,处理特殊情况下的乘法
                if other == 0:
                    return self
                else:
                    return self.__mul__(other)

        # 创建一个包含 DestructoBox 实例的 NumPy 对象数组,并进行形状重塑
        a = np.array([DestructoBox(i, 5) for i in range(1, 10)],
                     dtype='object').reshape(3, 3)

        # 断言异常应该由 np.einsum 方法抛出,计算字符串为 "ij->i",操作数组为 a
        assert_raises(CustomException, np.einsum, "ij->i", a)

        # 创建一个包含 DestructoBox 实例的三维 NumPy 对象数组,并进行形状重塑
        b = np.array([DestructoBox(i, 100) for i in range(0, 27)],
                     dtype='object').reshape(3, 3, 3)

        # 断言异常应该由 np.einsum 方法抛出,计算字符串为 "i...k->...",操作数组为 b
        assert_raises(CustomException, np.einsum, "i...k->...", b)

        # 创建一个包含 DestructoBox 实例的一维 NumPy 对象数组 b
        b = np.array([DestructoBox(i, 55) for i in range(1, 4)],
                     dtype='object')

        # 断言异常应该由 np.einsum 方法抛出,计算字符串为 "ij, j",操作数组分别为 a 和 b
        assert_raises(CustomException, np.einsum, "ij, j", a, b)

        # 断言异常应该由 np.einsum 方法抛出,计算字符串为 "ij, jh",操作数组分别为 a 和 a
        assert_raises(CustomException, np.einsum, "ij, jh", a, a)

        # 断言异常应该由 np.einsum 方法抛出,计算字符串为 "ij->",操作数组为 a
        assert_raises(CustomException, np.einsum, "ij->", a)

    @np._no_nep50_warning()
    # 使用装饰器,禁止在 NEP 50 警告下运行的 einsum 方法测试
    def test_einsum_sums_int8(self):
        # 调用 check_einsum_sums 方法,检查特定类型('i1')的 einsum 运算
        self.check_einsum_sums('i1')

    # 调用 check_einsum_sums 方法,检查特定类型('u1')的 einsum 运算
    def test_einsum_sums_uint8(self):
        self.check_einsum_sums('u1')

    # 调用 check_einsum_sums 方法,检查特定类型('i2')的 einsum 运算
    def test_einsum_sums_int16(self):
        self.check_einsum_sums('i2')

    # 调用 check_einsum_sums 方法,检查特定类型('u2')的 einsum 运算
    def test_einsum_sums_uint16(self):
        self.check_einsum_sums('u2')

    # 调用 check_einsum_sums 方法,检查特定类型('i4')的 einsum 运算,并进行额外的检查
    def test_einsum_sums_int32(self):
        self.check_einsum_sums('i4')
        self.check_einsum_sums('i4', True)

    # 调用 check_einsum_sums 方法,检查特定类型('u4')的 einsum 运算,并进行额外的检查
    def test_einsum_sums_uint32(self):
        self.check_einsum_sums('u4')
        self.check_einsum_sums('u4', True)

    # 调用 check_einsum_sums 方法,检查特定类型('i8')的 einsum 运算
    def test_einsum_sums_int64(self):
        self.check_einsum_sums('i8')

    # 调用 check_einsum_sums 方法,检查特定类型('u8')的 einsum 运算
    def test_einsum_sums_uint64(self):
        self.check_einsum_sums('u8')

    # 调用 check_einsum_sums 方法,检查特定类型('f2')的 einsum 运算
    def test_einsum_sums_float16(self):
        self.check_einsum_sums('f2')

    # 调用 check_einsum_sums 方法,检查特定类型('f4')的 einsum 运算
    def test_einsum_sums_float32(self):
        self.check_einsum_sums('f4')
    # 测试 np.einsum 在浮点数类型 'f8' 上的求和功能
    def test_einsum_sums_float64(self):
        self.check_einsum_sums('f8')  # 调用检查函数,对 'f8' 类型进行测试
        self.check_einsum_sums('f8', True)  # 同上,使用优化参数进行测试

    # 测试 np.einsum 在 np.longdouble 类型上的求和功能
    def test_einsum_sums_longdouble(self):
        self.check_einsum_sums(np.longdouble)  # 调用检查函数,对 np.longdouble 类型进行测试

    # 测试 np.einsum 在复数浮点数 'c8' 上的求和功能
    def test_einsum_sums_cfloat64(self):
        self.check_einsum_sums('c8')  # 调用检查函数,对 'c8' 类型进行测试
        self.check_einsum_sums('c8', True)  # 同上,使用优化参数进行测试

    # 测试 np.einsum 在更大精度的复数浮点数 'c16' 上的求和功能
    def test_einsum_sums_cfloat128(self):
        self.check_einsum_sums('c16')  # 调用检查函数,对 'c16' 类型进行测试

    # 测试 np.einsum 在 np.clongdouble 类型上的求和功能
    def test_einsum_sums_clongdouble(self):
        self.check_einsum_sums(np.clongdouble)  # 调用检查函数,对 np.clongdouble 类型进行测试

    # 测试 np.einsum 在对象 'object' 类型上的其他功能
    def test_einsum_sums_object(self):
        self.check_einsum_sums('object')  # 调用检查函数,对 'object' 类型进行测试
        self.check_einsum_sums('object', True)  # 同上,使用优化参数进行测试

    # 测试 np.einsum 的各种其他用途和特殊情况
    def test_einsum_misc(self):
        # 检验之前因 PyArray_AssignZero 错误而导致崩溃的情况
        a = np.ones((1, 2))
        b = np.ones((2, 2, 1))
        assert_equal(np.einsum('ij...,j...->i...', a, b), [[[2], [2]]])
        assert_equal(np.einsum('ij...,j...->i...', a, b, optimize=True), [[[2], [2]]])

        # 对于问题 #10369 的回归测试(测试 Python 2 中的 Unicode 输入)
        assert_equal(np.einsum('ij...,j...->i...', a, b), [[[2], [2]]])
        assert_equal(np.einsum('...i,...i', [1, 2, 3], [2, 3, 4]), 20)
        assert_equal(np.einsum('...i,...i', [1, 2, 3], [2, 3, 4],
                               optimize='greedy'), 20)

        # 迭代器在缓冲这种归约过程时存在问题
        a = np.ones((5, 12, 4, 2, 3), np.int64)
        b = np.ones((5, 12, 11), np.int64)
        assert_equal(np.einsum('ijklm,ijn,ijn->', a, b, b),
                     np.einsum('ijklm,ijn->', a, b))
        assert_equal(np.einsum('ijklm,ijn,ijn->', a, b, b, optimize=True),
                     np.einsum('ijklm,ijn->', a, b, optimize=True))

        # 问题 #2027,内部循环实现中连续的三参数存在问题
        a = np.arange(1, 3)
        b = np.arange(1, 5).reshape(2, 2)
        c = np.arange(1, 9).reshape(4, 2)
        assert_equal(np.einsum('x,yx,zx->xzy', a, b, c),
                     [[[1,  3], [3,  9], [5, 15], [7, 21]],
                     [[8, 16], [16, 32], [24, 48], [32, 64]]])
        assert_equal(np.einsum('x,yx,zx->xzy', a, b, c, optimize=True),
                     [[[1,  3], [3,  9], [5, 15], [7, 21]],
                     [[8, 16], [16, 32], [24, 48], [32, 64]]])

        # 确保明确设置 out=None 不会导致错误
        # 参见问题 gh-15776 和问题 gh-15256
        assert_equal(np.einsum('i,j', [1], [2], out=None), [[2]])

    # 测试包含对象的循环情况
    def test_object_loop(self):

        # 定义一个类 Mult,实现乘法操作返回 42
        class Mult:
            def __mul__(self, other):
                return 42

        # 创建包含 Mult 对象的 np.array
        objMult = np.array([Mult()])

        # 创建一个包含空对象的 np.ndarray
        objNULL = np.ndarray(buffer=b'\0' * np.intp(0).itemsize, shape=1, dtype=object)

        # 使用 pytest 的断言捕获 TypeError 异常
        with pytest.raises(TypeError):
            np.einsum("i,j", [1], objNULL)
        with pytest.raises(TypeError):
            np.einsum("i,j", objNULL, [1])

        # 检查 np.einsum 在 objMult 对象上的乘法操作是否返回了预期值 42
        assert np.einsum("i,j", objMult, objMult) == 42
    def test_subscript_range(self):
        # 对于拉丁字母表的所有字母(包括大写和小写),确保可以在创建数组的下标时使用
        # 创建两个全为1的数组,形状分别为 (2, 3) 和 (3, 4)
        a = np.ones((2, 3))
        b = np.ones((3, 4))
        # 使用 np.einsum 函数执行张量运算,不优化,指定下标操作
        np.einsum(a, [0, 20], b, [20, 2], [0, 2], optimize=False)
        np.einsum(a, [0, 27], b, [27, 2], [0, 2], optimize=False)
        np.einsum(a, [0, 51], b, [51, 2], [0, 2], optimize=False)
        # 断言语句,检查是否引发 ValueError 异常,lambda 函数用于调用 np.einsum
        assert_raises(ValueError, lambda: np.einsum(a, [0, 52], b, [52, 2], [0, 2], optimize=False))
        assert_raises(ValueError, lambda: np.einsum(a, [-1, 5], b, [5, 2], [-1, 2], optimize=False))

    def test_einsum_broadcast(self):
        # 处理省略号时的变化,修复“中间广播”错误
        # 仅在 prepare_op_axes 中使用“RIGHT”迭代
        # 在左侧自动广播,在右侧必须显式广播
        # 我们需要测试优化解析功能

        # 创建两个数组 A 和 B,分别为形状为 (2, 3, 4) 和 (3,)
        A = np.arange(2 * 3 * 4).reshape(2, 3, 4)
        B = np.arange(3)
        # 创建参考结果 ref,使用 np.einsum 执行张量运算,指定下标操作,不优化
        ref = np.einsum('ijk,j->ijk', A, B, optimize=False)
        # 循环测试优化和非优化的解析功能
        for opt in [True, False]:
            assert_equal(np.einsum('ij...,j...->ij...', A, B, optimize=opt), ref)
            assert_equal(np.einsum('ij...,...j->ij...', A, B, optimize=opt), ref)
            assert_equal(np.einsum('ij...,j->ij...', A, B, optimize=opt), ref)  # 曾引发错误

        # 创建两个数组 A 和 B,形状分别为 (4, 3) 和 (3, 2)
        A = np.arange(12).reshape((4, 3))
        B = np.arange(6).reshape((3, 2))
        # 创建参考结果 ref,使用 np.einsum 执行张量运算,指定下标操作,不优化
        ref = np.einsum('ik,kj->ij', A, B, optimize=False)
        # 循环测试优化和非优化的解析功能
        for opt in [True, False]:
            assert_equal(np.einsum('ik...,k...->i...', A, B, optimize=opt), ref)
            assert_equal(np.einsum('ik...,...kj->i...j', A, B, optimize=opt), ref)
            assert_equal(np.einsum('...k,kj', A, B, optimize=opt), ref)  # 曾引发错误
            assert_equal(np.einsum('ik,k...->i...', A, B, optimize=opt), ref)  # 曾引发错误

        # 创建维度列表 dims,形状为 [2, 3, 4, 5]
        dims = [2, 3, 4, 5]
        # 创建数组 a 和 v,形状分别为 dims 和 (4,)
        a = np.arange(np.prod(dims)).reshape(dims)
        v = np.arange(dims[2])
        # 创建参考结果 ref,使用 np.einsum 执行张量运算,指定下标操作,不优化
        ref = np.einsum('ijkl,k->ijl', a, v, optimize=False)
        # 循环测试优化和非优化的解析功能
        for opt in [True, False]:
            assert_equal(np.einsum('ijkl,k', a, v, optimize=opt), ref)
            assert_equal(np.einsum('...kl,k', a, v, optimize=opt), ref)  # 曾引发错误
            assert_equal(np.einsum('...kl,k...', a, v, optimize=opt), ref)

        # 设置 J、K、M 的值分别为 160
        J, K, M = 160, 160, 120
        # 创建数组 A 和 B,形状分别为 (1, 1, 1, 160, 160, 120) 和 (160, 160, 120, 3)
        A = np.arange(J * K * M).reshape(1, 1, 1, J, K, M)
        B = np.arange(J * K * M * 3).reshape(J, K, M, 3)
        # 创建参考结果 ref,使用 np.einsum 执行张量运算,指定下标操作,不优化
        ref = np.einsum('...lmn,...lmno->...o', A, B, optimize=False)
        # 循环测试优化和非优化的解析功能
        for opt in [True, False]:
            assert_equal(np.einsum('...lmn,lmno->...o', A, B, optimize=opt), ref)  # 曾引发错误
    def test_einsum_fixedstridebug(self):
        # Issue #4485 obscure einsum bug
        # This case revealed a bug in nditer where it reported a stride
        # as 'fixed' (0) when it was in fact not fixed during processing
        # (0 or 4). The reason for the bug was that the check for a fixed
        # stride was using the information from the 2D inner loop reuse
        # to restrict the iteration dimensions it had to validate to be
        # the same, but that 2D inner loop reuse logic is only triggered
        # during the buffer copying step, and hence it was invalid to
        # rely on those values. The fix is to check all the dimensions
        # of the stride in question, which in the test case reveals that
        # the stride is not fixed.
        #
        # NOTE: This test is triggered by the fact that the default buffersize,
        #       used by einsum, is 8192, and 3*2731 = 8193, is larger than that
        #       and results in a mismatch between the buffering and the
        #       striding for operand A.
        
        # 创建一个2行3列的浮点型数组A,用于测试
        A = np.arange(2 * 3).reshape(2, 3).astype(np.float32)
        # 创建一个2行3列2731深度的整型数组B,用于测试
        B = np.arange(2 * 3 * 2731).reshape(2, 3, 2731).astype(np.int16)
        # 使用einsum函数对A和B进行操作,计算结果存入es中
        es = np.einsum('cl, cpx->lpx', A, B)
        # 使用tensordot函数对A和B进行操作,计算结果存入tp中
        tp = np.tensordot(A, B, axes=(0, 0))
        # 断言es与tp的值相等
        assert_equal(es, tp)
        
        # 以下是原始的出现bug的测试用例,通过使用aranges函数使其可重复性
        # 创建一个3行3列的双精度浮点型数组A,用于测试
        A = np.arange(3 * 3).reshape(3, 3).astype(np.float64)
        # 创建一个3行3列64x64深度的单精度浮点型数组B,用于测试
        B = np.arange(3 * 3 * 64 * 64).reshape(3, 3, 64, 64).astype(np.float32)
        # 使用einsum函数对A和B进行操作,计算结果存入es中
        es = np.einsum('cl, cpxy->lpxy', A, B)
        # 使用tensordot函数对A和B进行操作,计算结果存入tp中
        tp = np.tensordot(A, B, axes=(0, 0))
        # 断言es与tp的值相等
        assert_equal(es, tp)

    def test_einsum_fixed_collapsingbug(self):
        # Issue #5147.
        # The bug only occurred when output argument of einssum was used.
        
        # 创建一个5x5x5x5的正态分布随机数组x,用于测试
        x = np.random.normal(0, 1, (5, 5, 5, 5))
        # 创建一个5x5的零数组y1,用于存放结果
        y1 = np.zeros((5, 5))
        # 使用einsum函数计算x的特定索引操作,结果存入y1中
        np.einsum('aabb->ab', x, out=y1)
        # 创建一个1维数组idx,用于后续索引操作
        idx = np.arange(5)
        # 使用索引数组idx对x进行多维索引操作,结果存入y2中
        y2 = x[idx[:, None], idx[:, None], idx, idx]
        # 断言y1与y2的值相等
        assert_equal(y1, y2)

    def test_einsum_failed_on_p9_and_s390x(self):
        # Issues gh-14692 and gh-12689
        # Bug with signed vs unsigned char errored on power9 and s390x Linux
        
        # 创建一个10x10x10x10的随机数组tensor,用于测试
        tensor = np.random.random_sample((10, 10, 10, 10))
        # 使用einsum函数对tensor进行特定操作,结果存入x中
        x = np.einsum('ijij->', tensor)
        # 使用trace方法计算tensor在指定轴的轨迹,结果存入y中
        y = tensor.trace(axis1=0, axis2=2).trace()
        # 断言x与y的值在允许误差范围内相等
        assert_allclose(x, y)
    def test_einsum_all_contig_non_contig_output(self):
        # Issue gh-5907, tests that the all contiguous special case
        # actually checks the contiguity of the output
        
        # 创建一个 5x5 的全1数组
        x = np.ones((5, 5))
        
        # 创建一个长度为10的全1数组,取出其偶数索引位置的切片作为输出
        out = np.ones(10)[::2]
        
        # 创建一个长度为10的全1数组作为正确的基础参考
        correct_base = np.ones(10)
        correct_base[::2] = 5
        
        # np.einsum的使用例子1:使用 x, x, x 进行矩阵乘法运算,并将结果输出到 out 中
        np.einsum('mi,mi,mi->m', x, x, x, out=out)
        
        # 断言,检查 out 的基础数据是否与正确的基础数据相等
        assert_array_equal(out.base, correct_base)
        
        # np.einsum的使用例子2:使用 x, x 进行矩阵乘法运算,并将结果输出到 out 中
        np.einsum('im,im,im->m', x, x, x, out=out)
        
        # 断言,检查 out 的基础数据是否与正确的基础数据相等
        assert_array_equal(out.base, correct_base)
        
        # 重新初始化 out,创建一个形状为 (2, 2, 2) 的数组,并取其最后一个维度的切片作为输出
        out = np.ones((2, 2, 2))[..., 0]
        
        # 创建一个形状为 (2, 2, 2) 的全1数组作为正确的基础参考
        correct_base = np.ones((2, 2, 2))
        correct_base[..., 0] = 2
        
        # 创建一个形状为 (2, 2) 的全1浮点数数组 x
        x = np.ones((2, 2), np.float32)
        
        # np.einsum的使用例子3:使用 x, x 进行矩阵乘法运算,并将结果输出到 out 中
        np.einsum('ij,jk->ik', x, x, out=out)
        
        # 断言,检查 out 的基础数据是否与正确的基础数据相等
        assert_array_equal(out.base, correct_base)

    @pytest.mark.parametrize("dtype",
             np.typecodes["AllFloat"] + np.typecodes["AllInteger"])
    def test_different_paths(self, dtype):
        # Test originally added to cover broken float16 path: gh-20305
        # Likely most are covered elsewhere, at least partially.
        dtype = np.dtype(dtype)
        # 创建一个包含指定数据类型的 NumPy dtype 对象

        arr = (np.arange(7) + 0.5).astype(dtype)
        # 创建一个包含 7 个元素的数组,元素为从 0.5 开始的浮点数序列,并转换为指定数据类型

        scalar = np.array(2, dtype=dtype)
        # 创建一个包含单个整数 2 的数组,并使用指定的数据类型

        # contig -> scalar:
        res = np.einsum('i->', arr)
        # 对数组进行 einsum 操作,求和所有元素,返回一个标量
        assert res == arr.sum()
        # 断言求和结果与 arr.sum() 相等

        # contig, contig -> contig:
        res = np.einsum('i,i->i', arr, arr)
        # 对两个数组进行 einsum 操作,对应元素相乘,返回一个数组
        assert_array_equal(res, arr * arr)
        # 断言结果数组与 arr * arr 相等

        # noncontig, noncontig -> contig:
        res = np.einsum('i,i->i', arr.repeat(2)[::2], arr.repeat(2)[::2])
        # 对两个非连续的数组进行 einsum 操作,对应元素相乘,返回一个数组
        assert_array_equal(res, arr * arr)
        # 断言结果数组与 arr * arr 相等

        # contig + contig -> scalar
        assert np.einsum('i,i->', arr, arr) == (arr * arr).sum()
        # 对两个数组进行 einsum 操作,求和所有对应元素的乘积,返回一个标量

        # contig + scalar -> contig (with out)
        out = np.ones(7, dtype=dtype)
        # 创建一个包含全部为 1 的数组,使用指定的数据类型
        res = np.einsum('i,->i', arr, dtype.type(2), out=out)
        # 对数组和标量进行 einsum 操作,每个元素乘以标量,将结果存储在指定的输出数组中
        assert_array_equal(res, arr * dtype.type(2))
        # 断言结果数组与 arr * dtype.type(2) 相等

        # scalar + contig -> contig (with out)
        res = np.einsum(',i->i', scalar, arr)
        # 对标量和数组进行 einsum 操作,标量乘以每个数组元素,返回一个数组
        assert_array_equal(res, arr * dtype.type(2))
        # 断言结果数组与 arr * dtype.type(2) 相等

        # scalar + contig -> scalar
        res = np.einsum(',i->', scalar, arr)
        # 对标量和数组进行 einsum 操作,标量乘以数组所有元素之和,返回一个标量
        # 使用 einsum 来比较,避免由于求和过程中的舍入误差导致差异
        assert res == np.einsum('i->', scalar * arr)
        # 断言结果与 np.einsum('i->', scalar * arr) 相等

        # contig + scalar -> scalar
        res = np.einsum('i,->', arr, scalar)
        # 对数组和标量进行 einsum 操作,数组乘以标量,返回一个标量
        # 使用 einsum 来比较,避免由于求和过程中的舍入误差导致差异
        assert res == np.einsum('i->', scalar * arr)
        # 断言结果与 np.einsum('i->', scalar * arr) 相等

        # contig + contig + contig -> scalar
        arr = np.array([0.5, 0.5, 0.25, 4.5, 3.], dtype=dtype)
        # 创建一个包含指定数据类型的数组,包含指定的元素
        res = np.einsum('i,i,i->', arr, arr, arr)
        # 对三个数组进行 einsum 操作,对应元素相乘并求和,返回一个标量
        assert_array_equal(res, (arr * arr * arr).sum())
        # 断言结果与 (arr * arr * arr).sum() 相等

        # four arrays:
        res = np.einsum('i,i,i,i->', arr, arr, arr, arr)
        # 对四个数组进行 einsum 操作,对应元素相乘并求和,返回一个标量
        assert_array_equal(res, (arr * arr * arr * arr).sum())
        # 断言结果与 (arr * arr * arr * arr).sum() 相等

    def test_small_boolean_arrays(self):
        # See gh-5946.
        # Use array of True embedded in False.
        a = np.zeros((16, 1, 1), dtype=np.bool)[:2]
        # 创建一个形状为 (2, 1, 1) 的布尔类型数组,初始化为 False
        a[...] = True
        # 将数组所有元素设置为 True
        out = np.zeros((16, 1, 1), dtype=np.bool)[:2]
        # 创建一个形状为 (2, 1, 1) 的布尔类型数组,初始化为 False
        tgt = np.ones((2, 1, 1), dtype=np.bool)
        # 创建一个形状为 (2, 1, 1) 的布尔类型数组,初始化为 True
        res = np.einsum('...ij,...jk->...ik', a, a, out=out)
        # 对两个布尔类型数组进行 einsum 操作,对应元素相乘,并将结果存储在指定的输出数组中
        assert_equal(res, tgt)
        # 断言结果与目标数组相等

    def test_out_is_res(self):
        a = np.arange(9).reshape(3, 3)
        # 创建一个形状为 (3, 3) 的数组,包含 0 到 8 的整数
        res = np.einsum('...ij,...jk->...ik', a, a, out=a)
        # 对两个数组进行 einsum 操作,对应元素相乘,并将结果存储在输入的数组中
        assert res is a
        # 断言结果与输入的数组是同一个对象
    def optimize_compare(self, subscripts, operands=None):
        # 对优化函数的所有路径进行测试,与常规的 einsum 进行比较

        # 如果没有操作数,则从子表达式中提取参数并生成随机数组
        if operands is None:
            args = [subscripts]
            terms = subscripts.split('->')[0].split(',')
            for term in terms:
                dims = [global_size_dict[x] for x in term]
                args.append(np.random.rand(*dims))
        else:
            args = [subscripts] + operands
        
        # 使用 optimize=False 参数执行 einsum 操作
        noopt = np.einsum(*args, optimize=False)
        
        # 使用 optimize='greedy' 参数执行 einsum 操作
        opt = np.einsum(*args, optimize='greedy')
        
        # 断言优化后的结果与未优化结果近似相等
        assert_almost_equal(opt, noopt)
        
        # 使用 optimize='optimal' 参数执行 einsum 操作
        opt = np.einsum(*args, optimize='optimal')
        
        # 断言优化后的结果与未优化结果近似相等
        assert_almost_equal(opt, noopt)

    def test_hadamard_like_products(self):
        # 测试 Hadamard 外积类似的乘积
        self.optimize_compare('a,ab,abc->abc')
        self.optimize_compare('a,b,ab->ab')

    def test_index_transformations(self):
        # 测试简单的索引转换情况
        self.optimize_compare('ea,fb,gc,hd,abcd->efgh')
        self.optimize_compare('ea,fb,abcd,gc,hd->efgh')
        self.optimize_compare('abcd,ea,fb,gc,hd->efgh')

    def test_complex(self):
        # 测试长的复杂用例
        self.optimize_compare('acdf,jbje,gihb,hfac,gfac,gifabc,hfac')
        self.optimize_compare('acdf,jbje,gihb,hfac,gfac,gifabc,hfac')
        self.optimize_compare('cd,bdhe,aidb,hgca,gc,hgibcd,hgac')
        self.optimize_compare('abhe,hidj,jgba,hiab,gab')
        self.optimize_compare('bde,cdh,agdb,hica,ibd,hgicd,hiac')
        self.optimize_compare('chd,bde,agbc,hiad,hgc,hgi,hiad')
        self.optimize_compare('chd,bde,agbc,hiad,bdi,cgh,agdb')
        self.optimize_compare('bdhe,acad,hiab,agac,hibd')

    def test_collapse(self):
        # 测试内积
        self.optimize_compare('ab,ab,c->')
        self.optimize_compare('ab,ab,c->c')
        self.optimize_compare('ab,ab,cd,cd->')
        self.optimize_compare('ab,ab,cd,cd->ac')
        self.optimize_compare('ab,ab,cd,cd->cd')
        self.optimize_compare('ab,ab,cd,cd,ef,ef->')

    def test_expand(self):
        # 测试外积
        self.optimize_compare('ab,cd,ef->abcdef')
        self.optimize_compare('ab,cd,ef->acdf')
        self.optimize_compare('ab,cd,de->abcde')
        self.optimize_compare('ab,cd,de->be')
        self.optimize_compare('ab,bcd,cd->abcd')
        self.optimize_compare('ab,bcd,cd->abd')
    def test_edge_cases(self):
        # Difficult edge cases for optimization
        # 调用 optimize_compare 方法进行优化比较,传入字符串参数
        self.optimize_compare('eb,cb,fb->cef')
        self.optimize_compare('dd,fb,be,cdb->cef')
        self.optimize_compare('bca,cdb,dbf,afc->')
        self.optimize_compare('dcc,fce,ea,dbf->ab')
        self.optimize_compare('fdf,cdd,ccd,afe->ae')
        self.optimize_compare('abcd,ad')
        self.optimize_compare('ed,fcd,ff,bcf->be')
        self.optimize_compare('baa,dcf,af,cde->be')
        self.optimize_compare('bd,db,eac->ace')
        self.optimize_compare('fff,fae,bef,def->abd')
        self.optimize_compare('efc,dbc,acf,fd->abe')
        self.optimize_compare('ba,ac,da->bcd')

    def test_inner_product(self):
        # Inner products
        # 调用 optimize_compare 方法进行优化比较,传入字符串参数
        self.optimize_compare('ab,ab')
        self.optimize_compare('ab,ba')
        self.optimize_compare('abc,abc')
        self.optimize_compare('abc,bac')
        self.optimize_compare('abc,cba')

    def test_random_cases(self):
        # Randomly built test cases
        # 调用 optimize_compare 方法进行优化比较,传入字符串参数
        self.optimize_compare('aab,fa,df,ecc->bde')
        self.optimize_compare('ecb,fef,bad,ed->ac')
        self.optimize_compare('bcf,bbb,fbf,fc->')
        self.optimize_compare('bb,ff,be->e')
        self.optimize_compare('bcb,bb,fc,fff->')
        self.optimize_compare('fbb,dfd,fc,fc->')
        self.optimize_compare('afd,ba,cc,dc->bf')
        self.optimize_compare('adb,bc,fa,cfc->d')
        self.optimize_compare('bbd,bda,fc,db->acf')
        self.optimize_compare('dba,ead,cad->bce')
        self.optimize_compare('aef,fbc,dca->bde')

    def test_combined_views_mapping(self):
        # gh-10792
        # 创建一个 5x3 的 NumPy 数组,并使用 einsum 计算特定乘积
        a = np.arange(9).reshape(1, 1, 3, 1, 3)
        b = np.einsum('bbcdc->d', a)
        # 断言计算结果与预期值相等
        assert_equal(b, [12])

    def test_broadcasting_dot_cases(self):
        # Ensures broadcasting cases are not mistaken for GEMM

        # 创建不同形状的随机 NumPy 数组
        a = np.random.rand(1, 5, 4)
        b = np.random.rand(4, 6)
        c = np.random.rand(5, 6)
        d = np.random.rand(10)

        # 调用 optimize_compare 方法进行优化比较,传入字符串参数和 operands 参数列表
        self.optimize_compare('ijk,kl,jl', operands=[a, b, c])
        self.optimize_compare('ijk,kl,jl,i->i', operands=[a, b, c, d])

        # 创建不同形状的随机 NumPy 数组,并调用 optimize_compare 方法进行优化比较,传入字符串参数和 operands 参数列表
        e = np.random.rand(1, 1, 5, 4)
        f = np.random.rand(7, 7)
        self.optimize_compare('abjk,kl,jl', operands=[e, b, c])
        self.optimize_compare('abjk,kl,jl,ab->ab', operands=[e, b, c, f])

        # 在 gh-11308 中发现的边缘情况
        # 创建一个形状为 (2, 4, 8) 的 NumPy 数组 g,然后进行优化比较,传入字符串参数和 operands 参数列表
        g = np.arange(64).reshape(2, 4, 8)
        self.optimize_compare('obk,ijk->ioj', operands=[g, g])
    def test_output_order(self):
        # 定义一个测试方法,用于验证输出顺序在优化情况下是否被尊重,以下压缩应该产生重塑的张量视图
        # 问题编号 gh-16415

        # 创建一个列序(列主序)的全为1的3维数组
        a = np.ones((2, 3, 5), order='F')
        # 创建一个列序(列主序)的全为1的2维数组
        b = np.ones((4, 3), order='F')

        # 对于优化和非优化两种情况进行循环
        for opt in [True, False]:
            # 使用 einsum 函数按照指定的公式 '...ft,mf->...mt' 计算结果 tmp
            tmp = np.einsum('...ft,mf->...mt', a, b, order='a', optimize=opt)
            # 断言 tmp 是列序连续的
            assert_(tmp.flags.f_contiguous)

            # 同上,但是指定了不同的输出顺序为 'f'
            tmp = np.einsum('...ft,mf->...mt', a, b, order='f', optimize=opt)
            # 断言 tmp 是列序连续的
            assert_(tmp.flags.f_contiguous)

            # 同上,但是指定了输出顺序为 'c'
            tmp = np.einsum('...ft,mf->...mt', a, b, order='c', optimize=opt)
            # 断言 tmp 是行序连续的
            assert_(tmp.flags.c_contiguous)

            # 同上,但是指定了输出顺序为 'k'
            tmp = np.einsum('...ft,mf->...mt', a, b, order='k', optimize=opt)
            # 断言 tmp 既不是行序连续的也不是列序连续的
            assert_(tmp.flags.c_contiguous is False)
            assert_(tmp.flags.f_contiguous is False)

            # 未指定输出顺序的情况下进行 einsum 计算
            tmp = np.einsum('...ft,mf->...mt', a, b, optimize=opt)
            # 断言 tmp 既不是行序连续的也不是列序连续的
            assert_(tmp.flags.c_contiguous is False)
            assert_(tmp.flags.f_contiguous is False)

        # 创建一个行序(行主序)的全为1的2维数组
        c = np.ones((4, 3), order='C')
        # 对于优化和非优化两种情况进行循环
        for opt in [True, False]:
            # 使用 einsum 函数按照指定的公式 '...ft,mf->...mt' 计算结果 tmp
            tmp = np.einsum('...ft,mf->...mt', a, c, order='a', optimize=opt)
            # 断言 tmp 是行序连续的
            assert_(tmp.flags.c_contiguous)

        # 创建一个行序(行主序)的全为1的3维数组
        d = np.ones((2, 3, 5), order='C')
        # 对于优化和非优化两种情况进行循环
        for opt in [True, False]:
            # 使用 einsum 函数按照指定的公式 '...ft,mf->...mt' 计算结果 tmp
            tmp = np.einsum('...ft,mf->...mt', d, c, order='a', optimize=opt)
            # 断言 tmp 是行序连续的
            assert_(tmp.flags.c_contiguous)
    # 定义一个测试类 TestEinsumPath,用于测试 np.einsum_path 函数的路径优化功能
class TestEinsumPath:
    # 根据给定的字符串和大小字典构建操作数列表
    def build_operands(self, string, size_dict=global_size_dict):
        # 初始化操作数列表,第一个元素为输入字符串本身
        operands = [string]
        # 解析输入字符串中的各个操作数
        terms = string.split('->')[0].split(',')
        # 根据每个操作数的维度在大小字典中查找并创建随机数组
        for term in terms:
            dims = [size_dict[x] for x in term]
            operands.append(np.random.rand(*dims))

        return operands

    # 断言两个路径列表是否相等
    def assert_path_equal(self, comp, benchmark):
        # 检查路径列表的长度是否相等
        ret = (len(comp) == len(benchmark))
        assert_(ret)
        # 逐个比较路径中的每个元素是否相等
        for pos in range(len(comp) - 1):
            ret &= isinstance(comp[pos + 1], tuple)
            ret &= (comp[pos + 1] == benchmark[pos + 1])
        assert_(ret)

    # 测试内存约束是否满足
    def test_memory_contraints(self):
        # 使用简单的操作数构建测试
        outer_test = self.build_operands('a,b,c->abc')

        # 使用 'greedy' 优化方法计算路径和路径字符串
        path, path_str = np.einsum_path(*outer_test, optimize=('greedy', 0))
        # 断言计算得到的路径与预期路径相等
        self.assert_path_equal(path, ['einsum_path', (0, 1, 2)])

        # 使用 'optimal' 优化方法计算路径和路径字符串
        path, path_str = np.einsum_path(*outer_test, optimize=('optimal', 0))
        # 断言计算得到的路径与预期路径相等
        self.assert_path_equal(path, ['einsum_path', (0, 1, 2)])

        # 使用复杂的操作数构建测试
        long_test = self.build_operands('acdf,jbje,gihb,hfac')

        # 使用 'greedy' 优化方法计算路径和路径字符串
        path, path_str = np.einsum_path(*long_test, optimize=('greedy', 0))
        # 断言计算得到的路径与预期路径相等
        self.assert_path_equal(path, ['einsum_path', (0, 1, 2, 3)])

        # 使用 'optimal' 优化方法计算路径和路径字符串
        path, path_str = np.einsum_path(*long_test, optimize=('optimal', 0))
        # 断言计算得到的路径与预期路径相等
        self.assert_path_equal(path, ['einsum_path', (0, 1, 2, 3)])

    # 测试长而复杂的路径情况
    def test_long_paths(self):
        # 长而复杂的测试 1
        long_test1 = self.build_operands('acdf,jbje,gihb,hfac,gfac,gifabc,hfac')
        # 使用 'greedy' 优化方法计算路径和路径字符串
        path, path_str = np.einsum_path(*long_test1, optimize='greedy')
        # 断言计算得到的路径与预期路径相等
        self.assert_path_equal(path, ['einsum_path',
                                      (3, 6), (3, 4), (2, 4), (2, 3), (0, 2), (0, 1)])

        # 使用 'optimal' 优化方法计算路径和路径字符串
        path, path_str = np.einsum_path(*long_test1, optimize='optimal')
        # 断言计算得到的路径与预期路径相等
        self.assert_path_equal(path, ['einsum_path',
                                      (3, 6), (3, 4), (2, 4), (2, 3), (0, 2), (0, 1)])

        # 长而复杂的测试 2
        long_test2 = self.build_operands('chd,bde,agbc,hiad,bdi,cgh,agdb')
        # 使用 'greedy' 优化方法计算路径和路径字符串
        path, path_str = np.einsum_path(*long_test2, optimize='greedy')
        # 断言计算得到的路径与预期路径相等
        self.assert_path_equal(path, ['einsum_path',
                                      (3, 4), (0, 3), (3, 4), (1, 3), (1, 2), (0, 1)])

        # 使用 'optimal' 优化方法计算路径和路径字符串
        path, path_str = np.einsum_path(*long_test2, optimize='optimal')
        # 断言计算得到的路径与预期路径相等
        self.assert_path_equal(path, ['einsum_path',
                                      (0, 5), (1, 4), (3, 4), (1, 3), (1, 2), (0, 1)])
    def test_edge_paths(self):
        # Difficult edge cases

        # Edge test1
        edge_test1 = self.build_operands('eb,cb,fb->cef')
        # 计算最优的求和路径和路径字符串,使用贪婪算法优化
        path, path_str = np.einsum_path(*edge_test1, optimize='greedy')
        # 断言路径是否符合预期
        self.assert_path_equal(path, ['einsum_path', (0, 2), (0, 1)])

        # 使用最优算法优化求和路径
        path, path_str = np.einsum_path(*edge_test1, optimize='optimal')
        # 断言路径是否符合预期
        self.assert_path_equal(path, ['einsum_path', (0, 2), (0, 1)])

        # Edge test2
        edge_test2 = self.build_operands('dd,fb,be,cdb->cef')
        # 计算最优的求和路径和路径字符串,使用贪婪算法优化
        path, path_str = np.einsum_path(*edge_test2, optimize='greedy')
        # 断言路径是否符合预期
        self.assert_path_equal(path, ['einsum_path', (0, 3), (0, 1), (0, 1)])

        # 使用最优算法优化求和路径
        path, path_str = np.einsum_path(*edge_test2, optimize='optimal')
        # 断言路径是否符合预期
        self.assert_path_equal(path, ['einsum_path', (0, 3), (0, 1), (0, 1)])

        # Edge test3
        edge_test3 = self.build_operands('bca,cdb,dbf,afc->')
        # 计算最优的求和路径和路径字符串,使用贪婪算法优化
        path, path_str = np.einsum_path(*edge_test3, optimize='greedy')
        # 断言路径是否符合预期
        self.assert_path_equal(path, ['einsum_path', (1, 2), (0, 2), (0, 1)])

        # 使用最优算法优化求和路径
        path, path_str = np.einsum_path(*edge_test3, optimize='optimal')
        # 断言路径是否符合预期
        self.assert_path_equal(path, ['einsum_path', (1, 2), (0, 2), (0, 1)])

        # Edge test4
        edge_test4 = self.build_operands('dcc,fce,ea,dbf->ab')
        # 计算最优的求和路径和路径字符串,使用贪婪算法优化
        path, path_str = np.einsum_path(*edge_test4, optimize='greedy')
        # 断言路径是否符合预期
        self.assert_path_equal(path, ['einsum_path', (1, 2), (0, 1), (0, 1)])

        # 使用最优算法优化求和路径
        path, path_str = np.einsum_path(*edge_test4, optimize='optimal')
        # 断言路径是否符合预期
        self.assert_path_equal(path, ['einsum_path', (1, 2), (0, 2), (0, 1)])

        # Edge test5
        edge_test4 = self.build_operands('a,ac,ab,ad,cd,bd,bc->',
                                         size_dict={"a": 20, "b": 20, "c": 20, "d": 20})
        # 计算最优的求和路径和路径字符串,使用贪婪算法优化
        path, path_str = np.einsum_path(*edge_test4, optimize='greedy')
        # 断言路径是否符合预期
        self.assert_path_equal(path, ['einsum_path', (0, 1), (0, 1, 2, 3, 4, 5)])

        # 使用最优算法优化求和路径
        path, path_str = np.einsum_path(*edge_test4, optimize='optimal')
        # 断言路径是否符合预期
        self.assert_path_equal(path, ['einsum_path', (0, 1), (0, 1, 2, 3, 4, 5)])

    def test_path_type_input(self):
        # Test explicit path handling
        path_test = self.build_operands('dcc,fce,ea,dbf->ab')

        # 计算不使用优化的求和路径和路径字符串
        path, path_str = np.einsum_path(*path_test, optimize=False)
        # 断言路径是否符合预期
        self.assert_path_equal(path, ['einsum_path', (0, 1, 2, 3)])

        # 使用最优算法优化求和路径
        path, path_str = np.einsum_path(*path_test, optimize=True)
        # 断言路径是否符合预期
        self.assert_path_equal(path, ['einsum_path', (1, 2), (0, 1), (0, 1)])

        # 预期的求和路径
        exp_path = ['einsum_path', (0, 2), (0, 2), (0, 1)]
        # 使用预期的路径进行优化
        path, path_str = np.einsum_path(*path_test, optimize=exp_path)
        # 断言路径是否符合预期
        self.assert_path_equal(path, exp_path)

        # Double check einsum works on the input path
        # 没有优化的求和
        noopt = np.einsum(*path_test, optimize=False)
        # 使用预期的路径进行优化求和
        opt = np.einsum(*path_test, optimize=exp_path)
        # 断言两种求和结果几乎相等
        assert_almost_equal(noopt, opt)
    # 定义测试函数,用于测试路径类型输入的内部追踪功能
    def test_path_type_input_internal_trace(self):
        #gh-20962
        # 创建测试用例,构建操作数
        path_test = self.build_operands('cab,cdd->ab')
        # 预期的优化路径
        exp_path = ['einsum_path', (1,), (0, 1)]

        # 调用 np.einsum_path 函数计算路径和路径字符串
        path, path_str = np.einsum_path(*path_test, optimize=exp_path)
        # 使用断言检查计算得到的路径是否与预期路径相等
        self.assert_path_equal(path, exp_path)

        # 双重检查 einsum 是否能够在输入路径上正常工作
        # 无优化的 einsum 计算结果
        noopt = np.einsum(*path_test, optimize=False)
        # 使用预期路径优化的 einsum 计算结果
        opt = np.einsum(*path_test, optimize=exp_path)
        # 使用断言检查两种计算结果是否几乎相等
        assert_almost_equal(noopt, opt)

    # 定义测试函数,用于测试路径类型输入的无效情况
    def test_path_type_input_invalid(self):
        # 构建操作数,创建测试用例
        path_test = self.build_operands('ab,bc,cd,de->ae')
        # 预期的优化路径
        exp_path = ['einsum_path', (2, 3), (0, 1)]
        # 使用断言检查是否抛出 RuntimeError 异常
        assert_raises(RuntimeError, np.einsum, *path_test, optimize=exp_path)
        assert_raises(
            RuntimeError, np.einsum_path, *path_test, optimize=exp_path)

        # 构建操作数,创建测试用例
        path_test = self.build_operands('a,a,a->a')
        # 预期的优化路径
        exp_path = ['einsum_path', (1,), (0, 1)]
        # 使用断言检查是否抛出 RuntimeError 异常
        assert_raises(RuntimeError, np.einsum, *path_test, optimize=exp_path)
        assert_raises(
            RuntimeError, np.einsum_path, *path_test, optimize=exp_path)

    # 定义测试函数,用于测试 np.einsum 中的空格情况
    def test_spaces(self):
        #gh-10794
        # 创建一个包含单个元素的二维数组
        arr = np.array([[1]])
        # 使用 itertools.product 生成所有可能的空格组合,长度为 4
        for sp in itertools.product(['', ' '], repeat=4):
            # 构造 einsum 的表达式字符串,测试不同的空格组合
            # 没有错误应该发生
            np.einsum('{}...a{}->{}...a{}'.format(*sp), arr)
# 定义一个函数用于测试矩阵乘法的重叠情况
def test_overlap():
    # 创建一个3x3的整数数组a,其中元素为0到8的序列
    a = np.arange(9, dtype=int).reshape(3, 3)
    # 创建一个3x3的整数数组b,也是0到8的序列
    b = np.arange(9, dtype=int).reshape(3, 3)
    # 计算矩阵a和b的矩阵乘法结果并赋值给d
    d = np.dot(a, b)
    # 进行一致性检查
    c = np.einsum('ij,jk->ik', a, b)
    assert_equal(c, d)
    # 通过使用已有的操作数之一来重叠输出,用于验证gh-10080问题
    c = np.einsum('ij,jk->ik', a, b, out=b)
    assert_equal(c, d)