Skip to content
This page has been auto-translated and may contain errors.View in English

클래스와 객체

docs.scrimba.com

지금까지 작업한 모든 타입(문자열, 리스트, 딕셔너리)은 실제로 클래스입니다. "hello".upper()를 호출할 때, 문자열 객체에서 메서드를 호출하는 것입니다. 클래스를 사용하면 자신의 데이터와 동작을 가진 고유한 타입을 정의할 수 있습니다. Player 클래스는 이름, 점수, 레벨을 보유하고 자신을 표시하는 방법을 알 수 있습니다.

클래스는 사용자 정의 타입의 메커니즘입니다. 클래스는 템플릿을 정의합니다: 각 인스턴스가 보유하는 데이터(속성, 그것에 저장된 값)와 지원하는 작업(메서드, 그것에 첨부된 함수). 평행 변수에서 값을 추적하고 모든 곳에 전달하는 대신, 명확한 인터페이스를 가진 하나의 객체로 묶습니다.

class 문은 새로운 타입(커스텀 값의 종류, strint와 같은 것)을 만들고 다른 할당처럼 이름에 바인딩합니다. 인스턴스(해당 타입의 하나의 구체적인 객체)를 만들려면 클래스를 호출합니다: Player("민수"). 뒤에서 파이썬은 빈 객체를 할당하고 __init__을 실행하여 그것을 채웁니다. 모델은 모든 방식으로 균일합니다: 클래스 자체는 변수에 저장하거나 함수에 전달하거나 런타임에 검사할 수 있는 객체이며(프로그램이 실행 중일 때), 이것이 이 장의 후반부에서 대체 생성자와 레지스트리를 가능하게 하는 것입니다.

청사진과 인스턴스

클래스는 청사진입니다. 인스턴스는 그 청사진에서 만든 구체적인 것입니다. 필요한 만큼 많은 인스턴스를 만들 수 있으며, 각각 자신의 데이터를 가지지만 클래스에 정의된 메서드를 공유합니다.

클래스는 구조와 동작을 정의합니다. 인스턴스는 그 클래스에서 생성된 객체입니다: 각각 자신의 데이터를 전달하지만 클래스의 메서드 객체를 공유합니다. 인스턴스를 만들려면 클래스를 함수처럼 호출합니다: Dog()는 새로운 Dog 인스턴스를 생성합니다.

클래스를 호출하는 것은 두 가지 작업을 순서대로 수행합니다: 빈 인스턴스를 할당한 다음 그것을 채우기 위해 __init__을 실행합니다. 완성된 객체는 자신의 클래스를 기억합니다(type(렉스)로 다시 읽을 수 있음), 그리고 그 링크가 렉스.bark()를 호출할 때 파이썬이 메서드를 찾는 방법입니다: 먼저 인스턴스에서, 그 다음 클래스에서 봅니다. 실용적인 결론은 메서드가 한 곳(클래스에서, 공유됨)에 있고 데이터가 인스턴스마다 있다는 것이므로, 천 개의 Dog 객체는 천 개의 데이터 세트 비용이 들지만 각 메서드의 복사본은 하나뿐입니다.

python
class Dog:
    def bark(self):
        print("Woof!")

rex = Dog()
luna = Dog()

rex.bark()    # "Woof!"
luna.bark()   # "Woof!"

Dog은 클래스입니다. rexluna는 인스턴스입니다: 두 마리의 서로 다른 개이며, 각각 클래스에 정의된 같은 동작을 공유합니다.

Juno청사진과 인스턴스 클래스는 청사진이고, 인스턴스는 그것에서 만든 하나의 것입니다. 클래스를 함수처럼 호출하면 Dog(), 새로운 인스턴스를 돌려받습니다. 모든 인스턴스는 클래스의 메서드를 공유하지만 자신의 데이터를 보유하므로, 렉스루나는 같은 동작을 하면서 서로 다른 개입니다.
Juno청사진과 인스턴스Dog()를 호출하면 인스턴스가 생성됩니다: 자신의 데이터, 클래스의 공유 메서드. 그 묶음이 전체 요점이며, 관련된 값과 그것에 작용하는 코드가 함께 이동하므로 손으로 전달하는 느슨한 변수로서가 아닙니다.
Juno청사진과 인스턴스 클래스를 호출하면 빈 객체를 할당한 다음 그것에 대해 __init__을 실행합니다. 메서드는 클래스에 한 번 앉고 공유되며, 데이터는 인스턴스마다 앉으므로, 렉스에 대한 메서드 호출은 인스턴스를 본 다음 클래스를 봄으로써 찾습니다. 그 분할이 만 개의 인스턴스가 저렴하게 유지되는 이유입니다.

__init__self

__init__은 새로운 인스턴스를 만들 때 파이썬이 자동으로 호출하는 메서드입니다. 객체의 시작 데이터를 설정하는 곳입니다. self는 메서드가 작동하는 특정 인스턴스를 참조하는 방법이며, 항상 첫 번째 매개변수입니다.

__init__은 새로 할당된 인스턴스를 초기화합니다. self는 모든 인스턴스 메서드의 첫 번째 매개변수에 대한 관례 이름입니다; alice.display()를 호출할 때 파이썬은 인스턴스를 자동으로 전달합니다. __init__ 내부의 self에 설정된 속성은 인스턴스 속성: 각 인스턴스는 자신의 복사본을 가집니다.

__init__은 이미 할당된 인스턴스에서 실행되고 그것을 설정합니다. self는 키워드가 아니라 첫 번째 매개변수이며, 파이썬이 당신을 위해 그것을 채웁니다: alice.display()를 호출할 때, 파이썬은 뒤에서 aliceself로 전달합니다. self.attr = value를 설정하는 것은 그 하나의 인스턴스에 씁니다. 따라서 두 개의 Player 객체는 절대로 각각의 데이터를 밟지 않습니다. 엄격한 언어에서 오는 사람들을 놀라게 하는 한 가지: 파이썬은 __init__에서만이 아니라 어디서나 속성을 추가하도록 허용합니다. 따라서 self.scor = 0과 같은 오타는 발생시키는 대신 새로운 속성을 만듭니다. 인스턴스가 사용할 모든 속성을 __init__에 설정하십시오. 심지어 None으로도, 객체의 모양이 한 곳에 선언되고 다른 곳의 오타가 눈에 띄도록.

python
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.nameself.score인스턴스 속성: 클래스 자체가 아니라 특정 객체에 속합니다. 각 Player 인스턴스는 자신의 namescore를 가집니다.

Juno__init__과 self__init__은 인스턴스를 만드는 순간 실행되므로, self.name = value로 시작 데이터를 설정하는 곳입니다. self는 파이썬이 작업 중인 인스턴스이며, 항상 메서드의 첫 번째 매개변수이며, 자동으로 전달됩니다. alice.display()를 호출할 때 `self`를 직접 전달하지 않습니다.
Juno__init__과 self__init__은 새로운 인스턴스를 설정하고, 거기에 `self`에 저장한 것은 인스턴스 속성이며, 객체마다 하나의 복사본입니다. self는 모든 인스턴스 메서드의 첫 번째 매개변수입니다; 파이썬이 alice.display()를 쓸 때 인스턴스를 전달하며, 당신은 정의에서만 그것의 이름을 지정합니다.
Juno__init__과 selfself는 인스턴스이며, 모든 호출에서 전달되고, self.attr에 쓰는 것은 그 하나의 객체에 접근합니다. 파이썬은 `__init__` 외부에 속성을 추가하는 것을 막지 않으므로, 오타인 `self.scor`은 조용히 새로운 속성을 만듭니다. __init__에서 모든 속성을 선언하십시오. 심지어 `None`으로도 하고, 오타는 숨을 곳이 없습니다.

메서드

클래스 내부에 정의된 모든 함수메서드입니다. 인스턴스 메서드는 항상 첫 번째 매개변수로 self를 가집니다; 파이썬은 자동으로 그것을 전달합니다. 메서드는 self를 통해 인스턴스의 데이터를 읽고 변경할 수 있습니다.

인스턴스 메서드는 클래스에 저장된 일반 함수입니다. instance.method에 접근할 때, 파이썬은 바인딩된 메서드를 돌려줍니다: self로 이미 연결된 같은 함수이므로, 나머지 인수만 전달합니다. 메서드에서 self를 반환하면 체이닝이 가능해집니다: obj.scale(2).rotate(90).

메서드는 모든 인스턴스에서 공유하는 클래스의 하나의 함수입니다. c.scale에 접근하면 원시 함수를 받지 않으며, 바인딩된 메서드를 받습니다: self로 이미 연결된 그 같은 함수이며, 이것이 self가 당신이 하지 않고도 전달되는 방법입니다. 두 패턴이 실제 코드에서 보상을 받습니다. 변경하는 메서드에서 self를 반환하면 유연한 인터페이스를 제공합니다(c.scale(2).scale(0.5)와 같은 체이닝된 호출); 빌더 스타일 객체에만 예약하세요. 여기서 체이닝은 잘 읽히고, 모든 것에는 아닙니다. self를 반환하는 메서드와 새 값을 반환하는 메서드는 호출 사이트에서 동일하게 보이기 때문입니다. 그리고 바인딩된 메서드가 일급 값이므로, 하나를 저장할 수 있습니다(handler = obj.save) 나중에 호출하면, 이것이 콜백과 이벤트 핸들러가 의존하는 것입니다.

python
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.159
Juno메서드 메서드는 클래스 내부에 정의된 함수이며, 첫 번째 매개변수는 항상 self이고, 그것이 작동하는 인스턴스입니다. 파이썬이 `self`를 전달하므로, c.area()를 추가로 호출합니다. self를 통해 메서드는 그 객체 자신의 데이터를 읽고 변경합니다.
Juno메서드 메서드는 클래스의 일반 함수입니다; 인스턴스를 통해 도달하면 `self`를 자동으로 바인딩하므로, 나머지만 전달합니다. c.scale(2).scale(0.5)와 같은 체이닝을 원할 때 self를 반환하세요. 그렇지 않으면 호출자가 요청한 값을 반환합니다.
Juno메서드obj.method는 바인딩된 메서드를 제공하고, obj는 `self`로 연결된 함수이며, 이것이 당신이 그것을 전달하지 않는 이유입니다. self를 반환하면 유연한 체이닝을 얻습니다. 빌더에게 가치가 있고, 모든 메서드가 그것을 하면 혼란스럽습니다. 그리고 바인딩된 메서드가 값이므로, handler = obj.save는 나중에 콜백에 대해 그것을 저장합니다.

클래스 변수 vs 인스턴스 변수

클래스에 직접 정의된 변수(__init__ 내부가 아님)는 클래스 변수입니다. 모든 인스턴스는 같은 클래스 변수를 공유합니다. __init__ 내부의 self에 설정된 변수는 인스턴스 변수이며, 각 객체에 고유합니다.

클래스 변수는 클래스에 한 번 저장되고 모든 인스턴스에서 공유합니다. 인스턴스 변수는 개별 객체에 저장됩니다. self.attr을 읽을 때, 파이썬은 먼저 인스턴스를 확인한 다음 클래스를 확인합니다. self.attr = value를 쓸 때, 그것은 항상 인스턴스의 자신의 복사본을 만들거나 업데이트하므로, 그 객체에만 클래스 변수를 숨깁니다.

self.attr을 읽으면 먼저 인스턴스를 확인한 다음 클래스를 확인하므로, 클래스 변수는 어떤 인스턴스가 자신의 값을 쓸 때까지 공유된 기본값으로 작동하고 그것을 음영합니다. 프로덕션에서 물어보는 덫은 변경 가능한 클래스 변수(목록이나 딕셔너리와 같이 제자리에 변경할 수 있는 것)입니다. self.x = ... 할당하는 것은 개인 복사본을 만들지만, self.x.append(...) 그렇지 않습니다: 그것은 하나의 공유 객체에 도달하고 모든 인스턴스가 변경을 봅니다. 인스턴스마다 새로운 목록을 원한다면, __init__에서 self.history = []로 만드세요. 절대로 클래스 레벨의 history = []로는 않습니다. 클래스 변수를 정말로 공유되는 것으로 예약하고, 그들을 불변으로 유지하세요: 상수, 기본값, 카운터.

python
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    # 모든 현재 및 미래 인스턴스에 대해 변경

클래스 변수를 모든 인스턴스에서 공유되는 값에 사용하세요: 상수, 카운터, 기본값. 인스턴스 변수를 객체마다 다른 데이터에 사용하세요.

Juno클래스 변수 vs 인스턴스 변수 클래스에 바로 설정된 변수는 모든 인스턴스에서 공유됩니다; `__init__`의 `self`에 설정된 변수는 그 하나의 객체에 속합니다. self.attr = value를 쓰는 것은 항상 인스턴스의 자신의 복사본을 만들거나 업데이트합니다. 따라서 클래스 변수에 도달할 때 모든 사람이 같은 값을 가지고, 인스턴스 변수일 때 객체마다 다릅니다.
Juno클래스 변수 vs 인스턴스 변수 `self.attr`을 읽으면 인스턴스를 확인한 다음 클래스를 확인하므로, 클래스 변수는 공유된 기본값입니다. `self.attr = value`를 쓰면 항상 인스턴스에 내려앉고 그 객체에 대해 클래스 변수를 숨깁니다. 클래스 변수를 상수와 기본값으로 유지하고, 객체별 상태는 `self`에 갑니다.
Juno클래스 변수 vs 인스턴스 변수 조용한 버그는 변경 가능한 클래스 변수입니다: `self.x = ...`는 개인 복사본을 만들지만 `self.x.append(...)`는 하나의 공유 객체를 변경하므로, 모든 인스턴스가 그것을 봅니다. 인스턴스별 목록을 원하면, `__init__`에서 `self.history = []`로 만드세요. 절대로 클래스 레벨의 `history = []`로는 않습니다.

__str____repr__

__str__은 당신의 객체에 대해 print()와 f-문자열이 표시하는 것을 제어합니다. __repr__은 콘솔에서 표시되고 디버깅에 대한 개발자 보기를 제어합니다. 항상 __repr__을 정의하세요. 디버그 보기와 별도로 깔끔한 사용자 대면 디스플레이를 원할 때 __str__을 정의하세요.

__str__str(), print(), 그리고 f-문자열에 의해 호출됩니다(출력 장에서), 사용자 대면 텍스트. __repr__repr()에 의해 호출되고 콘솔에서 객체가 출력될 때 표시되며, 개발자 보기. 오직 __repr__만 정의되면, 파이썬은 둘 다에 사용합니다. 규칙: __repr__은 객체를 다시 만드는 코드처럼 보이는 문자열을 반환합니다; __str__은 읽을 수 있는 요약을 반환합니다.

이들은 두 던더 메서드입니다(더블 언더스코어의 약자: 파이썬이 당신을 위해 올바른 순간에 호출하는 선행과 후행 __로 이름이 지어진 메서드). print(obj)str(obj)__str__을 호출하며, __str__이 없을 때 __repr__로 대체됩니다. repr(obj), 그리고 콘솔에서 보이는 객체는 __repr__을 호출합니다. 따라서 __repr__은 항상 작업이 있는 것입니다. 이것이 규칙인 이유입니다: 항상 __repr__을 정의하고, 사용자 대면 텍스트가 디버그 텍스트와 다를 때만 __str__을 추가하세요. __repr__을 객체를 만든 생성자 호출처럼 만드세요(Player(name='민수', score=87)) 그리고 필드에서 !r을 사용하세요. 따라서 문자열은 따옴표를 유지하고 흐릿한 공간이나 개행이 숨어있는 대신 나타납니다. 좋은 __repr__은 오전 2시에 로그 라인이나 스택 추적을 읽을 수 있게 하는 것입니다.

python
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__만 정의되면, 파이썬은 둘 다에 사용합니다.

Juno__str__과 __repr____str__print()와 f-문자열이 표시하는 것이며, 친절한 버전. __repr__은 콘솔에서 보이는 개발자 보기입니다. 항상 __repr__을 쓰세요; `__str__`을 잊어도 작업이 있는 것이 하나입니다. 사용자 대면 텍스트를 다르게 읽어야 할 때만 __str__을 추가하세요.
Juno__str__과 __repr____str__print()와 f-문자열에 대한 읽을 수 있는 버전입니다; __repr__은 디버그 버전이며, `__str__`이 없을 때 파이썬은 그것으로 대체됩니다. 항상 __repr__을 정의하고, 객체를 만든 호출처럼 만드세요. 그리고 당신은 나중에 객체가 로그에 나타날 때 자신을 저장합니다.
Juno__str__과 __repr____repr__은 항상 작업이 있고, __str__은 사용자 보기가 다를 때만 있으므로, __repr__을 먼저 정의하고 매번 합니다. 생성자처럼 모양을 만들고 필드에 `!r`을 넣으므로, 흐릿한 공간이나 개행이 나타나 숨습니다. 그 하나의 습관이 오전 2시 스택 추적을 읽을 수 있게 하는 것입니다.

개인 관례

파이썬은 진정한 개인 변수가 없지만, 이름 시작의 단일 언더스코어(_balance)는 "이것은 내부이고, 클래스 외부에서 직접 사용하지 마세요"라는 신호를 하는 관례입니다. 언어로 시행되지 않으며; 다른 개발자에게 통신합니다.

단일 언더스코어(_attr)는 내부 사용을 신호하는 관례입니다. 파이썬은 그것을 시행하지 않지만, 모든 린터, IDE, 개발자는 그것을 "내부"로 읽습니다. 이중 언더스코어(__attr)는 이름 망글링을 트리거합니다: 파이썬은 그것을 _ClassName__attr로 다시 씁니다. 이것은 부분 클래스에서 우발적인 충돌을 방지합니다. 진정한 개인성이 아닙니다; 충돌 회피 메커니즘입니다.

단일 언더스코어는 관례일 뿐입니다: 아무것도 그것을 시행하지 않지만, 린터(코드를 스타일과 가능한 버그에 대해 스캔하는 도구), 편집자, 검토자는 모두 _balance를 "내부, 외부에서 터치하지 마세요"로 읽습니다. 이중 언더스코어는 이름 망글링을 트리거합니다: 파이썬은 __attr_ClassName__attr로 다시 쓰므로 부분 클래스는 부모의 것과 우발적으로 충돌할 수 없습니다. 그것은 충돌 경비원이지, 개인성이 아닙니다: 망글링된 이름은 누군가가 주장하면 여전히 외부에서 도달할 수 있습니다. 광범위하게 부분 클래스될 기본 클래스에서만 도달하세요. 여기서 부분 클래스가 밟을 수 없는 속성이 필요합니다; 일반 코드에서는 단일 언더스코어가 올바른 기본값이고 이중 언더스코어는 대부분 혼동을 만듭니다. 임의의 속성이 모두 설정되는 것을 중지하고 싶으면(이전의 오타 self.scor), 속성 이름의 고정 목록으로 __slots__을 정의하세요: 인스턴스가 있을 수 있는 유일한 속성이라고 파이썬에 알립니다. 따라서 다른 할당은 조용히 붙는 대신 발생시키고, 인스턴스별 메모리를 트림하는 것이 보너스입니다.

python
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으로 이름을 바꿉니다. 드물게 필요합니다. 단일 언더스코어는 대부분의 코드에서 관례입니다.

Juno개인 관례 파이썬은 진정한 개인 변수가 없지만, 선행 언더스코어(_balance)는 "내부, 외부에서 혼자 두세요"라는 합의된 신호입니다. 아무것도 당신을 도달하는 것을 막지 않으며, 다른 개발자들에게 메시지입니다. 미래의 당신을 포함합니다. 이중 언더스코어는 부분 클래스에서 이름 충돌을 피하기 위한 더 드문 도구; 단일 언더스코어는 당신이 매일 사용할 것입니다.
Juno개인 관례 단일 언더스코어는 "내부"를 의미하고, 모든 도구와 검토자가 그것을 존중하더라도 파이썬은 그것을 시행하지 않습니다. 이중 언더스코어는 부분 클래스 이름 충돌로부터 보호하기 위해 `_ClassName__attr`로 망글링하고, 액세스로부터는 보호하지 않습니다. 기본적으로 단일 언더스코어에 도달하세요; 이중 언더스코어는 드뭅니다.
Juno개인 관례 단일 언더스코어는 관례이고, 이중 언더스코어는 `_ClassName__attr`로 망글링하여 부분 클래스 충돌을 멈추고, 둘 다 진정한 개인성이 아닙니다. 광범위하게 부분 클래스된 기본 클래스에 `__attr`을 유지하세요; 다른 곳에서는 혼동을 추가할 뿐입니다. 흐릿한 속성을 완전히 금지하고 오타 `self.scor`을 잡으려면, 고정 목록으로 `__slots__`을 설정하세요.

상속

클래스는 다른 클래스에서 상속할 수 있으며, 자동으로 모든 속성과 메서드를 얻습니다. 그 다음 부분 클래스에서 특정 메서드를 재정의하여 동작을 변경할 수 있습니다. 이것은 공통 기본을 재사용하고 필요한 곳에 특화하도록 합니다.

상속은 "is-a" 관계를 모델링합니다: DogAnimal입니다. 부분 클래스는 부모의 모든 메서드와 속성을 얻고 자신의 버전을 정의하여 어느 것이든 재정의할 수 있습니다. 메서드를 호출할 때, 파이썬은 부분 클래스에서 먼저 봅니다. 그 다음 부모까지 걷습니다. 따라서 재정의되지 않은 메서드는 자동으로 통과합니다. 그 조회 경로는 메서드 해석 순서(MRO)이며, 파이썬이 검색하는 클래스의 정렬 목록입니다.

obj.method를 호출할 때, 파이썬은 고정 순서인 MRO(메서드 해석 순서: 클래스와 그 선조의 평탄화된 목록, Dog.__mro__로 읽을 수 있음)에서 클래스를 검색하고 첫 번째 일치를 사용합니다. 단일 상속은 간단하고, 부분 클래스 다음 부모. MRO가 존재하는 이유는 다중 상속과 다이아몬드 경우(둘 다 공통 할아버지에서 상속하는 두 부모)입니다: 파이썬은 클래스를 주문하므로 할아버지는 한 번 나타나고, 두 부모 이후, 그리고 결정론적으로 호출을 해석합니다. 실용적 지침: 얕은 계층과 단일 상속을 선호하세요. 깊거나 다중 상속 트리는 "어느 메서드가 실제로 실행되었는지"를 추론하기 어렵게 만듭니다. 여러 기본을 섞을 때, 모든 __init__super()로 라우팅하세요(다음 섹션). 따라서 각각은 그 순서를 따라 정확히 한 번 실행합니다.

python
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())

DogCatAnimal에서 __init__을 상속하므로, 자신이 필요하지 않습니다. 그들은 자신의 특정 동작으로 speak()를 재정의합니다.

Juno상속 부분 클래스는 부모가 가진 모든 것을 상속하고, 변경하고 싶은 메서드만 재정의합니다. 재정의하지 않는 것은 무료로 부모에게로 통과합니다. 이것이 전체 요점입니다: 공유 동작을 한 번 쓰고, 그것이 다른 곳에 특화합니다. DogCatAnimal__init__을 재사용하고 speak()만 재정의합니다.
Juno상속 상속은 "is-a"입니다: 부분 클래스는 부모의 메서드를 얻고 필요한 것을 재정의하고, 재정의되지 않은 호출은 통과합니다. 파이썬은 MRO를 걸으면서 메서드를 찾습니다. 부분 클래스 먼저, 그 다음 부모까지. 공유 기본을 재사용하고, 다른 것만 재정의합니다.
Juno상속 파이썬은 MRO를 걸으면서 메서드를 해석하고 첫 번째 일치를 가져가므로, 다이아몬드 경우는 결정론적입니다. 계층을 얕게 유지하고 단일 상속에 의존하세요; 깊거나 다중 상속 트리는 "어느 메서드가 실행되었는지"를 추측 게임으로 바꿉니다. 기본을 섞어야 할 때, 모든 `__init__`을 super()로 체인합니다.

super()

super()은 부모 클래스에서 메서드를 호출합니다. 부모의 동작을 완전히 바꾸는 대신 확장하고 싶을 때 사용하세요: 부모의 __init__을 호출하여 그 설정을 실행한 다음, 부분 클래스가 필요한 것을 추가합니다.

super()은 MRO의 다음 클래스에 메서드 호출을 위임하는 프록시 객체를 반환합니다. 부모가 하나를 가지면 부분 클래스 __init__에서 항상 super().__init__()을 호출하세요. 그것을 건너뛰면 부모의 설정 코드는 실행되지 않으므로, 객체가 부러진 상태로 남을 수 있습니다.

super()은 "나의 직접 부모"를 의미하지 않고, "MRO 내의 다음 클래스"를 의미합니다(이전 섹션의 조회 순서). 이 다음 클래스는 부모이므로, 평구 부분 클래스에서 구별은 학술적으로 보입니다. 하지만 그것은 다중 상속이 작동하게 하는 것입니다: 트리의 모든 클래스가 super().__init__(...)을 호출하면, 각 __init__은 MRO 순서에서 한 번 실행되고, 클래스가 이름에 의해 부모를 하드 코딩하지 않으면, 클래스를 섞는 순간 체인을 깹니다. 그리고 공유된 기본을 두 번 실행할 수 있습니다. 이것이 super().__init__()Animal.__init__(self)가 아니라 쓰는 이유입니다: 부모를 직접 이름 지으면 다른 클래스를 섞는 순간 체인이 깨집니다. 인수 없는 맨 super()은 현대 형식입니다; 오래된 super(Dog, self)는 같은 것입니다. 당신은 여전히 구 버전을 대상으로 하는 코드에서 봅니다.

python
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__()을 호출하세요.

Junosuper()super()는 부모 클래스에 도달하므로, super().__init__()은 당신이 자신을 추가하기 전에 부모의 설정을 실행합니다. 부분 클래스가 자신의 `__init__`을 작성하고 부모가 하나를 가질 때 사용하세요. 건너뛰면 부모의 설정은 절대 실행되지 않으므로, 객체가 반만 만들어진 상태로 남습니다.
Junosuper()super()는 다음 클래스로 위임하므로, 부모가 하나를 가지면 부분 클래스 `__init__`에서 `super().__init__()`을 호출하세요. 잊으면 부모의 설정이 건너뛰어져서 객체가 부러진 상태로 남을 수 있습니다. 부분 클래스가 확장하는 부모에 연결하는 줄입니다.
Junosuper()super()는 "나의 부모"가 아니라 "MRO의 다음 클래스"를 의미합니다. 이것이 `Animal.__init__(self)`를 하드 코딩하면 클래스를 섞으면 체인이 깨지는 이유입니다. 모든 `__init__`이 `super()`을 호출하면, 각각은 순서대로 한 번 실행됩니다. 맨 `super()`은 현대 형식입니다; super(Dog, self)는 같은 것입니다.

클래스 메서드와 정적 메서드

@classmethod는 인스턴스 대신 클래스 자체를 받는 메서드를 만듭니다. 문자열, 파일, 또는 다른 형식에서 인스턴스를 만드는 대체 생성자에 유용합니다. @staticmethod는 조직상의 이유로 클래스 내부에 있는 일반 함수입니다; 인스턴스나 클래스를 받지 않습니다.

@classmethod는 인스턴스가 아닌 cls(클래스)를 첫 번째 인수로 받습니다. 주요 용도는 다양한 입력 형식에서 인스턴스를 만드는 대체 생성자입니다. @staticmethod는 클래스 하에 네임스페이스된 일반 함수; 클래스나 인스턴스에 액세스할 수 없습니다. 생성자에는 @classmethod를 사용하고, 클래스에 논리적으로 연관된 유틸리티 함수에는 @staticmethod를 사용하세요.

@classmethod는 인스턴스 대신 첫 번째 인수로 클래스(cls)를 받고, 자신의 급여를 얻는 세부사항은 cls가 정의된 클래스가 아닌 호출된 클래스라는 것입니다. 따라서 Player.from_stringcls(...)로 만들고 부분 클래스 ProPlayerProPlayer.from_string(...)을 호출하면, Player가 아닌 ProPlayer를 만듭니다. 이것이 정확히 대체 생성자가 클래스 이름을 하드 코딩하는 대신 @classmethod를 사용하는 이유입니다: 상속에서 작동합니다. @staticmethod는 인스턴스나 클래스를 받지 않습니다; 논리가 클래스와 함께 속하지만 데이터가 필요 없을 때 네임스페이싱에 대해 클래스 내에 주차된 일반 함수입니다. 어떤 포맷에서 인스턴스를 만들 때 classmethod에 도달하고, 관련 보조에는 staticmethod, 하나의 객체의 데이터를 만지는 것에는 일반 메서드에 도달하세요.

python
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")
python
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를 사용하세요.

Juno클래스 메서드와 정적 메서드@classmethod는 인스턴스 대신 클래스를 당신에게 전달하므로, 대체 생성자를 위한 편의점입니다: 당신이 가진 어떤 형식에서든 `Player`를 만드세요. @staticmethod는 단정을 위해 클래스 내부에 숨겨진 일반 함수; 클래스나 인스턴스를 받지 않습니다. 메서드는 하나의 객체의 데이터를 만집니다, 이 둘은 그렇지 않습니다.
Juno클래스 메서드와 정적 메서드@classmethod는 `cls`를 취하므로, 생성자를 위해 그것을 사용하되 `cls(...)`를 반환합니다. @staticmethod는 클래스 아래에 네임스페이스된 도우미입니다. 인스턴스 데이터를 위한 메서드, 생성자를 위한 classmethod, 관련 유틸리티를 위한 staticmethod.
Juno클래스 메서드와 정적 메서드 classmethod 생성자의 요점은 `cls`가 호출자이므로 `ProPlayer.from_string(...)`은 `Player`가 아닌 `ProPlayer`를 만듭니다. 클래스 이름을 하드 코딩하면 그 손실은 클래스를 섞는 순간입니다. 건설하려면 classmethod, 관련 도우미를 위한 staticmethod, 인스턴스를 만지는 모든 것을 위한 일반 메서드.

@property

@property는 메서드를 괄호 없이 속성처럼 액세스하도록 합니다. 다른 속성에서 계산되고 단순 속성 액세스로 읽는 것이 자연스러운 값에 사용하세요.

@property는 메서드를 읽기 전용 속성으로 바꿉니다. 속성에 액세스할 때 메서드가 실행됩니다. 이것은 저장된 데이터에서 파생된 계산된 값, 그리고 공개 인터페이스를 변경하지 않고 속성 액세스에 검증을 추가하는 데 유용합니다. 쌍을 이루는 @name.setter는 속성을 쓰기 가능하게 합니다.

@property는 메서드를 속성처럼 읽는 것으로 바꿉니다: c.area, 괄호 없음, 매번 메서드를 실행합니다. @area.setter를 추가하면 쓰기 가능해지고, 코드가 실행되어, 검증이 가는 곳입니다(음수 반지름을 저장되기 전에 거부). 실제 가치는 호출자에게 아무것도 변경하지 않는다는 것입니다: 일반 self.radius 속성으로 시작할 수 있고 나중에 검증을 가진 속성으로 승격하되, 그것을 읽거나 쓰는 단일 줄을 만지지 않으면 됩니다. 이것이 파이썬이 getter/setter 의식이 없는 이유이고, 당신은 평구 속성을 노출하고 하나가 계산이나 경비가 필요할 때만 속성에 도달합니다. 프로덕션을 위한 두 가지 경고: 액세스는 무료처럼 보이지만 코드를 실행하므로 싸게 유지하세요(비싼 결과를 캐시하기보다는 매번 다시 계산합니다). 그리고 조용히 무거운 작업이나 발생시키는 속성은 그것이 간단한 필드라고 가정한 사람을 놀라게 합니다.

python
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

속성은 계산된 값에 유용합니다: 다른 속성에서 파생되는 것들이 ()없이 평구 데이터로 액세스하는 것이 자연스럽습니다.

Juno@property@property는 메서드를 속성처럼 읽을 수 있게 합니다. 괄호 없음: c.area 대신 c.area() 대신. 다른 속성에서 일해졌고 평구 데이터로 읽는 것이 자연스러운 값에 맞습니다. 뒤에서 매번 메서드를 액세스할 때 실행합니다.
Juno@property@property는 메서드를 속성처럼 읽고, 쌍을 이루는 `@name.setter`는 그것을 쓰기 가능하게 하므로 들어오는 경로를 검증할 수 있습니다. 승리: 당신은 나중에 호출자를 변경하지 않고 평구 속성을 계산 또는 경비된 것으로 켤 수 있습니다. 계산 또는 경비 값에 사용하세요, 모든 필드에 습관으로서가 아닙니다.
Juno@property `@property`를 좋아하는 이유: 평구 속성은 나중에 호출자에게 변경 없이 계산 또는 경비된 것이 될 수 있습니다. 따라서 getter와 setter가 필요할 때까지 건너뜁니다. 액세스는 무료처럼 보이지만 코드를 실행하므로 싸게 유지하고, 비싼 것들을 캐시하고, 무거운 작업이나 발생을 필드처럼 보이는 것 뒤에 숨기지 마세요.

실제로

인스턴스 속성, 메서드, @property, 그리고 __str__을 가진 Player 클래스:

python
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

개인 속성을 @property getter, deactivate 메서드, 그리고 to_dict 직렬화기가 있는 User 클래스:

python
class User:
    def __init__(self, user_id: int, username: str, email: str):
        self.id = user_id
        self.username = username
        self.email = email
        self._active = True

    @property
    def active(self) -> bool:
        return self._active

    def deactivate(self) -> None:
        self._active = False

    def to_dict(self) -> dict:
        return {
            "id": self.id,
            "username": self.username,
            "email": self.email,
            "active": self._active,
        }

    def __repr__(self) -> str:
        return f"User(id={self.id}, username={self.username!r})"

alice = User(1, "alice", "alice@example.com")
print(alice.to_dict())
alice.deactivate()
print(alice.active)   # False

속성 뒤에 train/validation 분할을 캡슐화하는 DataSplit 클래스이며, 깔끔한 디버그 출력을 위한 __repr__:

python
class DataSplit:
    def __init__(self, data: list, train_ratio: float = 0.8):
        split = int(len(data) * train_ratio)
        self._train = data[:split]
        self._val = data[split:]

    @property
    def train(self) -> list:
        return self._train

    @property
    def val(self) -> list:
        return self._val

    @property
    def sizes(self) -> tuple[int, int]:
        return len(self._train), len(self._val)

    def __repr__(self) -> str:
        return f"DataSplit(train={len(self._train)}, val={len(self._val)})"

data = list(range(100))
split = DataSplit(data, train_ratio=0.8)
print(split)         # DataSplit(train=80, val=20)
print(split.sizes)   # (80, 20)

_train_val의 언더스코어 접두사는 호출자가 원시 목록을 직접 변경하는 대신 속성을 통과해야 한다는 신호입니다. 파이썬은 이것을 시행하지 않습니다. 하지만 명확한 계약을 설정합니다.