Python Quirks and Unexpected Behaviors Explained

The Peculiar Rounding Method

When rounding values exactly at the halfway mark, Python uses banker's rounding. This ties outcomes toward the nearest even digit rather than always moving upward.

print(round(9/2))  # 4
print(round(7/2))  # 4
print(round(3/2))  # 2

Both 3.5 and 4.5 steer toward 4, avoiding cumulative upward bias.

Attribute Rseolution and Dunder Methods

Instance attributes typically shadow class attributes, but dunder (double underscore) methods are resolved differently—they bypass the instance dictionary.

class Demo:
    shared = 9
    def __init__(self):
        self.val = 10
        self.__add__ = lambda x, y: x.val + y

    def __add__(self, other):
        return self.val - other

print(Demo() + 4)   # 6

The interpreter ignores the instance-level __add__ and uses the class method, computing 10 - 4.

Comparing Negative and Positive Zero

The max built-in returns the first occurrence when equal values compete.

print(max(-0.0, 0.0))   # -0.0

Since -0.0 and 0.0 are considered equal, the first one encountered wins.

Emptiness in any() and all()

Both predicates treat empty iterables in a lazy manner.

print(all([]))   # True
print(any([]))   # False

all() looks for a falsy element; finding none, it returns True. any() seeks a truthy element; with none present, it yields False.

Negative Multipliers on Sequences

Multiplying a string by a negative integer produces an empty string.

print("Hello" * (-1))   # ''

Negative multipliers are treated as zero, giving an empty sequence of the same type.

The Type–Object Relationship

type and object are mutually dependent, making every entity an object and every class a type instance.

print(isinstance(object, type))   # True
print(isinstance(type, object))   # True
print(isinstance(type, type))     # True
print(isinstance(object, object)) # True

Sum with Empty Iterables

If the iterable is empty, sum() returns the start value without modification.

print(sum(""))           # 0
print(sum("", []))       # []
print(sum("", {}))       # {}

Late Binding in Methods

Python does not resolve names inside a function body until it is called.

class Wrapper:
    def action(self):
        return Wrapper()

instance = Wrapper()
Wrapper = int
print(instance.action())   # 0

During execution, the name Wrapper is rebound to int, so calling action returns int().

Imaginary Parts Across Numeric Types

Every numeric type, including float('inf') and float('nan'), exposes .imag.

items = [0, 5, 10e9, float('inf'), float('nan')]
print(sum(a.imag for a in items))   # 0.0

Integer and Float Precision Collision

Operations mixing large integers with floats can produce misleading comparisons due to floating-point rounding.

value = (1 << 53) + 1   # 9007199254740993
result = value + 1.0
print(result > value)   # False

When adding 1.0, the integer is coerced into a float that rounds to 9007199254740992.0, keeping the final float lower than the original integer.

Tags: python quirks Rounding comparison language-internals

Posted on Sun, 28 Jun 2026 16:43:28 +0000 by lookee