如何确定一个年份是否为闰年?

73

我正在尝试制作一个简单的计算器,用于确定某个年份是否为闰年。

根据定义,闰年可被四整除,但不能被100整除,除非它可被400整除。

这是我的代码:

def leapyr(n):
    if n%4==0 and n%100!=0:
        if n%400==0:
            print(n, "is a leap year.")
    elif n%4!=0:
        print(n, "is not a leap year.")
print(leapyr(1900))

我在Python IDLE中尝试这个代码,但是模块返回了None。我相当确定应该得到1900是一个闰年


5
1900年不是闰年,但2000年是闰年。同时,2000年和1900年都可以被100整除,因此您永远也不会将2000年作为正的结果。 - StarPilot
即使是所谓的专家也可能会犯错:请参阅Excel错误地假定1900年是闰年 - PM 2Ring
1
@PM2Ring,您提供的链接很好地解释了为什么Excel出错。并不是因为他们不知道更好的方法,而是为了兼容性的原因。这是故意的。欲了解更多背景信息,请参见https://www.joelonsoftware.com/2006/06/16/my-first-billg-review/。 - Mark Ransom
2
请注意,您的函数没有返回任何内容,因此尝试“打印”结果将始终打印“None”。 - Mark Ransom
@MarkRansom 当然,Excel只是保持与Lotus 1-2-3的兼容性。我并没有声称Excel的作者们对正确的闰年规则一无所知,我只是复制了那篇微软文章的标题。 - PM 2Ring
显示剩余4条评论
13个回答

204

使用calendar.isleap函数:

import calendar
print(calendar.isleap(1900))

71

作为一行函数:

def is_leap_year(year):
    """Determine whether a year is a leap year."""

    return year % 4 == 0 and (year % 100 != 0 or year % 400 == 0)

这与Mark的答案类似,但是在第一个测试处使用了短路(请注意括号)。

或者,您可以使用标准库的calendar.isleap,它具有完全相同的实现

from calendar import isleap
print(isleap(1900))  # False

23

你对 n 进行了三种不同的测试:

n % 4
n % 100
n % 400

对于1900年:

1900 % 4 == 0
1900 % 100 == 0
1900 % 400 == 300

因为1900%100!=0False,所以1900不进入if语句。

但是,由于1900%4!=0也是False,所以1900也不进入else语句。

这意味着执行到函数的末尾,没有看到return语句,因此返回None

使用以下重写后的函数应该可以正常工作,并根据传递给它的年份数返回FalseTrue。(请注意,如同其他答案中提到的,必须返回结果而不是打印结果。)

def leapyr(n):
    if n % 400 == 0:
        return True
    if n % 100 == 0:
        return False
    if n % 4 == 0:
        return True
    return False
print leapyr(1900)

(来自维基百科的算法)


4
点赞您发现了逻辑错误。不过,帖子作者的代码中并没有包含“返回”语句。修复您在这里指出的错误并不会解决这个问题。 - inspectorG4dget

11
整个公式可以用一个表达式来表示:
def is_leap_year(year):
    return (year % 4 == 0 and year % 100 != 0) or year % 400 == 0

print n, " is a leap year" if is_leap_year(n) else " is not a leap year"

1
这个公式肯定是正确的,但 Eugene 的版本同样易读,而且具有更好的短路效应。然而,在这个层面上进行微观优化在大多数实际代码中都不会被注意到。 :) 调用 calendar.isleap 更易读,尽管由于 Python 函数调用的开销而无疑较慢。 - PM 2Ring
1
@PM2Ring 我并不反对,很高兴看到此时两个答案的投票都比我的高得多。我认为,即使另一种排列方式更有效率,但我的分组更符合闰年规则的精神。 - Mark Ransom

4
你的函数没有返回任何内容,这就是为什么当你在 print 语句中使用它时会得到 None。因此,要么只需像这样调用你的函数:
leapyr(1900)

或者修改你的函数以返回一个值(使用return语句),然后这个值将由你的print语句打印出来。
注意:这并不解决你在闰年计算中可能遇到的任何问题,但回答了你关于为什么在函数调用与print结合使用时会得到None结果的具体问题。
说明:
以下是上述内容的一些简短示例:
def add2(n1, n2):
    print 'the result is:', n1 + n2  # prints but uses no *return* statement

def add2_New(n1, n2):
    return n1 + n2    # returns the result to caller

现在当我调用它们时:

print add2(10, 5)

这个给出:
the result is: 15
None

第一行来自于add2()内部print语句。当我调用没有返回语句的add2()函数时,print语句会输出None。顺便提一下,如果我只是简单地调用add2()函数(注意,没有print语句):
add2()

如果我只是想要打印输出语句的结果,即the result is: 15,而不想要None(这似乎是您试图做的事情),则可以采用以下方法:

与以下内容进行比较:

print add2_New(10, 5)

这将会给出:

15

在这种情况下,结果是在函数add2_New()中计算的,没有打印语句,并返回给调用者,然后依次打印它。

@inspectorG4dget 很好的观点,我刚刚添加了return语句的提及,我会看看能否进一步澄清。谢谢。 - Levon
@JBernardo 我的回答解释了为什么 OP 得到 None.. 在没有返回值的函数中使用 print 语句将会打印出 None。我不是在修复她的闰年代码。 - Levon
1
@JBernardo,缺少返回语句是导致None的原因。如果你怀疑这一点,请加入return 'apple',你就不会得到None。正如我上面所说的,我的答案解释并处理了这个问题,而不是计算。 - Levon
更直接地说,1900年不是闰年。我建议您检查一下如何计算闰年的定义。 - Carl
@Carl 你可能想要在上面的原帖下方直接回复评论。我并没有计算或呈现一个判断闰年的算法,所以你的建议是错误的。 - Levon
显示剩余4条评论

2

闰年是指能被4整除的年份,但以00结尾的世纪年份需要符合能被400整除的条件才是闰年。例如:

if( (year % 4) == 0):
    if ( (year % 100 ) == 0):

        if ( (year % 400) == 0):

            print("{0} is a leap year".format(year))
        else:

            print("{0} is not a leap year".format(year))
    else:

        print("{0} is a leap year".format(year))

else:

    print("{0} is not a leap year".format(year))

0
如果你不想导入日历模块并使用 .isleap 方法,你可以尝试这个:
def isleapyear(year):
    if year % 4 == 0 and (year % 100 != 0 or year % 400 == 0):
        return True
    return False

你可以像这个答案中所示,直接返回语句的结果。 - planetp

0

我尝试用这种方法解决问题,它对我非常有效!!!

我应用的逻辑来判断是否为闰年

print([ (1900 % 4 == 0 ) , (1900 % 400 == 0) , (1900 % 100 == 0) ] )
print([ (2022 % 4 == 0 ) , (2022 % 400 == 0) , (2022 % 100 == 0) ] )
print([ (2000 % 4 == 0 ) , (2000 % 400 == 0) , (2000 % 100 == 0) ] )
print([ (1896 % 4 == 0 ) , (1896 % 400 == 0) , (1896 % 100 == 0) ] )
print([ (2020 % 4 == 0 ) , (2020 % 400 == 0) , (2020 % 100 == 0) ] )

输出:

[True, False, True]
[False, False, False]
[True, True, True]
[True, False, False]
[True, False, False]

我的代码:

yy = 2100

lst = [ (yy % 4  == 0)   , (yy % 400 == 0) , (yy % 100 == 0)  ]

if lst.count(True) in [0,2]:
  print('Not Leap Year')
else:
  print('Leap Year')

输出:

Not Leap Year

如果您在我的代码中发现任何问题,请随时指导我


0
缺失的部分是使用return语句:
def is_year_leap(year):
    if year % 100 == 0:
        if year %  400 == 0:
            return True
        else:
            return False

    elif year % 4 == 0:
        return True

    else:
        return False

x = is_year_leap(int(input('Enter any year: ')))
print(x)

0

一个替代的单行代码:

((((y % 4) + (int((y - (y % 100)) / y) * ((y % 400) / 100))) - 1) < 0)

这是我闲暇时候写的东西,也是与C语言完全兼容的。

(y % 4) >>>通过典型的模4检查,首先检查该年是否为闰年。

(int((y - (y % 100)) / y) >>>然后考虑那些能够被100整除的年份。如果该年是100的倍数,则结果为1,否则结果为0。

((y % 400) / 100))) >>>接下来,将该年份除以400(然后再除以100),如果不能整除,则返回1、2或3。

这两个值

(int(y - (y % 100)) / y)

&

((y % 400) / 100)))

然后将它们相乘。如果年份不可被100整除,这个值总是等于0;否则,如果它可以被100整除但不能被400整除,则结果为1、2或3;如果它既可以被100整除又可以被400整除,则结果为0。

这个值加上 (y % 4),只有当年份是闰年时才会等于0,考虑到边缘情况。

最后,从这个剩余值中减去1,如果年份是闰年,则结果为-1,如果不是,则为0、1或2。将此值与0进行比较,使用小于运算符。如果年份是闰年,则结果为True(如果在C中使用,则为1),否则返回False(如果在C中使用,则为0)。

请注意:这段代码效率极低,难以阅读,对于任何试图遵循正确实践的代码都是一种妨碍。这只是我的一个练习,没有其他意义。

此外,请注意,ZeroDivisionErrors 是输入年份等于0的结果,必须加以考虑。
例如,非常基本的 timeit 比较 1000 次执行显示,与使用简单 if 语句和模数运算符的等效代码块相比,这个一行代码要慢大约5倍。
话虽如此,我还是觉得它非常有趣!

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接