클래스와 객체

지금까지 작업한 모든 타입(문자열, 리스트, 딕셔너리)은 실제로 클래스입니다. "hello".upper()를 호출할 때, 문자열 객체에서 메서드를 호출하는 것입니다. 클래스를 사용하면 자신의 데이터와 동작을 가진 고유한 타입을 정의할 수 있습니다. Player 클래스는 이름, 점수, 레벨을 보유하고 자신을 표시하는 방법을 알 수 있습니다.
청사진과 인스턴스
클래스는 청사진입니다. 인스턴스는 그 청사진에서 만든 구체적인 것입니다. 필요한 만큼 많은 인스턴스를 만들 수 있으며, 각각 자신의 데이터를 가지지만 클래스에 정의된 메서드를 공유합니다.
class Dog:
def bark(self):
print("Woof!")
rex = Dog()
luna = Dog()
rex.bark() # "Woof!"
luna.bark() # "Woof!"Dog은 클래스입니다. rex과 luna는 인스턴스입니다: 두 마리의 서로 다른 개이며, 각각 클래스에 정의된 같은 동작을 공유합니다.
Dog(), 새로운 인스턴스를 돌려받습니다. 모든 인스턴스는 클래스의 메서드를 공유하지만 자신의 데이터를 보유하므로, 렉스와 루나는 같은 동작을 하면서 서로 다른 개입니다. __init__과 self
__init__은 새로운 인스턴스를 만들 때 파이썬이 자동으로 호출하는 메서드입니다. 객체의 시작 데이터를 설정하는 곳입니다. self는 메서드가 작동하는 특정 인스턴스를 참조하는 방법이며, 항상 첫 번째 매개변수입니다.
class Player:
def __init__(self, name, score=0):
self.name = name
self.score = score
def add_points(self, points):
self.score += points
def display(self):
print(f"{self.name}: {self.score} points")
alice = Player("민수")
bob = Player("영희", score=50)
alice.add_points(30)
alice.display() # "민수: 30 points"
bob.display() # "영희: 50 points"self.name과 self.score는 인스턴스 속성: 클래스 자체가 아니라 특정 객체에 속합니다. 각 Player 인스턴스는 자신의 name과 score를 가집니다.
__init__은 인스턴스를 만드는 순간 실행되므로, self.name = value로 시작 데이터를 설정하는 곳입니다. self는 파이썬이 작업 중인 인스턴스이며, 항상 메서드의 첫 번째 매개변수이며, 자동으로 전달됩니다. alice.display()를 호출할 때 `self`를 직접 전달하지 않습니다. 메서드
클래스 내부에 정의된 모든 함수는 메서드입니다. 인스턴스 메서드는 항상 첫 번째 매개변수로 self를 가집니다; 파이썬은 자동으로 그것을 전달합니다. 메서드는 self를 통해 인스턴스의 데이터를 읽고 변경할 수 있습니다.
class Circle:
def __init__(self, radius):
self.radius = radius
def area(self):
return 3.14159 * self.radius ** 2
def scale(self, factor):
self.radius *= factor
return self # self를 반환하면 체이닝이 가능해집니다: c.scale(2).scale(0.5)
c = Circle(5)
print(c.area()) # 78.53975
c.scale(2)
print(c.area()) # 314.159self이고, 그것이 작동하는 인스턴스입니다. 파이썬이 `self`를 전달하므로, c.area()를 추가로 호출합니다. self를 통해 메서드는 그 객체 자신의 데이터를 읽고 변경합니다. 클래스 변수 vs 인스턴스 변수
클래스에 직접 정의된 변수(__init__ 내부가 아님)는 클래스 변수입니다. 모든 인스턴스는 같은 클래스 변수를 공유합니다. __init__ 내부의 self에 설정된 변수는 인스턴스 변수이며, 각 객체에 고유합니다.
class Player:
max_lives = 3 # 클래스 변수, 모든 Player에 대해 동일
def __init__(self, name):
self.name = name # 인스턴스 변수, 각 Player에 고유
self.lives = Player.max_lives
def die(self):
self.lives -= 1
alice = Player("민수")
bob = Player("영희")
Player.max_lives = 5 # 모든 현재 및 미래 인스턴스에 대해 변경클래스 변수를 모든 인스턴스에서 공유되는 값에 사용하세요: 상수, 카운터, 기본값. 인스턴스 변수를 객체마다 다른 데이터에 사용하세요.
self.attr = value를 쓰는 것은 항상 인스턴스의 자신의 복사본을 만들거나 업데이트합니다. 따라서 클래스 변수에 도달할 때 모든 사람이 같은 값을 가지고, 인스턴스 변수일 때 객체마다 다릅니다. __str__과 __repr__
__str__은 당신의 객체에 대해 print()와 f-문자열이 표시하는 것을 제어합니다. __repr__은 콘솔에서 표시되고 디버깅에 대한 개발자 보기를 제어합니다. 항상 __repr__을 정의하세요. 디버그 보기와 별도로 깔끔한 사용자 대면 디스플레이를 원할 때 __str__을 정의하세요.
class Player:
def __init__(self, name, score):
self.name = name
self.score = score
def __str__(self):
return f"{self.name} ({self.score} pts)"
def __repr__(self):
return f"Player(name={self.name!r}, score={self.score})"
alice = Player("민수", 87)
print(alice) # "민수 (87 pts)" (__str__ 사용)
repr(alice) # "Player(name='민수', score=87)" (__repr__ 사용)항상 __repr__을 정의하세요. 디버그 보기와 별도로 깔끔한 사용자 표현을 원할 때 __str__을 정의하세요. 오직 __repr__만 정의되면, 파이썬은 둘 다에 사용합니다.
__str__은 print()와 f-문자열이 표시하는 것이며, 친절한 버전. __repr__은 콘솔에서 보이는 개발자 보기입니다. 항상 __repr__을 쓰세요; `__str__`을 잊어도 작업이 있는 것이 하나입니다. 사용자 대면 텍스트를 다르게 읽어야 할 때만 __str__을 추가하세요. 개인 관례
파이썬은 진정한 개인 변수가 없지만, 이름 시작의 단일 언더스코어(_balance)는 "이것은 내부이고, 클래스 외부에서 직접 사용하지 마세요"라는 신호를 하는 관례입니다. 언어로 시행되지 않으며; 다른 개발자에게 통신합니다.
class BankAccount:
def __init__(self, balance):
self._balance = balance # _ means "hands off"
def deposit(self, amount):
if amount > 0:
self._balance += amount
def balance(self):
return self._balance이중 언더스코어(__name)는 이름 망글링을 트리거합니다; 파이썬은 부분 클래스에서 충돌을 피하기 위해 속성을 _ClassName__name으로 이름을 바꿉니다. 드물게 필요합니다. 단일 언더스코어는 대부분의 코드에서 관례입니다.
_balance)는 "내부, 외부에서 혼자 두세요"라는 합의된 신호입니다. 아무것도 당신을 도달하는 것을 막지 않으며, 다른 개발자들에게 메시지입니다. 미래의 당신을 포함합니다. 이중 언더스코어는 부분 클래스에서 이름 충돌을 피하기 위한 더 드문 도구; 단일 언더스코어는 당신이 매일 사용할 것입니다. 상속
클래스는 다른 클래스에서 상속할 수 있으며, 자동으로 모든 속성과 메서드를 얻습니다. 그 다음 부분 클래스에서 특정 메서드를 재정의하여 동작을 변경할 수 있습니다. 이것은 공통 기본을 재사용하고 필요한 곳에 특화하도록 합니다.
class Animal:
def __init__(self, name):
self.name = name
def speak(self):
return "..."
class Dog(Animal):
def speak(self):
return f"{self.name} says Woof!"
class Cat(Animal):
def speak(self):
return f"{self.name} says Meow!"
pets = [Dog("렉스"), Cat("루나"), Dog("맥스")]
for pet in pets:
print(pet.speak())Dog과 Cat은 Animal에서 __init__을 상속하므로, 자신이 필요하지 않습니다. 그들은 자신의 특정 동작으로 speak()를 재정의합니다.
Dog과 Cat은 Animal의 __init__을 재사용하고 speak()만 재정의합니다. super()
super()은 부모 클래스에서 메서드를 호출합니다. 부모의 동작을 완전히 바꾸는 대신 확장하고 싶을 때 사용하세요: 부모의 __init__을 호출하여 그 설정을 실행한 다음, 부분 클래스가 필요한 것을 추가합니다.
class Animal:
def __init__(self, name, sound):
self.name = name
self.sound = sound
class Dog(Animal):
def __init__(self, name):
super().__init__(name, "Woof") # Animal.__init__ 호출
self.tricks = [] # 뭔가 추가
def learn(self, trick):
self.tricks.append(trick)
rex = Dog("렉스")
rex.learn("sit")
print(rex.tricks) # ["sit"]부분 클래스가 자신의 __init__을 가지고 부모도 가질 때 항상 super().__init__()을 호출하세요.
super()는 부모 클래스에 도달하므로, super().__init__()은 당신이 자신을 추가하기 전에 부모의 설정을 실행합니다. 부분 클래스가 자신의 `__init__`을 작성하고 부모가 하나를 가질 때 사용하세요. 건너뛰면 부모의 설정은 절대 실행되지 않으므로, 객체가 반만 만들어진 상태로 남습니다. 클래스 메서드와 정적 메서드
@classmethod는 인스턴스 대신 클래스 자체를 받는 메서드를 만듭니다. 문자열, 파일, 또는 다른 형식에서 인스턴스를 만드는 대체 생성자에 유용합니다. @staticmethod는 조직상의 이유로 클래스 내부에 있는 일반 함수입니다; 인스턴스나 클래스를 받지 않습니다.
class Player:
def __init__(self, name, score):
self.name = name
self.score = score
@classmethod
def from_string(cls, data):
name, score = data.split(",")
return cls(name, int(score))
alice = Player.from_string("민수,87")class Player:
@staticmethod
def is_valid_name(name):
return name.isalpha() and len(name) >= 2
Player.is_valid_name("민수") # True
Player.is_valid_name("A1") # False대체 생성자에는 @classmethod를 사용하세요. 클래스에 논리적으로 속하지만 인스턴스나 클래스 데이터가 필요 없는 유틸리티 함수에는 @staticmethod를 사용하세요.
@classmethod는 인스턴스 대신 클래스를 당신에게 전달하므로, 대체 생성자를 위한 편의점입니다: 당신이 가진 어떤 형식에서든 `Player`를 만드세요. @staticmethod는 단정을 위해 클래스 내부에 숨겨진 일반 함수; 클래스나 인스턴스를 받지 않습니다. 메서드는 하나의 객체의 데이터를 만집니다, 이 둘은 그렇지 않습니다. @property
@property는 메서드를 괄호 없이 속성처럼 액세스하도록 합니다. 다른 속성에서 계산되고 단순 속성 액세스로 읽는 것이 자연스러운 값에 사용하세요.
class Circle:
def __init__(self, radius):
self.radius = radius
@property
def area(self):
return 3.14159 * self.radius ** 2
@property
def diameter(self):
return self.radius * 2
c = Circle(5)
print(c.area) # 78.53975 (속성처럼 보이지만 메서드처럼 실행)
print(c.diameter) # 10속성은 계산된 값에 유용합니다: 다른 속성에서 파생되는 것들이 ()없이 평구 데이터로 액세스하는 것이 자연스럽습니다.
@property는 메서드를 속성처럼 읽을 수 있게 합니다. 괄호 없음: c.area 대신 c.area() 대신. 다른 속성에서 일해졌고 평구 데이터로 읽는 것이 자연스러운 값에 맞습니다. 뒤에서 매번 메서드를 액세스할 때 실행합니다. 실제로
인스턴스 속성, 메서드, @property, 그리고 __str__을 가진 Player 클래스:
class Player:
max_lives = 3
def __init__(self, name: str):
self.name = name
self.score = 0
self.lives = Player.max_lives
def earn_points(self, amount: int) -> None:
self.score += amount
def take_hit(self) -> bool:
self.lives -= 1
return self.lives > 0
@property
def is_alive(self) -> bool:
return self.lives > 0
def __str__(self) -> str:
return f"{self.name} | Score: {self.score} | Lives: {self.lives}"
alice = Player("민수")
alice.earn_points(50)
alice.take_hit()
print(alice) # "민수 | Score: 50 | Lives: 2"
print(alice.is_alive) # True
