Python 如何创建月份迭代器
我想创建一个python函数,它允许我在几个月内从起点迭代到终点。例如,它看起来像Python 如何创建月份迭代器,python,Python,我想创建一个python函数,它允许我在几个月内从起点迭代到终点。例如,它看起来像 def months(start_month, start_year, end_month, end_year): 调用个月(2010年8月、2011年3月)将返回: ((8, 2010), (9, 2010), (10, 2010), (11, 2010), (12, 2010), (1, 2011), (2, 2011), (3, 2011)) 该函数可以只返回一个元组,但我希望将其视为一个生成器(即使用
def months(start_month, start_year, end_month, end_year):
调用个月(2010年8月、2011年3月)
将返回:
((8, 2010), (9, 2010), (10, 2010), (11, 2010), (12, 2010), (1, 2011), (2, 2011), (3, 2011))
该函数可以只返回一个元组,但我希望将其视为一个生成器(即使用yield
)
我检查了日历
python模块,它似乎没有提供此功能。我可以为循环编写一个讨厌的,这样做就足够容易了,但我很想看看专业人士可以多么优雅地完成它
谢谢。也许可以改进此功能的优雅性或速度,但这是一个简单的解决方案:
def months(start_month, start_year, end_month, end_year):
month, year = start_month, start_year
while True:
yield month, year
if (month, year) == (end_month, end_year):
return
month += 1
if (month > 12):
month = 1
year += 1
编辑:这里有一个不那么简单的方法,它甚至可以通过使用生成器来避免使用yield
:
def months2(start_month, start_year, end_month, end_year):
return (((m_y % 12) + 1, m_y / 12) for m_y in
range(12 * start_year + start_month - 1, 12 * end_year + end_month))
日历是这样工作的
def month_year_iter( start_month, start_year, end_month, end_year ):
ym_start= 12*start_year + start_month - 1
ym_end= 12*end_year + end_month - 1
for ym in range( ym_start, ym_end ):
y, m = divmod( ym, 12 )
yield y, m+1
所有多单元的事情都是这样工作的。英尺、英寸、小时、分钟、秒等等。唯一的不简单的是月-天或月-周,因为月是不规则的。其他一切都是常规的,您需要在最细粒度的单元中工作。这不像其他解决方案那么简单,但很容易理解。基本上,它有两个分支
- 开始年份与结束年份相同
- 开始年份与结束年份不同
后一种情况分为三个阶段:
- 从起始月到起始年的12月
- 从开始年份到结束年份之间的每年的每个月
- 从1月到年底的月底
如果结束年是开始年之后的一年,则跳过上面的第二阶段(不需要显式测试,范围仅为空)
对于Python3.x,将xrange
更改为range
您的问题有点含糊不清,因为您需要一个迭代器,但随后显示一个返回元组元组的函数。这两个都是:
import calendar
import datetime
def months_iter(start_month, start_year, end_month, end_year):
start_date = datetime.date(start_year, start_month, 1)
end_date = datetime.date(end_year, end_month, 1)
date = start_date
while date <= end_date:
yield (date.month, date.year)
days_in_month = calendar.monthrange(date.year, date.month)[1]
date += datetime.timedelta(days_in_month)
def months(start_month, start_year, end_month, end_year):
return tuple(d for d in months_iter(start_month, start_year, end_month, end_year))
print months(8, 2010, 3, 2011)
# ((8, 2010), (9, 2010), (10, 2010), (11, 2010), (12, 2010), (1, 2011), (2, 2011), (3, 2011))
导入日历
导入日期时间
定义月份(开始月份、开始年份、结束月份、结束年份):
开始日期=日期时间.日期(开始年份,开始月份,1)
结束日期=日期时间.日期(结束年份,结束月份,1)
日期=开始日期
虽然date对Python内置迭代器有点兴趣,但肯定不是优雅的;)
从日期时间导入时间增量,日期
class MonthRange:
def __init__ (self, date1, date2):
self.start_date = date1 - timedelta(days=1)
self.end_date = date2
self.data = self.start_date
def __iter__(self):
return self
def next(self):
if self.data >= self.end_date.replace(day=1) + timedelta(days=32):
raise StopIteration
ret = self.data
self.data = self.data + timedelta(days=32)
return ret.replace(day=1)
for x in MonthRange(date.today(), date(2012, 11, 01)):
print (x.year, x.month)
dfan方法的更简单版本,也是比s.Lott方法更简单的解决方案(无除法,无模):
def月数(开始月、开始年、结束月、结束年):
月,年=开始月,开始年
而(年、月)12:
月份=1
年份+=1
这种方法接近于如果他们必须手工操作的话所使用的方法。它的运行时间与S.Lott的相同(上面代码中的测试所花费的时间与除法和模的时间差不多)。months
函数使用dateutil
模块
from dateutil.rrule import rrule, MONTHLY
from datetime import datetime
def months(start_month, start_year, end_month, end_year):
start = datetime(start_year, start_month, 1)
end = datetime(end_year, end_month, 1)
return [(d.month, d.year) for d in rrule(MONTHLY, dtstart=start, until=end)]
示例用法
print months(11, 2010, 2, 2011)
#[(11, 2010), (12, 2010), (1, 2011), (2, 2011)]
或者以迭代器的形式
def month_iter(start_month, start_year, end_month, end_year):
start = datetime(start_year, start_month, 1)
end = datetime(end_year, end_month, 1)
return ((d.month, d.year) for d in rrule(MONTHLY, dtstart=start, until=end))
迭代器用法
for m in month_iter(11, 2010, 2, 2011):
print m
#(11, 2010)
#(12, 2010)
#(1, 2011)
#(2, 2011)
由于其他人已经为生成器提供了代码,我想补充一点,Pandas有一个名为“”的方法,在本例中,该方法可以接受开始和结束、年份和月份,并返回适合迭代的周期索引
import pandas as pd
pr = pd.period_range(start='2010-08',end='2011-03', freq='M')
prTupes=tuple([(period.month,period.year) for period in pr])
#This returns: ((8, 2010), (9, 2010), (10, 2010), (11, 2010), (12, 2010), (1, 2011), (2, 2011), (3, 2011))
为什么专业人士不应该使用两个嵌套循环编写一些代码。可读性更好,代码更详细,而不是使用花哨的语言功能的密集而复杂的代码,这些功能在一天后很难理解。好的解决方案肯定会涉及至少一个for循环,但我能想到的唯一方法也包括各种if,即,if start\u year==end\u year
,如果结束年份-开始年份>1
等。。。似乎还有比这更漂亮的解决方案。dateutil
模块在这里可能很有用,它可能是使月末成为独占的,比如范围。另见:同意,我只是在匹配问题陈述。这就是我所寻找的美。我绝对没有那样想过。非常感谢。回答得很好,但是所有常规的事情都不是这样的,因为月份是从1..12开始计算的,而不是从0..11(比如英尺和英寸)。@martineau:True。但是所有的事情都在它们最细粒度的单元中运行良好。(不定期的月份除外)。±1是一种编码上的细微差别,通常很明显。从你的评论中,我想这并不总是显而易见的。@martineau:这不是一个真正的“微妙之处”。但是谢谢。我肯定有人被序数和基数搞糊涂了。为了得到一个月的天数(包括闰年特例),Python有一个方便的函数。需要注意的是,它是一个第三方模块。
for m in month_iter(11, 2010, 2, 2011):
print m
#(11, 2010)
#(12, 2010)
#(1, 2011)
#(2, 2011)
for year in range(2014, 2018):
for month in range(start_month_number, 13):
this_month = datetime.date.today().replace(year=year, month=month, day=1)
import pandas as pd
pr = pd.period_range(start='2010-08',end='2011-03', freq='M')
prTupes=tuple([(period.month,period.year) for period in pr])
#This returns: ((8, 2010), (9, 2010), (10, 2010), (11, 2010), (12, 2010), (1, 2011), (2, 2011), (3, 2011))