๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ

Python/Python

[Python ์ค‘๊ธ‰] Magic method์™€ named tuple

๋ฐ˜์‘ํ˜•

๐Ÿ”Š ํ•ด๋‹น ํฌ์ŠคํŒ…์€ ์ธํ”„๋Ÿฐ์˜ ํŒŒ์ด์ฌ ์ค‘๊ธ‰ ๊ฐ•์˜๋ฅผ ๊ณต๋ถ€ํ•˜๋ฉด์„œ ๋ณต์Šต์ฐจ์›์œผ๋กœ ์ •๋ฆฌํ•œ ํฌ์ŠคํŒ…์ž…๋‹ˆ๋‹ค. ํ•ด๋‹น ๋‚ด์šฉ์€ ์ฃผ๋กœ ๋ณธ์ธ์˜ ๋ณต์Šต์šฉ์ด๋ผ์„œ ์ž์„ธํ•œ ์„ค๋ช…์€ ์—†๋Š” ์  ์–‘ํ•ด ๋ถ€ํƒ๋“œ๋ฆฌ๊ฒ ์Šต๋‹ˆ๋‹ค. ์›๋ณธ ์ฝ”๋“œ๋Š” ์—ฌ๊ธฐ์— ์žˆ์Šต๋‹ˆ๋‹ค.

 

Python

 

์ด๋ฒˆ ํฌ์ŠคํŒ…์€ ์ง€๋‚œ ํฌ์ŠคํŒ…์—์„œ ์ž ๊น ์‚ดํŽด๋ณด์•˜๋˜ Magic method(๋งค์ง ๋ฉ”์†Œ๋“œ)์— ๋Œ€ํ•ด ์•Œ์•„๋ณด๊ณ  ์‹ค์Šต ์˜ˆ์ œ๋กœ ๊ตฌํ˜„ํ•ด๋ณด์ž. ๋˜ํ•œ Tuple ์„ฑ์งˆ์„ ์ง€๋‹ˆ์ง€๋งŒ ๋งˆ์น˜ ํŒŒ์ด์ฌ ์ž๋ฃŒ๊ตฌ์กฐ ์ค‘ ํ•˜๋‚˜์ธ Dictionary ์ฒ˜๋Ÿผ Key, Value๋ฅผ ๊ฐ–๋Š” Named Tuple์— ๋Œ€ํ•ด ์•Œ์•„๋ณด๊ณ  ๊ฐ„๋‹จํ•œ ์‹ค์Šต์œผ๋กœ ๊ตฌํ˜„ํ•ด๋ณด์ž.

 

์šฐ์„  Special method ๋ผ๊ณ ๋„ ๋ถˆ๋ฆฌ์šฐ๋Š” ๋งค์ง ๋ฉ”์†Œ๋“œ๋Š” ์ด๋ฏธ ๋‚ด์žฅ๋˜์–ด(built-in) ๋งŒ๋“ค์–ด์ง„ ๋ฉ”์†Œ๋“œ๋ฅผ ์˜๋ฏธํ•œ๋‹ค. ์ฆ‰, ๋‹ค์Œ ์ฝ”๋“œ์™€ ๊ฐ™์ด int ํ˜•๋„ ํ•˜๋‚˜์˜ ํด๋ž˜์Šค์ด๋ฉฐ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์—ฌ๋Ÿฌ๊ฐ€์ง€ ๋‚ด์žฅ๋œ ๋งค์ง ๋ฉ”์†Œ๋“œ๋ฅผ ๊ฐ–๊ณ  ์žˆ๋‹ค.

 

# int๋„ ํ•˜๋‚˜์˜ Class๋กœ ์ •์˜๋˜์–ด ์žˆ๋‹ค!
print(int)
print('-'*50)
# dir ๋ฉ”์†Œ๋“œ๋ฅผ ํ†ตํ•ด ๋งค์ง ๋ฉ”์†Œ๋“œ ํ™•์ธํ•ด๋ณด๊ธฐ
print(dir(int))

 

int ํด๋ž˜์Šค์— ๋‚ด์žฅ๋œ ์—ฌ๋Ÿฌ๊ฐ€์ง€ ๋งค์ง ๋ฉ”์†Œ๋“œ๋“ค

 

์œ„ ๊ฒฐ๊ณผํ™”๋ฉด์—์„œ __add__ ๋Š” ์šฐ๋ฆฌ๊ฐ€ ํ”ํžˆ ์‚ฌ์šฉํ•˜๋Š” '+' ๋ผ๋Š” ์—ฐ์‚ฐ์„ ์˜๋ฏธํ•œ๋‹ค. ์ฆ‰ ๋‹ค๋ฅธ ์—ฐ์‚ฐ  -, *, / ์—ฐ์‚ฐ๋“ค๋„ ์ด๋Ÿฐ ๋‚ด์žฅ๋œ ๋งค์ง ๋ฉ”์†Œ๋“œ๋กœ ์ด๋ฏธ ๊ตฌํ˜„๋˜์–ด ์žˆ๋‹ค๋Š” ๊ฒƒ์ด๋‹ค. ๊ฒฐ๊ตญ ์šฐ๋ฆฌ๋Š” ์ƒˆ๋กœ์šด ํด๋ž˜์Šค์™€ ๋งค์ง ๋ฉ”์†Œ๋“œ๋ฅผ ์žฌ์ •์˜ ํ•ด์คŒ์œผ๋กœ์จ ๋งค์ง ๋ฉ”์†Œ๋“œ๋ฅผ ๋ชฉ์ ์— ๋งž๊ฒŒ ์ปค์Šคํ„ฐ๋งˆ์ด์ง•ํ•˜๊ฒŒ ๋œ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์ด๋Ÿฐ ๊ธฐ์ˆ ์— ์ต์ˆ™ํ•ด์ง€๋‹ค๋ณด๋ฉด ์ข€ ๋” low-level ์—์„œ ํŒŒ์ด์ฌ์„ ๋‹ค๋ฃฐ ์ˆ˜ ์žˆ๋Š” ์‹ค๋ ฅ์ด ๋œ๋‹ค๊ณ  ๋ณผ ์ˆ˜ ์žˆ๋‹ค.

 

๊ทธ๋Ÿผ ์ด์ œ ๋งค์ง ๋ฉ”์†Œ๋“œ ๊ตฌํ˜„ํ•˜๋Š” ์ฝ”๋“œ๋ฅผ ์‚ดํŽด๋ณด์ž.

 

# ๊ณผ์ผ ํด๋ž˜์Šค๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๋ฉ”์ง ๋งค์†Œ๋“œ customizing ํ•˜๊ธฐ!
class Fruit:
    # ํด๋ž˜์Šค ๋ณ€์ˆ˜ ์ •์˜
    discount_rate = 1.0

    def __init__(self, name, price):
        self._name = name
        self._price = price

    # str ๋งค์ง ๋ฉ”์†Œ๋“œ ์ •์˜(ํด๋ž˜์Šค ํ• ๋‹นํ•˜๊ณ  runํ•˜๋ฉด ์ถœ๋ ฅ๋˜๋Š” ์ •๋ณด)
    def __str__(self):
        return "{} - {}".format(self._name, self._price)

    # add ๋งค์ง ๋ฉ”์†Œ๋“œ๋ฅผ ๋‚ด ๋ง˜๋Œ€๋กœ ์ •์˜ํ•ด๋ณด๊ธฐ
    def __add__(self, other_inst):
        return (self._price + other_inst._price)

    # mul ๋งค์ง ๋ฉ”์†Œ๋“œ๋ฅผ ๋‚ด ๋ง˜๋Œ€๋กœ ์ •์˜ํ•ด๋ณด๊ธฐ
    def __mul__(self, other_inst):
        return (self._price * other_inst._price)

    # le(less) & ge(greater) ๋Œ€์†Œ๋น„๊ต ๋งค์ง ๋ฉ”์†Œ๋“œ ๋‚ด๋ง˜๋Œ€๋กœ ์ •์˜ํ•ด๋ณด๊ธฐ
    def __le__(self, other_inst):
        if self._price <= other_inst._price:
            return True
        return False

    def __ge__(self, other_inst):
        if self._price >= other_inst._price:
            return True
        return False
    # ํ• ์ธ ๋œ ๊ฐ€๊ฒฉ ์ œ๊ณตํ•˜๋Š” ์ธ์Šคํ„ด์Šค ๋ฉ”์†Œ๋“œ
    def calc_discount(self):
        return "Discounted price: {}".format(self._price * Fruit.discount_rate)
    # ํ• ์ธ๋ฅ  ์ปค์Šคํ„ฐ๋งˆ์ด์ง• ์œ„ํ•œ ํด๋ž˜์Šค ๋ฉ”์†Œ๋“œ ์ •์˜
    @classmethod
    def change_rate(cls, rate):
        if rate >= 1.0:
            print("No longer applied to discount")
        cls.discount_rate = rate

    # ํ• ์ธ์œจ ์ƒ์Šนํ•˜
f1 = Fruit('orange', 2000)
f2 = Fruit('melon', 5500)

add = f1 + f2
print(add)
print('-'*50)
mul = f1 * f2
print(mul)
print('-'*50)
print(f1 <= f2)
print('-'*50)
print(Fruit.calc_discount(f1))
print('-'*50)
# ํ• ์ธ์œจ ๋ณ€๊ฒฝ!
Fruit.change_rate(0.5)
print(Fruit.calc_discount(f1))

 

๊ฒฐ๊ณผํ™”๋ฉด์€ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค. ๋ณด๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ์ผ๋ฐ˜์ ์ธ +(add), *(mul), ๋Œ€์†Œ๋น„๊ต(le, ge) ๋งค์ง ๋ฉ”์†Œ๋“œ๋ฅผ ์šฐ๋ฆฌ ์ž…๋ง›๋Œ€๋กœ ์ปค์Šคํ„ฐ๋งˆ์ด์ง• ํ•  ์ˆ˜ ์žˆ๋‹ค.

 

๊ฒฐ๊ณผํ™”๋ฉด

 

๋‹ค์Œ์€ named Tuple์„ ์•Œ์•„๋ณด์ž. 

 

from collections import namedtuple

# ๋„ค์ž„๋“œ ํŠœํ”Œ ์ •์˜ํ•˜๋Š” ๋ฐฉ๋ฒ•(์ด 4๊ฐ€์ง€)
Point1 = namedtuple('Point1', 'x y')
Point2 = namedtuple('Point2', 'x, y')
Point3 = namedtuple('Point3', ['x', 'y'])
Point4 = namedtuple('Point4', 'x, y, x, class',
                    rename=True) # ์ค‘๋ณต๋œ key ์ด๋ฆ„ ๋˜๋Š” class๊ฐ™์€ ์˜ˆ์•ฝ์–ด ์ด๋ฆ„๊ณผ ๋™์ผํ•  ๋•Œ key๊ฐ’์œผ๋กœ ์ง€์ •ํ•ด์ฃผ๊ธฐ ์œ„ํ•œ ํŒŒ๋ผ๋ฏธํ„ฐ

pt1 = Point1(30, 10)
pt1_2 = Point1(x=30, y=10) # ์ง€์ •ํ•œ key๊ฐ’ ์ด์šฉ๋„ ๊ฐ€๋Šฅ
print(pt1)
print(pt1_2)
print('-'*50)
# namedtuple์„ ์ด์šฉํ•ด์„œ ๋‘ ์  ๊ฐ„์˜ ์œ ํด๋ฆฌ๋””์•ˆ ๊ฑฐ๋ฆฌ ๊ตฌํ•˜๊ธฐ
pt1 = Point1(5, 6)
pt2 = Point1(3, 9)
dist = sqrt((pt1.x - pt2.x)**2 + (pt1.y - pt2.y)**2) # ๋ฌผ๋ก  key์ธ x,y๋Œ€์‹  [0],[1] ์ฒ˜๋Ÿผ ์ธ๋ฑ์Šค๋„ ์‚ฌ์šฉ๊ฐ€๋Šฅํ•˜์ง€๋งŒ ๊ฐ€๋…์„ฑ์ด key๊ฐ’์„ ์ด์šฉํ•˜๋Š” ๊ฒƒ์ด ๋” ์ข‹์Œ!
print(dist)
print('-'*50)
#-------------------------------------------------------------------------------------------------
# Dictionary to Namedtuple
temp_dict = {'x': 75, 'y': 55} # key์ด๋ฆ„์€ namedTuple์˜ key ์ด๋ฆ„๊ณผ ๋™์ผํ•˜๋„๋ก ์„ค์ •
temp_Point = Point1(**temp_dict)
print(temp_Point)
print('-'*50)

 

  • namedtuple์€ key๊ฐ€ ์žˆ๋Š” tuple ํ˜•ํƒœ์ด๋ฏ€๋กœ labeling ํ•˜๊ธฐ ์ข‹์Œ
  • ๊ธฐ์กด Tuple์€ ์š”์†Œ์— ์ ‘๊ทผํ•˜๋ ค๋ฉด ์ธ๋ฑ์Šค๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ–ˆ์ง€๋งŒ namedtuple์€ Key๊ฐ’์„ ์‚ฌ์šฉํ•˜๋ฉด ๋งค์šฐ ํŽธ๋ฆฌ
  • Key๊ฐ’์ด ๋™์ผํ•จ์ด ๊ฐ€์ •๋œ ๋”•์…”๋…€๋ฆฌ์—์„œ namedtuple๋กœ unpacking์„ ํ†ตํ•ด ๋ณ€ํ™˜์ด ๊ฐ€๋Šฅ

์ด์ œ namedtuple์ด ๊ฐ–๋Š” ๋ช‡ ๊ฐ€์ง€ ๋ฉ”์†Œ๋“œ๋ฅผ ์•Œ์•„๋ณด์ž.

 

# ๋„ค์ž„๋“œ ํŠœํ”Œ ๋ฉ”์†Œ๋“œ
# 1. _make() : ์ƒˆ๋กœ์šด ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑ - list to namedtuple
temp = [52, 39]
temp2 = (52, 39)
pt4 = Point1._make(temp)
pt5 = Point1._make(temp2)
print(pt4)
print(pt5)
# 2. _fields : ํ•„๋“œ ๋„ค์ž„(namedtuple์˜ key๊ฐ’๋“ค) ํ™•์ธ
print(pt4._fields)
# 3. _asdict() : namedtupe -> OrederedDict๋กœ ๋ณ€ํ™˜
print(pt4._asdict()) #๋””ํดํŠธ๋Š” key ๊ธฐ์ค€์œผ๋กœ ์ •๋ ฌ๋˜๋Š” ๋“ฏํ•จ!
print('-'*50)

 

๊ฒฐ๊ณผํ™”๋ฉด

 

  • _make : ๋ฆฌ์ŠคํŠธ ๋˜๋Š” ํŠœํ”Œ์„ namedtuple ํ˜•ํƒœ๋กœ ๋ฐ”๊ฟ€ ์ˆ˜ ์žˆ์Œ
  • _fields : namedtuple์„ ๋งŒ๋“ค ๋•Œ ์ •์˜ํ–ˆ๋˜ key๊ฐ’๋“ค์ด ๋ฌด์—‡์ธ์ง€ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Œ
  • _asdict : namedtuple์„ ์ˆœ์„œ๊ฐ€์ •๋ ฌ๋œ ๋”•์…”๋„ˆ๋ฆฌ์ธ Ordereddict ํ˜•ํƒœ๋กœ ๋ณ€ํ™˜ ํ•  ์ˆ˜ ์žˆ์Œ

์ด์ œ namedtuple์„ ํ™œ์šฉํ•œ ๊ฐ„๋‹จํ•œ ์‹ค์Šต์„ ๊ตฌํ˜„ํ•ด๋ณด์ž.

 

# 4๊ฐœ์˜ ๋ฐ˜(A,B,C,D)๊ฐ€ ์žˆ๊ณ  ๊ฐ 20๋ช…์˜ ํ•™์ƒ์ด ์žˆ์„ ๋•Œ, ๊ฐ ํ•™์ƒ๋ณ„ ๋ฐ˜-๋ฒˆํ˜ธ ๋„ค์ž„๋“œ ํŠœํ”Œ ๋งŒ๋“ค๊ธฐ
# namedtuple ์ •์˜
Classes = namedtuple('Classes', 'room, number')
rooms = 'A B C D'.split()
numbers = [str(n) for n in range(1, 21)]

# ์ด์ค‘ for ๋ฌธ list comprehension ์ด์šฉ
students = [Classes(room=room, number=number) for room in rooms for number in numbers]
print(len(students))

# ์ฝ”๋“œ ๊ฐ€๋…์„ฑ ์œ„ํ•œ ๋ฆฌํŒฉํ† ๋ง
students = [Classes(room, number)
            for room in 'A B C D'.split()
                for number in [str(n) for n in range(1, 21)]]
print(len(students))

# ๊ฐ ํ•™์ƒ๋“ค์˜ ๋ฐ˜-๋ฒˆํ˜ธ ์ถœ๋ ฅ
for idx, s in enumerate(students):
    print(f"{idx+1}๋ฒˆ ํ•™์ƒ์€ {s.room}๋ฐ˜, {s.number}๋ฒˆ ํ•™์ƒ์ž…๋‹ˆ๋‹ค.")
๋ฐ˜์‘ํ˜•