Python 使用带*运算符的变量函数

Python 使用带*运算符的变量函数,python,python-3.5,Python,Python 3.5,我在搞*和**,弄清楚这些操作符的用例是什么。在这项“研究”中,我编写了一个函数scandir_和_execute,它遍历一个目录(默认情况下是递归的),并对遇到的每个文件执行一个函数exec_func。该函数是可变的,这意味着当调用scandir\u和_execute时,程序员可以指示在每个文件上运行哪个函数。此外,为了弄清楚*,我添加了一个func_args变量(默认为空列表),该变量可以容纳任意数量的参数 其思想是程序员可以使用他们定义(或内置)的任何exec_func,文件是第一个参数,

我在搞*和**,弄清楚这些操作符的用例是什么。在这项“研究”中,我编写了一个函数
scandir_和_execute
,它遍历一个目录(默认情况下是递归的),并对遇到的每个文件执行一个函数
exec_func
。该函数是可变的,这意味着当调用
scandir\u和_execute
时,程序员可以指示在每个文件上运行哪个函数。此外,为了弄清楚
*
,我添加了一个
func_args
变量(默认为空列表),该变量可以容纳任意数量的参数

其思想是程序员可以使用他们定义(或内置)的任何
exec_func
,文件是第一个参数,并且他们自己在列表中提供所需的参数,然后在
exec_func
调用中展开

注意:运行此函数至少需要Python3.5

这是使用
*
的正确方法,还是我误解了文档和操作员的概念?就我所测试的而言,这个函数是可以工作的,但也许我做了一些警告或非pythonic的事情?例如,如果将未命名的“多余”参数组合在一起(或以其他方式),那么编写这样的函数会更好吗

这是如何使用SPLAT运算符,但考虑它是否需要是您的函数对PAS参数的责任。假设您现在这样使用它:

scandir_and_execute(root, foo, (foo_arg1, foo_arg2), recursive=True)
您可以重写
scandir\u和_execute
以接受一个可调用参数:

def scandir_and_execute(root, exec_func, recursive=True, verbose=False):
    if verbose:
        print(f"TRAVERSING {root}")

    # Use scandir to return iterator rather than list
    for entry in os.scandir(root):
        if entry.is_dir() and not entry.name.startswith('.'):
            if recursive:
                scandir_and_execute(entry.path, exec_func, True, verbose)
        elif entry.is_file():
            if verbose:
                print(f"\tProcessing {entry.name}")

            exec_func(entry.path)
并让来电者处理其业务:

scandir_and_execute(root, lambda path: foo(path, foo_arg1, foo_arg2))
然后完全删除回调并生成一个生成器:

def scandir(root, recursive=True, verbose=False):
    if verbose:
        print(f"TRAVERSING {root}")

    # Use scandir to return iterator rather than list
    for entry in os.scandir(root):
        if entry.is_dir() and not entry.name.startswith('.'):
            if recursive:
                yield from scandir(entry.path, True, verbose)
        elif entry.is_file():
            if verbose:
                print(f"\tProcessing {entry.name}")

            yield entry.path
(entry.path for entry in os.scandir(root) if entry.is_file())
(接近,但不完全!)现在非递归版本就是这个生成器:

def scandir(root, recursive=True, verbose=False):
    if verbose:
        print(f"TRAVERSING {root}")

    # Use scandir to return iterator rather than list
    for entry in os.scandir(root):
        if entry.is_dir() and not entry.name.startswith('.'):
            if recursive:
                yield from scandir(entry.path, True, verbose)
        elif entry.is_file():
            if verbose:
                print(f"\tProcessing {entry.name}")

            yield entry.path
(entry.path for entry in os.scandir(root) if entry.is_file())
因此,您最好只提供递归版本:

import os


def is_hidden(dir_entry):
    return dir_entry.name.startswith('.')


def scandir_recursive(root, *, exclude_dir=is_hidden):
    for entry in os.scandir(root):
        yield entry

        if entry.is_dir() and not exclude_dir(entry):
            yield from scandir_recursive(entry.path, exclude_dir=exclude_dir)
导入
info(f'TRAVERSING{root}')
对于scandir_递归(根)中的条目:
如果条目.is_dir():
logging.info(f'TRAVERSING{entry.path}')
elif entry.is_file():
logging.info(f'\t处理{entry.name}')

foo(entry.path,foo_arg1,foo_arg2)
注意
func(args=[])
func(*args)
不同。第一个调用为
func([a,b,c])
,后一个调用为
func(a,b,c)
。另外,请注意,使用
[]
作为默认值不是一种好的做法(但在本例中可能不是问题),因为每次调用该函数时都是同一个列表实例。@tobias_k我知道第一条注释,而不是第二条注释!你能给我一些建议,告诉我要更改什么,以及在哪里可以找到更多信息吗?@BramVanroy使用
*
与在传递参数时解包iterable对象是一样的。例如,
a=[1,2,3]
*a=>1,2,3
。我认为这是一个C-ism?如果人们需要,你应该放下
func_args
,让他们通过
exec_func=lambda x:foo(x,…)
。我明白你的意思,谢谢。对于我来说,我仍然在关注生成器和
yield
,我更喜欢第一个示例的可读性(使用lambda表达式)。与我的初始函数相比,使用生成器(您的示例)会有性能提升吗?@BramVanroy:没有性能差异;分离/生成器版本只是为了提高可读性和灵活性。
import os


def is_hidden(dir_entry):
    return dir_entry.name.startswith('.')


def scandir_recursive(root, *, exclude_dir=is_hidden):
    for entry in os.scandir(root):
        yield entry

        if entry.is_dir() and not exclude_dir(entry):
            yield from scandir_recursive(entry.path, exclude_dir=exclude_dir)
import logging

logging.info(f'TRAVERSING {root}')

for entry in scandir_recursive(root):
    if entry.is_dir():
        logging.info(f'TRAVERSING {entry.path}')
    elif entry.is_file():
        logging.info(f'\tProcessing {entry.name}')
        foo(entry.path, foo_arg1, foo_arg2)