> For the complete documentation index, see [llms.txt](https://brindha.gitbook.io/mylearning/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://brindha.gitbook.io/mylearning/7-days-genai-learning-challenge/puzzle-of-the-day.md).

# Puzzle of the Day

### 5 Similar Python Puzzles

**Puzzle 1**

```python
x = 0.1 + 0.2
print(x == 0.3)
```

```
Answer: False
Reason: Floating point precision makes 0.1 + 0.2 equal
        0.30000000000000004, not exactly 0.3.
```

***

**Puzzle 2**

```python
print(type(10 / 2))
```

```
Answer: <class 'float'>
Reason: The / operator always returns a float in Python 3,
        even when the result is a whole number.
```

***

**Puzzle 3**

```python
x = -7.9
print(int(x))
```

```
Answer: -7
Reason: int() truncates toward zero, not toward negative infinity.
        So -7.9 becomes -7, not -8.
```

***

**Puzzle 4**

```python
x = "5"
y = 3
print(x * y)
```

```
Answer: 555
Reason: Multiplying a string by an integer repeats it,
        rather than doing arithmetic.
```

***

**Puzzle 5**

```python
a = [1, 2, 3]
b = a
b.append(4)
print(a)
```

```
Answer: [1, 2, 3, 4]
Reason: b = a doesn't copy the list. Both variables point to the
        same object in memory, so modifying b also changes a.
```

**Puzzle 6**

```python
print(bool(""))
print(bool(" "))
```

```
Answer: False
        True
Reason: An empty string "" is falsy, but a string with a space " "
        is truthy because it is not empty.
```

***

**Puzzle 7**

```python
x = [1, 2, 3]
print(x[-1])
```

```
Answer: 3
Reason: Negative indexing starts from the end of the list.
        -1 refers to the last element, which is 3.
```

***

**Puzzle 8**

```python
x = (1)
y = (1,)
print(type(x))
print(type(y))
```

```
Answer: <class 'int'>
        <class 'tuple'>
Reason: A single value in parentheses without a trailing comma
        is just an integer. A trailing comma makes it a tuple.
```

***

**Puzzle 9**

```python
def func(a, b=[]):
    b.append(a)
    return b

print(func(1))
print(func(2))
print(func(3))
```

```
Answer: [1]
        [1, 2]
        [1, 2, 3]
Reason: Default mutable arguments are created only once and shared
        across all calls. The same list b is reused every time,
        so it keeps growing with each call.
```

***

**Puzzle 10**

```python
x = 5
def change():
    x = 10
change()
print(x)
```

```
Answer: 5
Reason: The x inside change() is a local variable. It does not
        affect the global x. To modify the global x, you would
        need to use the global keyword inside the function.
```

**Puzzle 11**

```python
print(2 ** 3 ** 2)
```

```
Answer: 512
Reason: The ** operator is right-associative, so it evaluates
        right to left. 3 ** 2 = 9 first, then 2 ** 9 = 512.
```

***

**Puzzle 12**

```python
x = "hello"
print(x[1:3])
```

```
Answer: el
Reason: Slicing is exclusive of the end index.
        x[1:3] gives characters at index 1 and 2, which are 'e' and 'l'.
```

***

**Puzzle 13**

```python
print(1 == 1.0)
print(1 is 1.0)
```

```
Answer: True
        False
Reason: == checks value equality, so 1 and 1.0 are equal.
        is checks identity (same object in memory), and an int
        and float are never the same object.
```

***

**Puzzle 14**

```python
a = [1, 2, 3]
b = a[:]
b.append(4)
print(a)
print(b)
```

```
Answer: [1, 2, 3]
        [1, 2, 3, 4]
Reason: a[:] creates a shallow copy of the list.
        So b is a separate object, and modifying b does not affect a.
```

***

**Puzzle 15**

```python
print(list(range(5, 0)))
print(list(range(5, 0, -1)))
```

```
Answer: []
        [5, 4, 3, 2, 1]
Reason: range(5, 0) with no step defaults to +1, so it produces
        nothing since 5 > 0. Adding step -1 counts down correctly.
```

***

**Puzzle 16**

```python
x = {"a": 1, "b": 2, "a": 3}
print(x)
```

```
Answer: {'a': 3, 'b': 2}
Reason: Dictionaries do not allow duplicate keys.
        The second "a": 3 overwrites the first "a": 1.
```

***

**Puzzle 17**

```python
print("5" + "3")
print(int("5") + int("3"))
```

```
Answer: 53
        8
Reason: "5" + "3" concatenates two strings, giving "53".
        Converting them to int first performs arithmetic addition.
```

***

**Puzzle 18**

```python
x = [1, 2, 3, 4, 5]
print(x[::2])
print(x[::-1])
```

```
Answer: [1, 3, 5]
        [5, 4, 3, 2, 1]
Reason: x[::2] picks every 2nd element starting from index 0.
        x[::-1] reverses the list using a step of -1.
```

***

**Puzzle 19**

```python
def multiply(n):
    return lambda x: x * n

double = multiply(2)
triple = multiply(3)

print(double(5))
print(triple(5))
```

```
Answer: 10
        15
Reason: multiply() returns a lambda that remembers the value of n
        via closure. double captures n=2, triple captures n=3.
```

***

**Puzzle 20**

```python
a = None
b = None
print(a == b)
print(a is b)
```

```
Answer: True
        True
Reason: None is a singleton in Python — there is only one None
        object in memory. So both == and is return True when
        comparing two None values.
```

**Puzzle 21**

```python
print(0.1 + 0.2 == 0.3)
print(round(0.1 + 0.2, 1) == 0.3)
```

```
Answer: False
        True
Reason: Raw floating point arithmetic has precision errors.
        round() fixes this by rounding to 1 decimal place,
        making the comparison accurate.
```

***

**Puzzle 22**

```python
x = [1, 2, 3]
y = [4, 5, 6]
print(x + y)
print(x * 2)
```

```
Answer: [1, 2, 3, 4, 5, 6]
        [1, 2, 3, 1, 2, 3]
Reason: + concatenates two lists together.
        * repeats the list n times, it does not multiply elements.
```

***

**Puzzle 23**

```python
x = "Python"
print(x.lower())
print(x.upper())
print(x)
```

```
Answer: python
        PYTHON
        Python
Reason: Strings are immutable in Python. lower() and upper()
        return new strings but do not modify the original string x.
```

***

**Puzzle 24**

```python
a = True
b = False
print(a + b)
print(a + a)
print(b + b)
```

```
Answer: 1
        2
        0
Reason: In Python, bool is a subclass of int.
        True equals 1 and False equals 0 in arithmetic operations.
```

***

**Puzzle 25**

```python
x = [1, 2, 3, 4, 5]
x.remove(3)
print(x)
x.pop(1)
print(x)
```

```
Answer: [1, 2, 4, 5]
        [1, 4, 5]
Reason: remove() deletes the first element matching the given value.
        pop() removes the element at the given index (not value),
        so pop(1) removes index 1, which is 2.
```

***

**Puzzle 26**

```python
def outer():
    x = 10
    def inner():
        nonlocal x
        x = 20
    inner()
    print(x)

outer()
```

```
Answer: 20
Reason: nonlocal allows inner() to modify x from the enclosing
        outer() scope. Without nonlocal, x = 20 would create
        a new local variable inside inner().
```

***

**Puzzle 27**

```python
print(type([])) 
print(type(()))
print(type({}))
print(type({1, 2, 3}))
```

```
Answer: <class 'list'>
        <class 'tuple'>
        <class 'dict'>
        <class 'set'>
Reason: {} alone creates a dict, not a set.
        To create an empty set, you must use set().
        {1, 2, 3} creates a set because it has values inside.
```

***

**Puzzle 28**

```python
nums = [1, 2, 3, 4, 5]
squared = [x**2 for x in nums if x % 2 == 0]
print(squared)
```

```
Answer: [4, 16]
Reason: The list comprehension filters only even numbers (2 and 4),
        then squares each one. 2**2 = 4 and 4**2 = 16.
```

***

**Puzzle 29**

```python
a = "hello"
b = "hello"
print(a == b)
print(a is b)
```

```
Answer: True
        True
Reason: Python interns short strings, meaning identical string
        literals share the same memory object. So both == and is
        return True. This may not hold for all strings at runtime.
```

***

**Puzzle 30**

```python
x = [1, [2, 3], 4]
y = x[:]
y[1].append(99)
print(x)
print(y)
```

```
Answer: [1, [2, 3, 99], 4]
        [1, [2, 3, 99], 4]
Reason: x[:] creates a shallow copy. The outer list is copied,
        but the inner list [2, 3] is still shared between x and y.
        Modifying y[1] also modifies x[1] because they point
        to the same inner list object.
```

**Puzzle 31**

```python
x = "hello world"
print(x.split())
print(x.split("o"))
```

```
Answer: ['hello', 'world']
        ['hell', ' w', 'rld']
Reason: split() with no argument splits on whitespace.
        split("o") splits at every occurrence of "o",
        removing it and returning the parts in between.
```

***

**Puzzle 32**

```python
a = [1, 2, 3]
b = [1, 2, 3]
print(a == b)
print(a is b)
```

```
Answer: True
        False
Reason: == checks if the values are equal, which they are.
        is checks if both variables point to the same object
        in memory. Lists are not interned, so they are different
        objects even if they have the same values.
```

***

**Puzzle 33**

```python
x = {"a": 1, "b": 2, "c": 3}
print(list(x.keys()))
print(list(x.values()))
print(list(x.items()))
```

```
Answer: ['a', 'b', 'c']
        [1, 2, 3]
        [('a', 1), ('b', 2), ('c', 3)]
Reason: keys() returns all dictionary keys, values() returns all
        values, and items() returns key-value pairs as tuples.
        All are wrapped in list() to display them clearly.
```

***

**Puzzle 34**

```python
print(all([True, True, False]))
print(any([False, False, True]))
print(all([]))
print(any([]))
```

```
Answer: False
        True
        True
        False
Reason: all() returns True only if every element is truthy.
        any() returns True if at least one element is truthy.
        all([]) returns True (vacuous truth) and
        any([]) returns False because there is nothing truthy.
```

***

**Puzzle 35**

```python
x = 10
y = 3
print(x // y)
print(x % y)
print(x / y)
```

```
Answer: 3
        1
        3.3333333333333335
Reason: // is floor division, returning the integer quotient.
        % is the modulus, returning the remainder.
        / always returns a float with full precision.
```

***

**Puzzle 36**

```python
a = (1, 2, 3)
a += (4, 5)
print(a)
```

```
Answer: (1, 2, 3, 4, 5)
Reason: Tuples are immutable, so += does not modify the original.
        Instead, it creates a brand new tuple by concatenating
        both tuples and reassigns it to a.
```

***

**Puzzle 37**

```python
x = [5, 3, 8, 1, 9, 2]
print(sorted(x))
print(sorted(x, reverse=True))
print(x)
```

```
Answer: [1, 2, 3, 5, 8, 9]
        [9, 8, 5, 3, 2, 1]
        [5, 3, 8, 1, 9, 2]
Reason: sorted() returns a new sorted list and does not modify
        the original. reverse=True sorts in descending order.
        The original list x remains unchanged.
```

***

**Puzzle 38**

```python
def counter():
    count = 0
    def increment():
        nonlocal count
        count += 1
        return count
    return increment

c = counter()
print(c())
print(c())
print(c())
```

```
Answer: 1
        2
        3
Reason: counter() returns the inner increment() function which
        remembers count via closure. Each call to c() increments
        and returns the same count variable from the enclosing scope.
```

***

**Puzzle 39**

```python
x = [1, 2, 3, 4, 5]
print(x[1:4])
print(x[:3])
print(x[3:])
print(x[:])
```

```
Answer: [2, 3, 4]
        [1, 2, 3]
        [4, 5]
        [1, 2, 3, 4, 5]
Reason: x[1:4] slices from index 1 up to (not including) index 4.
        x[:3] starts from the beginning up to index 3.
        x[3:] starts from index 3 to the end.
        x[:] returns a full shallow copy of the list.
```

***

**Puzzle 40**

```python
class Dog:
    sound = "Woof"

    def speak(self):
        return f"I say {self.sound}"

d1 = Dog()
d2 = Dog()
d2.sound = "Bark"

print(d1.speak())
print(d2.speak())
print(Dog.sound)
```

```
Answer: I say Woof
        I say Bark
        Woof
Reason: sound is a class variable shared by all instances.
        When d2.sound = "Bark" is assigned, it creates an
        instance variable on d2 only, shadowing the class variable.
        d1 and Dog.sound remain unchanged.
```

**Puzzle 41**

```python
x = "  hello world  "
print(x.strip())
print(x.lstrip())
print(x.rstrip())
```

```
Answer: "hello world"
        "hello world  "
        "  hello world"
Reason: strip() removes whitespace from both ends.
        lstrip() removes only from the left side.
        rstrip() removes only from the right side.
        None of them modify whitespace in the middle.
```

***

**Puzzle 42**

```python
a = [1, 2, 3]
b = [4, 5, 6]
c = zip(a, b)
print(list(c))
```

```
Answer: [(1, 4), (2, 5), (3, 6)]
Reason: zip() pairs elements from both lists by index into tuples.
        It stops at the shortest list if lengths differ.
        The result is a zip object, so list() is used to display it.
```

***

**Puzzle 43**

```python
x = {"a": 1}
x.update({"b": 2, "a": 99})
print(x)
```

```
Answer: {'a': 99, 'b': 2}
Reason: update() merges another dictionary into x.
        If a key already exists, its value is overwritten.
        So "a": 1 gets replaced by "a": 99, and "b": 2 is added.
```

***

**Puzzle 44**

```python
print(list(map(lambda x: x**2, [1, 2, 3, 4, 5])))
```

```
Answer: [1, 4, 9, 16, 25]
Reason: map() applies the lambda function to every element
        in the list. The lambda squares each element.
        list() converts the map object into a readable list.
```

***

**Puzzle 45**

```python
print(list(filter(lambda x: x % 2 == 0, [1, 2, 3, 4, 5, 6])))
```

```
Answer: [2, 4, 6]
Reason: filter() keeps only the elements for which the lambda
        returns True. x % 2 == 0 is True only for even numbers,
        so odd numbers are filtered out.
```

***

**Puzzle 46**

```python
x = [[1, 2], [3, 4], [5, 6]]
flat = [num for row in x for num in row]
print(flat)
```

```
Answer: [1, 2, 3, 4, 5, 6]
Reason: This is a nested list comprehension that flattens a 2D list.
        The outer loop iterates over each row, and the inner loop
        iterates over each element in that row.
```

***

**Puzzle 47**

```python
def greet(name, msg="Hello"):
    return f"{msg}, {name}!"

print(greet("Alice"))
print(greet("Bob", "Hi"))
print(greet(msg="Hey", name="Charlie"))
```

```
Answer: Hello, Alice!
        Hi, Bob!
        Hey, Charlie!
Reason: msg has a default value of "Hello" if not provided.
        Positional arguments fill left to right.
        Keyword arguments can be passed in any order.
```

***

**Puzzle 48**

```python
x = {1, 2, 3, 4}
y = {3, 4, 5, 6}
print(x & y)
print(x | y)
print(x - y)
print(x ^ y)
```

```
Answer: {3, 4}
        {1, 2, 3, 4, 5, 6}
        {1, 2}
        {1, 2, 5, 6}
Reason: & returns common elements (intersection).
        | returns all unique elements (union).
        - returns elements in x but not in y (difference).
        ^ returns elements in either but not both (symmetric difference).
```

***

**Puzzle 49**

```python
import sys
a = []
b = [1, 2, 3]
c = "hello"
print(sys.getsizeof(a))
print(type(sys.getsizeof(b)))
print(type(c))
```

```
Answer: 56  (may vary by system)
        <class 'int'>
        <class 'str'>
Reason: sys.getsizeof() returns the memory size of an object
        in bytes as an integer. An empty list still occupies memory.
        The exact size of a may vary depending on the system and
        Python version.
```

***

**Puzzle 50**

```python
class Counter:
    count = 0

    def __init__(self):
        Counter.count += 1

c1 = Counter()
c2 = Counter()
c3 = Counter()

print(Counter.count)
print(c1.count)
print(c2.count)
```

```
Answer: 3
        3
        3
Reason: count is a class variable shared across all instances.
        Each time __init__ runs, it increments the class-level count.
        Accessing count via c1 or c2 reads the same class variable
        since no instance variable named count was created.
```

**Puzzle 51**

```python
x = "python"
print(x.capitalize())
print(x.title())
print(x.swapcase())
```

```
Answer: Python
        Python
        PYTHON
Reason: capitalize() makes only the first letter uppercase.
        title() makes the first letter of each word uppercase.
        swapcase() flips all lowercase to uppercase and vice versa.
        Since "python" is all lowercase, swapcase() makes it all uppercase.
```

***

**Puzzle 52**

```python
a = [1, 2, 3]
a.insert(1, 99)
print(a)
a.insert(-1, 55)
print(a)
```

```
Answer: [1, 99, 2, 3]
        [1, 99, 2, 55, 3]
Reason: insert(1, 99) places 99 at index 1, shifting others right.
        insert(-1, 55) inserts 55 before the last element,
        not at the very end. To append at the end, use append().
```

***

**Puzzle 53**

```python
from functools import reduce
nums = [1, 2, 3, 4, 5]
result = reduce(lambda a, b: a + b, nums)
print(result)
```

```
Answer: 15
Reason: reduce() applies the lambda cumulatively from left to right.
        It computes ((((1+2)+3)+4)+5) = 15.
        This is essentially summing all elements in the list.
```

***

**Puzzle 54**

```python
x = "hello"
print(x.center(11))
print(x.center(11, "*"))
print(x.ljust(10, "-"))
print(x.rjust(10, "-"))
```

```
Answer: "   hello   "
        "***hello***"
        "hello-----"
        "-----hello"
Reason: center() pads both sides to reach the given width.
        A fill character like "*" replaces the default space.
        ljust() pads the right side, rjust() pads the left side.
```

***

**Puzzle 55**

```python
a = [3, 1, 4, 1, 5, 9, 2, 6]
print(max(a))
print(min(a))
print(sum(a))
print(len(a))
```

```
Answer: 9
        1
        31
        8
Reason: max() returns the largest element, min() the smallest.
        sum() adds all elements: 3+1+4+1+5+9+2+6 = 31.
        len() returns the total number of elements in the list.
```

***

**Puzzle 56**

```python
def gen():
    yield 1
    yield 2
    yield 3

g = gen()
print(next(g))
print(next(g))
print(next(g))
print(next(g))
```

```
Answer: 1
        2
        3
        StopIteration Error
Reason: yield makes gen() a generator function. Each call to next()
        resumes execution until the next yield. After the last yield,
        calling next() again raises a StopIteration exception
        because there are no more values to produce.
```

***

**Puzzle 57**

```python
x = [1, 2, 3, 4, 5]
y = x
x = [6, 7, 8]
print(x)
print(y)
```

```
Answer: [6, 7, 8]
        [1, 2, 3, 4, 5]
Reason: y = x makes y point to the original list [1, 2, 3, 4, 5].
        x = [6, 7, 8] creates a brand new list and reassigns x to it.
        y still points to the original list, so it remains unchanged.
```

***

**Puzzle 58**

```python
a = {"x": 1, "y": 2}
b = {"y": 3, "z": 4}
c = {**a, **b}
print(c)
```

```
Answer: {'x': 1, 'y': 3, 'z': 4}
Reason: ** unpacks dictionaries. When merging, if a key exists
        in both, the later dictionary wins. So "y": 2 from a
        is overwritten by "y": 3 from b in the final result.
```

***

**Puzzle 59**

```python
x = range(10)
print(2 in x)
print(11 in x)
print(type(x))
```

```
Answer: True
        False
        <class 'range'>
Reason: range() does not create a list in memory, it is a lazy
        object that generates numbers on demand. The in operator
        works efficiently on range without expanding it into a list.
```

***

**Puzzle 60**

```python
class Animal:
    def speak(self):
        return "Some sound"

class Dog(Animal):
    def speak(self):
        return "Woof"

class Cat(Animal):
    def speak(self):
        return "Meow"

animals = [Dog(), Cat(), Animal()]
for a in animals:
    print(a.speak())
```

```
Answer: Woof
        Meow
        Some sound
Reason: This is polymorphism in action. Each object calls its own
        version of speak() based on its class, even though they are
        all stored together in a list and iterated the same way.
        Dog and Cat override the parent Animal's speak() method.
```

**Puzzle 61**

```python
x = "hello"
print(x.replace("l", "r"))
print(x.replace("l", "r", 1))
print(x)
```

```
Answer: herro
        herlo
        hello
Reason: replace() substitutes all occurrences by default.
        The third argument limits how many replacements are made.
        replace("l", "r", 1) replaces only the first "l".
        Strings are immutable, so x remains unchanged.
```

***

**Puzzle 62**

```python
a = [1, 2, 3, 4, 5]
b = a[1:4:2]
print(b)
```

```
Answer: [2, 4]
Reason: a[1:4:2] slices from index 1 up to (not including) index 4
        with a step of 2. So it picks index 1 (value 2)
        and index 3 (value 4), skipping index 2.
```

***

**Puzzle 63**

```python
x = None
print(x is None)
print(x == None)
print(bool(x))
```

```
Answer: True
        True
        False
Reason: is None is the recommended way to check for None.
        == None also works but is discouraged by PEP 8.
        bool(None) returns False because None is falsy in Python.
```

***

**Puzzle 64**

```python
def power(base, exp=2):
    return base ** exp

print(power(3))
print(power(3, 3))
print(power(exp=4, base=2))
```

```
Answer: 9
        27
        16
Reason: power(3) uses the default exp=2, so 3**2 = 9.
        power(3, 3) overrides the default, so 3**3 = 27.
        Keyword arguments can be passed in any order,
        so power(exp=4, base=2) computes 2**4 = 16.
```

***

**Puzzle 65**

```python
x = [1, 2, 3]
y = [4, 5, 6]
print(list(zip(x, y)))
a, b = zip(*zip(x, y))
print(list(a))
print(list(b))
```

```
Answer: [(1, 4), (2, 5), (3, 6)]
        [1, 2, 3]
        [4, 5, 6]
Reason: zip(x, y) pairs elements together.
        zip(*zip(x, y)) unzips them back by unpacking the pairs
        and zipping again, effectively transposing the structure
        and recovering the original lists.
```

***

**Puzzle 66**

```python
x = "12345"
print(x.isdigit())
print(x.isalpha())
print(x.isalnum())
y = "hello123"
print(y.isalpha())
print(y.isalnum())
```

```
Answer: True
        False
        True
        False
        True
Reason: isdigit() returns True if all characters are digits.
        isalpha() returns True if all characters are alphabetic.
        isalnum() returns True if all characters are alphanumeric.
        "hello123" fails isalpha() because it has digits,
        but passes isalnum() because it has only letters and digits.
```

***

**Puzzle 67**

```python
from collections import Counter
text = "hello world"
c = Counter(text)
print(c.most_common(3))
```

```
Answer: [('l', 3), ('o', 2), ('h', 1)]
Reason: Counter counts the frequency of each character in the string.
        Spaces are also counted as characters.
        most_common(3) returns the top 3 most frequent characters
        as a list of (element, count) tuples in descending order.
```

***

**Puzzle 68**

```python
x = [1, 2, 3]

def modify(lst):
    lst.append(4)
    lst = [9, 9, 9]

modify(x)
print(x)
```

```
Answer: [1, 2, 3, 4]
Reason: lst.append(4) modifies the original list because lst
        still points to the same object as x at that point.
        lst = [9, 9, 9] reassigns the local variable lst to a
        new list, but this does not affect x outside the function.
```

***

**Puzzle 69**

```python
a = (1, 2, [3, 4])
a[2].append(5)
print(a)
```

```
Answer: (1, 2, [3, 4, 5])
Reason: Tuples are immutable, meaning you cannot reassign their
        elements. However, if an element is a mutable object like
        a list, the contents of that object can still be changed.
        a[2] points to the list [3, 4], so append(5) modifies it.
```

***

**Puzzle 70**

```python
x = [1, 2, 3, 4, 5]
even = list(filter(lambda n: n % 2 == 0, x))
squared = list(map(lambda n: n ** 2, even))
total = sum(squared)
print(total)
```

```
Answer: 20
Reason: filter() keeps only even numbers: [2, 4].
        map() squares each: [4, 16].
        sum() adds them together: 4 + 16 = 20.
        This is a classic functional programming pipeline in Python.
```

**Puzzle 71**

```python
x = "hello world"
print(x.find("o"))
print(x.find("z"))
print(x.index("o"))
print(x.index("z"))
```

```
Answer: 4
        -1
        4
        ValueError
Reason: find() returns the index of the first match, or -1 if
        not found. index() does the same but raises a ValueError
        if the substring is not found instead of returning -1.
        Always use find() when the substring might not exist.
```

***

**Puzzle 72**

```python
a = [1, 2, 3]
b = [1, 2, 3]
c = a

print(a == b)
print(a is b)
print(a is c)
print(id(a) == id(c))
```

```
Answer: True
        False
        True
        True
Reason: a == b compares values, which are equal.
        a is b checks memory identity — they are different objects.
        c = a makes c point to the same object as a, so a is c
        is True and their id() values are identical.
```

***

**Puzzle 73**

```python
x = [1, 2, 3, 4, 5]
x[1:3] = [20, 30, 40]
print(x)
```

```
Answer: [1, 20, 30, 40, 4, 5]
Reason: Slice assignment replaces the slice x[1:3] (elements at
        index 1 and 2) with the new list [20, 30, 40].
        The replacement list can be a different length than
        the slice being replaced, expanding or shrinking the list.
```

***

**Puzzle 74**

```python
from collections import defaultdict

d = defaultdict(int)
words = ["apple", "banana", "apple", "cherry", "banana", "apple"]
for word in words:
    d[word] += 1

print(dict(d))
```

```
Answer: {'apple': 3, 'banana': 2, 'cherry': 1}
Reason: defaultdict(int) initializes missing keys with 0 by default.
        So d[word] += 1 works without needing to check if the key
        exists first. This is a clean way to count word frequencies
        without KeyError exceptions.
```

***

**Puzzle 75**

```python
print(bool(0))
print(bool(0.0))
print(bool(""))
print(bool([]))
print(bool({}))
print(bool(None))
```

```
Answer: False
        False
        False
        False
        False
        False
Reason: All of these are falsy values in Python.
        0, 0.0, empty string, empty list, empty dict,
        and None all evaluate to False in a boolean context.
        Any non-zero number, non-empty collection is truthy.
```

***

**Puzzle 76**

```python
def fibonacci(n):
    a, b = 0, 1
    for _ in range(n):
        yield a
        a, b = b, a + b

print(list(fibonacci(7)))
```

```
Answer: [0, 1, 1, 2, 3, 5, 8]
Reason: This is a generator function that yields Fibonacci numbers.
        a, b = b, a + b swaps values simultaneously without a temp
        variable. yield pauses execution and returns a value each
        time, producing the sequence lazily on demand.
```

***

**Puzzle 77**

```python
x = "python is awesome"
words = x.split()
result = " ".join(reversed(words))
print(result)
```

```
Answer: awesome is python
Reason: split() breaks the string into a list of words.
        reversed() returns an iterator in reverse order.
        join() combines them back into a single string with
        spaces, effectively reversing the word order.
```

***

**Puzzle 78**

```python
class MyClass:
    def __init__(self, val):
        self.val = val

    def __str__(self):
        return f"MyClass with val={self.val}"

    def __repr__(self):
        return f"MyClass({self.val})"

obj = MyClass(42)
print(str(obj))
print(repr(obj))
```

```
Answer: MyClass with val=42
        MyClass(42)
Reason: __str__ is called by print() and str(), meant for
        human-readable output. __repr__ is meant for developers
        and debugging, ideally showing how to recreate the object.
        If __str__ is not defined, Python falls back to __repr__.
```

***

**Puzzle 79**

```python
x = [5, 3, 8, 6, 1]
x.sort(key=lambda n: -n)
print(x)
```

```
Answer: [8, 6, 5, 3, 1]
Reason: sort() sorts in ascending order by default. Using
        key=lambda n: -n negates each value before comparing,
        which effectively sorts in descending order.
        This is equivalent to sort(reverse=True).
```

***

**Puzzle 80**

```python
try:
    x = int("hello")
except ValueError as e:
    print("Caught:", e)
except TypeError as e:
    print("Type error:", e)
finally:
    print("Always runs")
```

```
Answer: Caught: invalid literal for int() with base 10: 'hello'
        Always runs
Reason: int("hello") raises a ValueError because "hello" cannot
        be converted to an integer. The except ValueError block
        catches it and prints the error message. The finally block
        always executes regardless of whether an exception occurred.
```

**Puzzle 81**

```python
x = [1, 2, 3, 4, 5]
print(x[10:20])
print(x[2:100])
```

```
Answer: []
        [3, 4, 5]
Reason: Slicing never raises an IndexError even if the indices
        are out of range. x[10:20] returns an empty list because
        there are no elements from index 10 onwards.
        x[2:100] safely returns all elements from index 2 to end.
```

***

**Puzzle 82**

```python
a = "hello"
b = "world"
print(f"{a} {b}")
print("{} {}".format(a, b))
print("%s %s" % (a, b))
```

```
Answer: hello world
        hello world
        hello world
Reason: All three are valid string formatting methods in Python.
        f-strings are the most modern and readable (Python 3.6+).
        format() is flexible and widely used.
        % formatting is the oldest style, inherited from C.
```

***

**Puzzle 83**

```python
from itertools import chain
a = [1, 2, 3]
b = [4, 5, 6]
c = [7, 8, 9]
result = list(chain(a, b, c))
print(result)
```

```
Answer: [1, 2, 3, 4, 5, 6, 7, 8, 9]
Reason: chain() from itertools links multiple iterables together
        into a single iterator without creating intermediate lists.
        It is more memory efficient than using + to concatenate
        multiple lists, especially for large datasets.
```

***

**Puzzle 84**

```python
x = {"a": 1, "b": 2, "c": 3}
y = {k: v * 2 for k, v in x.items()}
print(y)
```

```
Answer: {'a': 2, 'b': 4, 'c': 6}
Reason: This is a dictionary comprehension. It iterates over
        key-value pairs from x using items() and creates a new
        dictionary where each value is doubled. The keys remain
        the same while only the values are transformed.
```

***

**Puzzle 85**

```python
import os
print(type(os.environ))
print("PATH" in os.environ)
print(os.environ.get("FAKE_VAR", "default"))
```

```
Answer: <class 'os._Environ'>
        True
        default
Reason: os.environ is a special mapping object that holds
        environment variables. "PATH" exists on all systems.
        get() safely retrieves a value or returns the provided
        default if the key does not exist, avoiding KeyError.
```

***

**Puzzle 86**

```python
def decorator(func):
    def wrapper(*args, **kwargs):
        print("Before")
        result = func(*args, **kwargs)
        print("After")
        return result
    return wrapper

@decorator
def greet(name):
    print(f"Hello, {name}!")

greet("Alice")
```

```
Answer: Before
        Hello, Alice!
        After
Reason: @decorator is syntactic sugar for greet = decorator(greet).
        wrapper() runs before and after the original function.
        *args and **kwargs allow the wrapper to accept any
        arguments and pass them to the original function.
```

***

**Puzzle 87**

```python
x = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
result = [n for n in x if n % 2 == 0 if n % 3 == 0]
print(result)
```

```
Answer: [6]
Reason: Multiple if conditions in a list comprehension act like
        AND logic. The element must satisfy both conditions:
        divisible by 2 (even) AND divisible by 3.
        Only 6 satisfies both conditions in the range 1 to 10.
```

***

**Puzzle 88**

```python
class MyList:
    def __init__(self, data):
        self.data = data

    def __len__(self):
        return len(self.data)

    def __getitem__(self, index):
        return self.data[index]

m = MyList([10, 20, 30, 40])
print(len(m))
print(m[2])
print(m[-1])
```

```
Answer: 4
        30
        40
Reason: __len__ enables the len() function on custom objects.
        __getitem__ enables index access using square brackets.
        Implementing these dunder methods makes the custom class
        behave like a built-in sequence supporting negative indexing.
```

***

**Puzzle 89**

```python
a = [1, 2, 3]
b = [4, 5, 6]
c = [a, b]
print(c[0][1])
print(c[1][2])
c[0][0] = 99
print(a)
```

```
Answer: 2
        6
        [99, 2, 3]
Reason: c is a list containing references to a and b, not copies.
        c[0][1] accesses the second element of a, which is 2.
        c[1][2] accesses the third element of b, which is 6.
        Modifying c[0][0] directly modifies a since they share
        the same object in memory.
```

***

**Puzzle 90**

```python
def make_multiplier(n):
    return lambda x: x * n

multipliers = [make_multiplier(i) for i in range(1, 6)]

for m in multipliers:
    print(m(10))
```

```
Answer: 10
        20
        30
        40
        50
Reason: make_multiplier(n) returns a lambda that captures n via
        closure. Unlike using lambda i=i inside a loop, each call
        to make_multiplier() creates a fresh closure with its own n.
        So each multiplier correctly remembers its own value of n.
```

**Puzzle 91**

```python
x = "abcdef"
print(x[::2])
print(x[1::2])
print(x[::-2])
```

```
Answer: ace
        bdf
        fdb
Reason: x[::2] starts from the beginning and picks every 2nd character.
        x[1::2] starts from index 1 and picks every 2nd character.
        x[::-2] starts from the end and picks every 2nd character
        in reverse, giving f, d, b.
```

***

**Puzzle 92**

```python
from collections import namedtuple

Point = namedtuple("Point", ["x", "y"])
p = Point(3, 4)

print(p.x)
print(p.y)
print(p[0])
print(p._asdict())
```

```
Answer: 3
        4
        3
        {'x': 3, 'y': 4}
Reason: namedtuple creates a tuple subclass with named fields.
        Fields can be accessed by name (p.x) or by index (p[0]).
        _asdict() converts the namedtuple into an ordered dictionary.
        Like tuples, namedtuples are immutable.
```

***

**Puzzle 93**

```python
x = 5
y = 10
x, y = y, x
print(x)
print(y)
```

```
Answer: 10
        5
Reason: Python evaluates the right side fully before assigning.
        y, x creates a temporary tuple (10, 5) which is then
        unpacked into x and y. This is the Pythonic way to swap
        two variables without needing a temporary variable.
```

***

**Puzzle 94**

```python
class Animal:
    def __init__(self, name):
        self.name = name

    def __eq__(self, other):
        return self.name == other.name

a1 = Animal("Dog")
a2 = Animal("Dog")
a3 = Animal("Cat")

print(a1 == a2)
print(a1 == a3)
print(a1 is a2)
```

```
Answer: True
        False
        False
Reason: __eq__ is overridden to compare by name attribute.
        a1 and a2 have the same name so == returns True.
        a1 and a3 have different names so == returns False.
        is still checks memory identity, so a1 is a2 is False
        because they are two separate objects in memory.
```

***

**Puzzle 95**

```python
nums = [4, 2, 7, 1, 9, 3]
print(sorted(nums, key=lambda x: x % 3))
```

```
Answer: [9, 4, 1, 7, 2, 3]
Reason: sorted() uses the remainder when divided by 3 as the key.
        9%3=0, 4%3=1, 1%3=1, 7%3=1, 2%3=2, 3%3=0.
        Elements are sorted by remainder: 0 first, then 1, then 2.
        Within the same remainder group, original order is preserved
        because Python's sort is stable.
```

***

**Puzzle 96**

```python
def outer(x):
    def inner(y):
        def innermost(z):
            return x + y + z
        return innermost
    return inner

result = outer(1)(2)(3)
print(result)
```

```
Answer: 6
Reason: This is a curried function with three levels of nesting.
        outer(1) returns inner with x=1 captured.
        inner(2) returns innermost with y=2 captured.
        innermost(3) computes x + y + z = 1 + 2 + 3 = 6.
        Each level captures its argument via closure.
```

***

**Puzzle 97**

```python
x = [1, 2, 3, 4, 5]
y = iter(x)

print(next(y))
print(next(y))
x.append(6)
print(next(y))
print(list(y))
```

```
Answer: 1
        2
        3
        [4, 5, 6]
Reason: iter() creates an iterator over the list.
        next() advances it one step at a time.
        Since the iterator references the original list, appending 6
        is reflected when consuming the remaining elements.
        list(y) exhausts the remaining items in the iterator.
```

***

**Puzzle 98**

```python
import math

print(math.floor(4.9))
print(math.ceil(4.1))
print(math.trunc(4.9))
print(math.trunc(-4.9))
print(math.floor(-4.1))
```

```
Answer: 4
        5
        4
        -4
        -5
Reason: floor() rounds down toward negative infinity.
        ceil() rounds up toward positive infinity.
        trunc() cuts off the decimal toward zero.
        For negative numbers, floor(-4.1) = -5 (rounds down),
        while trunc(-4.9) = -4 (truncates toward zero).
```

***

**Puzzle 99**

```python
data = [
    {"name": "Alice", "age": 30},
    {"name": "Bob", "age": 25},
    {"name": "Charlie", "age": 35},
]

sorted_data = sorted(data, key=lambda x: x["age"])
for person in sorted_data:
    print(person["name"])
```

```
Answer: Bob
        Alice
        Charlie
Reason: sorted() accepts a key function to sort complex objects.
        lambda x: x["age"] extracts the age from each dictionary
        as the sorting key. The list is sorted in ascending order
        by age: Bob(25), Alice(30), Charlie(35).
```

***

**Puzzle 100**

```python
class Singleton:
    _instance = None

    def __new__(cls):
        if cls._instance is None:
            cls._instance = super().__new__(cls)
        return cls._instance

a = Singleton()
b = Singleton()

print(a is b)
print(id(a) == id(b))
```

```
Answer: True
        True
Reason: The Singleton pattern ensures only one instance is created.
        __new__ checks if _instance already exists before creating
        a new object. If it does, it returns the existing one.
        So a and b point to the exact same object in memory,
        making both is and id() comparisons True.
```
