상세 컨텐츠

본문 제목

[인프런 리프 2기] 07. 파이썬 중급 과정 2주차(5) 데이터 모델-2,3

경험/2021 인프런리프2기

by mizu-umi 2021. 3. 21. 23:34

본문

728x90

관련글 ▼

[인프런 리프 2기] 04. 파이썬 중급 과정 2주차(2) 클래스 심화-2

[인프런 리프 2기] 05. 파이썬 중급 과정 2주차(3) 클래스 심화-3

[인프런 리프 2기] 06. 파이썬 중급 과정 2주차(4) 데이터 모델-1

 


 

 

파이썬 데이터 모델 - 매직 메소드 (2)

  • 매직 메소드 심화
  • 클래스 매직 메소드 실습 코딩
  • 데이터 모델 설계

 

앞선 강의에서 매직 메소드에 대해 배웠다면 두번째 강의에서는 매직 메소드를 활용하는 방법에 대해 배웠다. 14분의 강의지만 예제와 실습을 중심으로 하는 강의라서 많은 내용을 공부한 건 아니었다 ㅎㅎ

 


매직 메소드 심화

class Vector(object):
    def __init__(self, *arg):  
        '''
        Create a vector, example: v=Vector(5,10)
        '''
        if len(arg)==0: #일종의 예외처리
            self._x, self._y = 0,0
        else:
            self._x, self._y = arg

매직 메소드를 활용하기 위해 Vector 값을 다루는 클래스를 예제로 만들었다. 여기서 *arg는 복수의 argument를 이용하는 메소드일 때 사용하는 예약어이다.

 

__init__에 왜 if문이 들어갈까?

여기서 만약 if문을 이용해 예외처리를 하지 않고 self._x, self._y = arg 라고 입력하면 아래와 같은 오류가 난다.

Traceback (most recent call last):
 File "c:\workspace\python_seed\venv\c03_02.py", line 29, in <module>
  v3 = Vector()
 File "c:\workspace\python_seed\venv\c03_02.py", line 10, in __init__
   self._x, self._y = arg
ValueError: not enough values to unpack (expected 2, got 0)

→ unpacking을 할 수 있는 value가 충분하지 않으므로 ValueError가 발생한다.

 

만약 print(Vector.__doc__)를 하면 어떻게 될까?

class에 주석이 달린게 아닌 __init__ 메소드에 주석이 달린 것이므로 아무것도 나오지 않는다. __init__에 달린 주석을 출력하고자 한다면 print(Vector.__init__.__doc__)라고 작성해야 한다.

 

...
    def __repr__(self):
        '''Return the vector information'''
        return'Vector(%r,%r)' % (self._x, self._y)
        
    def __add__(self, other):
        '''Return the addition of vector values'''
        return Vector(self._x + other._x, self._y + other._y)

    def __mul__(self, y):
        '''Return the multiplication of vector values'''
        return Vector(self._x * y, self._y * y)

    def __bool__(self):
        return bool(max(self._x, self._y))

Vector 클래스에 위와 같이 다양한 매직 메소드를 추가하면 입력된 vector값을 활용할 수 있다.

 

v1 = Vector(6,7)
v2 = Vector(12,54)
v3 = Vector()

print(v1,v2,v3)
print(v1+v2)
print(v1 * 3)
print(bool(v1),bool(v2))
print(bool(v3))

Vector 클래스를 토대로 위와 같은 값들을 출력해보면 아래과 같은 결과가 나온다.

 

Vector(6,7) Vector(12,54) Vector(0,0)
Vector(18,61)
Vector(18,21)
True True
False

 


파이썬 데이터 모델 - 매직 메소드 (3)

  • 데이터 모델 설계
  • NamedTuple 설명
  • Model Unpacking
  • 네임드 튜플 실습 코딩

 

매직 메소드를 이제 막 배웠는데 네임드 튜플이라니?!😲 튜플의 성질(수정, 삭제가 되지 않음 등)을 가지고 있는 무언가(?)인가 싶었다.

 


 

네임드튜플 NamedTuple

네임드 튜플이란 튜플로부터 상속받는 아무 유형이나 클래스에 적용되는 표현으로 네임드된 속성을 이용해 인덱싱이 가능한 요소에 접속 가능하다.(출처: 파이썬 용어집).

 

객체(object)란 파이썬의 데이터를 추상화한 것이며 (abstraction of data in Python) 모든 객체는 id와 type으로 확인할 수 있다. 

 

일반적인 튜플

pt1 = (1.0, 5.0)
pt2 = (2.5, 1.5)

from math import sqrt

l_len1 = sqrt((pt1[0]-pt2[0])**2 + (pt1[1]-pt2[1])**2)
print(l_len1)

튜플 자료형을 이용해서 특정 벡터값의 크기를 구하는 프로그램을 작성해보면 위와 같다. 튜플 값을 인덱싱해서 가져오는 등 코드를 복잡하게 작성해야 한다.

 

네임드 튜플을 사용하면?

from collections import namedtuple

네임드 튜플은 외부 컬렉션(collections)에 있기 때문에 별도로 import해서 작성해야 한다. import를 하지 않으면 구현할 수 없다.

 

튜플이지만 딕셔너리 형식이라서 key와 value를 갖는다.

 

Point = namedtuple('Point','x,y')

pt3 = Point(1.0,5.0) #클래스를 선언하는 방식과 비슷
pt4 = Point(2.5,1.5)

l_len2 = sqrt((pt3.x - pt4.x)**2 + (pt3.y - pt4.y)**2)
print(l_len2)

일반적인 튜플에서는 pt1[0] - pt2[0] 등 인덱싱을 이용해서 코드를 작성해야 한다는 번거로움이 있는 반면 네임드 튜플에서는 선언한 자료의 변수만을 가져와 대입하면 되기 때문에 훨씬 편리하다.

 

네임드 튜플의 선언 방법

Point1 = namedtuple('Point', ['x', 'y']) # list로 선언
Point2 = namedtuple('Point','x, y') # comma
Point3 = namedtuple('Point','x y') # space
Point4 = namedtuple('Point', 'x y x class', rename=True)

앞서 언급했듯 튜플이지만 딕셔너리 형식이기도 하기 때문에

  • x y x 처럼 key가 중첩되면 오류가 발생한다.
  • class나 def같은 예약어를 선언하면 오류가 발생한다.

따라서, label과 상관 없이 코딩을 하고 싶을 때는 꼭 rename=True을 사용해야 한다.

 

print(Point1, Point2, Point3, Point4)

p1=Point1(x=10, y=35)
p2=Point2(20,40)
p3=Point3(45, y=20)
p4=Point4(10,20,30,40)

print(p1)
print(p2)
print(p3)
print(p4)

여기서 가장 주목해야하는 값은 p4의 출력결과다. p1~p3까지를 출력하면 (x=숫자, y=숫자)로 출력되는 반면, 앞서 rename을 이용해 label을 무시한(?) p4의 경우, 아래와 같이 임의의 변수를 만들어 값을 할당한다.

 

Point(x=10, y=20, _2=30, _3=40)

 

딕셔너리를 튜플로 언패킹

# part1
temp_dict = {'x': 75, 'y': 55}
p5=Point3(**temp_dict) 
print(p5)

# part2
print(p1[0]+p2[1]) 
print(p1.x + p2.y)

# part3
x, y = p5 
print(x,y)

Part 1 : **가 딕셔너리를 tuple로 unpacking한다. 딕셔너리 안의 key와 namedtuple안의 key가 일치해야 오류없이 대입된다.

 

Part 2 : 인덱싱으로 값을 가져와서 뭔가를 수행하는 건 복잡하고 번거롭다. 따라서 해당 key값을 쉽게 가져올 수 있도록 네임드 튜플을 쓰는게 좋다.

 

Part 3 : p5를 x, y 라는 형식으로 unpacking할 수 있다.

 

리스트를 튜플로 언패킹

temp = [325,352]
p7 = Point1._make(temp) 

Point1은 앞서 선언한 네임드 튜플 중 하나이다. 여기서 _make() 를 이용하면 temp라는 이름으로 만들어진 list를 Point1이라는 네임드 튜플로 unpacking할 수 있다.

 

  • _fields : 필드명을 확인할 수 있다.
  • _asdict : OrderedDict를 반환해준다.

 

네임드 튜플의 실사용 실습

# 반에 20명, 4개의 반(A,B,C,D)

Classes = namedtuple('Classes', ['rank', 'number'])

# 그룹리스트
numbers = [str(n) for n in range(1,21)]
ranks ='A B C D'.split()

print(numbers)
print(ranks)

students  = [Classes(rank, number) for rank in ranks for number in numbers]
print(students)

split() : space를 기준으로 따옴표 안의 데이터를 리스트로 만들어준다.

 

위에서 작성한 코드를 좀 더 깔끔하게 작성하는 방법도 존재한다.

 

students2 = [Classes(rank, number)
            for rank in 'A B C D'.split()
                for number in [str(n) for n in range(1,21)]]

< 강의 출처 - 인프런 우리를 위한 프로그래밍 파이썬 중급 (Inflearn Original) >

728x90
반응형

관련글 더보기

댓글 영역