Python · Syntax · Beginner

OOP Basics

11 tasks

Practical exercises on the basics of OOP in Python: creating classes and objects, attributes, methods, __init__, changing object state, and simple entity modeling.

Classes and objects — the idea

#
A class is a blueprint. An object is what you build from that blueprint. The same class can produce thousands of objects, each with its own independent data. Before classes, if you needed to track multiple bank accounts, you would use separate variables for each balance. With a class, you define the structure once and create as many instances as you need — each one carries its own state. ```python class BankAccount: def __init__(self, balance): self.balance = balance acc1 = BankAccount(100) acc2 = BankAccount(500) print(acc1.balance) # 100 print(acc2.balance) # 500 ``` acc1 and acc2 are independent objects. Changing acc1.balance does not affect acc2. Classes group two things together: data (attributes) and behavior (methods). A method is just a function defined inside a class. This grouping is the main idea behind object-oriented programming — keep the data and the code that operates on it in the same place.

__init__ and self

#
__init__ is a special method Python calls automatically when you create a new object. It sets up the initial state of the instance. ```python class Rectangle: def __init__(self, width, height): self.width = width self.height = height r = Rectangle(3, 4) print(r.width, r.height) # 3 4 ``` self is a reference to the object being created. When you write self.width = width, you are storing the value on the object itself — not in a local variable that disappears when __init__ finishes. Every attribute you want the object to remember must be assigned to self. self is always the first parameter of every method, but you never pass it manually. Python passes it automatically. When you call r.area(), Python translates that to Rectangle.area(r) — r becomes self inside the method. ```python class Rectangle: def __init__(self, width, height): self.width = width self.height = height def area(self): return self.width * self.height r = Rectangle(3, 4) print(r.area()) # 12 ``` The method uses self.width and self.height — the values that were stored during __init__. The method has access to everything the object knows about itself through self.

Common class patterns

#
A method that modifies the object's state — returns nothing, just changes an attribute: ```python class Counter: def __init__(self, value): self.value = value def increment(self): self.value += 1 c = Counter(0) c.increment() c.increment() print(c.value) # 2 ``` A method that checks a condition and returns a boolean: ```python class Person: def __init__(self, name, age): self.name = name self.age = age def is_adult(self): return self.age >= 18 ``` A method that computes something from the attributes and returns a value: ```python class Temperature: def __init__(self, celsius): self.celsius = celsius def to_fahrenheit(self): return self.celsius * 9 / 5 + 32 ``` A method that both mutates state and validates input: ```python class BankAccount: def __init__(self, balance): self.balance = balance def deposit(self, amount): self.balance += amount ``` The pattern is always the same: access what you need through self, compute or modify, return only if there is a meaningful value to return.

OOP basics quick reference

#
**Class syntax** ```python class ClassName: def __init__(self, param1, param2): self.attr1 = param1 # store on the object self.attr2 = param2 def method(self): # self is always first return self.attr1 obj = ClassName(val1, val2) # create an instance obj.attr1 # read an attribute obj.method() # call a method ``` **`self` rules** | Rule | Why | |---|---| | Every method must have `self` as its first parameter | Python passes the object automatically | | Use `self.name` to store data on the object | plain `name =` is just a local variable | | Use `self.name` to read data in any method | all methods share the same object | | Never pass `self` manually when calling a method | `obj.method()` not `obj.method(obj)` | **Method types** | Type | Returns | Example | |---|---|---| | Mutate state | nothing (`None`) | `self.balance += amount` | | Check condition | `bool` | `return self.age >= 18` | | Compute value | result | `return self.w * self.h` | | Mixed | depends | validate then mutate | **Common mistakes** ```python # Forgetting self. — creates a local variable, lost after __init__: def __init__(self, name): name = name # wrong — local only self.name = name # correct # Forgetting self in a method call: def area(self): return width * height # NameError — should be self.width return self.width * self.height # correct # Missing self in method signature: def area(): # wrong — Python passes obj as first arg def area(self): # correct ```
01

User class with a name.

#

Create a User class with a name attribute. Create an object of this class and print its name.

class User:
    pass


u = User("Alex")
print(u.name)
Solution
class User:
    def __init__(self, name):
        self.name = name


u = User("Alex")
print(u.name)

# or you can create a method that returns the name
class User:
    def __init__(self, name):
        self.name = name

    def get_name(self):
        return self.name
02

Car class: brand and year.

#

Create a Car class with brand and year attributes. Create an object and print the string: "Car: Toyota, 2020"

class Car:
    pass
Solution
class Car:
    def __init__(self, brand, year):
        self.brand = brand
        self.year = year


c = Car("Toyota", 2020)
print(f"Car: {c.brand}, {c.year}")

# or

class Car:
    def __init__(self, brand, year):
        self.brand = brand
        self.year = year
    def __str__(self):
        return f"Car: {self.brand}, {self.year}"


c = Car("Toyota", 2020)
print(c)
03

Counter with increment.

#

Create a Counter class with a value attribute. Add an increment method that increases the value by 1.

class Counter:
    pass


c = Counter(0)
c.increment()
print(c.value)
Solution
class Counter:
    def __init__(self, value):
        self.value = value

    def increment(self):
        self.value += 1


c = Counter(0)
c.increment()
print(c.value)

# or you can do this: increment may return the new value
class Counter:
    def __init__(self):
        self.value = 0

    def increment(self):
        self.value += 1
        return self.value
04

Rectangle area.

#

Create a Rectangle class with width and height attributes. Add an area method that returns the area.

class Rectangle:
    pass


r = Rectangle(3, 4)
print(r.area())
Solution
class Rectangle:
    def __init__(self, width, height):
        self.width = width
        self.height = height

    def area(self):
        return self.width * self.height


r = Rectangle(3, 4)
print(r.area())

# or you can do this: save the area to a variable first
class Rectangle:
    def __init__(self, width, height):
        self.width = width
        self.height = height

    def area(self):
        result = self.width * self.height
        return result
05

Greeting with a class method.

#

Create a Greeting class with a say_hello method, which returns the string "Hello!" .

class Greeting:
    pass


g = Greeting()
print(g.say_hello())
Solution
class Greeting:
    def say_hello(self):
        return "Hello!"


g = Greeting()
print(g.say_hello())
06

Checking adulthood.

#

Create a Person class with name and age attributes. Add an is_adult method that returns True if the age is 18 or more.

class Person:
    pass


p = Person("John", 20)
print(p.is_adult())
Solution
class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def is_adult(self):
        return self.age >= 18


p = Person("John", 20)
print(p.is_adult())
07

Depositing to a bank account.

#

Create a BankAccount class with a balance attribute. Add a deposit method that increases the balance.

class BankAccount:
    pass


acc = BankAccount(100)
acc.deposit(50)
print(acc.balance)
Solution
class BankAccount:
    def __init__(self, balance):
        self.balance = balance

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


acc = BankAccount(100)
acc.deposit(50)
print(acc.balance)

# or you can do this: explicitly prevent depositing a negative amount
class BankAccount:
    def __init__(self, balance):
        self.balance = balance

    def deposit(self, amount):
        if amount > 0:
            self.balance += amount
08

Converting Celsius to Fahrenheit.

#

Create a Temperature class with a celsius attribute. Add a to_fahrenheit method that returns the temperature in degrees Fahrenheit. The formula is: celsius * 9 / 5 + 32.

class Temperature:
    pass


t = Temperature(0)
print(t.to_fahrenheit())
Solution
class Temperature:
    def __init__(self, celsius):
        self.celsius = celsius

    def to_fahrenheit(self):
        return self.celsius * 9 / 5 + 32


t = Temperature(0)
print(t.to_fahrenheit())
09

Message length method.

#

Create a Message class with a text attribute. Add a length method that returns the length of the message.

class Message:
    pass


m = Message("Hello world")
print(m.length())
Solution
class Message:
    def __init__(self, text):
        self.text = text

    def length(self):
        return len(self.text)


m = Message("Hello world")
print(m.length())
10

Timer with adding seconds.

#

Create a Timer class with a seconds attribute. Add an add_time method that increases the time by the specified number of seconds.

class Timer:
    pass


t = Timer(10)
t.add_time(5)
print(t.seconds)
Solution
class Timer:
    def __init__(self, seconds):
        self.seconds = seconds

    def add_time(self, extra):
        self.seconds += extra


t = Timer(10)
t.add_time(5)
print(t.seconds)
11

Formatter class for strings.

#

Write a Formatter class with a string attribute on the object. When an instance of this class (object) is created, a string is passed there and stored in this string attribute of the object. This class should have several methods that return modified versions of this string according to their purpose: - the trim method returns a string trimmed to the specified length at the end with "[...]" (the length of these added characters must be taken into account). - the truncate method returns a string trimmed to the specified length, but it does not "break words", instead it trims by spaces, but not longer than the specified length. This is enough. ATTENTION: - the original string in self.string must always remain unchanged. - do not create any other attributes for results, only return new values. Local variables are of course allowed. - you may create attributes for service data, for example for the string "[...]" Remember that methods are just special functions where the first self parameter automatically receives a reference to the object itself, but all other rules such as ordinary local variables and so on are the same as in regular functions. That is, inside a method it is not necessary to put everything into self.variable, you can simply create local variables as in regular functions.

class Formatter():
    def __init__(self, string):
        pass
    
    def trim(self, length):     
        pass
        
    def truncate(self, length):
        pass

    
x = Formatter("Эта строка для примера просто используется тут и не имеет другого смысла.")
print(x.trim(15))      # Эта строка[...]
print(x.truncate(15))  # Эта строка для
print(x.string)  # Изначальная строка

y = Formatter("Ура у меня получилось!.")
print(y.trim(8))      # Ура[...]
print(y.truncate(3))  # Ура
print(y.string)  # Изначальная строка
Solution
class Formatter():
    def __init__(self, string):
        self.string = string
        self.end = "[...]"
    
    def trim(self, length):     
        return self.string[: length - len(self.end)] + self.end
        
    def truncate(self, length):
        if self.string[length] == " ":
            return self.string[:length]
        
        return self.string[:length].rsplit(" ", 1)[0]
        
    
x = Formatter("Эта строка для примера просто используется тут и не имеет другого смысла.")
print(x.trim(15))      # Эта строка[...]
print(x.truncate(15))  # Эта строка для
print(x.string)  # Изначальная строка

y = Formatter("Ура у меня получилось!.")
print(y.trim(8))      # Ура[...]
print(y.truncate(3))  # Ура
print(y.string)  # Изначальная строка