【问题标题】:why do does generating a list of holidays in the year 2005 blow up this function?为什么在 2005 年生成假期列表会破坏此功能?
【发布时间】:2021-12-04 09:16:33
【问题描述】:

我正在编写一个使用datetimeholidaysdateutil 的Python 脚本来确定YYYY-MM-DD 格式的给定日期是否是交易假日。我正在使用生成器表达式从holidays 库提供的默认假期列表中删除市场未关闭的假期,

import datetime, holidays
import dateutil.easter as easter

def to_date(date_string):
    return datetime.datetime.strptime(date_string,'%Y-%m-%d').date()

def is_trading_holiday(date):
    us_holidays = holidays.UnitedStates(years=date.year)
    # generate list without columbus day and veterans day since markets are open on those days
    trading_holidays = [ "Columbus Day", "Columbus Day (Observed)", "Veterans Day", "Veterans Day (Observed)"]
    custom_holidays = [ date for date in us_holidays if us_holidays[date] not in trading_holidays ]
    # add good friday to list since markets are closed on good friday
    custom_holidays.append(easter.easter(year=date.year) - datetime.timedelta(days=2))

    return date in custom_holidays

if __name__=="__main__":
    first_date = to_date('2020-01-03')
    second_date = to_date('2015-11-26') # Thanksgiving
    third_date = to_date('2005-01-01') # New Years
    fourth_date = to_date('2005-01-07')

    print(is_trading_holiday(first_date))
    print(is_trading_holiday(second_date))
    print(is_trading_holiday(third_date))
    print(is_trading_holiday(fourth_date))

我已经针对各种日期对此进行了测试,它似乎在所有情况下都有效,但只有一种情况。当我使用 2005 年的日期时,这个函数会爆炸并告诉我,

Traceback (most recent call last):
  File "./test.py", line 26, in <module>
    print(is_trading_holiday(third_date))
  File "./test.py", line 11, in is_trading_holiday
    custom_holidays = [ date for date in us_holidays if us_holidays[date] not in trading_holidays ]
  File "./test.py", line 11, in <listcomp>
    custom_holidays = [ date for date in us_holidays if us_holidays[date] not in trading_holidays ]
RuntimeError: dictionary changed size during iteration

我不知道 2005 年有什么特别之处导致这个功能崩溃,或者即使是这一年导致了这个问题(我已经测试了这个可以追溯到 70 年代的日期,并且它有效)。我没有修改我在生成器表达式中迭代的字典(否则,我认为不是?),所以我不确定这个错误试图告诉我什么。

有人知道这里发生了什么吗?我错过了什么明显的东西吗?

【问题讨论】:

  • 如果您将理解更改为[date for date, holiday in us_holidays.items() if holiday in trading_holidays],它会改变什么吗?
  • 看来我的版本可以工作(在移动设备上的 colab 中尝试),所以我的猜测是 __getitem__ 有时会产生修改底层字典的副作用。 2005 年“元旦(已观察)”发生在 2004 年,但在迭代器中 元旦之后,这是修改字典的条目,因此它可能是库的实现细节当假期不正常时泄漏?可能值得将其报告为错误。
  • 啊,有趣的是,如果您在 2005 年的 us_holidays 中的每个条目上使用 __getitem__,然后再循环一遍,新年(观察到的)不会出现!显然__getitem__ 正在从 2004 年开始修剪日期。

标签: python python-datetime python-dateutil generator-expression python-holidays


【解决方案1】:

UnitedStates 类中似乎存在一个错误(或特殊情况),它为 2005 年生成 datetime.date(2004, 12, 31): "New Year's Day (Observed)"。这会导致您的列表理解中的 if us_holidays[date] 引用不同的年份(尚未加载)并导致更改您正在遍历的字典。

您可以通过迭代项目而不是使用键重新访问字典来解决该问题:

... for date,name  in us_holidays.items() if name not in trading_holidays]

或者,您可以只转换为列表,这样迭代就不会通过实际的字典:

... for date in list(us_holidays) if us_holidays[date] not in trading_holidays]

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-08-16
    • 1970-01-01
    • 2023-03-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-06-22
    • 1970-01-01
    相关资源
    最近更新 更多