如何让你的python程序,在未来某个时间去做一件事情

如何让你的python程序,在未来某个时间去做一件事情

如何让你的程序在未来某个时间去做一件事情呢,注意,这不是简单的闹钟,简单的闹钟就像我们起床,时间设置到每天的早上7点,这是在24小时内的定时,而现在要做到更大的范围,超过24小时,扩大到月,甚至年,那么该如何做呢?
其实一旦实现了年,那么还有哪个时间不能定时呢?也许有人说,上到年,下能到毫秒吗?其实毫秒跟24小时内的定时一样,用的是datetime。

signal能搞定所有时间跨度的定时

signal是python中的一个模块,叫做信号,关于这个功能,少有人知道,因为这个涉及到系统级的编程,需要一些Linux系统编程知识。signal模块能够捕捉系统中的很多信号,比如SIGINT(ctrl+c信号,当然python也可以通过异常KeyboardInterrupt捕捉到这个信号),SIGKILL,进程被杀死的信号,比如你这个进程被别人杀死了,可以捕捉到这个信号,SIGCHLD,子进程结束的信号,比如僵尸进程结束了,父进程就可以通过捕捉这个信号知道,等等…

先说说signal的好处

python是个无所不能的语言,有人说他不能操作硬件,但是因为python能够调用c,所以python可以间接操作硬件。
但是,同样的一个功能,在python里面有好几种解决方法,哪一种是最佳,最快,最简洁的方案,那要看开发者的知识面了。
比如下面这个场景,当我们要实现一个定时"炸弹",比如在未来的某一天,让我们的程序自动的去执行某一个动作,那么就可以用signal中的闹钟——alarm功能,当然我们不会真去做一个炸弹,只是一个功能形象说明,我们可以实现,从现在开始,到明天中午12点整,打印一条语句,如果你有树莓派,可以亮一个灯,响一个蜂鸣器。
也许有人说我可以用线程threading里面的定时器timer,但是,threading里面的timer其实并不涉及的时间,它的机制是通过另开一个线程,然后在这个线程里面,需要我们自己去获取时间,设置时间,总之相比signal来说,复杂多了。
signal的功能还有很多,只不过刚才的这个场景我们会经常用到,所以在这里主要介绍它的“闹钟”功能。

编程思路

我们需要先搞清楚signal的原理,我们先通过一个小实验来了解
“每隔1秒在屏幕上打印当前时间”

最容易想到的方法

就像这样:
在这里插入图片描述
这个功能的实现,有很多种方法,最简单的代码如下:

import time
while 1:
    print(time.ctime(time.time()))
    time.sleep(1)

如果只想显示时分秒,代码可以这样

import time
while 1:
    print(time.ctime(time.time())[-13:-5])
    time.sleep(1)

但是,我们项目中并不是只有这一个功能,我们还有其他功能,不能把sleep函数放在主函数的while 1中。
好,那么创建一个子线程来实现,代码就像这样:

import time,threading,sys
def prinTime():
    while 1:
        print(time.ctime(time.time())[-13:-5])
        time.sleep(1)
p = threading.Thread(target=prinTime,args=())
p.start()
while 1:
    time.sleep(100)  #模拟系统做其他事情

好,很好,我们用线程实现了,而且看上去就9条语句——python就是这么简洁

采用signal

signal模块中的alarm函数,其实就是为上面这个功能而设计的,其实很多的时候,我们只需要想想:计算机发展了30多年,linux系统如此成熟,前人不是到今天才有这个功能的需要,之前肯定有发展比较成熟的方法,如果你对Linux系统编程比较熟悉,那么对你来说,首先想到的不是线程,应该是signal,因为linux系统编程中,就有这个函数。
好,该怎么做呢?
代码非常简单

  1 import time,signal
  2 def alarm_fun(no,frame):
  3     print(time.ctime(time.time()))
  4     signal.alarm(1)
  5 signal.signal(signal.SIGALRM,alarm_fun)
  6 signal.alarm(1)
  7 while 1:
  8    time.sleep(100)

因为我们主要是讲解signal,所以我应该把这里的每一句讲清楚(上面的线程我就不解释了)。
第1行:同时导入time,signal 两个模块
第5行:我们要使用signal这个功能,必须通过该模块中的函数singal(函数名与模块名相同),设置要捕捉哪一个信号。我们这里就是想捕捉闹钟信号,所以第一个参数就传要捕捉的信号编号——SIGALRM,这里的SIGALRM是宏定义,它的值其实是14,你也可以直接传14.第二个参数是处理这个信号的函数——alarm_fun,这个函数是我们自己定义的,函数名也是我们自己定义的,你也可以定义其他函数名。
第2行:这个函数的形式,不能随便定义,也就是它必须有两个参数,至于这两个参数名字怎么定义,是我们的事情,你可以随便,但是数量上,必须是两个参数,第一个参数就是捕捉到的信号编号,第二个参数是一个对象,在这里意义不大。
第6行:就是启动定时器,也就是触发信号,因为我们这里的实验是实现每隔1秒打印一下系统时间,所以定时是1秒,当然你可以设置其他时间。
第4行:这一行不能少,就是启动下面的闹钟,就像我们手机设置起床闹钟一样,是一次行的,还是每天的。

如何定时到,未来某个时间

刚才我们定时的1秒,如果我们要定时到明天,或者下一个月,甚至明年怎么办呢,我们如何把明年到现在的秒数计算出来?
这里就介绍一个比较简单的方法,Python其实非常强大,什么都可以实现——条条道路通罗马,看你能不能找到最快的那条!

实现思路

首先time这个模块,很多人很熟悉,它有一个time()函数能够获取到当前时间到1970的秒数。比如:
在这里插入图片描述
下面那个很大的数,就是从现在开始,追溯到1970年的秒数。
如果,我们能知道未来某一天到1970年的秒数就OK了,而且未来某一天我们可以任意设置。
我们继续分析time模块中的函数,有一个mktime。

mktime(…)
mktime(tuple) -> floating point number
Convert a time tuple in local time to seconds since the Epoch.
Note that mktime(gmtime(0)) will not generally return zero for most
time zones; instead the returned value will either be equal to that
of the timezone or altzone attributes on the time module.

它需要一个元组,这个元组就像这样:
localtime返回的就是一个元组

我们如何得到元组呢,如果我们能够构造一个元组就好了。
但是构造元组,年月日,时分秒都好说,但是后面的星期,一年中的哪一天,是否是夏定时怎么知道呢?
经过本人的摸索,在datetime这个模块中有这个功能:
import time,datetime
print(datetime.datetime(2020,7,11,12,00,00).timetuple())
结果如下:
time.struct_time(tm_year=2020, tm_mon=7, tm_mday=11, tm_hour=12, tm_min=0, tm_sec=0, tm_wday=5, tm_yday=193, tm_isdst=-1)
这个元组正式我们需要的,这样的话:
我们设置一个目标时间,然后转成秒数就好了,就像这样:
import time,datetime
print(datetime.datetime(2020,7,11,12,00,00).timetuple())
goal = datetime.datetime(2020,7,11,12,0,0).timetuple()
print(time.mktime(goal))
运行结果:
time.struct_time(tm_year=2020, tm_mon=7, tm_mday=11, tm_hour=12, tm_min=0, tm_sec=0, tm_wday=5, tm_yday=193, tm_isdst=-1)
1594297830.0
因此,我们只需要把这个很大的秒数减去当前的秒数,差值给到signal.alarm()函数就行了
如果time()函数里面传一个指定日期,那么time.time(xxx)就返回那个日期到1970年的秒数。比如

  1 import signal,time,datetime
  2 goal = datetime.datetime(2020,7,11,12,0,0).timetuple()
  3 t = time.mktime(goal)
  4 def alarm_fun(sino,frame):
  5     print('主人:到时间了....')
  6 signal.signal(signal.SIGALRM,alarm_fun)
  7 signal.alarm(int(t)-int(time.time()))
  8 while 1:
  9     pass  #模拟其他时间

这样,过12个小时就会打印"主人:到时间了…",如果我们的时间定位到2021年也可以,随你了。

猜你喜欢

转载自blog.csdn.net/qq_27320195/article/details/107270031