오늘은 아래 클래스에 점진적으로 기능을 추가한다.
class Vector2D:
def __init__(self, x, y):
self.x = x
self.y = y
객체 표현 메서드
- repr(): 개발자에게 유용한 형태로 객체를 표현한 문자열 반환 (콘솔이나 디버거)
- str(): 사용자에게 유용한 형태로 객체를 표현한 문자열 반환 (print문)
@classmethod
와 @staticmethod
- @classmethod: 클래스에 연산을 수행하는 메서드를 정의. 클래스 자체를 첫 번째 인수로 받음
- @staticmethod: 메서드가 특별한 첫 번째 인수를 받지 않게 메서드를 변경
포맷된 함수
f-문자열
, format()
내장함수, str.format()
메서드는 각 자료형이 가진 __format__(format_spec)
메서드를 호출함
number = 42
print(f"{number:04}") # f-string
print(format(number, "04")) # format() 내장함수
print("{:04}".format(number)) # str.format() 메서드
- 모두 내부적으로
number.__format__("04")
호출
class Vector2D:
...
def __format__(self, format_spec):
return f"({format(self.x, format_spec)}, {format(self.y, format_spec)})"
v = Vector2D(3.1415, 2.718)
print(f"{v:.2f}") # (3.14, 2.72)
- vector2d클래스에 문자열 포매팅 함수 추가
해시가능한 Vector2d
해시 가능하게 (__hash__
+ __eq__
) 추가
__hash__()
메서드는 int 형을 반환해야 함.- 그리고 동일하다고 판단되는 객체는 해시값이 동일해야 하므로
__eq__()
메서드가 비교하는 객체 속성을 이용해 해시를 계산하는 것이 이상적임!!
class Vector2D:
...
def __eq__(self, other):
return isinstance(other, Vector2D) and self.x == other.x and self.y == other.y
def __hash__(self):
return hash((self.x, self.y))
s = {Vector2D(1, 2), Vector2D(1, 2)}
print(len(s)) # 1
- set, dict 의 키로도 사용 가능
위치 패턴 매칭 지원
__match_args_-()
클래스 속성 추가
class Vector2D:
def __init__(self, x, y):
self.x = x
self.y = y
v = Vector2D(1, 2)
match v:
case Vector2D(x=1, y=2):
print("Matched!")
- 적용 전
class Vector2D:
__match_args__ = ("x", "y")
def __init__(self, x, y):
self.x = x
self.y = y
v = Vector2D(1, 2)
match v:
case Vector2D(1, 2):
print("Matched!")
- 적용 후
Vector2D(x=1, y=2)
->Vector2D(1, 2)
이런 식으로 위치 기반 매칭 가능해짐
from dataclasses import dataclass
@dataclass
class Vector2D:
x: int
y: int
v = Vector2D(1, 2)
match v:
case Vector2D(1, 2):
print("Matched!") # dataclass는 자동으로 __match_args__ 생성
- 애초에
dataclass
활용해서 클래스 생성하면 자동! dataclass
까먹었으면? -> [Python] 데이터 클래스 빌더
파이썬에서의 비공개 속성과 보호된 속성
자바와 달리 파이썬에는 private
수정자 같은게 없다.
하지만 비공개
하려는 속성을 서브클래스에서 실수로 변경하지 못하게하는 간단한 메커니즘은 있다.
네임 맹글링
인스턴스 속성 명을 두 개의 언더바로 시작하고 언더바가 없거나, 하나의 언더바로 끝나도록 정의하면
파이썬은 언더바와 클래스명을 변수명 앞에 붙여 인스턴스의 __dict__
에 저장한다.
-> 클래스 외부에서 실수로 접근하지 않도록 보호하는 용도
class Test:
def __init__(self):
self.__value = 42
t = Test()
print(t.__value) # AttributeError!
- 사실은 이름이 바뀌어 있음:
print(t._Test__value) # 42
- 이런식으로 이름을 살짝 꼬아서 내부에서만 쓰게 만드는 것 == 네임 맹글링
- 진짜 보안은 아니고 개발자들끼리 약속하는 것
- 근데 언더바 두 개는 디버깅 시 불편함을 초래할 수 있어서 기본적으로는 언더바 하나로 외부 접근을 막는 신호만 줌
__slots__
로 메모리 절약
__slots__
을 쓰면 클래스 인스턴스가 미리 정해진 속성만 저장하도록 제한하고,__dict__
사용을 없애면서 메모리 사용량을 줄임
일반적으로 파이썬에서 클래스를 만들면, 인스턴스 속성은 __dict__
에 저장됨
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
p = Point(1, 2)
print(p.__dict__)
# {'x': 1, 'y': 2}
- 유연해서 언제든지 속성 추가 가능
p.z = 3
print(p.__dict__)
# {'x': 1, 'y': 2, 'z': 3}
- 하지만 속성 저장을 위해 인스턴스마다
__dict__
가 필요 -> 인스턴스를 많이 생성하면 메모리 낭비가 많아짐
class Point:
__slots__ = ('x', 'y')
def __init__(self, x, y):
self.x = x
self.y = y
__slots__
적용 시,__dict__
이 생기지 않음- 하지만
__slots__
에서 정의한 속성 외에 추가 불가능
p = Point(1, 2)
print(p.x) # 1
p.z = 3 # AttributeError: 'Point' object has no attribute 'z'
클래스 속성 오버라이드
클래스 속성을 객체 속성의 기본값으로 사용할 수 있다는 점은 파이썬의 독특한 특징이다.
class Dog:
species = 'Canis familiaris' # 클래스 속성
dog1 = Dog()
dog2 = Dog()
print(dog1.species) # Canis familiaris
print(dog2.species) # Canis familiaris
그러나 존재하지 않는 인스턴스 속성에 값을 저장하면, 새로운 인스턴스 속성을 생성하고, 동일한 이름의 클래스 속성은 변경하지 않는다.
dog1.species = 'Canis lupus' # dog1 인스턴스에 species 속성 추가
print(dog1.species) # Canis lupus (인스턴스 속성!)
print(dog2.species) # Canis familiaris (클래스 속성 그대로!)
그 후 인스턴스가 그 속성을 읽을 때 인스턴스 자체의 속성을 가져오므로, 클래스 속성과는 독립적임
클래스 속성을 바꾸면, 인스턴스 속성이 없는 애들만 영향을 받게 됨
Dog.species = 'Canis major'
print(dog1.species) # Canis lupus (dog1 은 인스턴스 속성이 있으니까 안 바뀜!)
print(dog2.species) # Canis major (dog2 는 클래스 속성을 따라가니까 바뀜!)
각 객체마다 서로 다른 속성 값을 갖도록 커스터마이징이 가능해짐
class Bulldog(Dog):
species = 'Canis bulldogus'
b = Bulldog()
print(b.species) # Canis bulldogus
클래스 속성은 공개되고, 모든 서브클래스가 상속함
클래스 데이터 속성을 커스터마이즈할 때는 클래스를 상속하는 것이 일반적
인스턴스 속성 조회 순서
- 인스턴스의
__dict__
- 클래스의
__dict__
- 상속받은 부모 클래스의
__dict__
- 에러 (AttributeError)
References
- 『 전문가를 위한 파이썬(2판) 』, 루시아누 하말류, 한빛미디어, Part 1 - Chapter 11
'CS' 카테고리의 다른 글
[Python] 인터페이스, 프로토콜, 추상 베이스 클래스 (1) | 2025.04.13 |
---|---|
[Python] 시퀀스 특별 메서드 (0) | 2025.04.12 |
[Python] 일급 함수 디자인 패턴 (2) | 2025.04.06 |
[Python] 데코레이터와 클로저 (0) | 2025.04.05 |
[Python] 함수에서의 자료형 힌트 (0) | 2025.04.05 |