我是一个新手程序员,目前正在通过一本书独自学习Python编程。我在一个章节中偶然发现了一个问题,花了我两天的时间才找到解决方案。尽管这可能不是解决该问题的有效代码,但是如果您能对此进行回顾,我将不胜感激。只是为了让大家都知道我的编程知识水平,所以我对函数,序列和决策结构只有基本的了解。


编写一个程序来检查日期是否有效?日期应为
,采用mm / dd / yyyy格式。和年份,如果对每个函数有效,则返回True。
输入一个字符串日期,用“ /”分隔,并将其分配给日期变量。
将日期变量分为3个单独的变量。
检查三个变量是否为True。
打印日期是否有效。




我没有考虑2月的where年有29天,并且还假设年份从公元1年开始到当前年份。

def monthcheck(month):
        if month > 0 and month <= 12: ## If month is between 1 and 12, return True.
            return True
        else:
            return False



def daycheck(month,day):
    monthlist1 = [1,3,5,7,8,10,12] ## monthlist for months with 31 days.
    monthlist2 = [4,6,9,11] ## monthlist for months with 30 days.
    monthlist3 = 2 ## month with month with 28 days.

    for mon in monthlist1: ## iterate through monthlist1.
        if month == mon: ## Check if the parameter month equals to any month with 31 days.
            if day >=1 and day <= 31: ## If the parameter day is between 1 and 31, return True.
                return True
            else:
                return False

    for mon in monthlist2: ## iterate through the monthlist with 30 days.
        if month == mon: ## check if the parameter month equals to any month with 30 days.
            if day >= 1 and day <= 30: ## if the parameter day is between 1 and 30,return True.
                return True
            else:
                return False

    if month == monthlist3: ## check if parameter month equals month with 28 days.
        if day >=1 and day <= 28: ## if the parameter day is between 1 and 28,return True.
            return True
        else:
            return False



def yearcheck(year):
    if len(year) >= 1 and len(year) <= 4: ## Check if year has between 1 to 4 numbers and return True.
        return True
    else:
        return False


def main():
    date = str(input("Enter the date in mm/dd/yyyy format: ")) ## Input date in the given format.

    month,day,year = date.split("/") ## split the date into 3 separate variables.

    monthvalidity = monthcheck(int(month)) 

    dayvalidity = daycheck(int(month),int(day)) 

    yearvalidity = yearcheck(year) 

    if monthvalidity == True and dayvalidity == True and yearvalidity == True: ## check if all 3 variables are valid or True
        print("The date {0} is valid.".format(date))

    else:
        print("The date {0} is invalid.".format(date))

main()


评论

欢迎来到地狱。验证日期是有史以来最困难的(编程)问题之一。例如,您不检查公历有效时的年份是否为日期范围的n。因此,对于不存在的日期,您可能返回true。阅读有关程序员在这里和这里相信时间的虚假信息。

对于清单中的一个简单示例:您根本不处理leap年,因此您将拒绝有效日期。当您开始时,请注意有一些特殊的规则,可以将年份除以4而不是leap年,即可以除以100。还有更多例外,这些例外不适合注释。日期计算非常非常困难。

@allo是正确的,但是1.在某些情况下,像这样的简单检查就足够了(即,如果您对某种类型的日期用作输入有一定程度的控制权),并且2.这是一本书的练习,因此,期望OP处理所有可能的边缘情况是不公平的,而这仅是为了提高Python的编程效率。
@Daniel:我认为您不应该或者不能处理软件中的每一个极端情况。这将是...地狱。但是至少要有一些leap年处理,并且使用4/100/400规则对于现实中的嗡嗡声是很不错的练习;-)。本文的其余部分更多地是关于下次执行与日期有关的事情时听说这些问题,并考虑是否需要考虑这些问题。

顺便说一句:ISO 8601现在已有30年历史了。

#1 楼

干得好!虽然肯定还有改进的余地,但最好还是自己动手发现一个解决方案。



def monthcheck(month):
    if month > 0 and month <= 12: ## If month is between 1 and 12, return True.
        return True
    else:
        return False


可以更简洁地写为

def monthcheck(month):
    return 0 < month <= 12


尽管为几乎每一行代码编写注释都可以帮助您学习,并且是跟踪工作位置的好方法通过伪代码,不要只是写代码已经告诉您的内容。
PEP 8-Python的样式指南-建议使用snake_case作为命名函数,因此monthcheck会变成month_check
如果我输入“测试”何时要求日期?程序崩溃。验证用户输入是否为您期望的格式是一个好主意。
程序要求输入mm/dd/yyyy格式的输入,但会接受m/d/y,这可能是不希望的。

不要与True / False显式比较
if monthvalidity == True and dayvalidity == True and yearvalidity == True:


如果写成

,则更容易阅读/>
if monthvalidity and dayvalidity and yearvalidity



daycheck有几个可以改进的地方。您可以使用字典(就像石南花一样),但是如果您还不了解这些字典,则仍然可以改进算法。


Python的in运算符还可以检查列表中是否有值,因此,您无需检查monthlist1monthlist2,而只需遍历if month in monthlist1即可。
由于monthlist1并未真正描述变量中的内容,而且我很难找到一个更好的名称,因此我将在列表中插入if month in [1, 3, 5, 7, 8, 10, 12]并添加清晰的注释。


不要害怕使用听起来很自然的变量名。我相信monthcheck最好命名为is_month_valid
负数呢? 1/1/-111显然是有效日期。这可能是理想的,但可能值得评论。
将数字转换为字符串以确定大小通常不是一个好主意,在浮点数/双精度数的更复杂情况下,您可能会遇到麻烦。我希望检查1 <= year < 10000
输入函数周围不需要str()input已经返回了一个字符串。 ,具有可以为我们做到这一点的功能!在生产代码中,应该使用此代码。一开始,获得很多反馈是完全正常的。您编写的代码越多,代码就越自然。

评论


\ $ \ begingroup \ $
一些建议:您可以使用datetime import datetime来使代码更简洁一些;如果您使用的是Python 3.6+,可以使用print(f“ date {date:%m /%d /%Y } 已验证。”)。最后,仅需注意一点-date是datetime中的保留字,我经常使用“ today”或“ date_val”代替“ date”一词。当然,如果您不从from datetime导入datetime,则date无关紧要,但是它有助于避免命名冲突,因此您可以使用预期的变量而不是意外地使用库函数。
\ $ \ endgroup \ $
– C. Harley
18年7月31日在4:49

\ $ \ begingroup \ $
谢谢!我对Python的了解不多,所以看起来我也要学很多东西。
\ $ \ endgroup \ $
– Gerrit0
18年7月31日在11:58

\ $ \ begingroup \ $
小尼特。我认为我会使用month_is_valid而不是is_month_valid,因为它读起来更好:if month_is_valid和...。
\ $ \ endgroup \ $
–rcollyer
18年7月31日在14:11

\ $ \ begingroup \ $
感谢@ Gerrit0的宝贵反馈。这真的激励了我学习更多并继续前进。我还没有真正了解字典,也没有真正吸收异常处理。下次发布任何代码时,我都会尝试使其更好。再次感谢您拨出宝贵时间来审核我的代码。
\ $ \ endgroup \ $
–mathlover92
18年7月31日在18:10

\ $ \ begingroup \ $
@rcollyer我不是Python程序员,但是用yes / no动词来命名布尔变量是很正常的:if is_valid_month,if is_leap_year,has_time等。如果是有效月份……”
\ $ \ endgroup \ $
– CJ Dennis
18年8月1日在13:57



#2 楼

在大多数情况下,对我来说看起来很不错!

Format

您没有使用下划线将很多变量名中的单词分开,这让我很困扰。 PEP8建议使用Snake_case,因此我对此进行了更改。

您还在第一个函数中添加了额外的缩进(无论是将代码复制粘贴到问题中还是弄乱了,我不知道),因此我将其删除。

算法

您的day_check是我更改最多的东西。我基本上只是创建了一个将月份数字与日期数字匹配的字典,并检查了函数中输入的日期是否在0和该字典定义的最大值之间。我感觉这大大缩短并弄清了功能。

我所做的另一项更改是将您的if主体声明更改为if month_validity and day_validity and year_validity:,我个人认为这更干净。

如果您正在使用main() ...

还要确保在调用main()时位于正确的位置,所以我添加了最后一个if语句。 br />
years年还不错。只需将年份作为参数添加到month_check并执行

if int(year) % 4 == 0:
    if int(year) % 100 == 0:
        days_in_month[2] = 28
    else:
        days_in_month[2] = 29
else:
    days_in_month[2] = 28


用户输入检查

如Gerrit0指出的那样

>
如果我要求输入日期时输入“测试”怎么办?程序崩溃。验证用户输入的格式是否符合您的期望是一个好主意。


可以通过简单的try / except进行修复-尝试main()函数中的所有代码(ValueError除外),并向用户打印正确格式的提醒。

负日期

Gerrit0还指出该函数允许年份中为负数(使我想起该XKCD)。虽然我没有将其添加到代码中,但我将更改year函数以确保实际数字在适当的范围内,这样可以使您可以直接在输入上进行所有类型转换,而不是直接进行输入转换在每个功能输入之前。它还提供了一种确保日期在公历中的好方法。

最终代码

def month_check(month):
    if month > 0 and month <= 12: ## If month is between 1 and 12, return True.
        return True
    else:
        return False



def day_check(month, day):
    days_in_month = {1:31, 2:28, 3:31, 4:30, 5:31, 6:30, 7:31, 8:31, 9:30, 10:31, 11:30, 12:31}
    if 0 < day <= days_in_month[month]:
        return True
    else:
        return False



def year_check(year):
    if len(year) >= 1 and len(year) <= 4: ## Check if year has between 1 to 4 numbers and return True.
        return True
    else:
        return False


def main():
    date = str(input("Enter the date in mm/dd/yyyy format: ")) ## Input date in the given format.

    try:
        month,day,year = date.split("/") ## split the date into 3 separate variables.

        month_validity = month_check(int(month)) 

        day_validity = day_check(int(month),int(day)) 

        year_validity = year_check(year)

        if month_validity and day_validity and year_validity: ## check if all 3 variables are valid or True
            print("The date {0} is valid.".format(date))

        else:
            print("The date {0} is invalid.".format(date))
    except ValueError:
        print('Your input was not valid. Please enter the date in a mm/dd/yyyy format.')

if __name__ == "__main__":
    main()


评论


\ $ \ begingroup \ $
感谢@heather查看我的代码。我已经开始实现命名函数和变量的正确方法。我不明白使用main()的部分。至于输入检查,它属于异常处理吗?我将在那部分阅读更多内容。
\ $ \ endgroup \ $
–mathlover92
18年7月31日在18:15

\ $ \ begingroup \ $
@ mathlover92因此,如果__name__ ==“ __main__”:要做的是检查是否从该文件的位置运行该文件-即,是否要导入它。这很有用,因为如果要导入文件,它将运行任何“松散”代码。因此,如果您想通过从filename导入main来将main()函数导入到另一个文件中,则它将自动运行main,并且您可能不希望这样做。 if语句阻止了这种情况的发生。是的,输入检查是异常处理,对控制程序流非常有用[...]
\ $ \ endgroup \ $
–希瑟
18年7月31日在18:22

\ $ \ begingroup \ $
@ mathlover92在使用异常处理时要牢记,除非捕获异常,否则它在计算上并不是很昂贵。因此,如果您一次又一次地因用户错误而捕获异常,例如此处,则是使用它的好地方。它基本上只是运行代码,如果遇到错误ValueError,它将进入代码的另一部分,说明发生这种情况时应采取的措施。如果引发了ValueError以外的错误,那么您会看到它,这就是为什么您通常不想尝试:...例外:...因为这意味着它捕获了所有错误,所以[...]
\ $ \ endgroup \ $
–希瑟
18年7月31日在18:24

\ $ \ begingroup \ $
@ mathlover92 [...]无论它们是否正确,这意味着如果您的程序表现不正常,您将看不到其他任何错误(或者在我们的案例中,ValueErrors不相关)到用户输入)弹出。使用它们是要小心的一件事。 tl; dr,查找异常=)
\ $ \ endgroup \ $
–希瑟
18年7月31日在18:25

\ $ \ begingroup \ $
谢谢@heather,我会在练习异常处理时发现一些问题,因此我可以很好地掌握它。.并且几乎所有程序都使用了异常处理吗?您是否为初学者推荐任何好的站点来练习python编程问题?
\ $ \ endgroup \ $
–mathlover92
18年7月31日在20:35

#3 楼

从希瑟的答案开始,使用简化的函数。

简化的月支票回报:

def month_check(month):
    return 0 < month <= 12


这是day_check的另一种实现,使用普通格式列表而不是字典,并返回简化后的内容:

def day_check(month, day):
    days_in_month = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
    return 0 < day <= days_in_month[month-1]


简化的year_check返回值


#4 楼

这没有回答OP的问题,但想提及dateutil作为将许多不同的日期字符串样式解析为日期(或日期时间)对象的快捷方式。

from dateutil.parser import parse

def valid_date(date_value):
    try:
        return parse(date_value).date()
    except ValueError as e:
        logger.exception(f"Not a valid date: '{date}' - reason: {e}.")


#5 楼

这里确实有很好的答案,但是我想向您展示当我看到评论时(以及每次我想添加评论时)的想法。


想要澄清您的代码。但是,如果您的代码足够清晰,注释将变得多余。并且
阅读多余的内容会使自己感觉自己在浪费时间,所以自然而然地开始注释是不必要的:可删除。在这里给出其他人的答案,所以我将重点关注评论:

def is_month_valid(month):
    return 0 < month <= 12 ## If month is between 1 and 12, return 


如果您要保留自己的评论,因为您发现它比您的评论更清晰代码:使您的代码看起来更像该注释。

例如,您可以尝试思考:

return 0 < month <= 12    ## If month is between 1 and 12, return
return 1 <= month <= 12 
return is_between(month, 1, 12)
return is_the(month).between(1, 12)
return comparable(month).is_between(1, 12)  ## Or you could call is_month_valid(comparable(month)) instead and this gives you...
return month.is_between(1, 12) ## If month is between 1 and 12, return
## Also, if month.is_between(1, 12): return... I think the other answers actually answers why this should be discarded. If not, keep reading and think why.


它们都是有效的解决方案。而且,如果看到的话,最后一个将使该注释真正变得多余:

def is_month_valid(month):
    return month.is_between(1, 12) ## If month is between 1 and 12, return 


那么,为什么要保留此注释?代码很清楚,实际上,该注释使您的代码看起来不整洁,因此除了删除该代码,您别无选择。

是的!...已修复? ... 这是否更好?我们只关注可读性,没有别的吗?不完全是。您必须记住每种解决方案的复杂性。因此:

return 0 < month <= 12    ## If month is between 1 and 12, return


这是一个很好的解决方案。为什么?易于理解,使注释变得多余。而且您不需要其他任何东西就可以使其工作。但是

return 1 <= month <= 12    ## If month is between 1 and 12, return


好吗?现在,此注释更加多余。而且您没有增加复杂性。这就像一个自由更改。另外,这不允许将(0,1)之间的数字作为有效月份。是的,这实际上更好。

return is_between(month, 1, 12) ## If month is between 1 and 12, return


好吧,至少对我来说,这比以前的那些还不清晰。并且需要您创建另一个函数来使其工作。这个功能值得吗?好吧,可能有人指出,如果创建它,则可以在不同的地方重复使用该功能,例如is_between(year, 1, 10000)。并且它允许您在此处集中整数验证...但是,您必须决定。那实际上是编程:做出决定并证明其合理性。 (就像命名事物...)

return is_the(month).between(1, 12) ## If month is between 1 and 12, return


这实际上是多余的并且更易读。但是,现在,您已经创建了一个具有可争论名称的类(另一个函数),并且还关联了另一个函数(例如方法)。但是,这保留了最后解决方案的优点。在这一点上,这开始感觉像是不必要的过度设计解决方案,但我们只是在解释我们的想法。

return comparable(month).is_between(1, 12) ## If month is between 1 and 12, return


为什么is_the可以说是一个名字?认为已经过去了一段时间,您会看到一个名为is_the的函数。我的主要反应是“这东西到底是什么!”。什么也没说。如果不阅读内部代码,就无法知道它的目的。因此,该解决方案修复了一些可争议的名称。现在,month可以与1和12相提并论。但是,这仍然比工程学略有优势(有助于干燥)。

return month.is_between(1, 12) ## If month is between 1 and 12, return


现在,这更好。该代码比以前的解决方案更具冗余性和可读性。但是,它的价格是多少?现在,每次您要调用is_month_valid时,都需要首先发送一个可比较的对象:is_month_valid(comparable(month))。并且不要忘记,您仍然必须创建可比较的和is_between才能使此工作生效。但是,是否需要这种过度设计的解决方案?这将取决于程序中该程序的必要性:这有助于干燥的程度以及将来可能发生的变化。如果不稳定,可能会导致更多问题,而不是要修复的问题。

当然,每种解决方案在特定时间都存在更好的解决方案。要获得他们,您只需要继续对其进行设计,并平衡他们的优缺点。我不会继续这样做,因为我认为现在很清楚。再看一下程序的大小,我确定最后一个解决方案太多了,而第二个解决方案(1 <= month <= 12)简单明了。


并继续这样做您有任何评论,或者很想发表评论。

monthlist1 = [1,3,5,7,8,10,12] ## monthlist for months with 31 days.
monthlist2 = [4,6,9,11] ## monthlist for months with 30 days.
monthlist3 = 2 ## month with month with 28 days.


这些行是否可以替换?

months_with_31_days = [1,3,5,7,8,10,12] ## monthlist for months with 31 days.
months_with_30_days = [4,6,9,11] ## monthlist for months with 30 days.
month_with_28_days = 2 ## month with month with 28 days.


现在有多余的评论,它们是只是弄乱了您的代码。但是,我的重点是展示在面对评论时我的想法。


months_with_31_days = [1,3,5,7,8,10,12]
months_with_30_days = [4,6,9,11]
february = 2 ## And month_with_28_days? Isn't that just february?.


我知道这将消失,但这是评论实际需要吗? for (each) month in monthlist:的清晰程度是否比遍历列表的清晰程度低?我敢肯定,大多数程序员只要阅读一遍,就会认识到这行在做什么。在这种情况下,我认为不需要注释。


for month in monthlist: ## iterate through monthlist.


这是一个有趣的注释,因为您需要一些上下文才能理解此代码。您不能只阅读并知道发生了什么。您需要这张照片:

if month == mon: ## Check if the parameter month equals to any month with 31 days.


现在,您可以理解为什么使用mon而不是月份,以及为什么还有另一个月份变量。这个情况;当您需要一些上下文来理解一行时;是不可取的。幸运的是,您已经在必需的上下文中添加了注释才能理解它,但是,这是否必要?这已通过@ Gerrit0解决:

def is_day_valid(month, day):
    ...
    for mon in months_with_31_days:
        if month == mon: ## Check if the parameter month equals to any month with 31 days.


该评论确实增加了噪音!删除它及其完美。但是,如果需要它怎么办?

if month in months_with_31_days: ## Check if the parameter month equals to any month with 31 days.


现在,如果需要,可以在读取for线时忘记if线。您不在乎是31天的哪个月,但是您知道是否在if块中输入是因为month有31天。而且,您可以像以前一样通过工程解决方案来提高单行的可读性...例如检查if days_in(month) == 31:,它将把先前的行从is_day_valid中移出。尝试使函数简短易读。每个函数中的代码行较少,因此需要更多上下文的可能性较小。


这又如何呢?

for month_with_31_days in months_with_31_days:
    if month == month_with_31_days: ## Check if the parameter month equals to any month with 31 days.


如果您读到该行,就知道您正在要求用户输入并将其保存在名为date的变量中。您实际上在那句话中说的更少。但是,多余的和不必要的。以及如何将用户交互划分为另一个功能。例如:

date = input("Enter the date in mm/dd/yyyy format: ") ## Input date in the given format.


太冗长,在这种情况下是不必要的。但是,我想指出的是,当您开始有多个输入时,您将开始问自己在哪里问这些输入。输出也会发生同样的情况。对于新手来说,一个好习惯是将main函数划分为: (将您的输出分组),最后显示您的解决方案。如果您有与输入相关的问题,则知道从哪里开始寻找。这同样适用于您的问题和最终陈述的核心逻辑。这是一个很好的起点,非常适合解决该问题时不需要用户交互的问题。


但是,认真的开始很不错。我以同样的问题开始了编程生涯。日期验证,我的代码比您的代码差很多:)

#6 楼

做得好

将代码打包到一个main函数和三个辅助函数中,每个函数都有特定的用途。

样式


/>未定义为此任务定义的最重要功能is_valid_mmddyyyy(date)!这将使您的工作可重复使用。
注释过多,主要用于指导Python初学者。代替这些注释,最好以docstrings的形式记录函数的用途。

您几乎不需要在代码中显式地编写TrueFalse。模式


if day >= 1 and day <= 31:
    return True
else:
    return False



…最好写成return 1 <= day <= 31

类似地,


if monthvalidity == True and dayvalidity == True and yearvalidity == True:



…最好写成

if monthvalidity and dayvalidity and yearvalidity:


名称monthcheckdaycheckyearcheck并未充分传达它们在有效或无效输入中的行为。他们会打印错误消息吗?他们会提出例外情况吗?最好将它们命名为is_valid_monthis_valid_dayis_valid_year,以明确它们是谓词(返回无副作用的函数TrueFalse)。
令人讨厌的是monthcheck(month)daycheck(month, day)接受整数作为输入,但yearcheck(year)接受字符串。

monthlist1及其朋友的名称不是最优的,因此需要像## monthlist for months with 31 days这样的注释。同样,monthlist3显然不是列表。稍微好一点的主意:

months_with_31_days = [1, 3, 5, 7, 8, 10, 12]
months_with_30_days = [4, 6, 9, 11]
months_with_28_days = [2]

if month in months_with_31_days:
    return 1 <= day <= 31
if month in months_with_30_days:
    return 1 <= day <= 30
if month in months_with_28_days:
    return 1 <= day <= 28



验证质量


您假设其中将有两个/字符输入。如果零件少或多,则崩溃的原因是ValueError
如果月份或日期不是整数,则崩溃的原因是ValueError
您只关心年份的长短,因此1/1/zero1/1/3.14都被视为有效日期。
如您所说,您无需费心支持both年。但是daycheck函数的设计使其很难增强代码以添加leap年支持。

建议的解决方案

主要挑战是确定其中有多少天。一个月。您的daycheck可以这样做,但是在两个方面都不够:


假定month有效。为了使daycheck能够完成其工作,无论如何它都需要执行查找,因此monthcheck实际上并不需要是一个单独的功能。您只需要指出查询表中没有月份即可。
如果您要支持leap年,仅靠月份是不够的:您还需要知道年份。

请记住,我将以这种方式重新设计程序:

def days_in_month(year, month):
    """
    Given a year and month as integers, return the last day of that month
    (assuming the Gregorian calendar).  Raise ValueError if the month is
    invalid.
    """
    if month == 2:
        is_leap_yr = (year % 4 == 0) and (year % 100 != 0 or year % 400 == 0)
        return 29 if is_leap_yr else 28
    try:
        return {
            1: 31,        3: 31,  4: 30,  5: 31,  6: 30,
            7: 31, 8: 31, 9: 30, 10: 31, 11: 30, 12: 31
        }[month]
    except KeyError:
        raise ValueError('Invalid month')


def is_valid_mmddyyyy(date):
    """
    Test whether the date is a string in mm/dd/yyyy format representing a valid
    date (assuming the Gregorian calendar).
    """
    try:
        mm, dd, yyyy = [int(part) for part in date.split("/")]
        return 0 < yyyy <= 9999 and 0 < dd <= days_in_month(yyyy, mm)
    except ValueError:
        # Too few slashes, too many slashes, non-integer part, or invalid month
        return False


def main():
    date = input("Enter the date in mm/dd/yyyy format: ")
    valid = is_valid_mmddyyyy(date)
    print("The date {0} is {1}.".format(date, "valid" if valid else "invalid"))

if __name__ == '__main__':
    main()


#7 楼

以前发布的答案几乎已经说完了。但是有两个注意事项:



如果您确实使用了诸如monthlist1,monthlist2,monthlist3之类的名称,则应以一致的方式使用它们:如果monthlist1和monthlist2是数字列表,则monthlist3应该也是数字列表。这样一致的命名使读取代码更加容易。所以

monthlist3 = [2]

如何正确计算二月的天数?



# calculate the days of february
if year%4==0:
    if year%100==0:
        if year%400==0:
            days = 29
        else:
            days = 28
    else:
        days = 29
else:
    days = 28


#8 楼

这里的大多数内容已经介绍。虽然年和月的检查相当简单,但日检查却很困难。

def daycheck(year, month, day):
    """
    Check if the day is valid.

    Parameters
    ----------
    year : int
    month : int
    day : int

    Returns
    -------
    is_valid : bool
    """
    assert  1 <= month <= 12
    max_days_per_month = {1: 31, 2: 30, 3:31,
                          4: 30, 5: 31, 6:30, 7: 31,
                          8: 31, 9: 30, 10: 31, 11: 30, 12: 31}
    if (day > max_days_per_month[day]) or (day <= 0):
        return False
    if month != 2:
        # Not quite true, e.g. September 1752 in the UK had less than 20 days.
        return True  
    dst_applies = (year % 4 == 0) and (year % 100 != 0 or year % 400 == 0)
    if not_dst_applies and day >= 29:
        return False
    return True


要学习的东西: />使用字典。它们使事情变得更短/更容易理解
是的,二月曾经有30天:https://en.wikipedia.org/wiki/February_30#Swedish_calendar

是的,如果本地日期是由于日历系统的切换,有效期取决于城市/国家/地区。
一开始就是捕获错误案例/简单案例。您的代码嵌套的深度越少,读取/检查就越容易。