You know what OOP is. Now let’s create our first class and demystify two of Python’s main puzzles:
__init__— what’s with the strange name?self— why is it everywhere?
Let’s go! 🚀
🎯 Creating a Class: Syntax
Basic Structure
class ClassName:
def __init__(self, parameters):
# Initialize attributes
self.attribute = value
def method(self):
# Object actions
pass
Example: Zombie
class Zombie:
def __init__(self, name, health):
self.name = name
self.health = health
def groan(self):
print(f"{self.name}: Граааах! 🧟")
# Creating an object
walker = Zombie("Ходок", 50)
walker.groan() # Ходок: Граааах! 🧟
What’s happening?
class Zombie:— declare the class__init__(self, name, health)— constructor (runs when the object is created)self.name = name— save attributeswalker = Zombie("Ходок", 50)— create an object
🔑 The __init__ Method: Constructor
What Is It?
__init__ = constructor = a method that is called automatically when an object is created.
Analogy: When a baby 👶 is born, it immediately gets a name, gender, and date of birth. __init__ does the same for objects.
Syntax
def __init__(self, parameter1, parameter2):
self.attribute1 = parameter1
self.attribute2 = parameter2
Important:
- The name is always __init__ (two underscores on each side)
- The first parameter is always self
- Called automatically when you write Zombie("Ходок", 50)
Example: Human
class Human:
def __init__(self, name, health, age):
self.name = name
self.health = health
self.age = age
self.kills = 0 # You can set a default value!
rick = Human("Рик", 100, 35)
print(rick.name) # Рик
print(rick.kills) # 0 (even though we didn't pass it!)
What happened here:
- We called
Human("Рик", 100, 35) - Python automatically called
__init__(self, "Рик", 100, 35) - Attributes were saved:
name,health,age,kills - The ready object
rickwas returned
🤔 The Mystery of self: What Is It?
Definition
self = a reference to the object itself.
When you write self.name, you’re saying: “The name attribute of this specific object”.
The Problem Without self
class Zombie:
def __init__(self, name):
name = name # ❌ Doesn't work!
walker = Zombie("Ходок")
print(walker.name) # AttributeError: 'Zombie' object has no attribute 'name'
Why doesn’t it work?
name is a local variable inside __init__. It dies after the method finishes!
The Solution: self
class Zombie:
def __init__(self, name):
self.name = name # ✅ Works!
walker = Zombie("Ходок")
print(walker.name) # Ходок
self.name = “The name attribute of the walker object”. It persists in the object!
Visualization
class Zombie:
def __init__(self, name, health):
self.name = name # walker.name = "Ходок"
self.health = health # walker.health = 50
walker = Zombie("Ходок", 50)
runner = Zombie("Бегун", 30)
# walker and runner are different objects!
print(walker.name) # Ходок
print(runner.name) # Бегун
# Each has its own attributes
print(walker.health) # 50
print(runner.health) # 30
self lets each object have its own attributes!
🔄 self in Methods
Accessing Attributes
class Zombie:
def __init__(self, name, health):
self.name = name
self.health = health
def groan(self):
# Use self to access name
print(f"{self.name}: Граааах!")
def take_damage(self, damage):
# Use self to access health
self.health -= damage
print(f"{self.name} took {damage} damage. Health: {self.health}")
walker = Zombie("Ходок", 50)
walker.groan() # Ходок: Граааах!
walker.take_damage(20) # Ходок took 20 damage. Health: 30
Without self, methods wouldn’t know which attributes to use!
Calling Methods from Methods
class Zombie:
def __init__(self, name):
self.name = name
def groan(self):
print(f"{self.name}: Граааах!")
def attack(self):
self.groan() # ✅ Call another method via self!
print(f"{self.name} attacks!")
walker = Zombie("Ходок")
walker.attack()
# Ходок: Граааах!
# Ходок attacks!
self.groan() = “Call the groan method of this object”.
🎨 Full Example: Human Class
class Human:
def __init__(self, name, health=100):
self.name = name
self.health = health
self.ammo = 12
self.kills = 0
def shoot(self):
if self.ammo > 0:
self.ammo -= 1
print(f"{self.name} shoots! Ammo: {self.ammo}")
return True
else:
print(f"{self.name}: Out of ammo!")
return False
def reload(self):
self.ammo = 12
print(f"{self.name} reloaded!")
def status(self):
print(f"{'='*30}")
print(f"Name: {self.name}")
print(f"Health: {self.health}")
print(f"Ammo: {self.ammo}")
print(f"Kills: {self.kills}")
print(f"{'='*30}")
# Usage
rick = Human("Рик", 100)
rick.status()
# ==============================
# Name: Рик
# Health: 100
# Ammo: 12
# Kills: 0
# ==============================
rick.shoot() # Рик shoots! Ammo: 11
rick.shoot() # Рик shoots! Ammo: 10
rick.reload() # Рик reloaded!
📝 Default Parameters
class Zombie:
def __init__(self, name, health=50, speed=2):
# ^^^^^^^^^^ ^^^^^^^
# Default values
self.name = name
self.health = health
self.speed = speed
# Can omit health and speed
walker = Zombie("Ходок")
print(walker.health) # 50 (default)
# Or pass your own values
runner = Zombie("Бегун", 30, 5)
print(runner.speed) # 5
Rule: Parameters with defaults must come after required parameters.
⚠️ Common Mistakes
Mistake 1: Forgot self
class Zombie:
def __init__(self, name):
name = name # ❌ Local variable!
walker = Zombie("Ходок")
print(walker.name) # AttributeError
Fix:
self.name = name # ✅ Object attribute
Mistake 2: Forgot self in Method Parameters
class Zombie:
def __init__(name): # ❌ No self!
self.name = name
walker = Zombie("Ходок") # TypeError
Fix:
def __init__(self, name): # ✅ self is the first parameter
Mistake 3: Typo in __init__
class Zombie:
def _init_(self, name): # ❌ Single underscore!
self.name = name
walker = Zombie("Ходок") # TypeError
Fix:
def __init__(self, name): # ✅ Two underscores on each side
Mistake 4: Accessing an Attribute Without self
class Zombie:
def __init__(self, name):
self.name = name
def groan(self):
print(f"{name}: Граааах!") # ❌ No self!
walker = Zombie("Ходок")
walker.groan() # NameError: name 'name' is not defined
Fix:
print(f"{self.name}: Граааах!") # ✅ self.name
🎯 When to Use What?
| What | Where | Why |
|---|---|---|
self.attribute |
In __init__ |
Save object data |
self.attribute |
In methods | Read/modify data |
self.method() |
In methods | Call another method |
parameter |
Without self |
Local variable (dies after the method) |
📚 Comparison: Functions vs Classes
Functions
def create_zombie(name, health):
return {"name": name, "health": health}
def zombie_groan(zombie):
print(f"{zombie['name']}: Граааах!")
walker = create_zombie("Ходок", 50)
zombie_groan(walker)
Problems:
- ❌ Functions and data are separate
- ❌ Easy to make mistakes (pass the wrong dict)
- ❌ No structure
Classes
class Zombie:
def __init__(self, name, health):
self.name = name
self.health = health
def groan(self):
print(f"{self.name}: Граааах!")
walker = Zombie("Ходок", 50)
walker.groan()
Advantages:
- ✅ Data and methods together
- ✅ Clear structure
- ✅ Fewer mistakes
💡 Checklist: Test Yourself
- [ ] Know how to create a class (
class ClassName:) - [ ] Understand why
__init__is needed (constructor) - [ ] Understand what
selfis (reference to the object) - [ ] Can create attributes (
self.attribute = value) - [ ] Can access attributes in methods (
self.attribute) - [ ] Know why
selfis always the first parameter - [ ] Understand the difference between
nameandself.name
🚀 Summary
__init__ — constructor, called automatically when an object is created.
self — reference to the object itself, lets you:
- Save attributes: self.name = name
- Access attributes: print(self.name)
- Call methods: self.other_method()
Syntax:
class ClassName:
def __init__(self, parameters):
self.attribute = value
Creating an object:
object = ClassName(arguments)
Next step: Learn to create methods — object actions! ⚔️
💬 Comments (0)
No comments yet
Be the first to share your opinion about this article!