编写python程序实现分段计算函数_气象编程 | 一文学会用python进行并行计算

b75bb354e2cc0667bbc90eb27f80af31.png

添加新云天气象小编微信或QQ:130188121,及时获取或发布气象升学、就业、会议、征稿及学术动态信息! 最新热点文章: 气象招聘 | 中南空管局气象中心2020年校园招聘公告 气象招聘 | 瑞士MDPI出版公司中国代表处诚聘学术编辑(含气象类)天气展望 | 南信大军训天气:汗水or雨水?

Python实现多线程/多进程,大家常常会用到标准库中的threadingmultiprocessing模块。

但从Python3.2开始,标准库为我们提供了concurrent.futures模块,它提供了ThreadPoolExecutorProcessPoolExecutor两个类,实现了对threadingmultiprocessing的进一步抽象,使得开发者只需编写少量代码即可让程序实现并行计算。

ThreadPoolExecutor和ProcessPoolExecutor

concurrent.futures模块的基础是Exectuor抽象类(包括mapsubmit , shutdown方法),但是它不能被直接使用。

一般会对它的两个子类ThreadPoolExecutorProcessPoolExecutor进行调用,两者分别被用来创建线程池和进程池。

当项目达到一定的规模,频繁创建/销毁进程或者线程是非常消耗资源的,这个时候我们就要编写自己的线程池/进程池,以空间换时间。

我们可以将相应的tasks直接放入线程池/进程池,不需要维护Queue来操心死锁的问题,线程池/进程池会自动帮我们调度。

1. ThreadPoolExecutor创建线程池

from concurrent.futures import ThreadPoolExecutorimport timet0 = time.time()def return_future_result(message1, message2):    s = 0    for i in range(50000000):        s *= i    return message1, message2pool = ThreadPoolExecutor(max_workers=3)  # 创建一个最大可容纳3个task的线程池future1 = pool.submit(return_future_result, message1="hello",                      message2='111')  # 往线程池里面加入一个taskfuture2 = pool.submit(return_future_result, message2="world",                      message1='222')  # 往线程池里面加入一个taskprint(future1.done())  # 判断task1是否结束print(future2.done())  # 判断task2是否结束print(future1.result())  # 查看task1返回的结果print(future2.result())  # 查看task2返回的结果print(future1.done())  # 判断task1是否结束print(future2.done())  # 判断task2是否结束t1 = time.time()print(f"total time consumed: {(t1-t0):{2}.{5}}")

输出:


FalseFalse('hello', '111')('222', 'world')TrueTruetotal time consumed: 9.325

2. ProcessPoolExecutor创建进程池

from concurrent.futures import ProcessPoolExecutorimport timet0 = time.time()def return_future_result(message1, message2):    s = 0    for i in range(50000000):        s *= i    return message1, message2pool = ProcessPoolExecutor(max_workers=3)  # 创建一个最大可容纳3个task的进程池future1 = pool.submit(return_future_result, message1="hello",                      message2='111')  # 往进程池里面加入一个taskfuture2 = pool.submit(return_future_result, message2="world",                      message1='222')  # 往进程池里面加入一个taskprint(future1.done())  # 判断task1是否结束print(future2.done())  # 判断task2是否结束print(future1.result())  # 查看task1返回的结果print(future2.result())  # 查看task2返回的结果print(future1.done())  # 判断task1是否结束print(future2.done())  # 判断task2是否结束t1 = time.time()print(f"total time consumed: {(t1-t0):{2}.{5}}")

输出:


FalseFalse('hello', '111')('222', 'world')TrueTruetotal time consumed: 3.7551

可以看到用进程池的时间小于线程池。由于GIL(global interpreter lock, 全局解释锁)的存在,使用多线程并不会真正意义上实现并发,使用多进程可以通过子进程的形式同时运行多个解释器,而它们的GIL是独立的,这样就可以是python程序充分利用多核CPU进行并行计算。

3. Future类[1]

一般由Executor.submit()创建,将可调用对象封装为异步执行。future是一种便利的模式用来追踪异步调用的结果。 常用的方法有done()result()exception()add_done_callback()等。

map和submit方法

ThreadPoolExecutorProcessPoolExecutor常用的方法有mapsubmit

1. map

map(func, *iterables, timeout=None, chunksize=1)

map方法类似于python标准库中的map方法[2]。不同的是:

•这里的可迭代对象(iterables)是被立即collect的,而不是惰性的执行返回;•func是异步执行的,可以实现并发。

需要注意的是,当func有多个参数时,如果对多个可迭代对象进行map操作时,最短的可迭代对象耗尽时则整个迭代也会结束,似于python内置的map方法。如下:

from concurrent.futures import ThreadPoolExecutorimport timedef return_future_result(message1, message2):    time.sleep(1)    return message1, message2lst1 = ['ddddd', "cccccc", 'eeeeeeeee']lst2 = ['1111111', '33333', '222222', '555555']# We can use a with statement to ensure threads are cleaned up promptlywith concurrent.futures.ThreadPoolExecutor(max_workers=2) as executor:    a = executor.map(return_future_result, lst1, lst2)print(a)print(list(a))# 输出:.result_iterator at 0x1075f0468>[('ddddd', '1111111'), ('cccccc', '33333'), ('eeeeeeeee', '222222')]  # no '555555'

函数有多参数的时候,我们可以通过functools.partial来“固定”一些参数:

from concurrent.futures import ThreadPoolExecutorimport timeimport functoolsdef return_future_result(message1, message2):    time.sleep(1)    return message1, message2lst1 = ['ddddd', "cccccc", 'eeeeeeeee']return_future_result_new = functools.partial(    return_future_result, message2='fixed_message2')# We can use a with statement to ensure threads are cleaned up promptlywith concurrent.futures.ThreadPoolExecutor(max_workers=2) as executor:    a = executor.map(return_future_result_new, lst1)print(a)print(list(a))# 输出:.result_iterator at 0x108e76f10>[('ddddd', 'fixed_message2'), ('cccccc', 'fixed_message2'), ('eeeeeeeee', 'fixed_message2')]

2. submit

submit(fn, *args, **kwargs),会调用fn(*args **kwargs) 并会返回一个Future对象,来保存程序执行的状态和结果。 在使用submit的过程中需要注意,一些函数内部的错误会被忽略,一些潜在的bug会不容易发现,例如有一些I/O操作出错的话,很容易被我们忽略。比如:

import timeimport pandas as pdfrom concurrent.futures import ProcessPoolExecutordef return_future_result(num):    df = pd.read_csv("no_such_file_%s.csv"%(num))    df.to_csv("no_such_file_%s.csv"%(num),index=None)with ProcessPoolExecutor(max_workers=2) as pool:    future1 = pool.submit(return_future_result, 666)print(future1.done()) # Trueprint(future1.exception()) # File b'no_such_file_666.csv' does not existprint(future1.result()) # 会报错:FileNotFoundError: File b'no_such_file_666.csv' does not exist

其中的错误,我们可以直观的从运行结果中得出。同时,从运行结果可看出,as_completed不是按照URLS列表元素的顺序返回的,会返回先执行完的结果。

我们可以在程序执行完后,用try去catch结果中的错误,使用方法如下:

from concurrent.futures import ThreadPoolExecutor, as_completedimport urllib.request as urURLS = ['https://www.python.org/', 'http://www.taobao.com',        'http://www.baidu.com', 'http://www.cctv.com/', '### I AM A BUG ###']def load_url(url, timeout):    with ur.urlopen(url, timeout=timeout) as conn:        return conn.read()# We can use a with statement to ensure threads are cleaned up promptlywith ThreadPoolExecutor(max_workers=3) as executor:    # Start the load operations and mark each future with its URL    future_to_url = {executor.submit(load_url, url, 60): url for url in URLS}    for future in as_completed(future_to_url):        url = future_to_url[future]        try:            data = future.result()        except Exception as exc:            print('%r generated an exception: %s' % (url, exc))        else:            print('%r page is %d bytes' % (url, len(data)))# 输出:'http://www.baidu.com' page is 153884 bytes'http://www.cctv.com/' page is 298099 bytes'### I AM A BUG ###' generated an exception: unknown url type: '### I AM A BUG ##''http://www.taobao.com' page is 143891 bytes'https://www.python.org/' page is 49114 bytes

如果执行ThreadPoolExecutormap方法,结果是按照URLS列表元素的顺序返回的,而且用map方法写出的代码更加简洁直观。但是一旦程序有异常,会保存在结果的生成器中,会增加debug的困难,所以更推荐上面的方法。

from concurrent.futures import ThreadPoolExecutorimport urllib.request as urURLS = ['https://www.python.org/', 'http://www.taobao.com',        'http://www.baidu.com', 'http://www.cctv.com/']def load_url(url):    with ur.urlopen(url, timeout=60) as conn:        return conn.read()# We can use a with statement to ensure threads are cleaned up promptlywith ThreadPoolExecutor(max_workers=3) as executor:    for url, data in zip(URLS, executor.map(load_url, URLS)):        print('%r page is %d bytes' % (url, len(data))) # 如果URLS 中有 '### I AM A BUG ###' 则会报错# 输出:'https://www.python.org/' page is 49255 bytes'http://www.taobao.com' page is 143891 bytes'http://www.baidu.com' page is 153380 bytes'http://www.cctv.com/' page is 298099 bytes

as_completed和wait方法

concurrent.futures模块中常用的方法有waitas_completed,用于处理Executors返回的Future对象。

1. as_completed

concurrent.futures.as_completed(fs, timeout=None) 将Future对象生成一个迭代器返回,并且先返回先执行完的结果(map会按照我们传入的可迭代对象中的顺序返回)。

submit + as_completed:

from concurrent.futures import ThreadPoolExecutor, as_completedfrom time import sleepfrom random import randintdef return_after_5_secs(num):    sleep_time = randint(0, 5)    sleep(sleep_time)    return "Return of {}, sleep_time: {}".format(num, sleep_time)pool = ThreadPoolExecutor(6)futures = []for x in range(5):    futures.append(pool.submit(return_after_5_secs, x))for x in as_completed(futures):    print(x.result())# 输出:Return of 4, sleep_time: 0Return of 0, sleep_time: 1Return of 3, sleep_time: 1Return of 2, sleep_time: 2Return of 1, sleep_time: 3

map:

from concurrent.futures import ThreadPoolExecutorfrom time import sleepfrom random import randintdef return_after_5_secs(num):    sleep_time = randint(0, 5)    sleep(sleep_time)    return "Return of {}, sleep_time: {}".format(num, sleep_time)pool = ThreadPoolExecutor(6)re_ge = pool.map(return_after_5_secs, [x for x in range(5)])for i in re_ge:    print(i)# 输出:Return of 0, sleep_time: 3Return of 1, sleep_time: 4Return of 2, sleep_time: 3Return of 3, sleep_time: 4Return of 4, sleep_time: 4

2. wait

concurrent.futures.wait(fs, timeout=None, return_when=ALL_COMPLETED)有更大的自由度,它将一个Future列表作为参数,并阻塞程序执行,最终会根据return_when等待Future对象完成到某个状态才返回结果,return_when可选参数有:FIRST_COMPLETEDFIRST_EXCEPTION 和 ALL_COMPLETED

返回结果为一个包含两个集合的named tuple,其中一个集合包含完成的,另一个为未完成的。

如果采用默认的ALL_COMPLETED,程序会阻塞直到线程池里面的所有任务都完成:

from concurrent.futures import ThreadPoolExecutor, wait, as_completedimport timefrom random import randintdef return_after_5_secs(num):    sleep_time = randint(0, 5)    time.sleep(sleep_time)    return "Return of {}, sleep_time: {}".format(num, sleep_time)pool = ThreadPoolExecutor(6)futures = []for x in range(5):    futures.append(pool.submit(return_after_5_secs, x))t0 = time.time()print(wait(futures))t1 = time.time()print(f"wait {t1-t0:{2}.{5}}")for x in as_completed(futures):    print(x.result())

输出如下:

return_when='ALL_COMPLETED',not_done无元素,wait的时间较长


DoneAndNotDoneFutures(done={, , , , }, not_done=set())wait 4.0018Return of 2, sleep_time: 2Return of 3, sleep_time: 1Return of 1, sleep_time: 4Return of 0, sleep_time: 4Return of 4, sleep_time: 0

如果采用FIRST_COMPLETED参数,程序并不会等到线程池里面所有的任务都完成:

from concurrent.futures import ThreadPoolExecutor, wait, as_completedimport timefrom random import randintdef return_after_5_secs(num):    sleep_time = randint(0, 5)    time.sleep(sleep_time)    return "Return of {}, sleep_time: {}".format(num, sleep_time)pool = ThreadPoolExecutor(6)futures = []for x in range(5):    futures.append(pool.submit(return_after_5_secs, x))t0 = time.time()print(wait(futures, return_when='FIRST_COMPLETED'))t1 = time.time()print(f"wait {t1-t0:{2}.{5}}")for x in as_completed(futures):    print(x.result())

输出如下:

return_when='FIRST_COMPLETED',not_done有元素,wait的时间很短


DoneAndNotDoneFutures(done={}, not_done={, , , })wait 0.00015497 Return of 3, sleep_time: 0Return of 4, sleep_time: 1Return of 0, sleep_time: 2Return of 2, sleep_time: 2Return of 1, sleep_time: 4

References

[1] Future类: https://docs.python.org/3/library/concurrent.futures.html#future-objects[2] map方法: https://docs.python.org/3/library/functions.html#map

往期推荐

气象遇见机器学习

告别繁琐,nc数据一键可视化

从xarray走向netCDF处理(二):数据索引

入门了python,然后呢?

告别NCL 拥抱Python

如何快速入门Python

往期回顾:

天气展望 | 南信大军训天气:汗水or雨水?

气象招聘 | 中南空管局气象中心2020年校园招聘公告

课业学习 | 南信大《天气学原理》在线课程:我们为你证明!

2020届气象本科考研与就业QQ群:930107262 2020届气象研究生毕业就业QQ群:346577427 2021届气象本科 考研与就业QQ群 639522239 a02e48326771796b75d31db412c1c013.png
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 对于 python 编写程序实现分段函数计算,可以使用 if 语句和 for 循环来实现。首先要根据函数分段条件定义每段函数的表达式,然后使用 if 语句和 for 循环来依次判断输入值 x 的属于哪段函数,最后根据相应的函数表达式计算出 y 的值。好的,下面是Python编写程序实现分段函数计算的示例代码: ```python def piecewise(x): if x < -1: return -1 elif -1 <= x < 1: return x else: return x**2 # 测试 print(piecewise(-2)) # 输出:-1 print(piecewise(-1)) # 输出:-1 print(piecewise(0)) # 输出:0 print(piecewise(1)) # 输出:1 print(piecewise(2)) # 输出:4 ``` 这是一个简单的分段函数,根据输入值x的不同范围返回不同的值。其中,当x小于-1时,返回-1;当-1<=x<1时,返回x;当x>=1时,返回x的平方。 你可以根据实际需求,修改函数定义和条件判断的方式来实现其他分段函数计算。 ### 回答2: 分段函数是指在一定区间内,函数值的表达式不一样。在Python编写程序实现分段函数计算,可以使用if语句或者numpy库中的piecewise函数。 以实现以下分段函数为例: f(x) = x + 1 (x<0) f(x) = x^2 (0<=x<1) f(x) = x + 2 (1<=x<2) f(x) = x^3 (x>=2) 1. 使用if语句 代码如下: def f(x): if x<0: return x+1 elif 0<=x<1: return x**2 elif 1<=x<2: return x+2 else: return x**3 这里我们定义一个函数f(x),输入x,然后通过if语句对x的值进行分类讨论,根据题目所给的分段函数表达式来计算对应的y值,并返回y。 测试代码: print(f(-1)) # 输出0 print(f(0.5)) # 输出0.25 print(f(1.5)) # 输出3.5 print(f(3)) # 输出27 可以看到,程序成功实现了给定的分段函数计算,并得到了正确答案。 2. 使用numpy库的piecewise函数 numpy库中的piecewise函数可以方便地实现分段函数计算。 代码如下: import numpy as np def f(x): return np.piecewise(x, [x<0, (0<=x)&(x<1), (1<=x)&(x<2), x>=2], [lambda x: x+1, lambda x: x**2, lambda x: x+2, lambda x:x**3]) 这里我们同样定义了一个函数f(x),使用numpy库中的piecewise函数,将分段函数的每一段表达式作为一个lambda表达式传入,然后按照给定的分段函数对x的值进行分类讨论,计算对应的y值,并返回y。 测试代码: print(f(-1)) # 输出0 print(f(0.5)) # 输出0.25 print(f(1.5)) # 输出3.5 print(f(3)) # 输出27 同样可以看到,程序成功实现了给定的分段函数计算,并得到了正确答案。 以上就是如何使用Python编写程序实现分段函数计算的方法。学习掌握这个技巧可以便捷地解决分段函数计算的问题。 ### 回答3: 分段函数,即将定义域划分成多个不同的区间,在每个区间内采用不同的函数形式进行计算Python编程语言可以轻松地实现分段函数计算,下面我将为您详细介绍如何编写Python程序实现分段函数计算。 首先,我们需要定义需要计算分段函数。例如,我们定义一个分段函数: f(x) = {1-x, x >= 1 {x^2, 0 <= x < 1 {0, x < 0 其中,x为自变量。这个分段函数在定义域中分成了三个区间,每个区间内采用不同的函数形式进行计算。在Python中,可以将这个分段函数计算分别写成三个函数形式: def f1(x): return 1 - x def f2(x): return x ** 2 def f3(x): return 0 然后,我们需要编写一个函数来确定自变量x所在的区间,并返回对应的函数形式。在这个例子中,可以直接使用if-else语句来实现: def f(x): if x >= 1: return f1(x) elif x >= 0: return f2(x) else: return f3(x) 最后,我们可以通过调用f(x)函数,传入自变量x的值来计算分段函数的值: print(f(1.5)) # 输出:-0.5 print(f(0.5)) # 输出:0.25 print(f(-0.5)) # 输出:0 完整的代码如下: def f1(x): return 1 - x def f2(x): return x ** 2 def f3(x): return 0 def f(x): if x >= 1: return f1(x) elif x >= 0: return f2(x) else: return f3(x) print(f(1.5)) # 输出:-0.5 print(f(0.5)) # 输出:0.25 print(f(-0.5)) # 输出:0 以上就是如何利用Python编写程序实现分段函数计算的完整步骤。在实际应用中,我们可以根据不同的分段函数来定义不同的函数形式,然后编写一个判断区间的函数来实现分段函数计算。通过这种方法,我们可以轻松地计算复杂的分段函数,提高了计算效率。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值