当前位置: 首页>>代码示例 >>用法及示例精选 >>正文


Python NetworkX argmap用法及代码示例


本文简要介绍 networkx.utils.decorators.argmap 的用法。

用法:

class argmap(func, *args, try_finally=False)

在调用函数之前将映射应用于参数的装饰器

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

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

参数

func可调用的

应用于参数的函数

*args可迭代的(int、str 或 tuple)

参数列表,指定为字符串(它们的名称)、整数(数字索引)或元组,其中可能包含整数、字符串和(递归)元组。每个都指示装饰器应该映射哪些参数。元组表示 map 函数以相同的顺序和嵌套结构接受(并返回)多个参数,如此处所示。

try_finally布尔(默认值:假)

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

注意

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

作为概述,argmap 对象返回一个新函数,其中包含原始函数的所有 dunder 值(如 __doc__ __name__ 等)。这个修饰函数的代码是基于原始函数的签名构建的。它首先将输入参数映射到潜在的新值。然后它用这些新值调用修饰函数来代替已映射的指定参数。然后返回原始函数的返回值。这个新函数是用户实际调用的函数。

提供了三个附加函数。

1)代码是懒惰编译的。也就是说,新函数在没有编译代码的情况下作为对象返回,但包含所有需要的信息,因此可以在第一次调用时对其进行编译。这样可以节省导入时间,但会在第一次调用函数时增加额外的时间。随后的调用与正常一样快。

2) 如果 “try_finally” 仅关键字参数为 True,则在每个映射的参数之后有一个 try 块,在包装调用的另一侧匹配,由一个 finally 块关闭该映射。我们期望 func 返回一个 2 元组:映射的值和在 finally 子句中调用的函数。包含此函数,因此 open_file 装饰器可以为装饰函数提供文件句柄并在函数调用后关闭文件句柄。它甚至根据是否必须打开文件或输入已经打开来跟踪是否关闭文件句柄。因此,装饰函数不需要包含任何代码来打开或关闭文件。

3) 应用的映射可以处理多个参数。例如,您可以使用映射交换两个参数,或将它们转换为它们的和和它们的差。这是为了允许 quality.py 模块中的装饰器检查输入 partition 是否是输入图 G 的节点的有效分区。在此示例中,Map具有输入 (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 构造的唯一内部使用的名称

用于装饰函数。

职能this 代码中使用的函数的字典

装饰函数,在 exec 中用作 globals 。这个字典被递归更新以允许嵌套装饰。

Map块映射传入参数的代码(作为字符串列表)

值到它们的映射值。

最后提供可能嵌套的代码(作为字符串列表)

如果需要,一组 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 个映射值。这种通用性通常不需要,但在处理多个参数时很方便实现。

例子

这些示例中的大多数都使用 @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

# the 2-tuple tells argmap that the map `swap` has 2 inputs/outputs.
@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))  # output is 1, (2, 3, 8, 5, 6)

最后尝试

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

例如,此Map打开一个文件,然后确保它已关闭:

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:
            # assume `path` handles the closing
            fclose = lambda: None
        return path, fclose
    return argmap(_opener, which_arg, try_finally=True)

然后可以用作:

@my_closing_decorator("file")
def fancy_reader(file=None):
    # this code doesn't need to worry about closing the file
    print(file.read())

相关用法


注:本文由纯净天空筛选整理自networkx.org大神的英文原创作品 networkx.utils.decorators.argmap。非经特殊声明,原始代码版权归原作者所有,本译文未经允许或授权,请勿转载或复制。