支持的 Python 功能

除了下面适用于 对象模式nopython 模式语言 部分之外,本页只列出了 nopython 模式 支持的功能。

警告

在某些情况下,Numba 的行为与 Python 语义不同。我们强烈建议查阅 与 Python 语义的偏差,以便熟悉这些差异。

语言

构造

Numba 致力于尽可能多地支持 Python 语言,但某些语言功能在 Numba 编译的函数内部不可用。下面是 Python 构造支持级别的快速参考。

支持的 构造

  • 条件分支: if .. elif .. else

  • 循环: while, for .. in, break, continue

  • 基本生成器: yield

  • 断言: assert

部分支持的 构造

  • 异常: try .. except, raise, elsefinally (详见本)

  • 上下文管理器: with (仅支持 numba.objmode())

  • 列表推导式 (详见本)

不支持的 构造

  • 异步功能: async with, async forasync def

  • 类定义: class (除了 @jitclass)

  • 集合、字典和生成器推导式

  • 生成器委托: yield from

  • 使用 del 语句进行删除

函数

函数调用

Numba 支持使用位置参数、命名参数、带默认值的参数以及 *args 进行函数调用(注意 *args 的参数只能是元组,不能是列表)。不支持显式的 **kwargs

只要本地定义的内部函数可以完全内联,Numba 就支持对其进行函数调用。

函数作为参数

函数可以作为参数传递给另一个函数。但是,它们不能作为返回值。例如

from numba import jit

@jit
def add1(x):
    return x + 1

@jit
def bar(fn, x):
    return fn(x)

@jit
def foo(x):
    return bar(add1, x)

# Passing add1 within numba compiled code.
print(foo(1))
# Passing add1 into bar from interpreted code
print(bar(add1, 1))

注意

Numba 不将函数对象作为真正的对象处理。一旦函数被赋值给一个变量,该变量就不能再被重新赋值给不同的函数。

内部函数和闭包

Numba 现在支持内部函数,只要它们是非递归的且仅在本地调用,而不作为参数传递或作为结果返回。在内部函数中使用闭包变量(在外部作用域中定义的变量)也受支持。

递归调用

大多数递归调用模式都受支持。唯一的限制是递归被调用者必须有一个不递归返回的控制流路径。Numba 能够对递归函数进行类型推断,而无需指定函数类型签名(这在 numba 0.28 及更早版本中是必需的)。递归调用甚至可以调用函数的不同重载。

生成器

Numba 支持生成器函数,并能够在 对象模式nopython 模式 下编译它们。返回的生成器既可以从 Numba 编译的代码中使用,也可以从常规 Python 代码中使用。

不支持生成器的协程功能(即 generator.send()generator.throw()generator.close() 方法)。

异常处理

raise 语句

raise 语句仅支持以下形式:

  • raise SomeException

  • raise SomeException(<arguments>)

目前不支持重新引发在编译代码中创建的异常。

try .. except

try .. except 构造部分受支持。支持以下形式:

  • 捕获所有异常的 except

    try:
        ...
    except:
        ...
    
  • except 子句中精确使用 Exception

    try:
      ...
    except Exception:
      ...
    

    这将按预期匹配任何 Exception 的子类异常。目前,Exception 及其子类的实例是编译代码中唯一可以引发的异常类型。

警告

Numba 目前屏蔽了 KeyboardInterruptSystemExit 等信号。这些信号异常在 Numba 编译代码执行期间被忽略。Python 解释器将在控制权返回给它时立即处理它们。

目前,异常对象不会在编译函数内部具体化。因此,无法将异常对象存储到用户变量中,也无法重新引发异常。有了这个限制,唯一现实的用例将类似于

try:
   do_work()
except Exception:
   handle_error_case()
   return error_code

try .. except .. else .. finally

try .. except 中的 else 块和 finally 块都受支持。

>>> @jit(nopython=True)
... def foo():
...     try:
...         print('main block')
...     except Exception:
...         print('handler block')
...     else:
...         print('else block')
...     finally:
...         print('final block')
...
>>> foo()
main block
else block
final block

不带 except 子句的 try .. finally 构造也受支持。

内置类型

int, bool

支持算术运算以及真值。

支持以下属性和方法

  • .conjugate()

  • .real

  • .imag

float, complex

支持算术运算以及真值。

支持以下属性和方法

  • .conjugate()

  • .real

  • .imag

str

Numba 支持 Python 3 中的(Unicode)字符串。字符串可以作为参数传递到 nopython 模式,也可以从 nopython 模式 中构造并返回。与 Python 中一样,切片(即使长度为 1)也会返回一个新的、引用计数的字符串。未来可能会引入优化后的代码路径以高效访问单个字符。

内存中的表示与 Python 3.4 中引入的表示相同,每个字符串都有一个标签,指示字符串在内存中是使用 1、2 还是 4 字节字符宽度。当不同编码的字符串组合时(例如拼接),结果字符串会自动使用两个输入字符串中较大的字符宽度。字符串切片也使用与原始字符串相同的字符宽度,即使切片可以用较窄的字符宽度表示。(当然,这些细节对用户是不可见的。)

目前支持以下构造函数、函数、属性和方法

  • str(int)

  • len()

  • + (字符串拼接)

  • * (字符串重复)

  • in, .contains()

  • ==, <, <=, >, >= (比较)

  • .capitalize()

  • .casefold()

  • .center()

  • .count()

  • .endswith()

  • .endswith()

  • .expandtabs()

  • .find()

  • .index()

  • .isalnum()

  • .isalpha()

  • .isdecimal()

  • .isdigit()

  • .isidentifier()

  • .islower()

  • .isnumeric()

  • .isprintable()

  • .isspace()

  • .istitle()

  • .isupper()

  • .join()

  • .ljust()

  • .lower()

  • .lstrip()

  • .partition()

  • .replace()

  • .rfind()

  • .rindex()

  • .rjust()

  • .rpartition()

  • .rsplit()

  • .rstrip()

  • .split()

  • .splitlines()

  • .startswith()

  • .strip()

  • .swapcase()

  • .title()

  • .upper()

  • .zfill()

普通字符串字面量(例如 "ABC")以及不带格式说明符(例如 "ABC_{a+1}")且只使用字符串和整型变量(具有 str() 重载的类型)的 f-字符串在 nopython 模式 中受支持。

未来的 Numba 版本将添加更多操作以及对 Python 2 字符串/Python 3 字节的支持。Python 2 Unicode 对象可能永远不会被支持。

警告

已知某些操作的性能比 CPython 实现慢。这包括子字符串搜索(in.contains()find())和字符串创建(如 .split())。提高字符串性能是一项正在进行中的任务,但对于单独的基本字符串操作,Numba 不太可能超越 CPython 的速度。Numba 最成功地应用于涉及字符串的更大算法,其中基本字符串操作不是瓶颈。

tuple(元组)

元组支持根据元组内容分为两类。第一类是同构元组,即元组中所有值的类型相同;第二类是异构元组,即元组中值的类型不同。

注意

tuple() 构造函数本身不被支持。

同构元组

同构元组示例

homogeneous_tuple = (1, 2, 3, 4)

同构元组支持以下操作

  • 元组构造。

  • 元组解包。

  • 元组之间的比较。

  • 迭代和索引。

  • 元组之间的加法(拼接)。

  • 使用常量切片对元组进行切片。

  • 元组上的 index 方法。

异构元组

异构元组示例

heterogeneous_tuple = (1, 2j, 3.0, "a")

异构元组支持以下操作

  • 元组之间的比较。

  • 使用编译时常量索引值进行索引,例如 mytuple[7],其中 7 显然是一个常量。

  • 元组迭代(需要实验性的 literal_unroll() 功能,详见下文)。

警告

以下功能(literal_unroll())是实验性的,并于 0.47 版中添加。

为了允许对异构元组进行迭代,必须使用特殊函数 numba.literal_unroll()。此函数除了作为允许使用此功能的令牌外,没有其他作用。使用示例

from numba import njit, literal_unroll

@njit
def foo():
    heterogeneous_tuple = (1, 2j, 3.0, "a")
    for i in literal_unroll(heterogeneous_tuple):
        print(i)

警告

literal_unroll() 的使用有以下限制:

  • literal_unroll() 只能用于元组和编译时常量的常量列表,例如 [1, 2j, 3, "a"],且列表不可变。

  • literal_unroll() 唯一支持的使用模式是循环迭代。

  • 每个循环嵌套只允许一个 literal_unroll() 调用(即禁止嵌套的异构元组迭代循环)。

  • 常见的类型推断/稳定性规则仍然适用。

literal_unroll() 更复杂的用法可能是类型特定的分派,回想一下字符串和整数字面量值被视为它们自己的类型,例如

from numba import njit, types, literal_unroll
from numba.extending import overload

def dt(x):
    # dummy function to overload
    pass

@overload(dt, inline='always')
def ol_dt(li):
    if isinstance(li, types.StringLiteral):
        value = li.literal_value
        if value == "apple":
            def impl(li):
                return 1
        elif value == "orange":
            def impl(li):
                return 2
        elif value == "banana":
            def impl(li):
                return 3
        return impl
    elif isinstance(li, types.IntegerLiteral):
        value = li.literal_value
        if value == 0xca11ab1e:
            def impl(li):
                # capture the dispatcher literal value
                return 0x5ca1ab1e + value
            return impl

@njit
def foo():
    acc = 0
    for t in literal_unroll(('apple', 'orange', 'banana', 3390155550)):
        acc += dt(t)
    return acc

print(foo())

list(列表)

警告

从 0.45.x 版本开始,Numba 中列表数据类型的内部实现正在发生变化。直到最近,列表数据类型只有一个实现,即所谓的 reflected-list(详见下文)。然而,由于其限制,从 0.44.0 版本开始,它已被计划弃用。从 0.45.0 版本开始,一个新的实现,即所谓的 typed-list(详见下文),作为实验性功能可用。更多信息,请参阅:弃用通知

支持从 JIT 编译的函数创建和返回列表,以及所有方法和操作。列表必须严格同构:Numba 会拒绝任何包含不同类型对象的列表,即使这些类型是兼容的(例如,[1, 2.5] 被拒绝,因为它包含一个 int 和一个 float)。

例如,创建数组列表

In [1]: from numba import njit

In [2]: import numpy as np

In [3]: @njit
  ...: def foo(x):
  ...:     lst = []
  ...:     for i in range(x):
  ...:         lst.append(np.arange(i))
  ...:     return lst
  ...:

In [4]: foo(4)
Out[4]: [array([], dtype=int64), array([0]), array([0, 1]), array([0, 1, 2])]

列表反射

在 nopython 模式下,Numba 不操作 Python 对象。list 被编译成内部表示。任何 list 参数在进入 nopython 模式时必须转换为这种表示,并且其包含的元素必须通过一个称为 反射 的过程恢复到原始 Python 对象中。反射是为了保持与常规 Python 代码相同的语义。然而,对于大型列表,反射过程可能开销很大,并且不支持包含反射数据类型的列表。由于此限制,用户不能将列表的列表作为参数使用。

注意

当将列表传递到 JIT 编译的函数中时,对列表进行的任何修改在函数返回之前都对 Python 解释器不可见。(这是反射过程的一个限制。)

警告

列表排序目前使用快速排序算法,其性能特性与 Python 使用的算法不同。

初始值

警告

这是一个实验性功能!

满足以下条件的列表:

  • 使用方括号语法构造

  • 具有字面量类型的值

其初始值将存储在类型的 .initial_value 属性中,以便在编译时检查这些值。如果需要强制基于值的分派,literally 函数将接受这样的列表。

示例

摘自 numba/tests/doc_examples/test_literal_container_usage.py 中的 test_ex_initial_value_list_compile_time_consts
 1from numba import njit, literally
 2from numba.extending import overload
 3
 4# overload this function
 5def specialize(x):
 6    pass
 7
 8@overload(specialize)
 9def ol_specialize(x):
10    iv = x.initial_value
11    if iv is None:
12        return lambda x: literally(x) # Force literal dispatch
13    assert iv == [1, 2, 3] # INITIAL VALUE
14    return lambda x: x
15
16@njit
17def foo():
18    l = [1, 2, 3]
19    l[2] = 20 # no impact on .initial_value
20    l.append(30) # no impact on .initial_value
21    return specialize(l)
22
23result = foo()
24print(result) # [1, 2, 20, 30] # NOT INITIAL VALUE!

类型化列表

注意

numba.typed.List 是一个实验性功能,如果您在使用过程中遇到任何功能缺陷或性能意外下降,请报告此问题,最好是在 Numba 问题跟踪器上开一个 issue。

从 0.45.0 版本开始,列表数据类型有了新的实现,即所谓的 typed-list。这是一种由编译库支持的、类型同构的列表数据类型,是对上述 reflected-list 的改进。此外,列表现在可以任意嵌套。由于该实现被认为是实验性的,您需要从 numba.typed 模块显式导入它

In [1]: from numba.typed import List

In [2]: from numba import njit

In [3]: @njit
...: def foo(l):
...:     l.append(23)
...:     return l
...:

In [4]: mylist = List()

In [5]: mylist.append(1)

In [6]: foo(mylist)
Out[6]: ListType[int64]([1, 23])

注意

随着 typed-list 的稳定,它将完全取代 reflected-list,并且构造函数 []list() 将创建 typed-list 而不是 reflected-list。

这是一个使用 List() 在 jit 编译函数内部创建 numba.typed.List 并让编译器推断项类型的示例

摘自 numba/tests/doc_examples/test_typed_list_usage.py 中的 ex_inferred_list_jit
 1from numba import njit
 2from numba.typed import List
 3
 4@njit
 5def foo():
 6    # Instantiate a typed-list
 7    l = List()
 8    # Append a value to it, this will set the type to int32/int64
 9    # (depending on platform)
10    l.append(42)
11    # The usual list operations, getitem, pop and length are
12    # supported
13    print(l[0])   # 42
14    l[0] = 23
15    print(l[0])   # 23
16    print(len(l)) # 1
17    l.pop()
18    print(len(l)) # 0
19    return l
20
21foo()
22

这是一个使用 List() 在 jit 编译函数外部创建 numba.typed.List,然后将其用作 jit 编译函数参数的示例

摘自 numba/tests/doc_examples/test_typed_list_usage.py 中的 ex_inferred_list
 1from numba import njit
 2from numba.typed import List
 3
 4@njit
 5def foo(mylist):
 6    for i in range(10, 20):
 7        mylist.append(i)
 8    return mylist
 9
10# Instantiate a typed-list, outside of a jit context
11l = List()
12# Append a value to it, this will set the type to int32/int64
13# (depending on platform)
14l.append(42)
15# The usual list operations, getitem, pop and length are supported
16print(l[0])   # 42
17l[0] = 23
18print(l[0])   # 23
19print(len(l)) # 1
20l.pop()
21print(len(l)) # 0
22
23# And you can use the typed-list as an argument for a jit compiled
24# function
25l = foo(l)
26print(len(l)) # 10
27
28# You can also directly construct a typed-list from an existing
29# Python list
30py_list = [2, 3, 5]
31numba_list = List(py_list)
32print(len(numba_list)) # 3
33

最后,这是一个使用嵌套 List() 的示例

摘自 numba/tests/doc_examples/test_typed_list_usage.py 中的 ex_nested_list
 1from numba.typed import List
 2
 3# typed-lists can be nested in typed-lists
 4mylist = List()
 5for i in range(10):
 6    l = List()
 7    for i in range(10):
 8        l.append(i)
 9    mylist.append(l)
10# mylist is now a list of 10 lists, each containing 10 integers
11print(mylist)
12

字面量列表

警告

这是一个实验性功能!

Numba 支持使用包含任何值的字面量列表,例如

l = ['a', 1, 2j, np.zeros(5,)]

这些列表的主要用途是作为配置对象。列表显示为继承自 LiteralLiteralList 类型,因此列表项的字面量值在编译时可用。例如

摘自 numba/tests/doc_examples/test_literal_container_usage.py 中的 test_ex_literal_list
 1from numba import njit
 2from numba.extending import overload
 3
 4# overload this function
 5def specialize(x):
 6    pass
 7
 8@overload(specialize)
 9def ol_specialize(x):
10    l = x.literal_value
11    const_expr = []
12    for v in l:
13        const_expr.append(str(v))
14    const_strings = tuple(const_expr)
15
16    def impl(x):
17        return const_strings
18    return impl
19
20@njit
21def foo():
22    const_list = ['a', 10, 1j, ['another', 'list']]
23    return specialize(const_list)
24
25result = foo()
26print(result) # ('Literal[str](a)', 'Literal[int](10)', 'complex128', 'list(unicode_type)') # noqa E501

关于这类列表需要注意的重要事项

  1. 它们是不可变的,使用修改方法(例如 .pop())将导致编译失败。支持只读静态访问和只读方法(例如 len())。

  2. 不可能动态访问项,例如 some_list[x],其中 x 不是编译时常量。这是因为无法静态确定正在访问的项的类型。

  3. 在编译器内部,这些列表实际上只是添加了一些额外内容以使其看起来像列表的元组。

  4. 它们不能从编译函数返回到解释器。

列表推导式

Numba 支持列表推导式。例如

In [1]: from numba import njit

In [2]: @njit
  ...: def foo(x):
  ...:     return [[i for i in range(n)] for n in range(x)]
  ...:

In [3]: foo(3)
Out[3]: [[], [0], [0, 1]]

注意

在 0.39.0 版本之前,Numba 不支持创建嵌套列表。

Numba 还支持“数组推导式”,即列表推导式后立即调用 numpy.array()。以下是一个生成 2D Numpy 数组的示例

from numba import jit
import numpy as np

@jit(nopython=True)
def f(n):
  return np.array([ [ x * y for x in range(n) ] for y in range(n) ])

在这种情况下,Numba 能够优化程序,直接分配和初始化结果数组,而无需分配中间列表对象。因此,这里的列表推导式嵌套不是问题,因为这里正在创建一个多维数组而不是嵌套列表。

此外,Numba 在 CPU 上结合 并行 选项时支持并行数组推导式。

set(集合)

JIT 编译函数中支持集合的所有方法和操作。

集合必须严格同构:Numba 会拒绝任何包含不同类型对象的集合,即使这些类型是兼容的(例如,{1, 2.5} 被拒绝,因为它包含一个 int 和一个 float)。不支持在集合中使用引用计数类型,例如字符串。

注意

当将集合传递到 JIT 编译的函数中时,对集合进行的任何修改在函数返回之前都对 Python 解释器不可见。

类型化字典

警告

numba.typed.Dict 是一个实验性功能。API 在未来版本中可能会发生变化。

注意

在 0.44 版本之前不支持 dict()。目前,调用 dict() 会转换为调用 numba.typed.Dict()

Numba 支持使用 dict()。这种用法在语义上等同于 {}numba.typed.Dict()。它将创建一个 numba.typed.Dict 实例,其中键值类型将根据后续用法推断。Numba 还明确支持 dict(iterable) 构造函数。

Numba 不完全支持 Python dict,因为它是一个非类型化容器,可以包含任何 Python 类型作为成员。为了生成高效的机器代码,Numba 需要字典的键和值具有预先声明的固定类型。为此,Numba 有一个类型化字典 numba.typed.Dict,其类型推断机制必须能够通过使用来推断键值类型,或者用户必须使用 Dict.empty() 构造方法显式声明键值类型。这个类型化字典与 Python dict 具有相同的 API,它实现了 collections.MutableMapping 接口,并且可以在解释型 Python 代码和 JIT 编译的 Numba 函数中使用。因为类型化字典以 Numba 的原生、非装箱数据布局存储键和值,所以将 Numba 字典传递到 nopython 模式的开销非常低。然而,这意味着从 Python 解释器中使用类型化字典比普通字典慢,因为 Numba 在获取或设置项时必须对键和值对象进行装箱和拆箱。

类型化字典与 Python 的 dict 的一个重要区别是,当存储键或值时会发生隐式类型转换。因此,如果类型转换失败,setitem 操作可能会失败。

应该注意的是,Numba 类型化字典是使用与 CPython 3.7 字典相同的算法实现的。因此,类型化字典是有序的,并且具有与 CPython 实现相同的冲突解决机制。

除了上述类型规范之外,类型化字典中可用作键和/或值的类型也存在限制,最值得注意的是 Numba SetList 类型目前不受支持。可接受的键/值类型包括但不限于:unicode 字符串、数组(仅限值)、标量、元组。预计随着 Numba 的不断改进,这些限制将得到放宽。

这是一个使用 dict(){} 创建 numba.typed.Dict 实例并让编译器推断键值类型的示例

摘自 numba/tests/doc_examples/test_typed_dict_usage.py 中的 test_ex_inferred_dict_njit
 1from numba import njit
 2import numpy as np
 3
 4@njit
 5def foo():
 6    d = dict()
 7    k = {1: np.arange(1), 2: np.arange(2)}
 8    # The following tells the compiler what the key type and the
 9    # value
10    # type are for `d`.
11    d[3] = np.arange(3)
12    d[5] = np.arange(5)
13    return d, k
14
15d, k = foo()
16print(d)    # {3: [0 1 2], 5: [0 1 2 3 4]}
17print(k)    # {1: [0], 2: [0 1]}

这是一个从解释代码创建 numba.typed.Dict 实例并在 jit 代码中使用该字典的示例

摘自 numba/tests/doc_examples/test_typed_dict_usage.py 中的 test_ex_typed_dict_from_cpython
 1import numpy as np
 2from numba import njit
 3from numba.core import types
 4from numba.typed import Dict
 5
 6# The Dict.empty() constructs a typed dictionary.
 7# The key and value typed must be explicitly declared.
 8d = Dict.empty(
 9    key_type=types.unicode_type,
10    value_type=types.float64[:],
11)
12
13# The typed-dict can be used from the interpreter.
14d['posx'] = np.asarray([1, 0.5, 2], dtype='f8')
15d['posy'] = np.asarray([1.5, 3.5, 2], dtype='f8')
16d['velx'] = np.asarray([0.5, 0, 0.7], dtype='f8')
17d['vely'] = np.asarray([0.2, -0.2, 0.1], dtype='f8')
18
19# Here's a function that expects a typed-dict as the argument
20@njit
21def move(d):
22    # inplace operations on the arrays
23    d['posx'] += d['velx']
24    d['posy'] += d['vely']
25
26print('posx: ', d['posx'])  # Out: posx:  [1.  0.5 2. ]
27print('posy: ', d['posy'])  # Out: posy:  [1.5 3.5 2. ]
28
29# Call move(d) to inplace update the arrays in the typed-dict.
30move(d)
31
32print('posx: ', d['posx'])  # Out: posx:  [1.5 0.5 2.7]
33print('posy: ', d['posy'])  # Out: posy:  [1.7 3.3 2.1]

这是一个从 jit 代码创建 numba.typed.Dict 实例并在解释代码中使用该字典的示例

摘自 numba/tests/doc_examples/test_typed_dict_usage.py 中的 test_ex_typed_dict_njit
 1import numpy as np
 2from numba import njit
 3from numba.core import types
 4from numba.typed import Dict
 5
 6# Make array type.  Type-expression is not supported in jit
 7# functions.
 8float_array = types.float64[:]
 9
10@njit
11def foo():
12    # Make dictionary
13    d = Dict.empty(
14        key_type=types.unicode_type,
15        value_type=float_array,
16    )
17    # Fill the dictionary
18    d["posx"] = np.arange(3).astype(np.float64)
19    d["posy"] = np.arange(3, 6).astype(np.float64)
20    return d
21
22d = foo()
23# Print the dictionary
24print(d)  # Out: {posx: [0. 1. 2.], posy: [3. 4. 5.]}

值得注意的是,numba.typed.Dict 不是线程安全的。具体来说,从多个线程修改字典的函数可能会破坏内存,导致一系列可能的故障。但是,只要字典的内容在并行访问期间不发生变化,就可以安全地从多个线程读取字典。

字典推导式

Numba 支持字典推导式,前提是可以从推导式创建 numba.typed.Dict 实例。例如

In [1]: from numba import njit

In [2]: @njit
   ...: def foo(n):
   ...:     return {i: i**2 for i in range(n)}
   ...:

In [3]: foo(3)
Out[3]: DictType[int64,int64]<iv=None>({0: 0, 1: 1, 2: 4})

初始值

警告

这是一个实验性功能!

满足以下条件的类型化字典:

  • 使用大括号语法构造

  • 具有字面量字符串键

  • 具有字面量类型的值

其初始值将存储在类型的 .initial_value 属性中,以便在编译时检查这些值。如果需要强制基于值的分派,literally 函数将接受类型化字典。

示例

摘自 numba/tests/doc_examples/test_literal_container_usage.py 中的 test_ex_initial_value_dict_compile_time_consts
 1from numba import njit, literally
 2from numba.extending import overload
 3
 4# overload this function
 5def specialize(x):
 6    pass
 7
 8@overload(specialize)
 9def ol_specialize(x):
10    iv = x.initial_value
11    if iv is None:
12        return lambda x: literally(x) # Force literal dispatch
13    assert iv == {'a': 1, 'b': 2, 'c': 3} # INITIAL VALUE
14    return lambda x: literally(x)
15
16@njit
17def foo():
18    d = {'a': 1, 'b': 2, 'c': 3}
19    d['c'] = 20 # no impact on .initial_value
20    d['d'] = 30 # no impact on .initial_value
21    return specialize(d)
22
23result = foo()
24print(result) # {a: 1, b: 2, c: 20, d: 30} # NOT INITIAL VALUE!

异构字面量字符串键字典

警告

这是一个实验性功能!

Numba 支持使用静态声明的字符串键到任意值字典,例如

d = {'a': 1, 'b': 'data', 'c': 2j}

这些字典的主要用途是协调高级编译分派或作为配置对象使用。字典显示为继承自 LiteralLiteralStrKeyDict 类型,因此键的字面量值和项的类型在编译时可用。例如

摘自 numba/tests/doc_examples/test_literal_container_usage.py 中的 test_ex_literal_dict_compile_time_consts
 1import numpy as np
 2from numba import njit, types
 3from numba.extending import overload
 4
 5# overload this function
 6def specialize(x):
 7    pass
 8
 9@overload(specialize)
10def ol_specialize(x):
11    ld = x.literal_value
12    const_expr = []
13    for k, v in ld.items():
14        if isinstance(v, types.Literal):
15            lv = v.literal_value
16            if lv == 'cat':
17                const_expr.append("Meow!")
18            elif lv == 'dog':
19                const_expr.append("Woof!")
20            elif isinstance(lv, int):
21                const_expr.append(k.literal_value * lv)
22        else: # it's an array
23            const_expr.append("Array(dim={dim}".format(dim=v.ndim))
24    const_strings = tuple(const_expr)
25
26    def impl(x):
27        return const_strings
28    return impl
29
30@njit
31def foo():
32    pets_ints_and_array = {'a': 1,
33                           'b': 2,
34                           'c': 'cat',
35                           'd': 'dog',
36                           'e': np.ones(5,)}
37    return specialize(pets_ints_and_array)
38
39result = foo()
40print(result) # ('a', 'bb', 'Meow!', 'Woof!', 'Array(dim=1')

关于这类字典需要注意的重要事项

  1. 它们是不可变的,使用修改方法(例如 .pop())将导致编译失败。支持只读静态访问和只读方法(例如 len())。

  2. 不可能动态访问项,例如 some_dictionary[x],其中 x 不是编译时常量。这是因为无法静态确定正在访问的项的类型。

  3. 在编译器内部,这些字典实际上只是添加了一些额外内容以使其看起来像字典的命名元组。

  4. 它们不能从编译函数返回到解释器。

  5. .keys().values().items() 方法均可正常操作,但返回的是元组而不是可迭代对象。

None(无)

None 值支持身份测试(当使用 optional 类型时)。

bytes, bytearray, memoryview

bytearray 类型以及在 Python 3 中,bytes 类型支持索引、迭代和获取 len()。

memoryview 类型支持索引、切片、迭代、获取 len(),以及以下属性:

内置函数

支持以下内置函数

哈希

hash() 内置函数受支持,并为所有支持的哈希类型生成哈希值,具体行为取决于 Python 版本:

在 Python 3 下,如果 sys.hash_info.algorithmsiphash24(默认),则 Numba 计算的哈希值将与 CPython 中计算的哈希值完全匹配。

PYTHONHASHSEED 环境变量以 CPython 文档中描述的精确方式影响哈希行为。

注意

从 NumPy 2.2 起,Numba 编译代码中计算的 numpy.timedelta64numpy.datetime64 实例的哈希值与 NumPy 对应的哈希值不匹配。在 NumPy 2.2 之前,numpy.timedelta64numpy.datetime64 实例的 NumPy 哈希值与其整数值表示等效。从 NumPy 2.2 起,它们的哈希值与 datetime 模块中等效类型的哈希值相同,Numba 不会复制此行为。

标准库模块

array

array.array 类型通过缓冲区协议提供有限支持。支持索引、迭代和获取 len()。除了 "u" 之外,所有类型代码都受支持。

collections

collections.namedtuple() 返回的命名元组类以与常规元组相同的方式受支持。属性访问和构造函数中的命名参数也受支持。

在 Numba 代码内部创建命名元组类受支持;该类必须在全局级别创建。

enum

enum.Enumenum.IntEnum 子类都受支持。

functools

functools.reduce() 函数受支持,但需要 initializer 参数。

random

Numba 支持 random 模块中的顶级函数,但不允许您创建单独的 Random 实例。使用 Mersenne-Twister 生成器,具有专用的内部状态。它在启动时使用从操作系统获取的熵进行初始化。

警告

从非 Numba 代码(或从 对象模式 代码)调用 random.seed() 将为 Python 随机生成器而非 Numba 随机生成器设置种子。要为 Numba 随机生成器设置种子,请参阅下面的示例。

from numba import njit
import random

@njit
def seed(a):
    random.seed(a)

@njit
def rand():
    return random.random()


# Incorrect seeding
random.seed(1234)
print(rand())

random.seed(1234)
print(rand())

# Correct seeding
seed(1234)
print(rand())

seed(1234)
print(rand())

注意

从 0.28.0 版本开始,该生成器是线程安全的,也是 fork 安全的。每个线程和每个进程都将生成独立的随机数流。

另请参阅

Numba 还支持 Numpy random 模块 中的大多数附加分布。

heapq

heapq 模块中的以下函数受支持

注意:堆必须至少用一个值进行播种,以便推断其类型;堆项的类型假定是同构的。

第三方模块

cffi

类似于 ctypes,Numba 能够调用 cffi 声明的外部函数,使用以下 C 类型和任何派生指针类型

  • char

  • short

  • int

  • long

  • long long

  • unsigned char

  • unsigned short

  • unsigned int

  • unsigned long

  • unsigned long long

  • int8_t

  • uint8_t

  • int16_t

  • uint16_t

  • int32_t

  • uint32_t

  • int64_t

  • uint64_t

  • float

  • double

  • ssize_t

  • size_t

  • void

cffi.FFICompiledFFI 对象的 from_buffer() 方法支持传递 Numpy 数组和其他类似缓冲区的对象。只接受连续的参数。from_buffer() 的参数被转换为相应 C 类型的原始指针(例如,float64 数组的 double *)。

缓冲区到相应 C 类型的额外类型映射可以在 Numba 中注册。这可能包括结构体类型,但只允许调用接受结构体指针的函数——不支持按值传递结构体。要注册映射,请使用

numba.core.typing.cffi_utils.register_type(cffi_type, numba_type)

行外 cffi 模块必须在使用其任何函数从 Numba 编译函数内部之前在 Numba 中注册

numba.core.typing.cffi_utils.register_module(mod)

将 cffi 行外模块 mod 注册到 Numba。

内联 cffi 模块无需注册。