파이썬에서 클래스를 다룰 때, 매번 비슷한 코드를 반복해서 작성하고 있다면—이제 __init__()
의 출동 시간입니다! 객체가 생성될 때 자동으로 호출되는 생성자는, 프로그램의 구조를 간결하게 만들고, 클래스의 초기 상태를 깔끔하게 설정할 수 있게 도와줍니다. 인스턴스 변수와 함께 사용하면 객체마다 서로 다른 고유 정보를 저장할 수도 있죠.
이번 장에서는 __init__()
이 무엇인지, 왜 필요한지, 그리고 인스턴스 변수와의 관계는 어떻게 되는지 예제를 통해 하나하나 차근히 살펴보겠습니다. 클래스와 객체를 진짜 내 것으로 만들고 싶다면, 꼭 이해하고 넘어가야 할 핵심 개념입니다!
- 생성자란 무엇인가?
- __init__()의 기본 구조
- self의 의미와 역할
- 인스턴스 변수란 무엇인가?
- __init__()에서 인스턴스 변수 초기화하기
- 여러 개의 인스턴스 변수 사용하기
- 생성자에 기본값 설정하기
- 클래스 변수와 인스턴스 변수의 차이
- __init__() 없이 인스턴스 변수 만들기
- 실전 예제: 학생 클래스 만들기
생성자란 무엇인가?
프로그래밍에서 '생성자(Constructor)'란 객체가 만들어질 때 자동으로 호출되는 특별한 메서드를 의미합니다. 파이썬에서는 __init__()
이라는 이름의 메서드가 이 역할을 합니다. 객체지향 프로그래밍에서 객체의 초기 상태를 지정하는 것은 매우 중요한데, 이때 생성자를 통해 필요한 값을 초기화할 수 있습니다. 예를 들어, 게임 캐릭터를 만들 때 이름과 체력, 공격력을 초기화하고 싶다면 생성자가 매우 유용하겠죠.
생성자는 객체가 생성되자마자 자동으로 호출되기 때문에 개발자가 별도로 호출하지 않아도 되고, 객체마다 다른 초기값을 전달받아 개별적인 특성을 설정할 수 있다는 장점이 있습니다. 또한, 생성자를 통해 필수 속성을 강제함으로써 더 견고한 코드 설계도 가능합니다.
class User:
def __init__(self):
print("User 객체가 생성되었습니다.")
u = User() # 실행 시 자동으로 __init__ 호출됨
위 예제에서 User()
를 호출하면 자동으로 __init__()
메서드가 실행되며, 객체 초기화 작업을 수행할 수 있습니다.
__init__()의 기본 구조
파이썬에서 생성자는 항상 __init__()
이라는 이름을 갖습니다. 이 메서드는 클래스가 인스턴스화될 때 자동으로 호출되며, 보통 객체의 초기 상태를 정의하는 데 사용됩니다. 기본적인 구조는 다음과 같습니다:
class ClassName:
def __init__(self, parameter1, parameter2, ...):
self.parameter1 = parameter1
self.parameter2 = parameter2
이 구조에서 중요한 키워드는 self
입니다. self는 생성되는 객체 자기 자신을 가리키는 참조로, 인스턴스 변수에 값을 할당할 때 반드시 사용됩니다. 예를 들어 self.name = name
은 "이 객체의 name 속성에 전달받은 name 값을 저장하라"는 의미입니다.
실제 예제를 보겠습니다:
class Book:
def __init__(self, title, author):
self.title = title
self.author = author
my_book = Book("1984", "George Orwell")
print(my_book.title) # 출력: 1984
print(my_book.author) # 출력: George Orwell
이 예제에서는 Book
클래스에 두 개의 속성 title
과 author
를 생성자에서 초기화하고 있습니다. 객체를 생성할 때 전달한 값이 해당 인스턴스에 저장되어, 이후에도 접근할 수 있게 되는 것이죠.
self의 의미와 역할
파이썬 클래스에서 self
는 매우 중요한 역할을 합니다. self는 해당 클래스의 인스턴스(객체) 자신을 가리키는 참조입니다. 클래스 안에 정의된 메서드의 첫 번째 매개변수로 반드시 self를 선언해야 하며, 이를 통해 메서드 내부에서 객체 자신의 속성과 메서드에 접근할 수 있게 됩니다.
예를 들어 self.name
은 '이 객체의 name이라는 속성'을 의미하고, self.some_method()
는 '이 객체의 some_method()라는 메서드'를 호출합니다. 만약 self 없이 인스턴스 변수를 정의하면, 해당 변수는 메서드 내부의 지역 변수로 취급되어 객체에 저장되지 않습니다.
class Dog:
def __init__(self, name):
self.name = name # self.name은 인스턴스 변수
def bark(self):
print(f"{self.name}이(가) 멍멍 짖습니다!")
dog1 = Dog("초코")
dog1.bark() # 출력: 초코이(가) 멍멍 짖습니다!
위 예제에서 self.name
은 객체마다 고유한 이름을 저장하는 인스턴스 변수이며, bark()
메서드에서도 self.name
을 통해 해당 객체의 이름을 사용할 수 있습니다. 만약 self
를 생략했다면, bark()
메서드는 인스턴스 변수에 접근할 수 없었을 것입니다.
self는 단순한 문법 요소가 아니라, 객체 지향 프로그래밍에서 객체 간의 경계를 구분하고 관리하는 핵심 도구입니다. self를 제대로 이해하면 클래스와 객체의 작동 원리를 자연스럽게 파악할 수 있습니다.
인스턴스 변수란 무엇인가?
인스턴스 변수는 객체마다 개별적으로 존재하는 속성입니다. 클래스의 생성자 __init__()
내부에서 self.변수명
형태로 정의되며, 각 인스턴스는 자신만의 독립된 값을 갖게 됩니다. 이를 통해 같은 클래스로부터 생성된 객체라도 서로 다른 정보를 저장하고 다르게 동작할 수 있습니다.
예를 들어, 사람(Person) 클래스를 정의하고 각각의 사람 이름과 나이를 저장하고자 할 때, 이 정보는 인스턴스 변수로 선언됩니다. 만약 인스턴스 변수를 사용하지 않고 일반 변수를 선언하면, 모든 객체가 동일한 값을 공유하게 되며 객체의 고유성을 보장할 수 없습니다.
class Person:
def __init__(self, name, age):
self.name = name # 인스턴스 변수
self.age = age # 인스턴스 변수
person1 = Person("영희", 23)
person2 = Person("철수", 30)
print(person1.name, person1.age) # 출력: 영희 23
print(person2.name, person2.age) # 출력: 철수 30
이 예제에서 person1
과 person2
는 각각 독립된 name
과 age
값을 갖고 있습니다. 인스턴스 변수는 객체마다 다른 상태를 저장하는 데 필수적이기 때문에, 객체지향 프로그래밍의 핵심 요소 중 하나입니다.
또한, 인스턴스 변수는 객체가 생성된 후에도 값을 변경하거나 읽을 수 있어 유연한 상태 관리가 가능합니다. 이는 나중에 객체의 기능 확장이나 메서드 동작에도 직접적인 영향을 미칩니다.
__init__()에서 인스턴스 변수 초기화하기
클래스의 생성자인 __init__()
메서드는 인스턴스가 생성될 때 자동으로 호출되어, 초기값을 설정하는 역할을 합니다. 이때 인스턴스 변수는 self.변수명 = 값
형태로 생성자 안에서 초기화됩니다. 이러한 방식은 클래스 외부에서 별도의 초기화 메서드를 호출할 필요 없이, 객체를 생성하는 순간 모든 준비를 마칠 수 있게 해줍니다.
예를 들어, 자동차 클래스에서 브랜드와 연식 같은 정보를 객체 생성 시점에 설정하고 싶다면 다음과 같이 구현할 수 있습니다.
class Car:
def __init__(self, brand, year):
self.brand = brand
self.year = year
my_car = Car("Hyundai", 2021)
print(my_car.brand) # 출력: Hyundai
print(my_car.year) # 출력: 2021
위 코드에서 brand
와 year
는 생성자에서 초기화된 인스턴스 변수입니다. 이러한 방식은 여러 개의 객체를 만들 때도 각각 고유한 속성을 부여할 수 있어 확장성과 유지보수 측면에서 매우 유리합니다.
초기화하지 않은 변수는 객체가 만들어질 때 존재하지 않기 때문에, 인스턴스 생성 직후 바로 사용할 변수들은 반드시 __init__()
에서 초기화해야 합니다. 그리고 이 작업은 객체의 상태(state)를 일관되게 유지하는 데 중요한 역할을 합니다.
여러 개의 인스턴스 변수 사용하기
하나의 클래스는 여러 개의 인스턴스 변수를 가질 수 있습니다. 실제 프로그램에서는 객체가 다양한 정보를 갖고 있어야 하므로, 생성자 __init__()
안에서 여러 속성을 동시에 초기화하는 것이 일반적입니다. 이때 각 변수는 self.변수명
형태로 선언되며, 객체마다 독립된 값을 저장하게 됩니다.
예를 들어, 학생 정보를 관리하는 클래스를 만든다고 할 때 이름, 나이, 학년, 전공 등 다양한 정보를 저장해야 할 수 있습니다. 아래 예제는 이를 잘 보여줍니다.
class Student:
def __init__(self, name, age, grade, major):
self.name = name
self.age = age
self.grade = grade
self.major = major
student1 = Student("지민", 21, "3학년", "컴퓨터공학")
student2 = Student("현우", 19, "1학년", "경영학")
print(f"{student1.name}, {student1.age}, {student1.grade}, {student1.major}")
print(f"{student2.name}, {student2.age}, {student2.grade}, {student2.major}")
위 예제처럼 인스턴스 변수는 몇 개든 추가할 수 있으며, 이를 통해 객체는 복잡하고 풍부한 상태를 표현할 수 있습니다. 또한 객체 생성 시 인자로 전달받은 데이터를 일괄적으로 설정하므로 코드의 가독성도 높아지고, 객체 설계가 명확해집니다.
클래스를 설계할 때 어떤 정보가 필요하고 어떤 변수로 표현할지를 먼저 고민하면, 이후 로직 작성도 훨씬 수월해집니다. 다양한 속성을 가진 객체를 관리하고자 할 때는 인스턴스 변수의 사용을 적극 활용해야 합니다.
생성자에 기본값 설정하기
파이썬의 생성자 __init__()
에서는 함수와 마찬가지로 매개변수에 기본값을 설정할 수 있습니다. 이 기능은 객체를 생성할 때 일부 값을 생략해도 자동으로 기본값이 채워지게 해주며, 유연한 클래스 설계에 매우 유용합니다.
예를 들어 회원 가입 시 닉네임은 필수지만 프로필 이미지는 선택일 수 있습니다. 이런 경우 생성자에 기본값을 지정해 사용자가 값을 생략해도 문제가 발생하지 않게 만들 수 있습니다.
class User:
def __init__(self, username, profile_img="default.png"):
self.username = username
self.profile_img = profile_img
user1 = User("hannah")
user2 = User("mike", "mike_profile.png")
print(user1.username, user1.profile_img) # hannah default.png
print(user2.username, user2.profile_img) # mike mike_profile.png
위 예제처럼 profile_img
는 기본값을 지정했기 때문에, User("hannah")
처럼 두 번째 인자를 생략해도 자동으로 "default.png"
가 들어갑니다. 반면 username
은 기본값이 없으므로 반드시 명시적으로 전달해야 합니다.
기본값 설정의 또 다른 장점은 코드 간결성과 함수 유연성입니다. 상황에 따라 필요한 인자만 전달하고, 나머지는 기본 동작을 따르게 할 수 있어 함수의 복잡도를 줄이고 사용성을 높일 수 있습니다. 단, 기본값이 있는 매개변수는 항상 기본값이 없는 매개변수 뒤에 위치해야 오류가 발생하지 않습니다.
클래스 변수와 인스턴스 변수의 차이
클래스 변수와 인스턴스 변수는 모두 클래스 내부에서 선언되지만, 저장 위치와 사용 방식에서 중요한 차이가 있습니다. 인스턴스 변수는 self.변수명
으로 선언되며, 각 객체마다 개별적으로 존재합니다. 반면 클래스 변수는 클래스 정의 바로 아래에 선언되며, 모든 객체가 공유하는 변수입니다.
클래스 변수를 사용하면 모든 인스턴스가 동일한 값을 공유하도록 설정할 수 있으며, 주로 전체 인스턴스 간에 공통된 데이터를 저장할 때 유용합니다. 반대로 객체마다 고유한 속성을 다뤄야 할 때는 반드시 인스턴스 변수를 사용해야 합니다.
class Dog:
species = "Canine" # 클래스 변수
def __init__(self, name):
self.name = name # 인스턴스 변수
dog1 = Dog("초코")
dog2 = Dog("바둑이")
print(dog1.name) # 초코
print(dog2.name) # 바둑이
print(dog1.species) # Canine
print(dog2.species) # Canine
위 코드에서 species
는 클래스 변수로, 모든 Dog 인스턴스가 동일한 값을 공유합니다. name
은 인스턴스 변수로, 각 객체마다 다르게 설정됩니다. 만약 클래스 변수를 객체 수준에서 변경하려 하면, 그 순간 새로운 인스턴스 변수가 생성되어 클래스 변수와는 분리됩니다.
이처럼 클래스 변수는 전역적인 설정값이나 공용 상태를 관리하는 데, 인스턴스 변수는 개별 객체의 고유 상태를 표현하는 데 적합합니다. 용도에 따라 정확히 구분하여 사용하는 것이 객체지향 설계의 기본입니다.
__init__() 없이 인스턴스 변수 만들기
일반적으로 인스턴스 변수는 __init__()
생성자 내부에서 정의하지만, 꼭 생성자 안에서만 만들 수 있는 것은 아닙니다. 인스턴스를 생성한 뒤, 외부에서 직접 변수에 값을 할당해도 해당 객체에 인스턴스 변수가 생성됩니다. 즉, 객체명.변수명 = 값
형태로 동적으로 속성을 추가할 수 있다는 뜻입니다.
이 방식은 간단한 실험이나 임시 속성을 추가할 때 유용할 수 있지만, 일반적으로 권장되는 방법은 아닙니다. 왜냐하면 클래스 설계가 느슨해지고, 코드의 일관성과 가독성이 떨어지기 때문입니다. 또한, 생성자 없이 정의한 속성은 다른 인스턴스에는 존재하지 않을 수 있어 예외가 발생할 위험도 있습니다.
class Animal:
pass
a1 = Animal()
a2 = Animal()
a1.name = "호랑이"
print(a1.name) # 출력: 호랑이
print(a2.name) # 오류 발생: 'Animal' object has no attribute 'name'
이 예제에서 a1
에는 name
속성이 동적으로 추가되었지만, a2
에는 해당 속성이 없어 오류가 발생합니다. 이는 클래스 내부 구조가 예측 불가능하게 되므로 유지보수가 어려워집니다.
따라서 대부분의 경우, 인스턴스 변수는 반드시 __init__()
안에서 정의하는 것이 안정적이고 권장되는 방식입니다. 다만, 간단한 테스트나 스크립트에서는 예외적으로 사용할 수 있습니다.
실전 예제: 학생 클래스 만들기
지금까지 배운 __init__()
생성자와 인스턴스 변수의 개념을 실제 예제를 통해 종합해보겠습니다. 이번에는 학생 정보를 담는 Student
클래스를 만들어 이름, 학번, 학과, 학년을 저장하고 출력하는 기능을 구현해보겠습니다. 이 예제는 생성자에서 인스턴스 변수를 초기화하고, 각 객체가 고유한 데이터를 가질 수 있도록 설계하는 연습이 됩니다.
class Student:
def __init__(self, name, student_id, major, year=1):
self.name = name
self.student_id = student_id
self.major = major
self.year = year
def introduce(self):
print(f"안녕하세요. 저는 {self.major}과 {self.year}학년 {self.name}입니다. 학번은 {self.student_id}입니다.")
student1 = Student("민지", "2023001", "컴퓨터공학", 2)
student2 = Student("영수", "2023002", "경영학") # year는 기본값 1
student1.introduce()
student2.introduce()
위 코드에서 Student
클래스는 4개의 인스턴스 변수를 사용하며, year
에는 기본값 1
을 지정해 선택적으로 사용할 수 있도록 했습니다. introduce()
메서드는 인스턴스 변수에 접근하여 학생 정보를 출력합니다. 이처럼 생성자와 인스턴스 변수를 활용하면, 객체 중심의 깔끔하고 유지보수 쉬운 코드를 작성할 수 있습니다.
실전 예제를 통해 생성자의 활용법과 객체 지향적인 설계 감각을 익히는 것이 중요합니다. 다양한 예제를 스스로 구현해보면서 객체 설계에 자신감을 가져보세요.
🔚 결론: 객체의 탄생을 책임지는 생성자
이번 장에서는 파이썬 클래스의 핵심인 __init__()
생성자와 인스턴스 변수의 개념과 활용에 대해 깊이 있게 살펴보았습니다. 객체가 생성되는 순간 호출되는 __init__()
을 통해 우리는 클래스의 구조를 명확히 설계하고, 각 객체가 고유의 상태를 가질 수 있도록 정의할 수 있습니다. 또한 self
의 의미, 클래스 변수와의 차이, 기본값 설정 등도 실제 예제를 통해 직접 확인해보았습니다.
이러한 개념들은 객체지향 프로그래밍의 핵심 원리인 캡슐화와 추상화를 실현하는 데 중요한 역할을 합니다. 특히 다양한 인스턴스 변수를 적절히 설정함으로써, 현실 세계의 복잡한 개체를 프로그램 안에서 자연스럽게 모델링할 수 있습니다.
다음 장에서는 클래스 내부에 기능을 추가하는 방법, 즉 메서드(Method)의 정의와 호출에 대해 배워보겠습니다. 객체가 상태(state)뿐만 아니라 동작(behavior)도 가질 수 있도록 만드는 중요한 개념이니, 지금까지의 내용을 복습하며 자연스럽게 이어가 보세요!
'프로그래밍 > 파이썬' 카테고리의 다른 글
13장. 상속과 다형성 (1) | 2025.05.26 |
---|---|
12장. 메서드와 self의 의미 (0) | 2025.05.26 |
10장. 클래스와 인스턴스 개념 (1) | 2025.05.19 |
9장. 예외 처리 (try, except, finally, raise) (0) | 2025.05.19 |
8장. 함수 정의와 호출 (매개변수, 반환값, 기본값, 키워드 인자) (0) | 2025.05.19 |