使用带 * 运算符的变量函数
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)
我正在研究 * 和 **,并弄清楚这些运算符的用例是什么。为此"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)