A student writes this method inside a BankAccount class in Python: `def deposit(amount): balance = balance + amount`. When they call account.deposit(100), they get an error. What are the two problems?
A`deposit` is not a valid method name — it should be called `add_funds` to follow conventions
B`self` is missing from the parameter list, and `balance` should be accessed as `self.balance` to reach the instance attribute
CThe method needs a `return` statement to update the account balance
D`deposit` must be defined outside the class to receive the object as an argument
Both errors relate to `self`. Without `self` as the first parameter, Python passes the object as an argument the method doesn't expect, causing a 'takes 1 positional argument but 2 were given' error. Even if `self` were added to the signature, writing `balance = balance + amount` creates or modifies a local variable named `balance`, not the instance attribute — `self.balance` is required to reach the object's stored data.
Question 2 Multiple Choice
Why is calling `account.withdraw(50)` preferable to writing `account.balance -= 50` directly from outside the class?
AThe dot notation syntax requires method calls — direct attribute modification raises a syntax error
BMethods execute faster than direct attribute access due to Python's internal optimizations
CThe method can enforce rules — such as checking for sufficient funds — before modifying the attribute, preventing invalid state
DDirect attribute modification is not allowed in Python; attributes are always read-only from outside the class
This is the point of encapsulation. `account.balance -= 50` bypasses any validation and can leave the account in an invalid state (negative balance). A `withdraw` method can check `if amount > self.balance: raise ValueError` before making the change. The object controls how its own data is modified — this is what makes methods more than just convenience wrappers.
Question 3 True / False
A method is fundamentally a function — the key difference is that a method automatically receives a reference to the object it was called on as its first argument.
TTrue
FFalse
Answer: True
Methods are not a fundamentally different kind of thing from functions — they are functions defined inside a class that receive the object implicitly. In Python this reference is `self`; in Java/C++ it is `this`. When you write `account.deposit(50)`, Python automatically passes `account` as the `self` argument. This is why the misconception that 'methods and functions are completely different' is worth correcting — understanding their relationship makes the `self` parameter logical rather than mysterious.
Question 4 True / False
In Python, if you define a method without the `self` parameter, the method will still work correctly as long as it doesn't read or write any instance attributes inside it.
TTrue
FFalse
Answer: False
Even if the method body uses no instance attributes, calling it as `obj.method()` will pass the object as the first positional argument. Since the method signature has no parameter to receive it, Python raises 'takes 0 positional arguments but 1 was given.' The `self` parameter must be present in the signature regardless of whether it is used in the body — Python always passes the object when a method is called on an instance.
Question 5 Short Answer
What is encapsulation, and how do attributes and methods work together to implement it in a class?
Think about your answer, then reveal below.
Model answer: Encapsulation is the principle that an object controls access to its own data — outside code interacts with the object through its method interface rather than reading or writing attributes directly. Attributes store the object's state; methods define the operations the outside world can request. By routing access through methods, the class can enforce invariants (e.g., balance cannot go negative), hide internal implementation details, and change how data is stored without breaking code that uses the class.
The BankAccount example makes this concrete: exposing `balance` as a public attribute lets anyone set it to any value, including invalid ones. Providing `deposit()` and `withdraw()` as the interface means every change to balance passes through validation logic. This is the beginning of object-oriented design — the class is not just a data container but a behavioral unit that takes responsibility for maintaining its own valid state.