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

Using a variable function with *-operator

我正在研究 * 和 **,并弄清楚这些运算符的用例是什么。为此"study",我写了一个函数scandir_and_execute遍历一个目录(默认递归),遇到每个文件就执行一个函数exec_func。该函数是可变的,这意味着当调用 scandir_and_execute 时,程序员可以在每个文件上指示 运行 哪个函数。此外,为了计算 *,我添加了一个 func_args 变量(默认为空列表),它可以包含任意数量的参数。

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

注意:运行此功能至少需要Python3.5。

import os

def scandir_and_execute(root, exec_func, func_args=[], 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:
                scan_and_execute(entry.path, exec_func, func_args, True, verbose)
        elif entry.is_file():
            if verbose:
                print(f"\tProcessing {entry.name}")

            # Unpack (splat) argument list, i.e. turn func_args into separate arguments and run exec_func
            exec_func(entry.path, *func_args)

这是 * 的正确使用方法吗?还是我误解了文档和运算符的概念?据我测试,该功能有效,但也许我做了一些警告或非 pythonic 事情?例如,将未命名的 "superfluous" 参数组合在一起(或以其他方式)组合在一起的函数是否更好?

def scandir_and_execute(root, exec_func, recursive=True, verbose=False, *func_args):

你如何使用 splat 运算符,但考虑它是否需要由你的函数负责传递参数。假设您现在这样使用它:

scandir_and_execute(root, foo, (foo_arg1, foo_arg2), recursive=True)

您可以重写 scandir_and_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
for path in scandir(root, recursive=True):
    foo(path, foo_arg1, foo_arg2)

(接近 walk,但不完全是!)现在非递归版本就是这个生成器:

(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)
import <strong><a href="https://docs.python.org/3/library/logging.html" rel="nofollow noreferrer" title="16.6. logging — Logging facility for Python — Python 3.6.4 documentation">logging</a></strong>

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)