개발 언어/파이썬
클래스
jjiiiinn
2024. 7. 10. 10:05
728x90
파이썬의 클래스
- 기본 개념:
- 클래스는 데이터와 기능을 함께 묶는 방법
- 새 클래스 생성은 새로운 객체 타입 생성
- 클래스 인스턴스는 자신만의 속성을 가질 수 있음
- 파이썬 클래스의 특징:
- 최소한의 새로운 문법으로 구현
- 다중 상속 지원
- 메서드 오버라이딩 가능
- 모든 표준 객체 지향 프로그래밍 기능 제공
- 파이썬 클래스의 특별한 점:
- 대부분의 멤버가 public
- 모든 메서드가 virtual (C++ 용어로)
- 내장 타입도 상속 가능
- 연산자 오버로딩 지원
- 동적 생성 및 수정 가능
- 객체와 이름의 관계 (Aliasing):
- 여러 이름이 같은 객체를 참조할 수 있음
- 가변 객체에서 중요한 개념
- 함수에 객체 전달 시 효율적 (포인터와 유사)
- 주요 특징:
- 클래스 자체도 객체
- 임포팅과 이름 변경 지원
- 내장 연산자들을 클래스 인스턴스에 대해 재정의 가능
파이썬 스코프와 이름 공간
- 이름 공간 (Namespace)
- 정의: 이름에서 객체로의 매핑
- 구현: 대부분 파이썬 딕셔너리로 구현됨
- 종류: a) 내장 이름 공간: 내장 함수, 예외 등 b) 모듈의 전역 이름 공간 c) 함수 호출의 지역 이름 공간 d) 객체의 속성
- 특징: 서로 다른 이름 공간의 이름들 간에는 관계가 없음
- 속성 (Attributes)
- 정의: 점(.) 뒤에 오는 모든 이름
- 예:
z.real
에서real
은 객체z
의 속성 - 모듈 속성: 읽기/쓰기 가능, 삭제 가능 (
del
사용)
- 스코프 (Scope)
- 정의: 이름 공간에 직접 접근 가능한 프로그램의 텍스트 영역
- 특징: 정적으로 결정되지만 동적으로 사용됨
- 종류 (검색 순서):
- a) 지역 스코프 (지역 스코프)
- b) 둘러싸는 함수의 스코프 (그 함수를 감싸고 있는 함수)
- c) 전역 스코프 (현재 모듈)
- d) 내장 이름 스코프 (내장 스코프)
- 이름 공간의 생성과 수명
- 내장 이름: 인터프리터 시작 시 생성, 영구 유지
- 모듈 전역 이름: 모듈 정의 읽을 때 생성, 인터프리터 종료 시까지 유지
- 함수 지역 이름: 함수 호출 시 생성, 함수 종료 시 삭제
- 최상위 호출 문장:
__main__
모듈의 일부로 취급
- 이름 바인딩 규칙
- 기본: 대입 연산은 가장 내부 스코프에 새 이름 생성
global
문: 변수를 전역 스코프에 연결nonlocal
문: 변수를 둘러싸는 스코프에 연결- 함수 정의와
import
문: 지역 스코프에 이름 연결
- 특별한 특징
- 스코프는 텍스트적으로 결정되나, 이름 검색은 실행 시 동적으로 수행
- 지역 변수는 이미 정적으로 결정됨
- 모듈의 속성과 전역 이름은 같은 이름 공간 공유
- 대입 연산은 데이터를 복사하지 않고, 이름을 객체에 연결
x = 10 # 전역 변수
def func():
x = 20 # 지역 변수
print("함수 안의 x:", x)
func()
print("함수 밖의 x:", x)
스코프와 이름 공간 예
- 주요 포인트:
- 지역 변수는 해당 함수 내에서만 영향을 미칩니다.
nonlocal
은 가장 가까운 외부 함수의 변수를 참조합니다.global
은 모듈 수준의 변수를 참조하거나 생성합니다.- 각 스코프는 독립적이며, 명시적으로 지정하지 않으면 외부 스코프의 변수에 영향을 주지 않습니다.
def scope_test():
def do_local():
spam = "local spam"
def do_nonlocal():
nonlocal spam
spam = "nonlocal spam"
def do_global():
global spam
spam = "global spam"
spam = "test spam"
do_local()
print("After local assignment:", spam)
do_nonlocal()
print("After nonlocal assignment:", spam)
do_global()
print("After global assignment:", spam)
scope_test()
print("In global scope:", spam)
출력
After local assignment: test spam
After nonlocal assignment: nonlocal spam
After global assignment: nonlocal spam
In global scope: global spam
클래스 정의 문법
- 클래스의 기본 구조:
- 'class' 키워드로 시작
- 클래스 이름 지정 (대문자로 시작하는 것이 관례)
- 콜론(:) 사용
- 들여쓰기로 클래스 내용 구분
예시:
class MyFirstClass:
# 클래스 내용
- 클래스의 특징:
- 새로운 타입의 객체를 만드는 방법
- 함수처럼 먼저 정의되어야 사용 가능
- 클래스 안에는 주로 함수들이 정의됨 (이를 '메서드'라고 부름)
- 클래스 정의 시 일어나는 일:
- 새로운 이름 공간(namespace) 생성
- 클래스 내의 모든 이름(변수, 함수 등)은 이 공간에 저장
- 클래스 정의가 끝나면:
- 클래스 객체가 생성됨
- 이 객체는 클래스 이름에 연결됨
클래스 객체
- 클래스 객체의 기본 개념:
- 클래스를 정의하면 클래스 객체가 생성됩니다.
- 클래스 객체는 두 가지 주요 기능을 합니다:
- a) 속성(attribute) 참조
- b) 인스턴스 생성
- 속성 참조:
- 문법:
클래스이름.속성이름
- 예:
MyClass.i
(변수 참조),MyClass.f
(함수 참조) - 클래스 속성은 변경 가능합니다.
__doc__
은 클래스의 문서 문자열을 반환하는 특별한 속성입니다.
- 문법:
- 인스턴스 생성:
- 문법:
클래스이름()
- 예:
x = MyClass()
- 이렇게 하면 클래스의 새로운 인스턴스가 생성됩니다.
- 문법:
- init 메서드:
- 클래스의 특별한 메서드로, 인스턴스를 초기화합니다.
- 인스턴스가 생성될 때 자동으로 호출됩니다.
self
는 생성된 인스턴스 자체를 가리킵니다.
def __init__(self):
self.data = []
- 인자를 받는 init:
- init 메서드는 추가 인자를 받을 수 있습니다.
class Complex:
def __init__(self, realpart, imagpart):
self.r = realpart
self.i = imagpart
x = Complex(3.0, -4.5)
인스턴스 객체
- 인스턴스 객체의 기본 개념:
- 클래스의 실제 예시(instance)입니다.
- 클래스를 바탕으로 만들어진 구체적인 객체입니다.
- 인스턴스 객체의 주요 기능:
- 속성 참조: 두 가지 종류의 속성이 있습니다.
- a) 데이터 속성
- b) 메서드
- 속성 참조: 두 가지 종류의 속성이 있습니다.
- 데이터 속성 (Data Attributes):
- 인스턴스 변수라고도 불립니다.
- 선언 없이 바로 사용 가능합니다.
- 예시:
x = MyClass()
x.counter = 1 # 데이터 속성 생성
print(x.counter) # 데이터 속성 사용
del x.counter # 데이터 속성 삭제
- 메서드 (Methods):
- 객체에 "속한" 함수입니다.
- 클래스에서 정의된 함수가 인스턴스의 메서드가 됩니다.
- 예시:
class MyClass:
def f(self):
return 'hello world'
x = MyClass()
print(x.f()) # 메서드 호출
- 메서드와 함수의 차이:
x.f
는 메서드 객체입니다.MyClass.f
는 함수 객체입니다.- 메서드는 인스턴스에 연결된 함수입니다.
메서드 객체
메서드 객체의 기본 개념:
- 메서드는 인스턴스에 연결된 함수입니다.
- 메서드 객체는 즉시 호출하지 않고 저장해 두었다가 나중에 호출할 수 있습니다.
메서드 호출 방식:
일반적인 호출:
x.f()
메서드 객체 저장 후 호출:
xf = x.f xf()
메서드 호출 시 일어나는 일:
- 인스턴스 객체가 자동으로 첫 번째 인자로 전달됩니다.
x.f()
는 내부적으로MyClass.f(x)
와 동일합니다.
인자 전달:
- 메서드 정의에
self
외의 인자가 있더라도, 호출 시 첫 번째 인자(self)는 자동으로 전달됩니다. - 추가 인자는 그 뒤에 전달됩니다.
- 메서드 정의에
메서드 작동 과정:
- 인스턴스의 속성을 참조할 때, 해당 클래스에서 그 이름을 찾습니다.
- 찾은 이름이 함수 객체라면, 인스턴스 객체와 함수 객체를 함께 메서드 객체로 묶습니다.
- 메서드 호출 시, 인스턴스 객체와 전달된 인자 목록으로 새 인자 목록을 만들어 함수를 호출합니다.
클래스 변수, 인스턴스 변수
클래스 변수:
- 클래스의 모든 인스턴스가 공유하는 변수
- 클래스 내부에서 정의되지만, 메서드 밖에 위치
- 예:
class Dog: kind = 'canine' # 클래스 변수
인스턴스 변수:
- 각 인스턴스마다 고유한 변수
- 보통
__init__
메서드 내에서self.변수명
으로 정의 - 예:
def __init__(self, name):
self.name = name # 인스턴스 변수
- 클래스 변수 vs 인스턴스 변수:
- 클래스 변수는 모든 인스턴스에서 같은 값을 공유
- 인스턴스 변수는 각 인스턴스마다 다른 값을 가질 수 있음
- 주의사항:
- 가변 객체(리스트, 딕셔너리 등)를 클래스 변수로 사용할 때 주의 필요
- 의도치 않게 모든 인스턴스에서 공유될 수 있음
- 올바른 사용 예:
class Dog:
def __init__(self, name):
self.name = name
self.tricks = [] # 각 개에 대해 새 리스트 생성
def add_trick(self, trick):
self.tricks.append(trick)
기타 주의사항들
- 속성 우선순위:
- 인스턴스 속성이 클래스 속성보다 우선합니다.
- 같은 이름의 속성이 있으면 인스턴스 속성이 사용됩니다.
- 데이터 은닉:
- 파이썬은 완전한 데이터 은닉을 강제하지 않습니다.
- 관례에 따라 데이터 접근을 제어합니다.
- 메서드 내에서의 속성 참조:
- 항상
self
를 사용하여 인스턴스 속성을 참조합니다. - 이는 지역 변수와 인스턴스 변수를 구분하는 데 도움이 됩니다.
- 항상
self
사용:- 메서드의 첫 번째 매개변수로
self
를 사용하는 것은 관례입니다. - 다른 이름을 사용할 수 있지만,
self
를 사용하는 것이 좋습니다.
- 메서드의 첫 번째 매개변수로
- 메서드 정의:
- 클래스 내부에 정의된 함수는 모두 메서드가 됩니다.
- 클래스 외부에서 정의한 함수도 클래스 속성으로 할당하면 메서드가 될 수 있습니다.
- 메서드 간 호출:
- 한 메서드에서 다른 메서드를 호출할 때는
self
를 사용합니다.
- 한 메서드에서 다른 메서드를 호출할 때는
- 전역 스코프:
- 메서드는 전역 이름을 참조할 수 있지만, 가급적 사용을 피하는 것이 좋습니다.
- 클래스는 전역 스코프로 사용되지 않습니다.
- 객체와 클래스:
- 모든 값은 객체이며, 각 객체는 클래스(타입)를 가집니다.
- 객체의 클래스는
object.__class__
에 저장됩니다.
상속
- 상속의 기본 개념:
- 기존 클래스(부모 클래스)의 특성을 새로운 클래스(자식 클래스)가 물려받는 것
- 코드 재사용과 계층 구조 생성에 유용
- 상속 문법:
class 자식클래스(부모클래스):
# 자식 클래스 내용
- 상속의 주요 특징:
- 자식 클래스는 부모 클래스의 모든 속성과 메서드를 상속받음
- 자식 클래스에서 부모 클래스의 메서드를 재정의(오버라이딩) 할 수 있음
- 다중 상속 가능 (여러 부모 클래스로부터 상속 받을 수 있음)
- 메서드 재정의:
- 자식 클래스에서 부모 클래스의 메서드를 같은 이름으로 다시 정의
- 부모 클래스 메서드 호출:
부모클래스.메서드(self, 인자들)
super()
함수를 사용하여 부모 클래스의 메서드를 쉽게 호출할 수 있음
- 속성 및 메서드 검색 순서:
- 자식 클래스에서 시작해 부모 클래스로 올라가며 검색
- 여러 부모 클래스가 있는 경우, 정의된 순서대로 검색
- 유용한 내장 함수:
isinstance(객체, 클래스)
: 객체가 해당 클래스의 인스턴스인지 확인issubclass(클래스A, 클래스B)
: 클래스A가 클래스B의 서브클래스인지 확인
다중 상속
- 다중 상속의 기본 개념:
- 한 클래스가 여러 부모 클래스로부터 속성과 메서드를 상속받는 것
- 문법:
class 자식클래스(부모클래스1, 부모클래스2, ...):
- 속성 검색 순서 (간단한 경우):
- 깊이 우선, 왼쪽에서 오른쪽으로 검색
- 중복 검색 방지
- 메서드 결정 순서 (MRO: Method Resolution Order):
- 실제로는 더 복잡한 동적 순서 결정 방식 사용
super()
함수를 통한 협력적 메서드 호출 지원
- 다이아몬드 관계:
- 한 클래스가 여러 경로로 같은 부모 클래스에 접근할 수 있는 상황
- 모든 클래스는
object
를 상속받기 때문에 다중 상속에서 항상 발생
- 동적 순서 결정의 특징:
- 왼쪽에서 오른쪽 순서 유지
- 각 부모 클래스를 한 번만 호출
- 단조성 유지 (서브클래스 생성이 부모 클래스의 우선순위에 영향을 주지 않음)
비공개 변수
- 진정한 비공개 변수의 부재:
- 파이썬에는 완전히 비공개인 인스턴스 변수가 없습니다.
- 대신 이름 규칙과 이름 뒤섞기(name mangling) 메커니즘을 사용합니다.
- 이름 규칙:
- 밑줄로 시작하는 이름 (예:
_variable
)은 비공개로 간주됩니다. - 이는 규약일 뿐, 실제로 접근을 막지는 않습니다.
- 밑줄로 시작하는 이름 (예:
- 이름 뒤섞기 (Name Mangling):
- 두 개의 밑줄로 시작하는 이름 (예:
__variable
) - 자동으로
_클래스이름__variable
로 변환됩니다. - 클래스 외부에서 직접 접근을 어렵게 만듭니다.
- 두 개의 밑줄로 시작하는 이름 (예:
- 이름 뒤섞기의 목적:
- 서브클래스에서의 이름 충돌 방지
- 클래스 내부 메서드 호출에는 영향을 주지 않음
- 예시:
class MyClass:
def __init__(self):
self.__private_var = 42
obj = MyClass()
print(obj._MyClass__private_var) # 42 출력
- 주의사항:
- 이름 뒤섞기는 완전한 보안을 제공하지 않습니다.
- 여전히 변수에 접근하고 수정할 수 있습니다.
exec()
,eval()
함수에서는 이름 뒤섞기가 적용되지 않습니다.
class Mapping:
def __init__(self, iterable):
self.items_list = []
self.__update(iterable)
def update(self, iterable):
for item in iterable:
self.items_list.append(item)
__update = update # private copy of original update() method
class MappingSubclass(Mapping):
def update(self, keys, values):
# provides new signature for update()
# but does not break __init__()
for item in zip(keys, values):
self.items_list.append(item)
- 이름 뒤섞기는 클래스 내부의 메서드 호출을 방해하지 않고 서브 클래스들이 메서드를 재정의할 수 있도록 하는 데 도움을 줍니다.
MappingSubclass
가__update
식별자를 도입하더라도 작동합니다.Mapping
클래스에서는_Mapping__update
로MappingSubclass
클래스에서는_MappingSubclass__update
로 각각 대체 되기 때문
잡동사니
- 데이터클래스 (Dataclasses):
- 여러 데이터 항목을 하나로 묶는 데 유용한 기능
- Pascal의 "레코드"나 C의 "구조체"와 유사한 개념
dataclasses
모듈을 사용하여 쉽게 구현 가능
from dataclasses import dataclass
@dataclass
class Employee:
name: str
dept: str
salary: int
john = Employee('john', 'computer lab', 1000)
print(john.dept) # 'computer lab' 출력
- 덕 타이핑 (Duck Typing) :
- 특정 추상 데이터 타입을 기대하는 코드에 해당 메서드를 구현한 클래스를 전달할 수 있음
- 예: 파일 객체 대신 문자열 버퍼로부터 데이터를 읽는 클래스 사용 가능
- 인스턴스 메서드 객체의 속성:
m.__self__
: 메서드m()
이 속한 인스턴스 객체m.__func__
: 메서드에 해당하는 함수 객체
이러한 개념들은 파이썬의 유연성과 강력함을 보여줍니다:
- 데이터클래스를 사용하면 간단한 데이터 구조를 쉽게 만들 수 있어, 코드를 더 간결하고 명확하게 만들 수 있습니다.
- 덕 타이핑은 파이썬의 동적 특성을 잘 보여주며, 인터페이스에 집중하게 해줍니다. 이는 코드의 재사용성과 유연성을 높입니다.
- 인스턴스 메서드 객체의 특별한 속성들은 파이썬의 내부 작동 방식을 이해하는 데 도움이 됩니다.
이터레이터(Iterator)
- 이터레이터의 기본 개념:
- 컨테이너 객체의 요소들을 순회할 수 있게 해주는 객체
for
루프와 같은 반복 구문에서 사용됨
- 이터레이터 프로토콜:
__iter__()
메서드: 이터레이터 객체를 반환__next__()
메서드: 다음 요소를 반환, 요소가 없으면 StopIteration 예외 발생
iter()
함수:- 객체의
__iter__()
메서드를 호출하여 이터레이터 객체를 얻음
- 객체의
next()
함수:- 이터레이터의
__next__()
메서드를 호출하여 다음 요소를 얻음
- 이터레이터의
- 이터레이터 구현 예시:
class Reverse:
def __init__(self, data):
self.data = data
self.index = len(data)
def __iter__(self):
return self
def __next__(self):
if self.index == 0:
raise StopIteration
self.index -= 1
return self.data[self.index]
- 이터레이터 사용 예:
rev = Reverse('spam')
for char in rev:
print(char)
# 출력: m, a, p, s
- 이터레이터를 사용하면 대용량 데이터를 효율적으로 처리할 수 있습니다.
- 커스텀 이터레이터를 만들어 복잡한 순회 로직을 간단하게 표현할 수 있습니다.
for
루프는 내부적으로 이터레이터를 사용합니다.- 이터레이터는 한 번만 순회할 수 있습니다. 재사용하려면 새로운 이터레이터 객체를 생성해야 합니다.
제너레이터와 제너레이터 표현식
- 제너레이터 (Generators)
- 정의: 이터레이터를 생성하는 간단하고 강력한 도구
- 특징:
- 일반 함수처럼 작성되지만
yield
키워드 사용 - 호출 간 상태 유지
- 메모리 효율적 (값을 필요할 때만 생성)
- 일반 함수처럼 작성되지만
def reverse(data):
for index in range(len(data)-1, -1, -1):
yield data[index]
for char in reverse('golf'):
print(char)
# 출력: f, l, o, g
- 제너레이터의 장점:
__iter__()
와__next__()
메서드 자동 생성- 지역 변수와 실행 상태 자동 보존
- 종료 시 자동으로
StopIteration
발생
- 제너레이터 표현식 (Generator Expressions)
- 정의: 간단한 제너레이터를 한 줄로 표현하는 방법
- 문법: 리스트 컴프리헨션과 유사하지만 대괄호 [] 대신 괄호 () 사용
sum(i*i for i in range(10)) # 0부터 9까지 제곱의 합
data = 'golf'
list(data[i] for i in range(len(data)-1, -1, -1)) # 문자열 뒤집기
제너레이터 표현식의 특징:
- 메모리 효율적 (큰 데이터셋 처리에 유용)
- 즉시 사용되는 상황에 적합
- 리스트 컴프리헨션보다 간결하지만 융통성은 떨어짐
팁:
- 대량의 데이터를 다룰 때 제너레이터를 사용하면 메모리 사용을 최적화할 수 있습니다.
- 제너레이터는 한 번만 순회할 수 있습니다. 재사용이 필요하면 함수를 다시 호출해야 합니다.
- 간단한 이터레이션이 필요할 때는 제너레이터 표현식을 사용하면 코드를 간결하게 만들 수 있습니다.
- 복잡한 로직이 필요한 경우 일반 제너레이터 함수를 사용하는 것이 좋습니다.
728x90