πŸŽ“ Velnov

🐍 Python · Topic 14 of 20

Classes & Objects β€” Python Questions with Answers

Start with the study notes on Classes & Objects (definitions, examples and a quick revision), then work through 30 practice questions, each with the correct answer and a clear explanation of why it is right. When you are ready, take this topic as an interactive quiz to test yourself.

11 easy Β· 14 medium Β· 5 hard Β· Part of Python: Master Python from basics to advanced - 20 topics.

Topic overview

Classes and Objects – your own data types

1. Class and object

A class is a blueprint; an object (or instance) is a thing built from it. A class bundles data (attributes) with the functions that work on it (methods).

Example: A Dog class is the cookie cutter; rex and buddy are two cookies, each with their own name and age.

2. Defining a class: __init__ and self

class Dog:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def bark(self):
        print(self.name, "says woof")

rex = Dog("Rex", 3)
buddy = Dog("Buddy", 5)
rex.bark()
print(buddy.name, buddy.age)
OutputRex says woof
Buddy 5

Remember: rex.bark() is really Dog.bark(rex): the object is passed in as self. Forget self in the def, and the call fails with β€œtakes 0 positional arguments but 1 was given”.

3. Objects keep their own state

Each object has its own attributes, and methods can change them.

class Account:
    def __init__(self, balance=0):
        self.balance = balance

    def deposit(self, amount):
        self.balance += amount
        return self.balance

a = Account(100)
b = Account()
print(a.deposit(50), b.balance)
Output150 0

4. Class attributes vs instance attributes

class Dog:
    count = 0                  # class attribute: shared
    def __init__(self, name):
        self.name = name       # instance attribute: per dog
        Dog.count += 1

a, b, c = Dog("Rex"), Dog("Max"), Dog("Leo")
print(Dog.count, a.name, c.name)
Output3 Rex Leo

The shared list trap

A list in the class body is shared by every object. Create per-object lists in __init__.

class Team:
    members = []               # ONE list for all teams!
    def add(self, name):
        self.members.append(name)

x, y = Team(), Team()
x.add("Ana")
print(y.members)
Output['Ana']

5. Printing and comparing objects

By default, print(obj) shows something like <__main__.Dog object at 0x...>, and == checks whether two names are the same object. Defining __str__ and __eq__ changes this (see Magic Methods).

class Dog:
    def __init__(self, name):
        self.name = name
    def __str__(self):
        return f"Dog named {self.name}"
    def __eq__(self, other):
        return self.name == other.name

print(Dog("Rex"))
print(Dog("Rex") == Dog("Rex"))
OutputDog named Rex
True

6. Inspecting objects

class Dog:
    def __init__(self, name):
        self.name = name

rex = Dog("Rex")
rex.age = 3                       # objects accept new attributes
print(rex.__dict__)
print(isinstance(rex, Dog))
print(hasattr(rex, "color"), getattr(rex, "color", "unknown"))
Output{'name': 'Rex', 'age': 3}
True
False unknown

Remember: Reading a missing attribute raises AttributeError. Define all attributes in __init__, so a typo like rex.nmae = ... doesn't silently create a new one.

7. Why use classes?

Classes keep related data and behaviour together, so bigger programs stay organised. This idea is called encapsulation.

Example: A BankAccount keeps its balance together with deposit() and withdraw(), instead of loose variables and separate functions.

8. Common misconceptions

MisconceptionCorrect understanding
self is a keyword you can leave out.self must be the first parameter of every normal method.
Changing one object's attribute changes the others.Instance attributes belong to each object separately.
A list in the class body gives each object its own list.It is shared by all objects; create it in __init__.
Two objects with equal data are == by default.By default == checks identity; define __eq__ to compare data.
You need new to create an object.Call the class like a function: Dog("Rex").

9. Quick revision

Class
A blueprint for objects.
Object / instance
A thing made from a class.
__init__
Sets up a new object's attributes.
self
The object a method is working on.
Attribute
Data stored on an object, e.g. rex.name.
Method
A function defined in a class.
Class attribute
Shared by all objects of the class.
Encapsulation
Keeping data and its methods together.

10. Test your understanding

  1. What is the difference between a class and an object?

    Show answer

    The class is the blueprint; an object is a thing built from it.

  2. When does __init__ run?

    Show answer

    Automatically, whenever a new object is created.

  3. What does self refer to?

    Show answer

    The object the method was called on.

  4. How do you create a Dog object?

    Show answer

    Dog("Rex")

  5. What error do you get if a method is defined without self?

    Show answer

    TypeError: it takes 0 positional arguments but 1 was given.

  6. What is a class attribute?

    Show answer

    One value shared by all objects of the class.

  7. Why is members = [] in a class body a trap?

    Show answer

    All objects share the same list.

  8. What does print(obj) show without __str__?

    Show answer

    Something like <__main__.Dog object at 0x...>.

Key takeaway: A class is a blueprint: __init__ sets up each object's data through self, methods act on it, and each object keeps its own state.

Notes written by Velnov. Every code example with an output was run in Python 3 to check that the output shown is exactly what Python prints.

Key concepts in this topic

ClassesInstancesclass keywordNaming conventions__init__ConstructorsselfInstantiationAttributesDot notationMethod callsTypeErrorInstance attributesIndependencePEP 8MethodsFunctionsStateClass attributesCounting instancestype()isinstance()AttributeErrorhasattr()

Jump to: Easy Β· Medium Β· Hard

Easy questions (11)

Q1. easy

What is the relationship between a CLASS and an OBJECT?

class Dog:            # blueprint
    pass

rex = Dog()            # object #1
buddy = Dog()          # object #2
  1. A A class is the blueprint; objects are the individual things built from it
  2. B They are the same thing
  3. C Objects contain classes
  4. D A class is one specific example of an object

Answer: A β€” A class is the blueprint; objects are the individual things built from it

The class defines what attributes and methods exist (the cookie cutter); each object (instance) is a concrete item made from it (a cookie) with its own data.

ClassesInstances

Q2. easy

Which is the correct syntax to define a class?

class Student:
    def __init__(self, name):
        self.name = name
  1. A class Student:
  2. B def Student:
  3. C class Student():{}
  4. D new class Student

Answer: A β€” class Student:

The class keyword, a name (CamelCase by convention), and a colon. The body - methods and attributes - is indented below.

class keywordNaming conventions

Q3. easy

What is the purpose of the __init__ method?

class Student:
    def __init__(self, name, grade):
        self.name = name
        self.grade = grade

s = Student("Ravi", "A")   # __init__ runs here
print(s.name)                # Ravi
  1. A To delete an object
  2. B To initialize a new object's attributes - it runs automatically on creation
  3. C To import the class
  4. D To print the object

Answer: B β€” To initialize a new object's attributes - it runs automatically on creation

__init__ is the initializer: Python calls it automatically every time you create an instance, letting you set up the object's starting attribute values.

__init__Constructors

Q4. easy

What does self refer to inside a class method?

class Dog:
    def __init__(self, name):
        self.name = name
    def bark(self):
        print(self.name, "says woof")

rex = Dog("Rex")
rex.bark()    # Rex says woof  (self = rex)
  1. A The class itself
  2. B The specific OBJECT the method was called on
  3. C The Python interpreter
  4. D The parent class

Answer: B β€” The specific OBJECT the method was called on

self is the current instance - when you call rex.bark(), self IS rex inside the method. It's how each object accesses ITS OWN attributes: self.name, self.age.

selfInstances

Q5. easy

How do you CREATE an object of class Dog?

class Dog:
    def __init__(self, name):
        self.name = name

d = Dog("Rex")
  1. A d = Dog()
  2. B d = new Dog()
  3. C d = create Dog
  4. D d = Dog.new()

Answer: A β€” d = Dog()

Call the class like a function: Dog(). Arguments go to __init__: Dog("Rex", 3). No 'new' keyword in Python (that's Java/JavaScript/C++).

Instantiation

Q6. easy

What does self.name = name do inside __init__(self, name)?

def __init__(self, name):
    self.name = name     # kept on the object
    nickname = name       # lost after __init__!
  1. A Stores the parameter's value as an ATTRIBUTE on the object, so it survives after __init__ ends
  2. B Creates a global variable
  3. C Renames the class
  4. D Nothing - it's redundant

Answer: A β€” Stores the parameter's value as an ATTRIBUTE on the object, so it survives after __init__ ends

The parameter name is a temporary local variable; self.name attaches the value to the OBJECT itself, making it available later as obj.name. Without self. the value would vanish when __init__ returns.

Attributesself

Q7. easy

How do you read the name attribute of object s?

s = Student("Ana")
print(s.name)      # Ana
s.name = "Anita"  # attributes can be updated too
  1. A s.name
  2. B s[name]
  3. C name(s)
  4. D s->name

Answer: A β€” s.name

Dot notation: object.attribute. (Square brackets are for lists/dicts; arrows are C++.)

Dot notationAttributes

Q11. easy

By convention, class names are written in:

  1. A CamelCase: BankAccount, StudentRecord
  2. B snake_case: bank_account
  3. C ALLCAPS: BANKACCOUNT
  4. D lowercase: bankaccount

Answer: A β€” CamelCase: BankAccount, StudentRecord

PEP 8: classes use CamelCase (CapWords); functions/variables use snake_case; constants use ALL_CAPS. Seeing BankAccount() you instantly know it's a class.

PEP 8Naming conventions

Q22. easy

What is the main benefit of OOP (classes) over loose variables and functions?

  1. A It bundles related DATA and the FUNCTIONS that work on it into one unit
  2. B Programs always run faster
  3. C Less typing in all cases
  4. D It's required by Python

Answer: A β€” It bundles related DATA and the FUNCTIONS that work on it into one unit

A BankAccount class keeps balance (data) and deposit/withdraw (behavior) together - instead of parallel lists and free functions hoping to stay in sync. Encapsulation makes bigger programs manageable.

EncapsulationOOP benefits

Q27. easy

A class Car should store its color and speed. Which __init__ is right?

  1. A def __init__(self, color, speed):\n self.color = color\n self.speed = speed
  2. B def __init__(color, speed):\n color = color
  3. C def init(self):\n self.color
  4. D def __init__(self):\n color = color

Answer: A β€” def __init__(self, color, speed):\n self.color = color\n self.speed = speed

Take self plus the values, then store each with self.attr = value. Option B forgets self entirely; C misspells __init__ and stores nothing; D never receives the values.

__init__Attributes

Q30. easy

In rex = Dog("Rex"), which part triggers __init__?

  1. A Calling the class: Dog("Rex")
  2. B The assignment to rex
  3. C The quotes around Rex
  4. D __init__ must be called manually

Answer: A β€” Calling the class: Dog("Rex")

Calling a class creates the object and automatically runs __init__ with your arguments. The assignment merely names the finished object.

Instantiation__init__

Medium questions (14)

Q8. medium

Why must methods declare self as their first parameter?

class Dog:
    def bark(self):        # receives the instance
        print("woof")

rex.bark()                  # same as Dog.bark(rex)
  1. A Python passes the instance automatically as the first argument - self receives it
  2. B It's optional decoration
  3. C For speed
  4. D To make methods private

Answer: A β€” Python passes the instance automatically as the first argument - self receives it

rex.bark() is really Dog.bark(rex) behind the scenes - the instance is passed in as the first argument, which the method receives as self. Forget self in the def and calls break.

selfMethod calls

Q9. medium

What error results from this, and when? class Dog: def bark(): print("woof") Dog().bark()

class Dog:
    def bark(self):    # fixed
        print("woof")
  1. A TypeError when calling - bark() takes 0 arguments but 1 was given (the instance!)
  2. B SyntaxError at definition
  3. C No error
  4. D NameError

Answer: A β€” TypeError when calling - bark() takes 0 arguments but 1 was given (the instance!)

The def compiles fine, but the CALL passes the instance automatically - and bark() declared no parameter to receive it. The confusing message 'takes 0 positional arguments but 1 was given' almost always means: you forgot self.

  • Learn to recognize this error message - everyone hits it.

selfTypeError

Q10. medium

a = Dog("Rex") and b = Dog("Buddy"). Does changing a.name affect b.name?

a = Dog("Rex")
b = Dog("Buddy")
a.name = "King"
print(b.name)    # Buddy - unaffected
  1. A No - each instance has its OWN attributes
  2. B Yes - all objects share attributes
  3. C Only if they were created on the same line
  4. D b is deleted

Answer: A β€” No - each instance has its OWN attributes

Instance attributes (set via self.x = ...) belong to each object separately. a and b are independent - that's the point of instances.

Instance attributesIndependence

Q12. medium

What is the difference between a FUNCTION and a METHOD?

  1. A A method is a function defined inside a class and called on an object: obj.method()
  2. B Methods are faster
  3. C Functions can't take arguments
  4. D No difference at all

Answer: A β€” A method is a function defined inside a class and called on an object: obj.method()

Methods live in classes and receive the instance (self) automatically: "hi".upper(), list.append(), dog.bark(). Plain functions stand alone: len(), print().

MethodsFunctions

Q13. medium

What is the output? class Counter: def __init__(self): self.count = 0 def increment(self): self.count += 1 c = Counter() c.increment() c.increment() print(c.count)

  1. A 0
  2. B 1
  3. C 2
  4. D Error

Answer: C β€” 2

count starts at 0 in __init__; each increment() adds 1 to THIS object's count: 0 -> 1 -> 2. Objects carrying state between method calls is the essence of OOP.

StateMethods

Q16. medium

What does type(rex) return for rex = Dog("Rex")?

print(type(rex))              # <class '__main__.Dog'>
print(isinstance(rex, Dog))   # True
  1. A <class '__main__.Dog'> - the object's class
  2. B 'Rex'
  3. C object
  4. D dog

Answer: A β€” <class '__main__.Dog'> - the object's class

type() reveals which class made the object. For checks, prefer isinstance(rex, Dog) - it also accepts subclasses.

type()isinstance()

Q17. medium

Accessing an attribute that doesn't exist (rex.age when never set) raises:

print(hasattr(rex, "age"))       # False
print(getattr(rex, "age", 0))    # 0 - default
  1. A AttributeError
  2. B KeyError
  3. C NameError
  4. D ValueError

Answer: A β€” AttributeError

AttributeError: 'Dog' object has no attribute 'age'. Check safely with hasattr(rex, "age") or supply a fallback with getattr(rex, "age", 0).

AttributeErrorhasattr()getattr()

Q18. medium

Can one method call another method of the same object?

class Order:
    def total(self):
        return self.subtotal() + self.tax()
    def subtotal(self): ...
    def tax(self): ...
  1. A Yes - via self: self.other_method()
  2. B No, methods are isolated
  3. C Only with global
  4. D Only in subclasses

Answer: A β€” Yes - via self: self.other_method()

self gives access to everything on the object - attributes AND other methods. Building bigger methods from smaller ones keeps classes tidy.

selfMethod composition

Q19. medium

What does this print? class Product: def __init__(self, name, price=100): self.name = name self.price = price p = Product("Pen") print(p.price)

  1. A 100
  2. B None
  3. C Error - price missing
  4. D 0

Answer: A β€” 100

__init__ is a normal function - default parameter values work exactly the same: price defaults to 100 when not supplied.

Default arguments__init__

Q21. medium

What does print(rex) show by DEFAULT (no __str__ defined)?

class Dog:
    def __init__(self, name):
        self.name = name
    def __str__(self):
        return f"Dog named {self.name}"

print(Dog("Rex"))   # Dog named Rex
  1. A Something like <__main__.Dog object at 0x000001A2B3C4D5E6>
  2. B All the attributes nicely formatted
  3. C The name attribute
  4. D Nothing

Answer: A β€” Something like <__main__.Dog object at 0x000001A2B3C4D5E6>

The default representation shows the class and memory address - not helpful. Define __str__ to control what print() shows (covered in the Magic Methods topic).

__str__Object representation

Q23. medium

Can you add a NEW attribute to an object after creation? rex = Dog("Rex") rex.tricks = ["sit"]

  1. A Yes - Python objects accept new attributes at any time (by default)
  2. B No - only __init__ can create attributes
  3. C Only with add_attribute()
  4. D It deletes the object

Answer: A β€” Yes - Python objects accept new attributes at any time (by default)

Python is dynamic - rex.tricks = [...] just works, attaching the attribute to that one object. Handy but risky (typos create NEW attributes silently: rex.nmae = ... !). Best practice: define everything in __init__.

  • A typo like rex.nmae = 'x' creates a new attribute instead of failing!

Dynamic attributesBest practices

Q24. medium

Trace the output: class Account: def __init__(self, balance): self.balance = balance def deposit(self, amt): self.balance += amt return self.balance acc = Account(100) print(acc.deposit(50))

  1. A 150
  2. B 100
  3. C 50
  4. D None

Answer: A β€” 150

balance starts at 100; deposit adds 50 and returns the new balance: 150. A miniature of every real-world class: state + methods that change it.

StateTracing

Q26. medium

Where does the code in the CLASS BODY (outside methods) run? class Config: print("setting up") version = 1

  1. A Once, when the class statement is first executed - NOT on each instance
  2. B Every time an object is created
  3. C Never
  4. D On every method call

Answer: A β€” Once, when the class statement is first executed - NOT on each instance

The class body executes top-to-bottom ONCE to build the class - creating class attributes and methods. Creating instances later runs only __init__, not the body again.

Class bodyClass creation

Q29. medium

isinstance(x, (int, float)) returns True when...

def half(x):
    if not isinstance(x, (int, float)):
        raise TypeError("number required")
    return x / 2
  1. A x is an int OR a float (or a subclass of either)
  2. B x is both at once
  3. C x is exactly the tuple (int, float)
  4. D Never - invalid syntax

Answer: A β€” x is an int OR a float (or a subclass of either)

isinstance accepts a tuple of types - True if x matches ANY of them. The standard way to accept 'any number'.

isinstance()Type checking

Hard questions (5)

Q14. hard

What is the difference between these two attributes? class Dog: species = "canine" # (1) def __init__(self): self.name = "Rex" # (2)

print(Dog.species)     # canine - via the class
a, b = Dog(), Dog()
print(a.species)        # canine - shared
a.name = "King"        # only a's name changes
  1. A (1) is a CLASS attribute shared by all dogs; (2) is an INSTANCE attribute unique to each dog
  2. B Both are identical
  3. C (1) is private, (2) is public
  4. D (2) is shared, (1) is unique

Answer: A β€” (1) is a CLASS attribute shared by all dogs; (2) is an INSTANCE attribute unique to each dog

species lives on the CLASS - one copy, seen by every instance (Dog.species). self.name lives on EACH object separately. Use class attributes for values common to all instances.

Class attributesInstance attributes

Q15. hard

What is the output? class Dog: count = 0 def __init__(self): Dog.count += 1 a = Dog(); b = Dog(); c = Dog() print(Dog.count)

  1. A 0
  2. B 1
  3. C 3
  4. D Error

Answer: C β€” 3

Dog.count is a class attribute - one shared counter. Each __init__ increments it, so after three instances it's 3. A classic pattern for counting how many objects exist.

Class attributesCounting instances

Q20. hard

What is the output? a = Dog("Rex") b = Dog("Rex") print(a == b)

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

print(Dog("Rex") == Dog("Rex"))   # True now
  1. A False - by default == compares object IDENTITY, and these are two separate objects
  2. B True - same name means equal
  3. C Error
  4. D None

Answer: A β€” False - by default == compares object IDENTITY, and these are two separate objects

Unless a class defines __eq__, == falls back to 'are they the SAME object?' - and a, b are two distinct instances, however identical their data. Defining __eq__ lets you compare by content.

__eq__Identity vs equality

Q25. hard

What does obj.__dict__ show?

rex = Dog("Rex")
rex.age = 3
print(rex.__dict__)   # {'name': 'Rex', 'age': 3}
  1. A A dict of the object's instance attributes: {'name': 'Rex', 'age': 3}
  2. B The class's methods
  3. C Python's built-in dictionary
  4. D Nothing - it's an error

Answer: A β€” A dict of the object's instance attributes: {'name': 'Rex', 'age': 3}

Instance attributes are literally stored in a dict per object - __dict__ exposes it. Great for debugging: see exactly what data an object carries.

__dict__Introspection

Q28. hard

What is the output? class Team: members = [] def add(self, name): self.members.append(name) a = Team(); b = Team() a.add("Ana") print(b.members)

class Team:
    def __init__(self):
        self.members = []   # each team its own list
  1. A ['Ana'] - the CLASS-level list is shared by ALL instances!
  2. B [] - each team has its own list
  3. C Error
  4. D None

Answer: A β€” ['Ana'] - the CLASS-level list is shared by ALL instances!

members = [] in the class body creates ONE list on the class, shared by every instance - a.add() mutates the list b sees too. For per-instance lists, create them in __init__: self.members = [].

  • The mutable-class-attribute trap - sibling of the mutable default argument.

Class attributesMutability traps

Practice β€œClasses & Objects” as a quiz