支持的 Python 功能
除了下面适用于 对象模式 和 nopython 模式 的 语言 部分之外,本页只列出了 nopython 模式 支持的功能。
警告
在某些情况下,Numba 的行为与 Python 语义不同。我们强烈建议查阅 与 Python 语义的偏差,以便熟悉这些差异。
语言
构造
Numba 致力于尽可能多地支持 Python 语言,但某些语言功能在 Numba 编译的函数内部不可用。下面是 Python 构造支持级别的快速参考。
支持的 构造
条件分支:
if .. elif .. else
循环:
while
,for .. in
,break
,continue
基本生成器:
yield
断言:
assert
部分支持的 构造
异常:
try .. except
,raise
,else
和finally
(详见本节)上下文管理器:
with
(仅支持 numba.objmode())列表推导式 (详见本节)
不支持的 构造
异步功能:
async with
,async for
和async 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 目前屏蔽了 KeyboardInterrupt
和 SystemExit
等信号。这些信号异常在 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
构造也受支持。
内置类型
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 最成功地应用于涉及字符串的更大算法,其中基本字符串操作不是瓶颈。
同构元组
同构元组示例
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,)]
这些列表的主要用途是作为配置对象。列表显示为继承自 Literal
的 LiteralList
类型,因此列表项的字面量值在编译时可用。例如
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
关于这类列表需要注意的重要事项
它们是不可变的,使用修改方法(例如
.pop()
)将导致编译失败。支持只读静态访问和只读方法(例如len()
)。不可能动态访问项,例如
some_list[x]
,其中x
不是编译时常量。这是因为无法静态确定正在访问的项的类型。在编译器内部,这些列表实际上只是添加了一些额外内容以使其看起来像列表的元组。
它们不能从编译函数返回到解释器。
列表推导式
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 Set
和 List
类型目前不受支持。可接受的键/值类型包括但不限于: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}
这些字典的主要用途是协调高级编译分派或作为配置对象使用。字典显示为继承自 Literal
的 LiteralStrKeyDict
类型,因此键的字面量值和项的类型在编译时可用。例如
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')
关于这类字典需要注意的重要事项
它们是不可变的,使用修改方法(例如
.pop()
)将导致编译失败。支持只读静态访问和只读方法(例如len()
)。不可能动态访问项,例如
some_dictionary[x]
,其中x
不是编译时常量。这是因为无法静态确定正在访问的项的类型。在编译器内部,这些字典实际上只是添加了一些额外内容以使其看起来像字典的命名元组。
它们不能从编译函数返回到解释器。
.keys()
、.values()
和.items()
方法均可正常操作,但返回的是元组而不是可迭代对象。
bytes, bytearray, memoryview
bytearray
类型以及在 Python 3 中,bytes
类型支持索引、迭代和获取 len()。
memoryview
类型支持索引、切片、迭代、获取 len(),以及以下属性:
内置函数
支持以下内置函数
getattr()
:属性必须是字符串字面量,并且返回类型不能是函数类型(例如,不支持getattr(numpy, 'cos')
,因为它返回函数类型)。int
:仅支持单参数形式iter()
:仅支持单参数形式next()
:仅支持单参数形式print()
:仅支持数字和字符串;不支持file
或sep
参数range
:range 唯一允许的用法是作为可调用函数(不能将 range 作为参数传递给 jit 函数,也不能从 jit 函数返回 range)。sorted()
:不支持key
参数str()
type()
:仅支持单参数形式,且仅适用于某些类型(例如数字和命名元组)
哈希
hash()
内置函数受支持,并为所有支持的哈希类型生成哈希值,具体行为取决于 Python 版本:
在 Python 3 下,如果 sys.hash_info.algorithm
是 siphash24
(默认),则 Numba 计算的哈希值将与 CPython 中计算的哈希值完全匹配。
PYTHONHASHSEED
环境变量以 CPython 文档中描述的精确方式影响哈希行为。
注意
从 NumPy 2.2 起,Numba 编译代码中计算的 numpy.timedelta64
和 numpy.datetime64
实例的哈希值与 NumPy 对应的哈希值不匹配。在 NumPy 2.2 之前,numpy.timedelta64
和 numpy.datetime64
实例的 NumPy 哈希值与其整数值表示等效。从 NumPy 2.2 起,它们的哈希值与 datetime
模块中等效类型的哈希值相同,Numba 不会复制此行为。
标准库模块
array
array.array
类型通过缓冲区协议提供有限支持。支持索引、迭代和获取 len()。除了 "u"
之外,所有类型代码都受支持。
collections
由 collections.namedtuple()
返回的命名元组类以与常规元组相同的方式受支持。属性访问和构造函数中的命名参数也受支持。
在 Numba 代码内部创建命名元组类不受支持;该类必须在全局级别创建。
ctypes
Numba 能够使用以下参数和返回类型调用 ctypes 声明的函数
enum
enum.Enum
和 enum.IntEnum
子类都受支持。
operator
operator
模块中的以下函数受支持
operator.imatmul()
(Python 3.5 及以上)operator.matmul()
(Python 3.5 及以上)
functools
functools.reduce()
函数受支持,但需要 initializer 参数。
random
Numba 支持 random
模块中的顶级函数,但不允许您创建单独的 Random 实例。使用 Mersenne-Twister 生成器,具有专用的内部状态。它在启动时使用从操作系统获取的熵进行初始化。
random.getrandbits()
:位数不得大于 64random.seed()
:仅接受整数参数random.shuffle()
:序列参数必须是 NumPy 一维数组或提供缓冲区的对象(如bytearray
或array.array
);不支持第二个(可选)参数
警告
从非 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
模块中的以下函数受支持
heapq.nlargest()
:仅限前两个参数heapq.nsmallest()
:仅限前两个参数
注意:堆必须至少用一个值进行播种,以便推断其类型;堆项的类型假定是同构的。
第三方模块
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.FFI
和 CompiledFFI
对象的 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 模块无需注册。