본문 바로가기
프로그래밍/파이썬

11장. 생성자 __init__()과 인스턴스 변수

by 사업가 Jay 2025. 5. 26.

파이썬에서 클래스를 다룰 때, 매번 비슷한 코드를 반복해서 작성하고 있다면—이제 __init__()의 출동 시간입니다! 객체가 생성될 때 자동으로 호출되는 생성자는, 프로그램의 구조를 간결하게 만들고, 클래스의 초기 상태를 깔끔하게 설정할 수 있게 도와줍니다. 인스턴스 변수와 함께 사용하면 객체마다 서로 다른 고유 정보를 저장할 수도 있죠.

이번 장에서는 __init__()이 무엇인지, 왜 필요한지, 그리고 인스턴스 변수와의 관계는 어떻게 되는지 예제를 통해 하나하나 차근히 살펴보겠습니다. 클래스와 객체를 진짜 내 것으로 만들고 싶다면, 꼭 이해하고 넘어가야 할 핵심 개념입니다!

  1. 생성자란 무엇인가?
  2. __init__()의 기본 구조
  3. self의 의미와 역할
  4. 인스턴스 변수란 무엇인가?
  5. __init__()에서 인스턴스 변수 초기화하기
  6. 여러 개의 인스턴스 변수 사용하기
  7. 생성자에 기본값 설정하기
  8. 클래스 변수와 인스턴스 변수의 차이
  9. __init__() 없이 인스턴스 변수 만들기
  10. 실전 예제: 학생 클래스 만들기

 

생성자란 무엇인가?

프로그래밍에서 '생성자(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 클래스에 두 개의 속성 titleauthor를 생성자에서 초기화하고 있습니다. 객체를 생성할 때 전달한 값이 해당 인스턴스에 저장되어, 이후에도 접근할 수 있게 되는 것이죠.

🔝 목차로 돌아가기

 

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

이 예제에서 person1person2는 각각 독립된 nameage 값을 갖고 있습니다. 인스턴스 변수는 객체마다 다른 상태를 저장하는 데 필수적이기 때문에, 객체지향 프로그래밍의 핵심 요소 중 하나입니다.

또한, 인스턴스 변수는 객체가 생성된 후에도 값을 변경하거나 읽을 수 있어 유연한 상태 관리가 가능합니다. 이는 나중에 객체의 기능 확장이나 메서드 동작에도 직접적인 영향을 미칩니다.

🔝 목차로 돌아가기

 

__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

위 코드에서 brandyear생성자에서 초기화된 인스턴스 변수입니다. 이러한 방식은 여러 개의 객체를 만들 때도 각각 고유한 속성을 부여할 수 있어 확장성과 유지보수 측면에서 매우 유리합니다.

초기화하지 않은 변수는 객체가 만들어질 때 존재하지 않기 때문에, 인스턴스 생성 직후 바로 사용할 변수들은 반드시 __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)도 가질 수 있도록 만드는 중요한 개념이니, 지금까지의 내용을 복습하며 자연스럽게 이어가 보세요!