使用* -operator

时间:2018-01-12 09:15:35

标签: python python-3.5

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

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

注意:运行此功能至少需要Python 3.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的事情?例如,编写这样的函数会更好吗?未命名的"多余的"参数是一起组合的(或另一种方式)?

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

1 个答案:

答案 0 :(得分:1)

你如何使用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 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)