1. 팩토리 함수(Factory Function)란?
팩토리 함수는 객체 생성 과정을 추상화하여 객체를 생성하는 함수 를 가리킴.
즉, 객체를 직접 생성하는 대신 함수를 호출하여 객체를 생성하는 방식으로 다음과 같은 장점이 있음:
- 객체 생성 로직의 캡슐화.
- 생성 과정의 세부 사항 은닉.
- 동일한 인터페이스로 다양한 유형의 객체 생성.
- 코드의 재사용성 향상.
Python에서 namedtuple
은 대표적인 팩토리 함수의 예시.
2. namedtuple 팩토리 함수
namedtuple
은
- Python의
collections
모듈에서 제공하는 팩토리 함수로, - 이름이 있는 필드를 가진
tuple
의 서브클래스를 동적으로 생성.
2023.10.06 - [Python] - [Python] tuple
[Python] tuple
tuple Tuple은 immutable list라고 자주 불릴 정도로 list와 유사하다. immutable이기 때문에 한번 assign된 이후 item의 update는 불가함.update가 안되기 때문에 list 보다 적은 수의 methods를 제공.때문에 보다 적
ds31x.tistory.com
2.1. 기본 문법
collections.namedtuple(
typename, # 생성될 클래스의 이름 (문자열)
field_names, # 필드 이름들 (문자열 리스트 또는 공백/콤마로 구분된 문자열)
*,
rename=False, # 유효하지 않은 필드 이름을 자동으로 변경할지 여부 (기본값: False)
defaults=None,# 필드의 기본값 (Python 3.7+)
module=None, # 클래스의 `__module__` 속성 값 (기본값: 호출자의 모듈 이름)
)
2.2. 반환값
namedtuple
은 지정된 이름과 필드를 가진 새로운 tuple의 서브클래스를 반환.- 반환된 객체를 나중에 인스턴스 생성을 위한 생성자로 사용가능.
3. namedtuple 사용 튜토리얼
3.1. 기본 사용법
from collections import namedtuple
# 1. namedtuple 클래스 생성
Person = namedtuple('Person', ['name', 'age', 'job'])
# 2. 생성된 클래스의 인스턴스 만들기
john = Person('John Doe', 30, 'Developer')
# 3. 인덱스와 이름으로 접근하기
print(john[0]) # 'John Doe' (일반 tuple처럼 인덱스로 접근)
print(john.name) # 'John Doe' (필드 이름으로 접근)
print(john.age) # 30 (나이 값)
print(john.job) # 'Developer' (직업 정보)
# 4. 반복 가능한 객체로 사용하기
for value in john:
print(value)
# 5. 언패킹하기
name, age, job = john
print(f"{name}는 {age}살이고 {job}의 직업.")
3.2. 다양한 필드 이름 지정 방법
# 리스트로 필드 지정
Point1 = namedtuple('Point', ['x', 'y', 'z'])
# 공백으로 구분된 문자열로 필드 지정
Point2 = namedtuple('Point', 'x y z')
# 콤마로 구분된 문자열로 필드 지정
Point3 = namedtuple('Point', 'x, y, z')
# 모두 동일한 결과 생성
p1 = Point1(1, 2, 3)
p2 = Point2(1, 2, 3)
p3 = Point3(1, 2, 3)
print(p1) # Point(x=1, y=2, z=3)
print(p2) # Point(x=1, y=2, z=3)
print(p3) # Point(x=1, y=2, z=3)
3.3. rename 매개변수 사용하기
Python 식별자 규칙에 맞지 않는 필드 이름이나 예약어 처리 시 rename=True
사용.
# Python 예약어와 유효하지 않은 식별자가 있는 경우
# class, def, 1stField 등은 유효한 식별자가 아닙니다
Fields = namedtuple('Fields', ['class', 'def', '1stField'], rename=True)
# 자동 이름 변경:
# class -> _0, def -> _1, 1stField -> _2
f = Fields('Math', 'function', 'First')
print(f) # Fields(_0='Math', _1='function', _2='First')
print(f._0) # 'Math'
print(f._1) # 'function'
3.4. defaults 매개변수 사용하기 (Python 3.7+)
# 기본값 설정
Employee = namedtuple('Employee', ['name', 'id', 'dept', 'salary'], defaults=[0, 'N/A'])
# 마지막 두 필드 기본값 적용: dept=0, salary='N/A'
# 기본값 있는 필드 생략 가능
emp1 = Employee('John', 1001) # dept와 salary는 기본값 사용
print(emp1) # Employee(name='John', id=1001, dept=0, salary='N/A')
# 모든 값 명시적 제공 가능
emp2 = Employee('Jane', 1002, 'HR', 50000)
print(emp2) # Employee(name='Jane', id=1002, dept='HR', salary=50000)
# 클래스 기본값 정보 확인
print(Employee._field_defaults) # {'dept': 0, 'salary': 'N/A'}
3.5. 유용한 메서드들
Point = namedtuple('Point', 'x y z')
p = Point(1, 2, 3)
# 1. _make(): 반복 가능 객체로부터 새 인스턴스 생성
data = [4, 5, 6]
p2 = Point._make(data)
print(p2) # Point(x=4, y=5, z=6)
# 2. _asdict(): OrderedDict로 변환 (Python 3.1+)
p_dict = p._asdict()
print(p_dict) # OrderedDict([('x', 1), ('y', 2), ('z', 3)])
# 3. _replace(): 필드 값 변경한 새 인스턴스 생성
p3 = p._replace(y=20, z=30)
print(p) # Point(x=1, y=2, z=3) - 원본은 변경되지 않음
print(p3) # Point(x=1, y=20, z=30)
# 4. _fields: 필드 이름 튜플
print(p._fields) # ('x', 'y', 'z')
3.6. 클래스 메서드 상속과 확장
namedtuple
로 생성된 클래스는 일반 클래스처럼 상속이나 메서드 추가 가능.
class PointWithMethods(namedtuple('Point', 'x y z')):
# 새 메서드 추가 방법
def distance_from_origin(self):
return (self.x ** 2 + self.y ** 2 + self.z ** 2) ** 0.5
# 특수 메서드 재정의 예시
def __str__(self):
return f"Point({self.x}, {self.y}, {self.z}) - 원점과의 거리: {self.distance_from_origin():.2f}"
p = PointWithMethods(3, 4, 5)
print(p.distance_from_origin()) # 7.0710678118654755
print(p) # Point(3, 4, 5) - 원점과의 거리: 7.07
4. 실제 사용 사례
4.1. CSV 데이터 처리
import csv
from collections import namedtuple
# CSV 데이터의 namedtuple 변환
def read_csv_as_namedtuples(file_path):
with open(file_path, 'r') as f:
reader = csv.reader(f)
header = next(reader) # 첫 번째 행의 헤더 처리
Row = namedtuple('Row', header)
return [Row(*row) for row in reader]
# 사용 예시
# 예: users.csv 파일에 name,age,email 열이 있는 경우
# users = read_csv_as_namedtuples('users.csv')
# for user in users:
# print(f"{user.name}의 이메일은 {user.email}입니다.")
4.2. 데이터베이스 결과 처리
import sqlite3
from collections import namedtuple
def query_as_namedtuples(db_path, query):
conn = sqlite3.connect(db_path)
cursor = conn.cursor()
cursor.execute(query)
# 결과 컬럼 이름 추출
column_names = [desc[0] for desc in cursor.description]
Row = namedtuple('Row', column_names)
# 결과의 namedtuple 변환
results = [Row(*row) for row in cursor.fetchall()]
conn.close()
return results
# 사용 예시
# results = query_as_namedtuples('example.db', 'SELECT id, name, age FROM users')
# for user in results:
# print(f"ID: {user.id}, 이름: {user.name}, 나이: {user.age}")
4.3. coordinate와 shape 처리
from collections import namedtuple
import math
Point2D = namedtuple('Point2D', 'x y')
class Circle(namedtuple('CircleBase', 'center radius')):
@property
def area(self):
return math.pi * self.radius ** 2
@property
def circumference(self):
return 2 * math.pi * self.radius
def contains_point(self, point):
return math.sqrt((point.x - self.center.x)**2 +
(point.y - self.center.y)**2) <= self.radius
# 사용 예시
center = Point2D(0, 0)
circle = Circle(center=center, radius=5)
print(f"원의 면적: {circle.area:.2f}")
print(f"원의 둘레: {circle.circumference:.2f}")
test_point = Point2D(3, 4)
if circle.contains_point(test_point):
print(f"점 {test_point}는 원 내부 위치.")
else:
print(f"점 {test_point}는 원 외부 위치.")
5. 고급 기법: 동적 namedtuple 생성
런타임에 필드를 동적으로 결정해야 하는 경우 사용 가능한 기법.
def create_record_type(schema):
"""
스키마 딕셔너리에서 동적으로 namedtuple 타입을 생성
schema: {필드명: 설명} 형태의 딕셔너리
"""
field_names = list(schema.keys())
RecordType = namedtuple('Record', field_names)
# docstring 추가
field_docs = '\n'.join(f' {name}: {desc}' for name, desc in schema.items())
RecordType.__doc__ = f'Record with fields:\n{field_docs}'
return RecordType
# 사용 예시
user_schema = {
'id': '사용자 고유 식별자',
'username': '사용자 로그인 이름',
'email': '이메일 주소',
'active': '계정 활성화 상태'
}
UserRecord = create_record_type(user_schema)
print(UserRecord.__doc__)
user = UserRecord(1, 'john_doe', 'john@example.com', True)
print(user)
6. namedtuple vs 다른 데이터 구조 비교
6.1. namedtuple vs 일반 tuple
# 일반 tuple
person_tuple = ('John Doe', 30, 'Developer')
print(person_tuple[0]) # 'John Doe' - 인덱스만으로 접근
# namedtuple
Person = namedtuple('Person', 'name age job')
person_named = Person('John Doe', 30, 'Developer')
print(person_named.name) # 'John Doe' - 이름으로 접근
# 가독성 비교
age = person_tuple[1] # 인덱스만으로 데이터 속성 불명확
age = person_named.age # 명확한 age 데이터 속성 인식
6.2. namedtuple vs dict
# dict
person_dict = {'name': 'John Doe', 'age': 30, 'job': 'Developer'}
print(person_dict['name']) # 'John Doe'
# namedtuple
Person = namedtuple('Person', 'name age job')
person_named = Person('John Doe', 30, 'Developer')
print(person_named.name) # 'John Doe'
# 차이점
person_dict['age'] = 31 # dict의 변경 가능성
# person_named.age = 31 # namedtuple의 불변성으로 인한 오류 발생
# 메모리 사용량 비교
import sys
print(f"딕셔너리 크기: {sys.getsizeof(person_dict)} 바이트")
print(f"namedtuple 크기: {sys.getsizeof(person_named)} 바이트")
# namedtuple의 일반적 메모리 사용량 우위
6.3. namedtuple vs class
# 일반 클래스
class PersonClass:
def __init__(self, name, age, job):
self.name = name
self.age = age
self.job = job
# namedtuple
Person = namedtuple('Person', 'name age job')
# 인스턴스 생성
person_class = PersonClass('John Doe', 30, 'Developer')
person_named = Person('John Doe', 30, 'Developer')
# 접근 방식은 유사
print(person_class.name) # 'John Doe'
print(person_named.name) # 'John Doe'
# 주요 차이점 비교
person_class.age = 31 # 클래스 인스턴스의 변경 가능성
# person_named.age = 31 # 오류! namedtuple은 불변
# 비교 동작이 다름
p1 = PersonClass('John Doe', 30, 'Developer')
p2 = PersonClass('John Doe', 30, 'Developer')
print(p1 == p2) # False (기본적 객체 ID 비교 결과)
n1 = Person('John Doe', 30, 'Developer')
n2 = Person('John Doe', 30, 'Developer')
print(n1 == n2) # True (값 비교 결과)
7. 정리: namedtuple 팩토리 함수의 장점과 한계
7.1. 장점:
- 필드 이름 접근을 통한 가독성 향상
- 일반 tuple의 모든 기능 유지 (인덱싱, 언패킹 등의 기능)
- 메모리 효율성 (딕셔너리보다 적은 메모리 사용량)
- 불변성으로 인한 데이터 안전성 확보
- 값 기반 동등성 비교 (같은 값이면 같은 객체로 간주하는 특성)
- 간결한 클래스 정의 방식
7.2. 한계:
- 불변성 (필요에 따른 단점 가능성)
- 메서드 추가 시 상속 필요성
- 속성 검증 로직 구현의 어려움
- 디폴트 값 설정 옵션의 Python 3.7 이상 제한
8. 마무리
namedtuple
팩토리 함수는 간결하면서도 명확한 데이터 구조를 만들 수 있는 강력한 도구.- 특히 불변성이 필요하고 필드 이름을 통한 접근이 중요한 경우에 적합.
- 간단한 데이터 모델링, 반환 값 패키징, 다중 값 반환 등 다양한 상황에서의 유용한 활용 방안.
그러나 복잡한 비즈니스 로직, 상태 변경 필요 시,
또는 고급 데이터 검증 필요 시에는 일반 클래스나 데이터 클래스(dataclasses
모듈)의 고려 필요성.
같이보면 좋은 자료들
2025.04.04 - [Python] - [Py] collections 모듈 (summary) - 작성중
[Py] collections 모듈 (summary) - 작성중
Python의 collections 모듈은 파이썬의 built-in 자료구조를 확장한 special container 클래스들을 제공함.1. Counter요소의 개수를 세는 dictionary의 subclass.해시 가능한 객체의 카운트를 저장함.from collections impor
ds31x.tistory.com
'Python' 카테고리의 다른 글
[OpenCV] macOS에서 Qt 지원하도록 빌드. (0) | 2025.04.05 |
---|---|
[Py] collections.ChainMap (0) | 2025.04.04 |
[Py] collection.OrderedDict (0) | 2025.04.04 |
[Py] collections 모듈 (summary) - 작성중 (0) | 2025.04.04 |
[Py] print 함수 (0) | 2025.04.02 |