支持的 Python 特性

除了下面的 语言 部分(适用于 对象模式nopython 模式),本页仅列出 nopython 模式 中支持的功能。

警告

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

语言

构造

Numba 致力于尽可能支持 Python 语言的大部分内容,但在 Numba 编译的函数中,某些语言特性是不可用的。以下是 Python 构造支持级别的快速参考。

支持 的结构:

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

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

  • 基本生成器:yield

  • 断言:assert

部分支持 的结构:

  • 异常处理:try .. exceptraiseelse``finally``(详见此 部分

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

  • 列表推导(详见此 部分

不支持 的结构:

  • 异步功能:async withasync forasync def

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

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

  • 生成器委托: yield from

函数

函数调用

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

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

作为参数的函数

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

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(<参数>)

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

try .. except

try .. except 结构部分支持。以下形式是支持的:

  • 捕获所有异常的 bare 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 .. exceptelse 块和 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")以及不带格式说明的 f-字符串(例如 "ABC_{a+1}"),仅使用字符串和整数变量(具有 str() 重载的类型)在 nopython 模式 中受支持。

Numba 的未来版本将添加对 Python 2 字符串 / Python 3 字节的其他操作和支持。Python 2 Unicode 对象可能永远不会得到支持。

警告

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

元组

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

备注

tuple() 构造函数本身是不受支持的。

同质元组

一个同质元组的示例:

homogeneous_tuple = (1, 2, 3, 4)

以下操作支持在同质元组上进行:

  • 元组构造。

  • 元组解包。

  • 元组之间的比较。

  • 迭代与索引。

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

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

  • 元组上的索引方法。

异构元组

一个异构元组的例子:

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())

列表

警告

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

从即时编译的函数中创建和返回列表是支持的,以及所有方法和操作。列表必须是严格同质的: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 代码中相同的语义。然而,对于大型列表,反射过程可能会很昂贵,并且不支持包含反射数据类型的列表。由于这种限制,用户不能使用列表的列表作为参数。

备注

当将列表传递给即时编译的函数时,对该列表所做的任何修改在函数返回之前对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 问题跟踪器上打开一个新问题。

从版本 0.45.0 开始,一个新的列表数据类型实现可用,即所谓的 类型化列表。这是一个由编译库支持的、类型同质的列表数据类型,是对上述 反射列表 的改进。此外,列表现在可以任意嵌套。由于该实现被认为是实验性的,您需要从 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])

备注

随着类型化列表的稳定,它将完全取代反射列表,构造函数 []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,)]

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

来自 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() 的操作。以下是一个生成二维 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 上结合 parallel 选项时进行并行数组推导。

设置

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

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

备注

当将一个集合传递给即时编译的函数时,对该集合所做的任何修改在函数返回之前对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() 构造方法显式声明键值类型。这个类型化字典的 API 与 Python 的 dict 相同,它实现了 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}

这些字典的主要用途是协调高级编译调度或作为配置对象的容器。这些字典表现为 LiteralStrKeyDict 类型,该类型继承自 Literal,因此键的字面值和项的类型在编译时是可用的。例如:

来自 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 值支持用于身份测试(当使用 optional 类型时)。

字节, 字节数组, 内存视图

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

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

内置函数

支持以下内置函数:

哈希

支持 hash() 内置函数,并为所有支持的可哈希类型生成哈希值,具有以下特定于Python版本的行为:

在 Python 3 下,Numba 计算的哈希值将与 CPython 在 sys.hash_info.algorithm``siphash24``(默认)时计算的哈希值完全匹配。

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

标准库模块

array

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

cmath

以下是 cmath 模块中支持的函数:

collections

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

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

ctypes

Numba 能够调用 ctypes 声明的函数,支持以下参数和返回类型:

enum

支持 enum.Enumenum.IntEnum 子类。

math

以下是 math 模块中支持的函数:

operator

以下 operator 模块中的函数是被支持的:

functools

支持 functools.reduce() 函数,但 initializer 参数是必需的。

random

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

警告

从非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起,生成器是线程安全和分叉安全的。每个线程和每个进程将生成独立的随机数流。

参见

Numba 还支持 Numpy 随机模块 中的大多数其他分布。

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)

使用 Numba 注册 cffi 的离线模块 mod

内联 cffi 模块无需注册。