수달이네 기술 블로그

3. 파이썬 매직 메서드, 객체지향, 예외처리 본문

언어/Python

3. 파이썬 매직 메서드, 객체지향, 예외처리

슬픈 수달이 2025. 11. 10. 21:50

더블 언더스코어(__)로 시작하고 끝나는 메서드 이름을 갖는 메서드들

특정 구문, 내장함수를 사용할 때 파이썬 인터프리터에 의해 자동으로 호출된다.

repr

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

    def __repr__(self):
        return f"Dog(name='{self.name}', age={self.age})"
 Rucy = Dog('루시', 15)
 print(repr(Rucy))
 print(Rucy)

# Dog(이름='루시', 나이=15)
# Dog(이름='루시', 나이=15)

위처럼 객체값을 출력할 때 나오는 값을 정해준다.

eval()

주어진 문자열을 파이썬 표현식으로 인식하고 실행하여 결과를 반환

x = 10
y = 3
result = x + y
print(result)
result = eval("x + y")
print(result)

# 13
# 13

이걸 이용하여

위의 repr을

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

    def __repr__(self):
        return f"Dog(name='{self.name}', age={self.age})"

위와 같이 변수명과 같이 넣어준다면,

rucy_repr = repr(rucy)
result = eval(rucy_repr)

위와 같이 객체를 생성해 줄수도 있으며 해당 객체는

print(result)
print(result == rucy)

# Dog(name='루시', age=15)
# False

실제로 작동하며, 원래 rucy와 다른 객체로 나오게 된다.

str()

class Book:
    def __init__(self, title):
        self.title = title
        
book = Book('미친듯이 재밌는 파이썬')
print(book)
print(str(book))

# <__main__.Book object at 0x0000021785611D30>
# <__main__.Book object at 0x0000021785611D30>

class Book:
    def __init__(self, title):
        self.title = title
    def __str__(self):
        return self.title

book = Book('미친듯이 재밌는 파이썬')
print(book)
print(str(book))

# 미친듯이 재밌는 파이썬
# 미친듯이 재밌는 파이썬

위처럼 객체값을 출력할 때 나오는 값을 정해준다.

그러나 __repr__과의 차이점은 repr의 경우 개발자가 실제 객체가 뭔지 알기 위해 사용하는 것인데,

__str__의 경우 사용자가 원하는 내용을 찍어주고 싶을 때 사용한다. 즉, 객체의 내용이 중요하지 않다.

그렇다면 두 함수 모두 있을때는?

  • str이 없으면 repr을 출력.
  • str이 있으면 str을 출력
class Book:
    def __init__(self, title):
        self.title = title
    def __str__(self):
        return f"str='{self.title}'"
    def __repr__(self):
        return f"repr='{self.title}'"

# str='미친듯이 재밌는 파이썬'
# str='미친듯이 재밌는 파이썬'

len()

클래스의 길이를 반환해주는 함수

class Queue:
    def __init__(self):
        self.items = [1, 2, 3, 4, 5]

    def __len__(self):
        return len(self.items)

li = [1, 2, 3, 4, 5]
print(len(li))
print(li)

queue = Queue()
print(queue)
print(len(queue))

# 5
# [1, 2, 3, 4, 5]
# <__main__.Queue object at 0x0000021785612A50>
# 5

위처럼 원래 구현되지 않은 len함수를 작동되도록 한다.

getitem()

파이썬에서 인덱싱을 지원하기 위해 사용하는 메소드

  • 객체의 특정 인덱스 혹은 키를 반환한다.
class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

pt = Point(5, 3)
print(pt)
print(pt[0])
print(pt[1])
print(pt[-100])

# <__main__.Point object at 0x0000021785612BA0>
#---------------------------------------------------------------------------
# TypeError: 'Point' object is not subscriptable

만약 인덱스를 사용하고 싶어서 위처럼 사용할 경우 오류가 생기는 것을 볼 수 있다

class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __getitem__(self, index):
        if index == 0:
            return self.x
        elif index == 1:
            return self.y
        else:
            return -1

pt = Point(5, 3)
print(pt)
print(pt[0])
print(pt[1])
print(pt[-100])

# <__main__.Point object at 0x0000021785612CF0>
# 5
# 3
# -1

위처럼 여러 변수가 담긴 클래스를 인덱스로 변수 하나하나 가져올 수 있다.

call()

파이썬에서 객체를 함수처럼 호출할 수 있는 메서드

class CallableObject:
    pass
    
coj = CallableObject()
coj()

# ---------------------------------------------------------------------------
# TypeError: 'CallableObject' object is not callable

당연히 위처럼 함수를 부르는 것은 불가능 하다. 하지만

class CallableObject:
    def __call__(self, *args, **kwargs):
        print(f'args:{args}, kwargs:{kwargs}')
       
coj = CallableObject()
coj(1,2,3, a = 'A', b = 'B')

# args:(1, 2, 3), kwargs:{'a': 'A', 'b': 'B'}

위처럼 argument와 keyward argument를 이용하여 함수를 만들어줄 수 있다.

파이썬 객체지향의 4대 패러다임

캡슐화(Encapsulation)

객체 안에 데이터(속성)와 메서드(함수)를 감추고, 외부에 필요한 기능만 제공하는 것

왜 캡슐화가 필요한가?

  • 보안: 다른데서 객체를 생성한 후 값을 임의로 넣어 잘못된 결과를 만들 수 있다.
  • 위로 인해 코드가 오류가 생길수도 있다.
class Animal:
    def __init__(self, name, age):
        self.name = name
        self.__age = age  # __로 시작하면 private 속성

    def get_age(self):
        return self.__age

    def set_age(self, age):
        if age > 0:
            self.__age = age
        else:
            print("나이는 양수여야 합니다.")

파이썬에서는 private속성으로 만들기 위해서는 __변수명을 이용한다.

직접 접근은 불가하지만 접근하기 위해서 get메서드를 만들어 접근할 수 있으며, set메서드를 만들어 넣어줄 수 있다.

class Animal:
    def __init__(self, name, age):
        self.name = name
        self.__age = age  # __로 시작하면 private 속성

    def get_age(self):
        return self.__age

    def set_age(self, age):
        if age > 0:
            self.__age = age
        else:
            print("나이는 양수여야 합니다.")

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

    def bark(self):
        print(f"{self.name}가 멍멍 짖습니다!")

# 사용 예
dog = Dog("루시", 15, "포메")
dog.bark()  # 루시가 멍멍 짖습니다!

print(dog.name)      # 루시
print(dog.breed)     # 포메
print(dog.get_age())

dog.set_age(14)
print(dog.get_age()) # 14

dog.__age = 100      # 외부에서 수정 시도 (실제 속성은 바뀌지 않음)
print(dog.get_age()) # 여전히 14

# 루시가 멍멍 짖습니다!
# 루시
# 포메
# 15
# 14
# 14

위처럼 사용 가능하다

상속(inheritance)

기존 클래스(부모)의 속성과 기능을 새로운 클래스(자식)가 물려받아 재사용하는 것

class Animal:
    def speak(self):
        print("동물이 소리를 냅니다.")

class Dog(Animal):
    def speak(self):
        print("멍멍!")

# 사용 예
a = Animal()
a.speak()  # 동물이 소리를 냅니다.

rucy = Dog()
rucy.speak()  # 멍멍! (부모 메서드를 오버라이딩)

부모 클래스의 메서드를 자식 클래스가 물려받아 사용

다형성 (Polymorphism)

동일한 이름의 메서드가 상황에 따라 다르게 동작할 수 있는 것

class Cat:
    def speak(self):
        print("야옹!")

class Dog:
    def speak(self):
        print("멍멍!")

def make_sound(animal):
    animal.speak()

# 사용 예
c = Cat()
d = Dog()
make_sound(c)  # 야옹!
make_sound(d)  # 멍멍!

같은 이름의 메서드지만 다른 함수가 작동한다.

추상화 (Abstraction)

핵심 개념만 정의하고, 구체적인 내용은 자식 클래스에서 채우도록 설계하는 것 (설계의 틀 제공)

	from abc import ABC, abstractmethod 

class Animal(ABC):
    @abstractmethod 
    def speak(self):
        pass  # 추상 메서드, 자식이 반드시 구현해야 함

class Dog(Animal):
    def speak(self):
        print("멍멍!")

class Cat(Animal):
    def speak(self):
        print("야옹!")

rucy = Dog()
rucy.speak()  # 멍멍!

@abstractmethod: 추상 메서드를 생성

  • 이걸 상속받는 클래스는 반드시 해당 메서드를 구현해야한다.

왜 이걸 사용할까?

  • 한 회사가 기능 하나를 만들었을 때
  • 다른 회사에서 해당 모듈을 사용하여 상속하고, 오버라이딩하여, 그 회사가 가진 정보를 넣어 작동하도록 만들어 줄 수 있다.

예외

print(10 / 3)
print(5 / 0) # ZeroDivisionError: division by zero
print(4 / 2)

파이썬은 에러가 날 경우 다음 실행할 코드가 실행되지 않는다.

만약 이게 서버일 경우, 여러 사용자가 사용하던 중 한 사용자가 이상행동을 하여 오류가 나면, 서버가 닫히며 모든 사용자가 사용 못하게 될 것이다.

  • 이걸 막기 위해 예외처리를 이용해 프로그램을 종료하지 않고 다음 코드를 낼 수 있도록 만든다.

예외 발생 경우

ValueError

  • 잘못된 값을 함수나 연산에 제공할 때 발생합니다.
  • 예) 숫자가 아닌 문자열을 int() 함수로 변환하려고 할 때 발생.

TypeError

  • 올바르지 않은 유형의 객체를 연산에 사용하려 할 때 발생합니다.
  • 예) 문자열과 숫자를 함께 더하려고 할 때 발생.

ZeroDivisionError

  • 숫자를 0으로 나누려고 할 때 발생합니다.

IndexError

  • 리스트, 튜플, 문자열 등의 시퀀스 유형에서 범위를 벗어난 인덱스에 접근하려 할 때 발생합니다.
  • 예) 길이가 3인 리스트에 대해 4번째 요소에 접근하려고 할 때 발생.

KeyError

  • 딕셔너리에서 존재하지 않는 키를 사용하여 값을 검색하려고 할 때 발생합니다.

AttributeError

  • 객체에 없는 속성이나 메서드에 접근하려고 할 때 발생합니다.

FileNotFoundError

  • 존재하지 않는 파일을 열려고 할 때 발생합니다.

ImportError

  • 존재하지 않는 모듈을 가져오려고 할 때 또는 모듈 내에 해당 속성/함수가 없을 때 발생합니다.

NameError

  • 정의되지 않은 변수나 함수를 사용하려고 할 때 발생합니다.
  • 예) 프로그램에서 정의되지 않은 변수 x를 사용하려고 할 때 발생.

OverflowError

  • 수치 연산 결과가 너무 커서 표현할 수 없을 때 발생합니다.

MemoryError

  • 프로그램이 사용 가능한 모든 메모리를 소진했을 때 발생합니다.
try:
    # 예외가 발생할 가능성이 있는 코드
except ExceptionType1:  # 'ExceptionType1'에는 실제 예외 유형이 들어갑니다.
    # ExceptionType1 예외가 발생했을 때 실행될 코드
except ExceptionType2:  # 'ExceptionType2'에는 다른 예외 유형이 들어갑니다.
    # ExceptionType2 예외가 발생했을 때 실행될 코드
# 추가적인 except 블록을 계속 추가할 수 있습니다.
else:
    # try 블록에서 예외가 발생하지 않았을 때 실행될 코드
finally:
    # 예외 발생 여부와 관계없이 항상 실행될 코드

위와 같이 생성할 수 있다.

  • exceptionType이 생략될 경우 모든 오류를 받아들인다
except Exception: 
  • 이것과 같은 내용
try:
    print(10 / 3)
    print(5 / 0)
    print(4 / 2)
except ZeroDivisionError:
    print('예외가 발생했어요')
print('프로그램을 종료합니다')

finally

def hi():
    try:
        data = [10, 20, 30, 40, 50]
        print(data[5])
    except IndexError as e:
        print(e)
        return
    except ValueError as e:
        print(e)
    except ZeroDivisionError as e:
        print(e)
        return
    except Exception as e:
        print(e)
    else:
        print('에러가 발생하지 않은 정상적인 프로그램')
    finally:
        print('에러에 관계없이 무조건 실행되는 문장')
    print('프로그램을 종료합니다')

hi()

# list index out of range
# 에러에 관계없이 무조건 실행되는 문장

위와같은 상황에서 보면 return 이 불러와져 나가야 하지만 finally를 부르고 나간다.

이처럼 오류가 발생하든 안하든 무조건 실행하는 문장이 finally이다

'언어 > Python' 카테고리의 다른 글

0.모듈  (0) 2025.11.12
1. 파이썬 객체지향  (0) 2025.11.05
2. 파이썬에서의 상속  (0) 2025.11.05
17. 스레드  (0) 2025.09.04
16. 이터레이터, 제너레이터  (0) 2025.09.03