Lesson 02 · 9 min read · Python for placements

Numbers, types and truthiness

Integers that never overflow, floats that cannot hold 0.1, is against ==, and what Python considers false — with the traps interviewers reach for.

Python has few types and strong opinions about them. Learn the opinions and you stop guessing.

Integers do not overflow

In C or Java, an int is a fixed number of bits and adding one to the largest one wraps around. In Python, an integer grows as large as your memory allows:

x = 2 ** 200
print(x)
# 1606938044258990275541962092341162602522202993782792835301376
print(len(str(2 ** 10000)))   # 3011 digits, no error

This is genuinely useful in coding rounds. A factorial or a fibonacci question that would need BigInteger in Java needs nothing in Python. There is no int and long distinction — Python 3 has one integer type.

Floats cannot hold 0.1

This is the single most common "gotcha" question, and the answer has nothing to do with Python:

print(0.1 + 0.2)            # 0.30000000000000004
print(0.1 + 0.2 == 0.3)     # False

A float is stored in binary. 0.1 in binary is a repeating fraction, the same way 1/3 is 0.333… in decimal. It gets cut off, so what is stored is very slightly not 0.1. Every language with IEEE-754 doubles behaves this way — Java, C, JavaScript included.

Two correct fixes, depending on what you need:

# comparing measurements — allow a tolerance
import math
print(math.isclose(0.1 + 0.2, 0.3))          # True

# money — never use float for money
from decimal import Decimal
print(Decimal("0.1") + Decimal("0.2"))       # 0.3  (exactly)
print(Decimal("0.1") + Decimal("0.2") == Decimal("0.3"))   # True

Note Decimal("0.1") with a string argument. Decimal(0.1) passes an already-broken float in and preserves the error faithfully.

Integer division, and what % does to negatives

Python has two division operators:

print(7 / 2)      # 3.5   true division, always a float
print(7 // 2)     # 3     floor division
print(7 % 2)      # 1     remainder

// floors — it rounds towards negative infinity, not towards zero. This surprises anyone coming from C or Java:

print(-7 // 2)    # -4   (not -3)
print(-7 % 2)     #  1   (not -1)
print(7 % -2)     # -1

The rule Python keeps is that a % b always has the sign of b, and (a // b) * b + (a % b) == a always holds. That second identity is why // must floor. In C, -7 / 2 is -3 and -7 % 2 is -1 — a different, equally consistent choice.

If you want C behaviour, ask for it explicitly:

import math
print(math.trunc(-7 / 2))    # -3, rounds towards zero

This is a real source of wrong answers in coding rounds involving negative indices or circular arrays. Test one negative case before you submit.

is versus ==

== compares values. is compares identity — whether two names point at the same object.

a = [1, 2]
b = [1, 2]
print(a == b)   # True  — same contents
print(a is b)   # False — two separate lists

Now the trap. Small integers and short strings are cached by CPython, so identity accidentally agrees with equality:

x = 256
y = 256
print(x is y)    # True — CPython pre-allocates -5..256

x = 257
y = 257
print(x is y)    # False in a fresh interpreter session

The lesson is not the number 256. It is this: never use is to compare values. Use it for exactly one thing:

if value is None:
    ...

None is a singleton — there is precisely one None object in a running program — so is None is both correct and faster than == None. The same applies to is True and is False, though those are rarely what you actually want.

Truthiness

if x: does not require a boolean. Python asks the object whether it considers itself true. These are all false:

False, None, 0, 0.0, 0j, "", [], (), {}, set(), range(0)

Everything else is true — including "0", "False", [0] and {"": 0}, all of which are non-empty and therefore true. This gives you the most idiomatic line in Python:

items = []

if not items:               # idiomatic
    print("nothing here")

if len(items) == 0:         # works, reads like a translation from another language
    print("nothing here")

And one real trap:

def fetch(count=None):
    if not count:                 # WRONG: 0 is a legitimate count
        count = 10
    return count

print(fetch(0))    # 10 — the caller asked for zero and got ten

def fetch_ok(count=None):
    if count is None:             # right: only the missing case
        count = 10
    return count

print(fetch_ok(0))  # 0

not count conflates "no value given" with "the value zero". Reach for is None whenever zero or an empty string is a meaningful input.

Type conversion and checking

int("42")        # 42
int("42abc")     # ValueError
int(3.9)         # 3  — truncates, does not round
round(3.9)       # 4
float("3.5")     # 3.5
str(42)          # "42"
bool("False")    # True — a non-empty string

round() has one more surprise: it uses banker's rounding, breaking ties towards the even number, which reduces bias when you round a large set of values.

print(round(0.5))    # 0
print(round(1.5))    # 2
print(round(2.5))    # 2

To check a type, prefer isinstance over type(x) ==, because isinstance accepts subclasses and a tuple of options:

print(isinstance(True, int))          # True — bool subclasses int
print(isinstance(3, (int, float)))    # True

Yes: True == 1 and True + True == 2. bool is a subclass of int. That is a genuine interview question and the answer is one line long.

What to take into an interview

  • Python 3 integers are arbitrary precision — no overflow, no separate long.
  • 0.1 + 0.2 != 0.3 because of binary floating point, in every language. Use math.isclose, or Decimal for money.
  • // floors towards negative infinity, so -7 // 2 == -4. a % b takes the sign of b.
  • is compares identity, == compares value. Use is only for None.
  • Empty containers, zero and None are falsy. not x and x is None are not the same test.
  • bool is a subclass of int, so True + True == 2.
Lesson 2 of 12 · all 12 are free to read, no account. Course contents