HAZEL

[ Python : Data Model ] 네임드 튜플, magic 메소드, 오버로딩, 오버라이딩 본문

PROGRAMMING/Python

[ Python : Data Model ] 네임드 튜플, magic 메소드, 오버로딩, 오버라이딩

Rmsid01 2021. 8. 9. 23:56
  • FastCampus 파이썬 웹 개발 올인원 패키지 Online. 강의 중, Advanced part 를 듣고 정리한 글입니다.

Chapter 02 : Data Model

 

1. 튜플 vs 네임드 튜플

01. 튜플 

: 리스트와 다르게 변하지 않는 자료형을 말한다. 따라서, 리스트보다 속도가 더 빠르다.

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

 

02. 네임드 튜플

: 네임드 튜플은 튜플과 마찬가지로, 변하지 않는 성질을 가지고 있다. 

뿐만 아니라, 인덱스를 통한 접근과 키 값을 통한 접근 모두 가능하다.

 

아래의 예시와 같이, 클래스처럼 튜플을 사용할 수 있다. - 실제로, 타입을 찍어보면 클래스 형태로 되어있다.

# 네임드 튜플 사용
from collections import namedtuple

# 네임드 튜플 선언
Point = namedtuple('Point', 'x y')

# 두점 선언
pt1 = Point(1.0 , 5.0)
pt2 = Point(2.5 , 1.5)

line_length2 = sqrt((pt2.x - pt1.x) ** 2 + (pt2.y - pt1.y ) **2)

 

 

1) 네임드 튜플을 선언하는 방법

: 네임드 튜플을 선언 할 때, 이름과 변수를 같게 하는 것이 규칙이다. 

Point1 = namedtuple('Point',[ 'x', 'y'])  # 리스트 넣기
Point2 = namedtuple('Point', 'x, y') # 문자 사이에 ,  넣기
Point3 = namedtuple('Point', 'x y') # 공백을 넣어도되지만, 가능하면 리스트나 , 를 넣어주기

 

 

* 예외 상황 : 튜플은 중복을 허용하지 않기 때문에, 같은 값을 넣거나 예약어 사용하면 에러가 난다.

 

- 아래와 같은 코드의 경우, x 가 2개 들어가있고, class 는 예약어이므로 원래는  에러가 난다.

   하지만 아래와 같이 rename = True 를 설정하면, 에러가 발생하지 않는다. 

    rename = True 의 의미는, 변수의 의미가 잘못됬다면 알아서 이름을 지정해줘도 된다 라는 뜻이다.

# rename = False 가 디폴트이다.
Point4 = namedtuple('Point', 'x y x class', rename= True)

 

위의 코드 결과, 아래와 같이 같은 값인 x 와 예약어의 이름이 _2, _3 으로 바뀐것을 확인할 수 있다. 

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

 

2 ) 네임드 튜플의 객체를 생성하는 방법

: 위에서 선언한 네임드 튜플에 데이터를 넣어서 객체를 생성하는 방법

# dict to unpacking
temp_dict = { 'x' : 10,   'y' : 20}

p1 = Point1(x = 1 , y = 3) # ( 1 , 3 ) 를 하면 순서대로 넣어지는데  지정해도됨
p2 = Point2(8 , 5)
p3 = Point3(56 , y  = 20)  # 하나만 지정해도 된다.
p4 = Point4(10 , 20, 30 ,40)
p5 = Point3(**temp_dict) # Point(x=8, y=99) 언페킹을 이용해서도 할 수 있음

 

3 ) 네임드 튜플의 사용

- 튜플이라는 이름처럼, 인덱싱도 가능하다. 

- 하지만, 인덱싱 보다는 클래스 변수 접근 방식처럼 사용하는 것이 일반적이다. 

print(p1.x + p2.y)

 

4 ) 네임드 튜플 메소드

a. _make( ) : 새로운 객체를 생성하는 메소드

temp = [2,3]
p4 = Point1._make(temp)
# output  : Point(x=52, y=38)

 

b. _fields( ) : 필드 네임을 확인하는 메소드

print( p1._fields, p2._fields, p3._fields)
# output  : ('x', 'y') ('x', 'y') ('x', 'y')

 

 

c. _asdict( ) : 정렬된 딕셔너리로 반환 

print('ex4-3 : ', p1._asdict(), p2._asdict(), p4._asdict())
# output : OrderedDict([('x', 10), ('y', 35)]) OrderedDict([('x', 20), ('y', 40)]) OrderedDict([('x', 52), ('y', 38)])

print(dict(p1._asdict())) # 딕셔너리로 정렬 가능
# output : {'x': 10, 'y': 35}

 

d. _replace( ) : 수정된 '새로운' 객체 반환 - id 값이 달라진다.

print(p2._replace(y =100))
# output : Point(x=20, y=100)

 

2. 매직 메소드

- '메소드(method)' : 클래스 안에 정의된 함수

- 매직 메소드 : 메소드 중에서 __로 시작해서 __로 끝나는 메소드들

 

01. 모든 속성 및 메소드를 출력

: dir() 로 감싸면 그 함수 / 클래스에 들어있는 메소드를 출력할 수 있다. 

print(dir(int))
# output : ['__abs__', '__add__', '__and__', '__bool__', '__ceil__', '__class__', '__delattr__', '__dir__', '__divmod__', '__doc__', '__eq__', '__float__', '__floor__', '__floordiv__', '__format__', '__ge__', '__getattribute__', '__getnewargs__', '__gt__', '__hash__', '__index__', '__init__', '__init_subclass__', '__int__', '__invert__', '__le__', '__lshift__', '__lt__', '__mod__', '__mul__', '__ne__', '__neg__', '__new__', '__or__', '__pos__', '__pow__', '__radd__', '__rand__', '__rdivmod__', '__reduce__', '__reduce_ex__', '__repr__', '__rfloordiv__', '__rlshift__', '__rmod__', '__rmul__', '__ror__', '__round__', '__rpow__', '__rrshift__', '__rshift__', '__rsub__', '__rtruediv__', '__rxor__', '__setattr__', '__sizeof__', '__str__', '__sub__', '__subclasshook__', '__truediv__', '__trunc__', '__xor__', 'as_integer_ratio', 'bit_length', 'conjugate', 'denominator', 'from_bytes', 'imag', 'numerator', 'real', 'to_bytes']

 

02. 다양한 매직 메소드

- __add__() : + 와 같은 기능을 하는 메소드 

n.__add__(200)

 

- __doc__ : 설명해주는 메소드

n.__doc__

- __bool__() : Ture, False 로 나타내는 메소드

n.__bool__(), bool(n)

- __mul__(): 곱해주는 메소드 

n * 100, n.__mul__(100)

 

03. 클래스 내부에서 오버로딩과 오버라이딩

: 클래스 안에서, 오버로딩과 오버라이딩을 통해서, 메소드를 직접 새로 구현할 수 있다.

 

- 오버라이딩 이란?

: 상위 클래스가 가지고 있는 멤버 변수가 하위 클래스로 상속되는 것처럼,  상위 클래스가 가지고 있는 메서드를 하위 클래스에서 재정의 하여 사용하는 것. 즉, 이름은 같으나 상속받은 메서드를 새로 정의해서 다시 사용한다는 개념이다. 

: 메소드 이름이 같아야하고, 파라미터 개수나 자료형이 동일해야한다. 하지만 내용적으로는 달라도 된다. 

 

- 오버 로딩 이란 ?

: 같은 이름의 함수(메서드)를 여러개 정의하고 , 매개변수의 유형과 개수를 다르게 하여 다양한 유형의 호출에 응답할 수 있게 하는 것 

 

 

class Office:
    def __init__(self, name, work):
        self._name = name
        self._work = work

    # 오버라이딩 : 이미 가지고 있는것을  우리가 다시 선언해주는 것이다.
    # 원래 선언해주지 않는다면, 그냥 string으로 나올텐데, 오버라이딩을 해줌으로 아래처럼 정보가 출력된다.
    def __str__(self):
        return 'Office Class Info : {} {}'.format(self._name, self._work)

    # 오버로딩 
    def __ge__(self, x):
        print('Called >> __ge__ Method')
        if self._work >= x._work:
            return True
        else:
            return False

    def __le__(self , x):
        print('Called >> __le__ Method')
        if self._work <= x._work:
            return True
        else:
            return False

    def __sub__(self ,x ):
        print('Called >> __sub__ Method')
        return self._work - x._work

 

위에 코드에서 왜?! __str__ 은 오버라이딩 이고, 나머지 __ge__, __ le__ , __sub__ 는 오버로딩 이냐는 것은, 

위에서 만들어 놓은 메소드를 주석 처리하고, 결과를 실행해보면 나온다.

 

1 ) __str__ 를 주석 처리 해보면!? == 오버라이딩을 주석 처리해 보면?!

: 위에서 언급했듯이, 오버라이딩은 이미 상위 클래스에서 만들어진 것을 받아온 것이다.

   따라서, 내가 만들어준 함수에서 선언하지 않아도 실행 된다 !  ( 다만 알수없는 함수 형태로 반환 된다 )

o1 = Office('Hazel', 90)

print(o1)

# output : <__main__.Office object at 0x105f1b130>

 

위의 output을 보면, 에러는 안나지만, <__main__.Office object at 0x105f1b130>  로 알수 없는 결과물을 받을 수 있다.

이것을 내가 이해할 수 있는 언어로 볼려면, '오버라이딩'을 해주어야 한다는 것이다.

주석을 다시 해제 하고 출력을 하면!  위에서 오버라이딩 해준 문자열로 나오게 되는 것을 확인 할 수있다.

print(o1)
# output : Office Class Info : Hazel 90

 

2) __ge__ 를 주석 처리 해보면 !? == 오버로딩을 주석 처리를 해보면 ?!

: 만약 __ge__ 라는 메소드를 만들어 놓지 않았다면, 관련 함수를 실행 했을 때, 에러가 난다.

print('ex2-1 : ', o1 >= o2 )

# TypeError: '>=' not supported between instances of 'Office' and 'Office'

 

위에서의 작성한 클래스 같은 경우, 모두 다른 이름으로 함수를 만들었지만, 오버로딩(overloading)은 이름은 같은 함수를 여러개 만들 수 있다. 즉, 이름은 같지만 파라미터 갯수나 타입을 조정해서 다른 메소드를 중복으로 선언이 가능하다는 의미이다.