{"id":"logic-chained","slug":"chained-comparisons","title":"Ranges and longer conditions","text":"Checking that a value is between two limits is so common that Python has a shorter way to write it. Instead of `grade >= 4 and grade <= 10`, you can write `4 <= grade <= 10`, as in mathematics:\n\n```python\ngrade = int(input(\"Grade (4 to 10): \"))\nif 4 <= grade <= 10:\n    print(\"Grade saved.\")\nelse:\n    print(\"That's not a valid grade.\")\n```\n\n```input\n11\n```\n\n```output\nGrade (4 to 10): 11\nThat's not a valid grade.\n```\n\nThis is called a **chained comparison**. Python reads `4 <= grade <= 10` as `4 <= grade and grade <= 10`, so the value in the middle is compared with both limits. Any comparison operators can be chained:\n\n```pycon\n>>> x = 7\n>>> 0 < x < 10\nTrue\n>>> 1 <= x <= 5\nFalse\n>>> a = 3\n>>> b = 3\n>>> a == b == 3\nTrue\n```\n\nThe two ways mean exactly the same, and you'll meet both in Python code. The chained one is shorter, and it shows the variable between its two limits, the way you'd write it in a math class.\n\n### Longer conditions\n\nA condition can combine as many parts as it needs, and conditions with `and` and `or` work in an `elif` like anywhere else. In this quiz game, a player who scores at least 90 points within 60 seconds wins gold, a player who manages one of the two wins silver, and 50 points are enough for bronze:\n\n```python\npoints = int(input(\"Points: \"))\nseconds = int(input(\"Time in seconds: \"))\nif points >= 90 and seconds <= 60:\n    print(\"Gold medal\")\nelif points >= 90 or seconds <= 60:\n    print(\"Silver medal\")\nelif points >= 50:\n    print(\"Bronze medal\")\nelse:\n    print(\"No medal this time\")\n```\n\n```input\n95\n75\n```\n\n```output\nPoints: 95\nTime in seconds: 75\nSilver medal\n```\n\nNotice the order. The `and` condition comes first, because it is the strictest one: it needs both parts. A result that has both parts also has at least one of them, so if the `or` condition came first, it would catch every gold result too, and nobody would ever win gold. The order of conditions matters just as much with `and` and `or` as without them.\n","task":"Fill in the blank with a chained comparison, so the program accepts the days of a month, 1 to 31:\n\n```\nDay of the month: 31\nOK\n```\n","kind":"fill","seed":"day = int(input(\"Day of the month: \"))\nif [[1 <= day <= 31]]:\n    print(\"OK\")\nelse:\n    print(\"No month has that day.\")\n","solution":"day = int(input(\"Day of the month: \"))\nif 1 <= day <= 31:\n    print(\"OK\")\nelse:\n    print(\"No month has that day.\")\n","hints":["A chained comparison puts the variable between the two limits: `low <= variable <= high`.","Here the limits are 1 and 31: `1 <= day <= 31`."],"tests":[{"name":"Test 1","stdin":"31\n","expect":"Day of the month: 31\nOK","hidden":false},{"name":"Test 2","stdin":"32\n","expect":"Day of the month: 32\nNo month has that day.","hidden":true},{"name":"Test 3","stdin":"0\n","expect":"Day of the month: 0\nNo month has that day.","hidden":true},{"name":"Test 4","stdin":"1\n","expect":"Day of the month: 1\nOK","hidden":true}],"require":[{"pattern":"\\d\\s*<=?\\s*day\\s*<=?\\s*\\d","message":"Write the range as a chained comparison, with `day` between the two limits."}],"forbid":[],"more":[{"task":"Write a program that asks for the hours and the minutes of a time on a 24-hour clock and tells whether the time is valid. The hours must be from 0 to 23 and the minutes from 0 to 59. Use two chained comparisons.\n\n```\nHours: 23\nMinutes: 59\nThat's a valid time.\n```\n\nOtherwise the program prints `That's not a valid time.`\n","kind":"code","seed":"hours = int(input(\"Hours: \"))\nminutes = int(input(\"Minutes: \"))\n","solution":"hours = int(input(\"Hours: \"))\nminutes = int(input(\"Minutes: \"))\nif 0 <= hours <= 23 and 0 <= minutes <= 59:\n    print(\"That's a valid time.\")\nelse:\n    print(\"That's not a valid time.\")\n","hints":["Each part has its own range: `0 <= hours <= 23` and `0 <= minutes <= 59`.","Both parts must be valid at the same time, so join the two chained comparisons with `and`."],"tests":[{"name":"Test 1","stdin":"23\n59\n","expect":"Hours: 23\nMinutes: 59\nThat's a valid time.","hidden":false},{"name":"Test 2","stdin":"24\n0\n","expect":"Hours: 24\nMinutes: 0\nThat's not a valid time.","hidden":true},{"name":"Test 3","stdin":"7\n65\n","expect":"Hours: 7\nMinutes: 65\nThat's not a valid time.","hidden":true},{"name":"Test 4","stdin":"0\n0\n","expect":"Hours: 0\nMinutes: 0\nThat's a valid time.","hidden":true},{"name":"Test 5","stdin":"-1\n30\n","expect":"Hours: -1\nMinutes: 30\nThat's not a valid time.","hidden":true}],"require":[{"pattern":"\\d\\s*<=?\\s*hours\\s*<=?\\s*\\d","message":"Check the hours with a chained comparison, such as `0 <= hours <= 23`."},{"pattern":"\\d\\s*<=?\\s*minutes\\s*<=?\\s*\\d","message":"Check the minutes with a chained comparison, such as `0 <= minutes <= 59`."}],"forbid":[]},{"task":"Write a program that asks for the lengths of the three sides of a triangle and tells what kind of triangle it is: `Equilateral` when all three sides are equal, `Isosceles` when exactly two are equal, and `Scalene` when all are different.\n\n```\nSide 1: 5\nSide 2: 3\nSide 3: 5\nIsosceles\n```\n","kind":"code","seed":"a = int(input(\"Side 1: \"))\nb = int(input(\"Side 2: \"))\nc = int(input(\"Side 3: \"))\n","solution":"a = int(input(\"Side 1: \"))\nb = int(input(\"Side 2: \"))\nc = int(input(\"Side 3: \"))\nif a == b == c:\n    print(\"Equilateral\")\nelif a == b or b == c or a == c:\n    print(\"Isosceles\")\nelse:\n    print(\"Scalene\")\n","hints":["All three are equal when `a == b == c`, a chained comparison. Check that first, since it's the strictest case.","Two sides are equal when `a == b`, `b == c` or `a == c`: join the three with `or`.","Whatever is left has three different sides."],"tests":[{"name":"Test 1","stdin":"5\n3\n5\n","expect":"Side 1: 5\nSide 2: 3\nSide 3: 5\nIsosceles","hidden":false},{"name":"Test 2","stdin":"4\n4\n4\n","expect":"Side 1: 4\nSide 2: 4\nSide 3: 4\nEquilateral","hidden":true},{"name":"Test 3","stdin":"3\n4\n5\n","expect":"Side 1: 3\nSide 2: 4\nSide 3: 5\nScalene","hidden":true},{"name":"Test 4","stdin":"2\n2\n3\n","expect":"Side 1: 2\nSide 2: 2\nSide 3: 3\nIsosceles","hidden":true},{"name":"Test 5","stdin":"7\n2\n2\n","expect":"Side 1: 7\nSide 2: 2\nSide 3: 2\nIsosceles","hidden":true}],"require":[],"forbid":[]}]}