Python Fundamentals for GATE CS
Data types, built-in structures, list comprehensions, functions, OOP in Python — everything GATE CS tests in Python, with output-prediction examples.
Last updated: April 2026 | GATE CS 2024–2026 syllabus
Key Takeaways for GATE
- Python is dynamically typed and garbage collected. Everything is an object.
- Mutable: list, dict, set, bytearray. Immutable: int, float, str, tuple, frozenset.
- List comprehension:
[expr for x in iter if cond]— O(n), creates a new list. - Python passes arguments by object reference — rebinding inside function doesn’t affect caller; mutating the object does.
ischecks identity (same object in memory);==checks equality (same value).- OOP:
selfis explicit first parameter.__init__is constructor.__str__for string representation. - Generator uses
yield— lazy evaluation, O(1) memory vs O(n) for list.
1. Data Types & Mutability
| Type | Mutable | Ordered | Duplicates | Syntax |
|---|---|---|---|---|
| int, float, complex | No | — | — | x = 42 |
| str | No | Yes | Yes | s = "hello" |
| list | Yes | Yes | Yes | [1, 2, 3] |
| tuple | No | Yes | Yes | (1, 2, 3) |
| dict | Yes | Yes (3.7+) | Keys: No | {'a': 1} |
| set | Yes | No | No | {1, 2, 3} |
| frozenset | No | No | No | frozenset({1,2}) |
a = [1, 2, 3] b = [1, 2, 3] a == b # True (same values) a is b # False (different objects) c = a a is c # True (same object)
Small int caching: Python caches integers -5 to 256. a = 100; b = 100; a is b → True. a = 1000; b = 1000; a is b → False (implementation-defined).
2. Built-in Data Structures
a = [1, 2, 3] a.append(4) # O(1) amortised a.insert(0, 0) # O(n) — shifts elements a.pop() # O(1) — removes last a.pop(0) # O(n) — removes first, shifts
Dict — O(1) average get/set/delete (hash table):
d = {'a': 1, 'b': 2}
d['c'] = 3 # insert
d.get('x', 0) # get with default — no KeyError
del d['a']
'b' in d # O(1) membership testSet — O(1) add/remove/membership, O(n) union/intersection:
s = {1, 2, 3}
s.add(4)
s.discard(10) # no error if not present (unlike remove())
s1 & s2 # intersection
s1 | s2 # union
s1 - s2 # difference3. Comprehensions
squares = [x**2 for x in range(10)] evens = [x for x in range(20) if x % 2 == 0] flat = [x for row in matrix for x in row] # nested
Dict comprehension:
sq_dict = {x: x**2 for x in range(5)} # {0:0, 1:1, 2:4, 3:9, 4:16}Set comprehension:
unique_sq = {x**2 for x in [-2,-1,0,1,2]} # {0, 1, 4}Generator expression (lazy):
gen = (x**2 for x in range(10)) # O(1) memory, computed on demand sum(gen) # 285
4. Functions
def f(x, lst=[]): # TRAP: lst shared across all calls!
lst.append(x)
return lst
f(1) # [1]
f(2) # [1, 2] — NOT [2]!
# Fix: def f(x, lst=None): if lst is None: lst = []*args and **kwargs:
def f(*args, **kwargs):
print(args) # tuple of positional args
print(kwargs) # dict of keyword argsLambda (anonymous function):
sq = lambda x: x**2 sorted_list = sorted(data, key=lambda x: x[1])
map, filter, reduce:
list(map(lambda x: x*2, [1,2,3])) # [2, 4, 6] list(filter(lambda x: x%2==0, range(10))) # [0, 2, 4, 6, 8] from functools import reduce reduce(lambda a,b: a+b, [1,2,3,4]) # 10
5. Scope & Closures (LEGB Rule)
global keyword:
x = 10
def f():
global x # without this, x = 20 creates a new local x
x = 20
f(); print(x) # 20nonlocal keyword (for closures):
def outer():
count = 0
def inner():
nonlocal count
count += 1
return count
return inner
c = outer()
c() # 1
c() # 2 — count persists in enclosing scope6. OOP in Python
class Animal:
species = "Unknown" # class variable (shared)
def __init__(self, name):
self.name = name # instance variable
def speak(self): # instance method
return f"{self.name} speaks"
@classmethod
def create(cls, name): # class method — gets cls, not self
return cls(name)
@staticmethod
def info(): # no self or cls
return "I am an animal"
class Dog(Animal):
def speak(self): # override
return f"{self.name} barks"
d = Dog("Rex")
print(d.speak()) # "Rex barks" (polymorphism)
print(isinstance(d, Animal)) # True Special methods (dunder):
__init__ (constructor), __str__ (str()), __repr__ (repr()), __len__ (len()), __eq__ (==), __lt__ (<)
7. Generators & Iterators
def count_up(n):
i = 0
while i < n:
yield i # pauses here, returns i, resumes on next()
i += 1
g = count_up(3)
next(g) # 0
next(g) # 1
next(g) # 2
next(g) # StopIteration Iterator protocol: Object with __iter__ and __next__ methods.
Generator vs list: Generator is lazy (O(1) memory at any time). List materialises all values (O(n) memory).
yield from: Delegates to a sub-generator.
def chain(*iters):
for it in iters:
yield from it8. Exceptions
try:
x = int("abc")
except ValueError as e:
print(f"Error: {e}")
except (TypeError, KeyError): # multiple exceptions
pass
else:
print("No exception") # runs if no exception
finally:
print("Always runs") # runs regardless
raise ValueError("bad input") # raise exceptionCommon built-in exceptions: ValueError, TypeError, KeyError, IndexError, AttributeError, ZeroDivisionError, NameError, RecursionError, StopIteration.
9. GATE Examples
def f(x, lst=[]):
lst.append(x)
return lst
print(f(1))
print(f(2))
print(f(3, [])) f(1): lst=[1] (default). f(2): same lst=[1,2] (default persists!). f(3, []): new list=[3].
Output: [1] [1, 2] [3]
a = [1, 2, 3] b = a # b and a refer to same list b.append(4) print(a)
b = a makes b point to the same list object. Mutating through b changes a.
Output: [1, 2, 3, 4]
result = [x*y for x in range(1,4) for y in range(1,4) if x != y] print(len(result))
Pairs (x,y) where x,y in {1,2,3} and x≠y: (1,2),(1,3),(2,1),(2,3),(3,1),(3,2) = 6 pairs.
Output: 6
10. Common Mistakes
- Mutable default argument:
def f(lst=[])— the list is created once at function definition and shared across calls. UseNoneas default and create a new list inside the function. isvs==:ischecks object identity (same memory address),==checks value equality. For small ints and interned strings,ismay give True coincidentally — don’t rely on it for equality testing.- Modifying a list while iterating: Skips elements or causes errors. Iterate over a copy:
for x in lst[:]:or collect indices to remove and delete after. - Generator exhaustion: A generator can only be iterated once. After exhaustion, it’s empty. Must create a new generator or use a list.
- Class vs instance variable:
self.x = valcreates instance variable.ClassName.xis class variable (shared). Assigning via self creates a new instance variable that shadows the class variable.
11. FAQ
- What is the difference between a list and a tuple in Python?
- Lists are mutable — you can add, remove, or change elements after creation. Tuples are immutable — contents fixed at creation. Both are ordered sequences allowing duplicates and mixed types. Tuples are faster, use less memory, and are hashable (can be dict keys or set elements). Use tuples for fixed collections and lists for collections that need to change.
- How does Python manage memory?
- Python uses automatic garbage collection primarily via reference counting — each object tracks how many references point to it; when the count reaches 0 the object is freed. The gc module handles cyclic references (A → B → A with no other references). Python also caches small integers (-5 to 256) and interned strings to avoid repeated allocation.
- What is a generator in Python?
- A generator is a function containing yield that returns values lazily — producing one value at a time only when requested via next(). It maintains state between calls and uses O(1) memory regardless of how many values it can produce. Generators are ideal for large datasets, infinite sequences, and pipelines where you don’t need all values simultaneously.
- What is the difference between @staticmethod and @classmethod in Python?
- @staticmethod: no implicit first argument — it’s a regular function grouped in the class namespace. Cannot access class or instance state without explicit reference. @classmethod: takes cls as first argument (the class itself) — can access and modify class-level state, call other class methods, and is used for alternative constructors (factory methods).