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)