闲来没事,分享一个工作上遇到的糗事。
先把问题抛出来,假设现在需要定义一个函数myFunction(f,lst),其中参数f是一个函数,lst是一个列表,要求myFunction的运行结果是参数为的函数f依次接受lst中每一项为参数的运行结果,即myFunction(f,lst) = f(lst[0],lst[1], ... ,lst[n]),假设lst中有n+1个元素。
其实这个问题很容易,只是如果没有接触过这个知识点就很难想到该怎么写。因为lst的长度是未知的,并不能用显式表达出f(lst[0],lst[1], ... ,lst[n])
举个例子,如何编写myFunction函数使得下面的脚本能够正常运行
def f(a,b=1,c=2,d=3):
print("a: {}".format(a))
print("b: {}".format(b))
print("c: {}".format(c))
print("d: {}".format(d))
def myFunction(f,lst):
""" Edit Start """
""" Edit End """
pass
lst1 = [1]
lst2 = [1,3]
lst3 = [1,3,5]
lst4 = [1,3,5,7]
myFunction(f,lst1)
myFunction(f,lst2)
myFunction(f,lst3)
myFunction(f,lst4)
笔者遇到这个问题时先去复习了一下动态参数的函数写法
def f1(a,*args,**kwargs):
print(a)
print(args)
print(kwargs)
f1(1,2,3,4,b=1,c=2)
# 输出
"""
1
(2, 3, 4)
{'b': 1, 'c': 2}
"""
复习完之后发现好像没有什么用处,并不能解决这个问题,然后就写了一个又臭又长的myFunction函数
def f(a,b=1,c=2,d=3):
print("a: {}".format(a))
print("b: {}".format(b))
print("c: {}".format(c))
print("d: {}".format(d))
def myFunction(f,lst):
""" Edit Start """
if len(lst)==1: f(lst[0])
elif len(lst)==2: f(lst[0],lst[1])
elif len(lst)==3: f(lst[0],lst[1],lst[2])
elif len(lst)==4: f(lst[0],lst[1],lst[2],lst[3])
else: assert False
""" Edit End """
pass
lst1 = [1]
lst2 = [1,3]
lst3 = [1,3,5]
lst4 = [1,3,5,7]
myFunction(f,lst1)
myFunction(f,lst2)
myFunction(f,lst3)
myFunction(f,lst4)
写完之后笔者想想不行呀,万一以后f的参数列表超过4个怎么办,要是参数列表有几百上千那不得改得累死,一顿操作后笔者想出了一个绝妙的写法
def f(a,b=1,c=2,d=3):
print("a: {}".format(a))
print("b: {}".format(b))
print("c: {}".format(c))
print("d: {}".format(d))
def myFunction(f,lst):
""" Edit Start """
string = "f("
for i in range(len(lst)):
string += "lst[{}],".format(i)
string += ")"
eval(string)
""" Edit End """
pass
lst1 = [1]
lst2 = [1,3]
lst3 = [1,3,5]
lst4 = [1,3,5,7]
myFunction(f,lst1)
myFunction(f,lst2)
myFunction(f,lst3)
myFunction(f,lst4)
笔者想到可以用eval函数完美解决这个问题,eval函数可以将一段字符串当成代码直接运行,lst有多长不知道怎么办?直接把整个表达式用字符串表达出来就好了呗。
写完这个笔者还专门测试了一下发现这样的写法是不成立的
eval("f(lst[0],lst[1],lst[2],lst3)") # 可行
f(eval("lst[0],lst[1],lst[2],lst[3]")) # 不可行
然后就心满意足的push了代码。
写完之后笔者忽然想起在多进程multiprocess库的使用中似乎有相似的情形,创建进程时只要把函数和参数列表传入Process就可以直接运行函数了
import multiprocessing
def do(n) :
#获取当前线程的名字
name = multiprocessing.current_process().name
print(name,'starting')
print("worker ",n)
if __name__ == '__main__' :
numList = []
for i in range(5):
p = multiprocessing.Process(target=do, args=(i,))
numList.append(p)
p.start()
p.join()
print("Process end.")
诶,那么multiprocess包里究竟是怎么写这个类似myFunction的函数的呢?是不是跟笔者写得一样妙?
出于好奇去查看了multiprocess的源码(python3.6/Lib/multiprocessing/process.py BaseProcess类节选)
class BaseProcess(object):
'''
Process objects represent activity that is run in a separate process
The class is analogous to `threading.Thread`
'''
def _Popen(self):
raise NotImplementedError
def __init__(self, group=None, target=None, name=None, args=(), kwargs={},
*, daemon=None):
assert group is None, 'group argument must be None for now'
count = next(_process_counter)
self._identity = _current_process._identity + (count,)
self._config = _current_process._config.copy()
self._parent_pid = os.getpid()
self._popen = None
self._target = target
self._args = tuple(args)
self._kwargs = dict(kwargs)
self._name = name or type(self).__name__ + '-' + \
':'.join(str(i) for i in self._identity)
if daemon is not None:
self.daemon = daemon
_dangling.add(self)
def run(self):
'''
Method to be run in sub-process; can be overridden in sub-class
'''
if self._target:
self._target(*self._args, **self._kwargs)
看到run函数里的写法笔者猛得一惊,赶紧测试了一下
def f(a,b=1,c=2,d=3):
print("a: {}".format(a))
print("b: {}".format(b))
print("c: {}".format(c))
print("d: {}".format(d))
def myFunction(f,lst):
""" Edit Start """
f(*lst)
""" Edit End """
pass
lst1 = [1]
lst2 = [1,3]
lst3 = [1,3,5]
lst4 = [1,3,5,7]
myFunction(f,lst1)
myFunction(f,lst2)
myFunction(f,lst3)
myFunction(f,lst4)
所以其实直接在lst前加个*号就可以了。
实在是丢人现眼的写法,趁还没有人看到赶紧pull下来[汗]