Python

[파이썬 코드 업] 9-1장. 클래스와 매직 메서드

patrick-star 2023. 5. 6. 12:54
728x90

대부분 클래스를 정의할 수 있는 기능을 갖고 있다. 파이썬은 클래스에 매직 메서드라는 기능을 추가했다.

9.1 클래스와 객체 기본 문법

class Car : 
    accel = 3.0
    mpg = 25 

car1 = Car()
car2 = Car()

print('car1.accel : ' , car1.accel) # 3.0 출력
print('car1.mpg : ' , car1.mpg) # 25 출력

print('car2.accel : ' , car2.accel) # 3.0 출력
print('car2.mpg : ' , car2.mpg) # 25 출력 

car1이 갖고 있는 인스턴스의 변수를 내 맘대로 수정할 수도 있다.

car1.accel = 5.0
car1.mpg = 300

>>> car1.accel
5.0
>>> car1.mpg       
3000

>>> car2.accel
3.0
>>> car2.mpg  
25

9.2 인스턴스 변수

파이썬의 인스턴스 변수는 클래스 안에서 직접 생성되지 않는다.
필요할 때 마다 바로 생성되거나 __init__ 메서드 안에서 생성된다.

자바와 같은 다른 언어는 인스턴스 변수도 사용하기 전에 멤버 변수로 미리 정의해야 하지만,
파이썬 인스턴스 변수는 미리 정의하지 않고 필요할 때 바로 값을 대입한다는 것을 의미함

ex) Dog 클래스로 생성한 인스턴스에 여러 속성 추가

class Dog : 
    pass # 처리할 것이 없을 때 pass 키워드 사용하면 됨 

my_dog = Dog() # Dog 클래스의 인스턴스를 생성 

# 원래 Dog 클래스에서 정의하지 않은 name, breed, age 라는 멤버변수를 
# 바로 만들어내서 대입하는 코드 

my_dog.name = 'Chanm"
my_dog.breed = 'Great Dane'
my_dog.age = 5

이렇게 3개의 변수를 my_dog 객체에 할당했다. 각 변수에 접근하고 싶다면 my_dog.name,my_dog.breed, my_dog.age으로 접근하면 된다.

당연히 name, breed, age는 my_dog 인스턴스에만 할당된 변수이지
Dog 클래스를 가지고 만든 다른 인스턴스에는 해당 변수들이 저장되지 않는다.

그렇다면.. 어떻게 동일한 클래스의 모든 객체가 같은 속성(인스턴스 변수들)을 가질 수 있을까?
이를 위해 __init__ 메서드를 사용한다.

9.3 __init__ 메서드, __new__ 메서드

클래스에서 __init__ 메서드를 정의했다면 해당 클래스의 인스턴스가 생성될 때 마다 자동으로 __init__ 메서드가 호출된다.
클래스의 모든 인스턴스가 동일한 변수들의 집합을 가지면서 각 인스턴스 별로 독립적인 값을 갖도록 할 때 사용할 수 있다.

class 클래스이름 : 
    def __init__ (self, args) : 
        코드 

ex) Dog 클래스

class Dog : 
    def __init__ (self, name, breed, age) :
        self.name = name
        self.breed = breed
        self.age = age

위와 같이 정의하면 Dog의 객체를 생성할 때 마다 반드시 3개의 인수를 입력해야 한다.
그 인수들은 __init__ 메서드로 전달되면서 해당 인스턴스의 변수에 값을 넣어준다.

top_dog = Dog('Hand', 'Bulldog', 10) 

__new__ 메서드는 2가지 상황에서만 사용된다.

1) 메모리 할당을 위한 특별한 기법을 사용하는 경우
2) 하위 클래스가 불변(immutable)이거나 내장(built-in) 클래스인 경우

9.4 클래스와 선행 참조 문제

class Marriage : 
    def __init__ (self) :
        self.wife = Person('f')
        self.husband = Person('m')

a_marriage = Marriage() # 코드 순서를 보면 아직 Person 클래스를 정의하지 않았다. 
                        # 하지만, Marriage 클래스의 인스턴스를 생성하는 과정에서
                        # 아직 생성하지 않은 클래스를 사용하려고 하기 때문에 오류가 발생한다. 

class Person : 
    def __init__ (self, gender) :
        self.gender = gender

위에서는 Marriage 클래스에서 Person 클래스의 인스턴스를 생성하려고 하니까
Person 클래스의 선언이 완료된 이후에 Marriage 클래스이 인스턴스를 생성해야 한다.

9.6 전역변수/ 메서드와 지역변수/ 메서드

객체 지향 언어를 사용하는 목적 중 하나가 캡슐화다. 캡슐화를 통해 클래스 내부의 내용을 바깥으로 노출하지 않도록 한다.

파이썬의 철학은 이 사상과 대치된다. 스크립팅 언어인 파이썬은 모든 것을 노출하는 경향이 있으며 모든 것을 볼 수 있다.
때문에, 다른 언어에 비해 보안과 타입 확인 능력(3.6에서는 다소 개선됨) 이 다소 부족하다.

대신, 파이썬은 유용한 규약을 따르고 있다.

  • _ 1개로 변수와 메서드 이름이 시작 : 내부용
  • _ 2개로 변수와 메서드 이름이 시작 : 맹글링(mangling, 의도치 않은 접근을 통제)

ex)

class Odd : 
    def __init__(self) :
        self.x = 10
        self.y = 20
        self.__z = 30 # 맹글링 적용

    def pr(self) :
        print('__z = ' , self.__z) # 클래스 내부에서는 맹글링 없이 접근 가능

>>> o.x
10
>>> o.y
20
>>> o.__z # 에러 발생
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'Odd' object has no attribute '__z'

에러가 발생하는 이유는 파이썬이 변수 __z를 클래스 이름과 변수 이름의 조합으로 훼손된(mangled) 이름으로 교체하기 때문이다.

하지만, 클래스 내부에서는 맹글링 없이 접근할 수 있다.

9.7 상속

파이썬은 하위 클래스 만들기(subclassing), 상속(inheritance)을 지원한다.

  • 상위 클래스를 단일 상속
class 클래스이름(상위_클래스) : 
    코드 

위와 같이 만든 클래스는 상위 클래스의 모든 클래스 변수와 메소드를 상속받게 된다.
새로운 변수, 메서드 정의를 추가할 수 있고 기존에 정의된 메서드를 재정의할 수도 있다.

ex)

class Mammal : 
    def __init__ (self, name, size) :
        self.name = name
        self.size = size

    def speak(self) :
        print("My name is" , self.name)

    def call_out(self) :
        self.speak()
        self.speak()

class Dog(Mammal) : # Mammal 클래스를 상속받은 Dog 클래스 
    def speak(self) : # Mammal 클래스에서 정의되어 있던 speak 메서드를 재정의
        print("월월!!")

class Cat(Mammal) : # Mammal 클래스를 상속받은 Cat 클래스 
    def speak(self) : # Mammal 클래스에서 정의되어 있던 speak 메서드를 재정의
        print("야옹!")

>>> my_cat = Cat('Pre', 11)
>>> my_cat.call_out() # speak 메서드가 Cat 클래스에서 재정의한 걸 볼 수 있다
야옹!
야옹! 

위 코드에서 Dog, Cat 클래스가 추가적인 초기화 작업을 진행하고 싶다면 어떻게 해야 할까? super().__init__을 이용하면 된다.

class Dog(Mammal) :
    def speak(self) :
        print("월월!!")

    def __init__ (self, name, size, breed) :
        super().__init__(name, size) # 상위 클래스에서 상속받은 인스턴스 변수들을 초기화하고 
        self.breed = breed              # 추가로 정의한 변수를 초기화한다. 

9.8 다중 상속

하나의 클래스가 2개 이상의 상위 클래스를 상속받을 수 있다.

class 클래스이름(상위_클래스1, 상위_클래스2, 상위_클래스3, ...) : 
    코드 

ex) Dog 클래스가 Mammal, Pet, Carnivore 라는 3개의 클래스를 상속받고 있다고 하자.

class Dog(Mammal, Pet, Carnivore) : 
    def speak(self) :
        print("월월!!")

    def __init__ (self, name, size, nickname, breed) :
        Mammal.__init__(self, name, size)
        Pet.__init__(self, nickname)
        self.breed = breed