networkx.utils.decorators.argmap#

class argmap(func, *args, try_finally=False)[source]#

一个装饰器,用于在调用函数之前对参数应用映射

此类提供了一个装饰器,该装饰器在调用函数之前对函数的参数进行映射(转换)。例如,我们在许多函数中有类似的代码来确定参数是要创建的节点数量,还是需要处理的节点列表。装饰器提供了接受任一参数的代码——在实际调用函数之前将指定参数转换为节点列表。

这个装饰器类允许我们处理单个或多个参数。要处理的参数可以通过字符串指定,命名参数,或通过索引指定,指定 args 列表中的项。

Parameters:
funccallable

要应用于参数的函数

*args可迭代对象(int, str 或 tuple)

参数列表,指定为字符串(名称)、整数(数值索引)或元组,元组可以包含整数、字符串和(递归地)元组。每个参数指示装饰器应映射的参数。元组表示映射函数接受(并返回)多个参数,顺序和嵌套结构与这里指示的相同。

try_finallybool(默认:False)

当为 True 时,在 try-finally 块中包装函数调用,finally 块的代码由 func 创建。这在映射函数构造一个需要后处理(如关闭)的对象(如文件句柄)时使用。

注意:try_finally 装饰器不能用于装饰生成器函数。

See also

not_implemented_for
open_file
nodes_or_number
py_random_state
networkx.algorithms.community.quality.require_partition

Notes

此类对象是可调用的,旨在定义装饰器时使用。通常,装饰器接受一个函数作为输入并构造一个函数作为输出。具体来说, argmap 对象返回装饰/包装后的输入函数,以便在调用装饰函数之前将指定参数映射(转换)为新值。

概述一下,argmap 对象返回一个新函数,该函数具有原始函数的所有双下划线值(如 __doc____name__ 等)。这个装饰函数的代码是基于原始函数的签名构建的。它首先将输入参数映射到潜在的新值,然后使用这些新值调用装饰函数,代替已映射的指定参数。然后返回原始函数的返回值。这个新函数是用户实际调用的函数。

提供了三个附加特性。
  1. 代码是惰性编译的。也就是说,新函数作为一个对象返回,代码未编译,但包含了所有必要的信息,以便在第一次调用时编译。这节省了导入时的时间,但增加了第一次调用函数的时间。后续调用则与正常速度相同。

  2. 如果关键字参数 “try_finally” 为 True,则在每个映射参数之后跟随一个 try 块,在包装调用的另一侧跟随一个 finally 块关闭该映射。我们期望 func 返回一个 2-元组:映射值和一个在 finally 子句中调用的函数。这个特性是为了使 open_file 装饰器能够向装饰函数提供文件句柄并在函数调用后关闭文件句柄而包含的。它甚至跟踪是否需要关闭文件句柄,基于它是否必须打开文件或输入已经打开。因此,装饰函数不需要包含任何打开或关闭文件的代码。

  3. 应用的映射可以处理多个参数。例如,你可以使用映射交换两个参数,或将它们转换为它们的和与差。这被包含在内,以允许 quality.py 模块中的装饰器检查输入 partition 是否是输入图 G 的节点的有效分区。在这个示例中,映射有输入 (G, partition) 。在检查有效分区后,映射要么引发异常,要么保持输入不变。因此,许多进行此检查的函数可以使用装饰器,而不是将检查代码复制到每个函数中。更复杂的嵌套参数结构在下面描述。

其余的注释描述了该类的代码结构和方法,以帮助理解如何使用它。

实例化一个 argmap 对象只是存储映射函数和要映射的参数的输入标识符。结果装饰器准备使用此映射来装饰任何函数。调用该对象( argmap.__call__ ,但通常通过 @my_decorator 完成)构造一个惰性编译的装饰函数的薄包装,并使用必要的函数双下划线属性(如 __doc____name__ )进行包装。该薄包装函数作为装饰函数返回。当调用该装饰函数时,薄包装代码调用 argmap._lazy_compile ,该函数编译装饰函数(使用 argmap.compile )并用新编译的代码替换薄包装的代码。这节省了每次导入 networkx 时的编译步骤,代价是在第一次调用装饰函数时进行编译。

当装饰函数被编译时,代码使用 argmap.assemble 方法递归地组装。递归性质是必要的,以防有嵌套装饰器。组装的结果是一些有用的对象。

sig : 原始装饰函数的函数签名,由 argmap.signature() 构造。这是使用 inspect.signature 构造的,但增强了属性字符串 sig_defsig_call ,以及其他特定于此函数参数映射的信息。此信息用于构造定义新装饰函数的代码字符串。

wrapped_name : argmap 构造的用于装饰函数的唯一内部使用的名称。

functions : 在此装饰函数代码中使用的函数的字典,用作 exec 中的 globals 。该字典递归更新以允许嵌套装饰。

mapblock : 将传入参数值映射到其映射值的代码(作为字符串列表)。

finallys : 如果需要,提供可能嵌套的 finally 子句集的代码(作为字符串列表)。

mutable_args : 一个布尔值,指示是否应将 sig.args 元组转换为列表以便可以发生突变。

在这个递归组装过程之后, argmap.compile 方法构造代码(作为字符串)以在需要时将元组 sig.args 转换为列表。它将定义代码与适当的缩进连接起来并编译结果。最后,评估此代码,并用编译版本替换原始包装的实现(有关更多详细信息,请参见 argmap._lazy_compile )。

argmap 的其他方法包括 _name_count ,它们允许内部生成的名称在 Python 会话中保持唯一。方法 _flatten_indent 将嵌套的字符串列表处理为准备编译的适当缩进的 Python 代码。

也允许更复杂的嵌套参数元组,尽管通常不使用。对于简单的 2 参数情况,argmap 输入 (“a”, “b”) 意味着映射函数将接受 2 个参数并返回一个 2-元组的映射值。一个更复杂的示例,argmap 输入 ("a", ("b", "c")) 要求映射函数接受 2 个输入,第二个输入为 2-元组。然后它必须以相同的嵌套结构 (newa, (newb, newc)) 输出 3 个映射值。这种一般性水平不常需要,但在处理多个参数时很方便实现。

Examples

大多数这些示例使用 @argmap(...) 将装饰器应用于下一行定义的函数。然而,在 NetworkX 代码库中, argmap 在函数内部用于构造装饰器。也就是说,装饰器定义了一个映射函数,然后使用 argmap 构建并返回一个装饰过的函数。一个简单的例子是一个指定报告货币的装饰器。装饰器(名为 convert_to )的使用方式如下:

@convert_to(“US_Dollars”, “income”) def show_me_the_money(name, income):

print(f”{name} : {income}”)

创建装饰器的代码可能是:

def convert_to(currency, which_arg):
def _convert(amount):
if amount.currency != currency:

amount = amount.to_currency(currency)

return amount

return argmap(_convert, which_arg)

尽管这种 argmap 的常见用法,但以下大多数示例使用 @argmap(...) 惯用法以节省空间。

这是一个使用 argmap 对函数两个参数的元素求和的示例。装饰过的函数:

@argmap(sum, “xlist”, “zlist”) def foo(xlist, y, zlist):

return xlist - y + zlist

是以下代码的语法糖:

def foo(xlist, y, zlist):

x = sum(xlist) z = sum(zlist) return x - y + z

并且等价于(使用参数索引):

@argmap(sum, “xlist”, 2) def foo(xlist, y, zlist):

return xlist - y + zlist

或:

@argmap(sum, “zlist”, 0) def foo(xlist, y, zlist):

return xlist - y + zlist

可以对多个参数应用转换函数,例如:

def swap(x, y):

return y, x

# 2-元组告诉 argmap 映射 swap 有 2 个输入/输出。 @argmap(swap, (“a”, “b”)): def foo(a, b, c):

return a / b * c

等价于:

def foo(a, b, c):

a, b = swap(a, b) return a / b * c

更一般地,应用的参数可以是字符串或整数的嵌套元组。语法 @argmap(some_func, ("a", ("b", "c"))) 期望 some_func 接受 2 个输入,第二个输入应为 2-元组。然后应返回 2 个输出,第二个为 2-元组。返回值将分别替换输入 “a”、”b” 和 “c”。类似地,对于 @argmap(some_func, (0, ("b", 2)))

此外,注意对于可变参数函数,索引大于命名参数数量的索引是允许的。例如:

def double(a):

return 2 * a

@argmap(double, 3) def overflow(a, *args):

return a, args

print(overflow(1, 2, 3, 4, 5, 6)) # 输出是 1, (2, 3, 8, 5, 6)

Try Finally

此外,这个 argmap 类可以用来创建一个启动 try…finally 块的装饰器。装饰器必须编写为返回转换后的参数和关闭函数。这个特性是为了启用 open_file 装饰器而包含的,该装饰器可能需要根据是否必须打开该文件来决定是否关闭文件。这个特性使用 @argmap 的关键字参数 try_finally

例如,这个映射打开一个文件,然后确保它被关闭:

def open_file(fn):

f = open(fn) return f, lambda: f.close()

装饰器将该映射应用于函数 foo

@argmap(open_file, “file”, try_finally=True) def foo(file):

print(file.read())

是以下代码的语法糖:

def foo(file):

file, close_file = open_file(file) try:

print(file.read())

finally:

close_file()

并且等价于(使用索引):

@argmap(open_file, 0, try_finally=True) def foo(file):

print(file.read())

这是一个使用 try_finally 特性创建装饰器的示例:

def my_closing_decorator(which_arg):
def _opener(path):
if path is None:

path = open(path) fclose = path.close

else:

# 假设 path 处理关闭 fclose = lambda: None

return path, fclose

return argmap(_opener, which_arg, try_finally=True)

然后可以这样使用:

@my_closing_decorator(“file”) def fancy_reader(file=None):

# 这段代码不需要担心关闭文件 print(file.read())

try_finally = True 的装饰器不能用于生成器函数,因为 finally 块在生成器耗尽之前执行:

@argmap(open_file, “file”, try_finally=True) def file_to_lines(file):

for line in file.readlines():

yield line

等价于:

def file_to_lines_wrapped(file):
for line in file.readlines():

yield line

def file_to_lines_wrapper(file):
try:

file = open_file(file) return file_to_lines_wrapped(file)

finally:

file.close()

其行为类似于:

def file_to_lines_whoops(file):

file = open_file(file) file.close() for line in file.readlines():

yield line

因为 file_to_lines_wrapperfinally 块在调用者有机会耗尽迭代器之前执行。

__init__(func, *args, try_finally=False)[source]#

Methods

assemble(f)

收集装饰函数 f 的源代码组件。

compile(f)

编译装饰的函数。

signature(f)

构建一个描述 f 的 Signature 对象