My task was to create a leapyear program. The following was created:

def LeapYear(year):
    if year % 400 == 0:
        return True
    elif year % 100 == 0:
        return False
    elif year % 4 == 0:
        return True
    else:
        return False

print (LeapYear(400))
print (LeapYear(1000))
print (LeapYear(1012))
print (LeapYear(2000))
print (LeapYear(2010))

Very raw and basic as you can see - However this isn't an issue for me at the moment, im just figuring out the forumla's.

I understand this version with if, elif and else statements but i found there was another way of doing the same thing.

def isLeapYear(year):
    """
    return True if year is a leap year
    """
    return (not year%4 and year%100 or not year%400) and True

# Testing ...
print( isLeapYear(400) )
print( isLeapYear(800) )
print( isLeapYear(1600) )
print( isLeapYear(1601) )
print( isLeapYear(2004) )

Which is boolean logic right? Anyway i've figured out that:

not year%4

is the equivalent to

year%4 ==0

And i already understand truth values, the expression True AND True = True, the expression True AND True OR False = True... etc.

I just do not understand what this not is doing, not is negation, so not True is False but when its used in the example below

not year%4

I sort of know what its doing but i don't quite understand the concept of how?!

Some insight to the exact workings of this line would be much apperciated!

(not year%4 and year%100 or not year%400) and True

Recommended Answers

All 14 Replies

Member Avatar for CobRalf

you can translate the "not" statement with "is zero":
not 1 -> "1 is 0" -> false/0
not 0 -> "0 is 0" -> true/1
not 3 -> "3 is 0" -> false/0
not "bla" -> ""bla" is 0" -> false/0

...need some more explanations?
greetz,
CobRalf

...need some more explanations?

erm yes please lol!

why don't you just use return year%4 == 0
because

>>> 0 is False
False
>>> 0 == False
True

Thats what the first version of the program uses. I want to know what the second version of the program is doing, e.g. what the not in this situation is doing. Basically an explaination of how this line of code is working:

(not year%4 and year%100 or not year%400) and True

I know that

if year % 400 == 0:
    return True

means that if the year has no remainder "==0" when divided by 4 return the value True.

May aswell use my double posts constructively lol.

I've been trying to understand what this version of code is acutally doing for ages:

(not year%4 and year%100 or not year%400) and True

you should see the notes ive been writing down on paper LOL

I mean if you refer to Boolean operator priority all not's are done before anything else in this expression?
Then ands' and or's respectively.

So not year%4 is evaluated first and then not year%400? or is it not because thats part of the or expression :S

Lets use an example: not 2004% will give the truth value True because 2004 is dividable by 4? Now where do i go? Because this is true does it skip any part of the expression, the 2004%100 for example?

I hope this gives a better understanding of what im trying to get a better understanding of haha

apparently triple posted it damn it :\

(not year%4 and year%100 or not year%400) and True
4 divides year (divides -> 0 = False -> not False = True)
same time 100 does not divide year (anything but 0 is True)
but if 400 divides the year it is OK
( divides -> 0 = False -> not False = True)


Is word art any clarification? And then alternative one liner

def LeapYear(year):
    if year % 400 == 0:
        return True
    elif year % 100 == 0:
        return False
    elif year % 4 == 0:
        return True
    else:
        return False

def ly(year): return not year % 400 or not( year % 4 or not year % 100)

def ly2(year): return (not year%4 and year%100 or not year%400) and True


for i in [8400,1000,1012,2000]:
    for message,func in (('Long',LeapYear),('Mine',ly),('Oneliner',ly2)):
        print i, message, func(i)
    print 'All same', LeapYear(i)==ly(i)==ly2(i)
    print '-'*20

if you take out and True from your OneLiner, then it stays valid but the value is not Truth value allways.

One of the text cases (normal 4 years rule) is so:

1012 Long True
1012 Mine True
1012 Oneliner 12
All same [B]False[/B]

This is because Python's or/and try to return more useful value than True/False in case of True:

>>> False or 'Genius'
'Genius'
>>> 'Boy' and 'Girl'
'Girl'
>>>

Don't know how it was possible but i've managed to become more confused.

This is because Python's or/and try to return more useful value than True/False in case of True:

>>> False or 'Genius'
      'Genius'
>>> 'Boy' and 'Girl'
      'Girl'
>>>

I can grasp the first part, python wants to return a more useful value than true or false. So in the first example False or 'Genius', Genius is produced because its a string and therefore must be more useful. Correct right?

However the boy and girl example. What are the reasons behind python giving the output girl?

Plus i'm no closer to figuring out what

(not year%4 and year%100 or not year%400) and True

is actually doing. So i think i'll just attempt to carry on and hopefully it'll become more clear the further into python i get :S

EDIT:

Am i correct in saying that by default python will give the value on the left True and the value on the right False?

As this would make your example of boy and girl work correctly. I tested it.

What if we decided to be awkward and have something like 'boy' or 'girl' or 'baby' what turth value is assigned to the extra value baby? false? nothing at all? because it doesn't effect the output from what i can see.

Here's my 2 cents on what (not year%4 and year%100 or not year%400) and True is doing. Python knows it is evaluating a Boolean expression and so it evaluates as much as it needs to, evaluating each component expression according to the rules of precedence, until it get a definite answer to whether the whole expression is true or false. But what it returns seems to be the last component expression evaluated. Accordingly,

>>> 'Boy' and 'Girl'
      'Girl'
>>>

returns 'Girl', not because a girl is more useful than a boy, but because 'Girl' was the last component that needed to be evaluated to determine the value of the whole expression. But that's not all. If instead of 'Girl' we had 3 > 2 the result would be True. Apparently expressions such as 'Girl', 7, etc. will evaluate to True in a Boolean context but otherwise their values are not automatically converted to True or False. But other expressions such as "Fred" > "Barney" are automatically evaluated as Boolean.

>>> 0 and "Genius"
0
>>> 3 < 2 and "Genius"
False
>>>

The False values are in addition to 0
Empty objects:
''
[]
()
{}
set()

But False is actually in reality 0:

>>> if False == 0: print 'False is 0'

False is 0
>>> if True==1: print 'True is 1'

True is 1
>>> 
>

So sentence with and between things has value of the last thing (because every object has not been 0 or one of above) or first of above things which count for False in sentence. (stops on False)

And sentence with or between things returns the first value which is not above mentioned False thing or the last False-like value in the end. (stops on True)

>>> print 0 or None
None
>>> print None or 0
0
>>> print 'a' and notexistingvar

Traceback (most recent call last):
  File "<pyshell#32>", line 1, in <module>
    print 'a' and notexistingvar
NameError: name 'notexistingvar' is not defined
>>> print 'a' or notexistingvar
a
>>> print [] and ''
[]
>>> print [] or ''

>>>

This is call short circuit or and it is useful to avoid program crashing because some function did not return value, for example:

>>> b=None
>>> a=['a','b']
>>> b=b or 0
>>> b
0
>>> a and a[b]
'a'
>>> a=[]
>>> a and a[b]
[]
>>> a[b]

Traceback (most recent call last):
  File "<pyshell#16>", line 1, in <module>
    a[b]
IndexError: list index out of range
>>>

One trick that I like, is to use None for signifying "any value ok/no limit"

>>> sm=4 ## at least 4
>>> big=None ## no upper limit
>>> a='abcdrr'
>>> sm and sm<=len(a) and big and len(a)<=big
>>> print (not sm or sm<=len(a) ) and( not big or len(a)<=big)
True
>>> big=5 ## upper limit
>>> print (not sm or sm<=len(a) ) and( not big or len(a)<=big)
False
>>> sm= None
>>> big=7 ## upper limit
>>> print (not sm or sm<=len(a) ) and( not big or len(a)<=big)
True
>>>

On the issue of code you can understand and is therefore Pythonic, your first code ...

def LeapYear(year):
    if year % 400 == 0:
        return True
    elif year % 100 == 0:
        return False
    elif year % 4 == 0:
        return True
    else:
        return False

Follows the leap-year rule clearly

all years divisible by 400 are leap years
all remaining years divisible by 100 are not leap years
all remaining years divisible by 4 are leap years
all remaining years are not leap years

and should not be considered raw.

This code maybe clever, but leads to a lot of head scratching ...

def isLeapYear1(year):
    """
    return True if year is a leap year
    this syntax needs Python25 or higher
    """
    return False if year%4 else True if year%100 else not year%400

In other words tony i should research short circuiting which i have just found a page on in my book. It's quite brief but thats what googles for i guess! Cheers!


On the issue of code you can understand and is therefore Pythonic, your first code ...

def LeapYear(year):
      if year % 400 == 0:
      return True

      elif year % 100 == 0:
      return False

      elif year % 4 == 0:
      return True

      else:
      return False

Follows the leap-year rule clearly

all years divisible by 400 are leap years
all remaining years divisible by 100 are not leap years
all remaining years divisible by 4 are leap years
all remaining years are not leap years

and should not be considered raw.

This code maybe clever, but leads to a lot of head scratching ...
Python Syntax (Toggle Plain Text)

def isLeapYear1(year):
      """
      return True if year is a leap year

      this syntax needs Python25 or higher

      """
      return False if year%4 else True if year%100 else not year%400

Head scratching is one way of putting it haha. Basically the first version is fine - a normal way of doing this task and then the second version is well a much more advanced way of doing it. Not pythonic as you put it because it's not easily interpreted.

I won't worry about understanding it too much then, i will however try to understand this short circuiting more.

Thanks everyone

The much more advanced way of doing it, as you put it, relies on double negatives that can be confusing.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.