코드 추상화: 클래스와 객체 2부

μ•ˆλ‚΄: python-textbook.readthedocs.io의 Classes λ‚΄μš©μ„ μš”μ•½ 및 μˆ˜μ •ν•œ λ‚΄μš©μž…λ‹ˆλ‹€.

메서드 종류

ν΄λž˜μŠ€μ—μ„œ μ„ μ–Έλœ λ©”μ„œλ“œλŠ” μ„Έ μ’…λ₯˜λ‘œ λ‚˜λ‰œλ‹€.

  1. μΈμŠ€ν„΄μŠ€ λ©”μ„œλ“œ(instance method)
    • 첫째 λ§€κ°œλ³€μˆ˜λ‘œ self μ‚¬μš© (κ΄€λ‘€)
    • 클래슀의 μΈμŠ€ν„΄μŠ€κ°€ μƒμ„±λœ 후에 μΈμŠ€ν„΄μŠ€μ™€ ν•¨κ»˜ μ‚¬μš©.
  2. 클래슀 λ©”μ„œλ“œ(class method)
    • 첫째 λ§€κ°œλ³€μˆ˜λ‘œ cls μ‚¬μš© (κ΄€λ‘€)
    • 클래슀 λ‚΄μ˜ λͺ¨λ“  속성과 λ©”μ„œλ“œλ₯Ό cls μ§€μ •μžμ™€ ν•¨κ»˜ μ‚¬μš© κ°€λŠ₯ 단, μΈμŠ€ν„΄μŠ€ 속성 μ‚¬μš© λΆˆκ°€.
    • μΈμŠ€ν„΄μŠ€ 없이 클래슀 이름과 ν•¨κ»˜ μ‚¬μš©
    • ν•΄λ‹Ή 클래슀의 λͺ¨λ“  μΈμŠ€ν„΄μŠ€μ—μ„œλ„ μ‚¬μš© κ°€λŠ₯
  3. 정적 λ©”μ„œλ“œ(static method)
    • 첫째 λ§€κ°œλ³€μˆ˜μ— λŒ€ν•œ μ˜λ¬΄μ‚¬ν•­ μ—†μŒ.
    • 클래슀 λ‚΄μ˜ λͺ¨λ“  속성과 λ©”μ„œλ“œλ₯Ό 클래슀 이름과 ν•¨κ»˜ μ‚¬μš©ν•΄μ•Ό 함. 단, μΈμŠ€ν„΄μŠ€ 속성 μ‚¬μš© λΆˆκ°€.
    • μΈμŠ€ν„΄μŠ€ 없이 클래슀 이름과 ν•¨κ»˜ μ‚¬μš©
    • ν•΄λ‹Ή 클래슀의 λͺ¨λ“  μΈμŠ€ν„΄μŠ€μ—μ„œλ„ μ‚¬μš© κ°€λŠ₯

클래슀 λ©”μ„œλ“œμ™€ 정적 λ©”μ„œλ“œλŠ” 항상 μž₯μ‹μžμ™€ ν•¨κ»˜ μ„ μ–Έλ˜μ–΄μ•Ό ν•œλ‹€. λ°˜λ©΄μ— selfλ₯Ό 첫째 λ§€κ°œλ³€μˆ˜λ‘œ μ‚¬μš©ν•˜λŠ” μΈμŠ€ν„΄μŠ€ λ©”μ„œλ“œλŠ” νŠΉλ³„ν•œ μž₯μ‹μžκ°€ ν•„μš” μ—†λ‹€. 예λ₯Ό λ“€μ–΄, μ½”λ“œ 좔상화: ν΄λž˜μŠ€μ™€ 객체 1λΆ€μ—μ„œ μ‚΄νŽ΄λ³Έ λͺ¨λ“  λ©”μ„œλ“œλŠ” μΈμŠ€ν„΄μŠ€ λ©”μ„œλ“œμ΄λ‹€.

클래스 장식자

μž₯μ‹μž(decorator)λŠ” λ‹€λ₯Έ ν•¨μˆ˜μ˜ κΈ°λŠ₯에 λ‹€λ₯Έ κΈ°λŠ₯을 μΆ”κ°€ν•  λ•Œ μ‚¬μš©λ˜λŠ” ν•¨μˆ˜μ΄λ‹€. 즉, μž₯μ‹μžλŠ” ν•¨μˆ˜λ₯Ό 인자둜 λ°›μ•„ κ·Έ ν•¨μˆ˜κ°€ ν•˜λŠ” 일에 더해 λ‹€λ₯Έ κΈ°λŠ₯도 μˆ˜ν–‰ν•˜λŠ” ν•¨μˆ˜λ₯Ό λ¦¬ν„΄κ°’μœΌλ‘œ λ‚΄μ€€λ‹€. 이런 μž₯μ‹μžλ₯Ό ν•¨μˆ˜λ‘œ μ •μ˜ν•  수 μžˆλŠ” μ΄μœ λŠ” ν•¨μˆ˜κ°€ 제1μ’… 객체이기 λ•Œλ¬Έμ΄λ‹€. 즉, λ‹€λ₯Έ ν•¨μˆ˜μ˜ 인자 λ˜λŠ” λ¦¬ν„΄κ°’μœΌλ‘œ μ‚¬μš©λ  수 μžˆλ‹€.

μ°Έκ³ : μž₯μ‹μžλ₯Ό 기쑴의 ν•¨μˆ˜μ— μœ μš©ν•œ μΆ”κ°€κΈ°λŠ₯을 μ œκ³΅ν•˜μ—¬ 포μž₯ν•œλ‹€λŠ” μ˜λ―Έμ—μ„œ 래퍼(wrapper)의 μΌμ’…μœΌλ‘œ κ°„μ£Όν•œλ‹€.

νŒŒμ΄μ¬μ—μ„œ 기본으둜 μ œκ³΅ν•˜λŠ” μž₯μ‹μžκ°€ 맀우 λ‹€μ–‘ν•˜λ©°, μ‚¬μš©μžκ°€ 직접 μž₯μ‹μžλ₯Ό μ •μ˜ν•  μˆ˜λ„ μžˆλ‹€. μ—¬κΈ°μ„œλŠ” ν΄λž˜μŠ€μ—μ„œ μ„ μ–Έλœ λ©”μ„œλ“œμ˜ μ’…λ₯˜λ₯Ό κ΅¬λΆ„ν•˜κΈ° μœ„ν•΄ μ‚¬μš©λ˜λŠ” 두 개의 μž₯μ‹μž @classmethod와 @staticmethodλ₯Ό μ†Œκ°œν•œλ‹€.

클래스 메서드 장식자: @classmethod

클래슀의 μΈμŠ€ν„΄μŠ€λ₯Ό μƒμ„±ν•˜μ§€ μ•Šμ•„λ„ 클래슀 이름과 ν•¨κ»˜ μ‚¬μš©ν•  수 μžˆλŠ” λ©”μ„œλ“œλ₯Ό 클래슀 λ©”μ„œλ“œ(class method)라 λΆ€λ₯΄λ©°, @classmethodλΌλŠ” μž₯μ‹μžμ™€ ν•¨κ»˜ μ„ μ–Έλœλ‹€.

@classmethod
def ν•¨μˆ˜μ΄λ¦„(cls,인자1, ..., 인자k):
    λ³Έλ¬Έ

클래슀 λ©”μ„œλ“œ μ—­μ‹œ 첫째 인자둜 ν•΄λ‹Ή 클래슀λ₯Ό 받을 μ€€λΉ„λ₯Ό ν•˜λŠ” λ§€κ°œλ³€μˆ˜λ₯Ό λ°˜λ“œμ‹œ μ‚¬μš©ν•΄μ•Ό ν•œλ‹€. ν•˜μ§€λ§Œ self λŒ€μ‹ μ— 클래슀(class) 자체λ₯Ό κ°€λ¦¬ν‚¨λ‹€λŠ” 의미둜 clsλ₯Ό κ΄€λ‘€μ μœΌλ‘œ μ‚¬μš©ν•œλ‹€. 클래슀 λ©”μ„œλ“œλ₯Ό ν˜ΈμΆœν•˜λ©΄ cls에 ν•΄λ‹Ή 클래슀 이름이 μžλ™μœΌλ‘œ μ‚½μž…λœλ‹€. λ”°λΌμ„œ 클래슀 λ©”μ„œλ“œλ₯Ό ν˜ΈμΆœν•  λ•Œ 첫째 μΈμžλŠ” μƒλž΅ν•œλ‹€.

클래슀 λ©”μ„œλ“œλ₯Ό μ„ μ–Έν•  λ•Œ μ‚¬μš©λ˜λŠ” cls λ§€κ°œλ³€μˆ˜λŠ” 클래슀 μžμ‹ μ„ κ°€λ¦¬ν‚€λŠ” μ§€μ •μž 역할을 μˆ˜ν–‰ν•œλ‹€. λ”°λΌμ„œ 클래슀 λ©”μ„œλ“œ λ‚΄λΆ€μ—μ„œλŠ” μΈμŠ€ν„΄μŠ€ 속성과 μΈμŠ€ν„΄μŠ€ λ©”μ„œλ“œλ₯Ό ν™œμš©ν•˜μ§€ λͺ»ν•œλ‹€.

클래스 메서드 활용법

클래슀 λ©”μ„œλ“œλ₯Ό μ‚¬μš©ν•˜λŠ” μ΄μœ λŠ” 크게 두 가지이닀.

첫째, μƒμˆ˜(constant) 역할을 μˆ˜ν–‰ν•˜λŠ” κ°’μ΄λ‚˜ 클래슀 속성을 직접 ν™œμš©ν•˜κ³ μž ν•  λ•Œ μœ μš©ν•˜λ‹€. μ΄λŠ” μƒμˆ˜μ™€ 클래슀 속성을 ν™œμš©ν•˜κΈ° μœ„ν•΄ νŠΉμ • 객체가 ν•„μš”ν•˜μ§€ μ•ŠκΈ° λ•Œλ¬Έμ΄λ‹€. λ˜ν•œ κ²½μš°μ— 따라 μ—°κ΄€λœ μƒμˆ˜λ‚˜ ν•¨μˆ˜λ“€μ„ ν•˜λ‚˜λ‘œ 클래슀둜 λ¬Άμ–΄μ„œ ν™œμš©ν•  μˆ˜λ„ μžˆλ‹€. 이런 경우 ꡳ이 객체λ₯Ό 생성할 ν•„μš”κ°€ μ—†λ‹€.

In [1]:
class Title:
    TITLES = ('Dr', 'Mr', 'Mrs', 'Ms')

    @classmethod
    def allowed_titles_starting_with(cls, startswith):
        # startwith둜 μ‹œμž‘ν•˜λŠ” 타이틀 μ°ΎκΈ°
        return [t for t in cls.TITLES if t.startswith(startswith)]

    @classmethod
    def allowed_titles_ending_with(cls, endswith):
        # endswith둜 λλ‚˜λŠ” 타이틀 μ°ΎκΈ°
        return [t for t in cls.TITLES if t.endswith(endswith)]


print(Title.allowed_titles_starting_with("M"))
print(Title.allowed_titles_ending_with("s"))
['Mr', 'Mrs', 'Ms']
['Mrs', 'Ms']

PythonTutor 활용 1

μœ„ μ½”λ“œλ₯Ό PythonTutor: 클래슀 μž₯μ‹μž 예제 1μ—μ„œ μ‹€ν–‰ν•˜λ©΄μ„œ 확인할 수 μžˆλ‹€.

λ‘˜μ§Έ, λ¦¬ν„΄κ°’μœΌλ‘œ ν•΄λ‹Ή 클래슀의 μΈμŠ€ν„΄μŠ€λ₯Ό μƒμ„±ν•˜λŠ” 클래슀 λ©”μ„œλ“œλ₯Ό μ‚¬μš©ν•˜λŠ” κ²½μš°κ°€ μ’…μ’… μžˆλ‹€. μ΄λ ‡κ²Œ ν•˜λ©΄ ν•΄λ‹Ή 클래슀의 μΈμŠ€ν„΄μŠ€λ₯Ό μƒμ„±ν•˜κΈ° μœ„ν•œ 쀀비사항을 이 클래슀 λ©”μ„œλ“œκ°€ μ•Œμ•„μ„œ μ²˜λ¦¬ν•΄μ€€λ‹€. 예λ₯Ό λ“€μ–΄, μ•„λž˜ Person 클래슀의 fromDict λ©”μ„œλ“œλŠ” νŠΉμ • ν…μŠ€νŠΈ νŒŒμΌμ— μ €μž₯된 정보λ₯Ό ν™•μΈν•œ ν›„ κ·Έ 정보λ₯Ό 적절히 ν™œμš©ν•˜μ—¬ Person 클래슀의 μΈμŠ€ν„΄μŠ€λ₯Ό 생성해쀀닀.

In [2]:
class Person:

    def __init__(self, name, surname):
        self.name = name
        self.surname = surname

    @classmethod
    def fromDict(cls, nameDict):
        # {'이름': 'Jane', 'μ„±': 'Doe'} ν˜•μ‹μ˜ 사전 μžλ£Œν˜•μ—μ„œ
        # 이름과 μ„± 정보λ₯Ό μΆ”μΆœν•΄μ„œ Person 클래슀 객체 생성
        params = nameDict.values()
        return cls(*params)
    
janeDoe = {'이름':'Jane', 'μ„±':'Doe'}

jDoe = Person.fromDict(janeDoe)

print(jDoe.name)
print(jDoe.surname)
Jane
Doe

PythonTutor 활용 2

μœ„ μ½”λ“œλ₯Ό PythonTutor: 클래슀 μž₯μ‹μž 예제 2μ—μ„œ μ‹€ν–‰ν•˜λ©΄μ„œ 확인할 수 μžˆλ‹€.

정적 메서드 장식자: @staticmethod

정적 λ©”μ„œλ“œλŠ” ν΄λž˜μŠ€λ‚˜ μΈμŠ€ν„΄μŠ€λ₯Ό μ§€μ •ν•˜λŠ” 인자λ₯Ό μ‚¬μš©ν•˜μ§€ μ•ŠλŠ”λ‹€. λ”°λΌμ„œ 일반 ν•¨μˆ˜λ₯Ό μ„ μ–Έν•˜λŠ” 것과 μ™„λ²½ν•˜κ²Œ λ™μΌν•˜λ‹€. λ‹€λ§Œ, 클래슀 λ‚΄λΆ€μ—μ„œ μ„ μ–Έλ˜μ—ˆκΈ° λ•Œλ¬Έμ— 항상 ν•΄λ‹Ή 클래슀의 이름을 μ§€μ •μžλ‘œ μ‚¬μš©ν•˜μ—¬ ν˜ΈμΆœλœλ‹€. λ˜ν•œ 정적 λ©”μ„œλ“œμ˜ λ³Έλ¬Έμ—μ„œ ν•΄λ‹Ή 클래슀의 μΈμŠ€ν„΄μŠ€ 속성과 μΈμŠ€ν„΄μŠ€ λ©”μ„œλ“œλŠ” ν™œμš©λ  수 μ—†λ‹€. λ°˜λ©΄μ— μΈμŠ€ν„΄μŠ€ λ©”μ„œλ“œλŠ” 클래슀 λ©”μ„œλ“œμ™€ 정적 λ©”μ„œλ“œλ₯Ό λͺ¨λ‘ ν™œμš©ν•  수 μžˆλ‹€.

결둠적으둜, 클래슀 λ©”μ„œλ“œμ˜ μ„ μ–Έ 및 ν™œμš© 방식과 거의 λ™μΌν•˜λ‹€. λ‹€λ§Œ, 정적 λ©”μ„œλ“œλŠ” 클래슀λ₯Ό 지정할 λ•Œ cls λŒ€μ‹ μ— ν•΄λ‹Ή 클래슀의 이름을 직접 μ–ΈκΈ‰ν•΄μ•Ό ν•œλ‹€λŠ” 차이점이 μžˆμ„ 뿐이닀. μ•„λž˜ μ˜ˆμ œκ°€ μ„Έ μ’…λ₯˜μ˜ λ©”μ„œλ“œ ν™œμš©λ²•μ„ 잘 보여쀀닀.

In [3]:
class Person:
    TITLES = ('Dr', 'Mr', 'Mrs', 'Ms')

    def __init__(self, name, surname):
        self.name = name
        self.surname = surname

    # μΈμŠ€ν„΄μŠ€ λ©”μ„œλ“œ
    def fullname(self):
        return f"{self.name} {self.surname}"

    # 클래슀 λ©”μ„œλ“œ
    @classmethod
    def allowed_titles_starting_with(cls, startswith):
        return [t for t in cls.TITLES if t.startswith(startswith)]

    # 정적 λ©”μ„œλ“œ
    @staticmethod
    def allowed_titles_ending_with(endswith):
        return [t for t in Person.TITLES if t.endswith(endswith)]


jane = Person("Jane", "Smith")

print(jane.fullname())

print(jane.allowed_titles_starting_with("M"))
print(Person.allowed_titles_starting_with("M"))

print(jane.allowed_titles_ending_with("s"))
print(Person.allowed_titles_ending_with("s"))
Jane Smith
['Mr', 'Mrs', 'Ms']
['Mr', 'Mrs', 'Ms']
['Mrs', 'Ms']
['Mrs', 'Ms']

PythonTutor 활용 3

μœ„ μ½”λ“œλ₯Ό PythonTutor: μž₯μ‹μž μ˜ˆμ œμ—μ„œ μ‹€ν–‰ν•˜λ©΄μ„œ 확인할 수 μžˆλ‹€.

연습 4

  1. λ‹€μŒ 속성과 λ©”μ„œλ“œλ₯Ό ν¬ν•¨ν•˜λŠ” 클래슀 Numbersλ₯Ό μ •μ˜ν•˜λΌ.
    • MULTIPLIER: 클래슀 속성
    • __init__ λ©”μ„œλ“œ: 숫자 두 개λ₯Ό μž…λ ₯λ°›μ•„ 각각 μΈμŠ€ν„΄μŠ€ 속성 x와 y둜 μ €μž₯.
    • add: μΈμŠ€ν„΄μŠ€ λ©”μ„œλ“œ. x와 y의 ν•© λ‚΄μ£ΌκΈ°
    • multiply: 클래슀 λ©”μ„œλ“œ. ν•˜λ‚˜μ˜ 숫자 aλ₯Ό μž…λ ₯ λ°›μ•„ MULTIPLIER와 κ³±μ„Ό κ²°κ³Ό λ‚΄μ£ΌκΈ°
    • subtract: 정적 λ©”μ„œλ“œ. b와 c 숫자 두 개λ₯Ό μž…λ ₯ λ°›μ•„ b-c λ‚΄μ£ΌκΈ°
In [4]:
class Numbers:
    MULTIPLIER = 3.5

    def __init__(self, x, y):
        self.x = x
        self.y = y

    def add(self):
        return self.x + self.y

    @classmethod
    def multiply(cls, a):
        return cls.MULTIPLIER * a

    @staticmethod
    def subtract(b, c):
        return b - c
In [5]:
twoAndfive = Numbers(2,5)

print(twoAndfive.add())

print(Numbers.multiply(4))
print(twoAndfive.multiply(4))

print(Numbers.subtract(7, 2))
print(twoAndfive.subtract(7,2))
7
14.0
14.0
5
5

object 클래스와 매직 메서드

μƒμ„±λœ 객체와 κ΄€λ ¨λœ 속성과 λ©”μ„œλ“œλ₯Ό ν™•μΈν•˜λ €λ©΄ dir ν•¨μˆ˜λ₯Ό ν™œμš©ν•œλ‹€. 예λ₯Ό λ“€μ–΄, λ‹€μŒ Person 클래슀의 μΈμŠ€ν„΄μŠ€μΈ jane을 μƒμ„±ν•΄λ³΄μž.

In [6]:
class Person:
    def __init__(self, name, surname):
        self.name = name
        self.surname = surname

    def fullname(self):
        return f"{self.name} {self.surname}"

jane = Person("Jane", "Smith")

이제 dir ν•¨μˆ˜λ₯Ό μ΄μš©ν•˜μ—¬ jane이 κ°€λ¦¬ν‚€λŠ” 객체의 속성과 λ©”μ„œλ“œλ₯Ό ν™•μΈν•΄λ³΄μž.

In [7]:
dir(jane)
Out[7]:
['__class__',
 '__delattr__',
 '__dict__',
 '__dir__',
 '__doc__',
 '__eq__',
 '__format__',
 '__ge__',
 '__getattribute__',
 '__gt__',
 '__hash__',
 '__init__',
 '__init_subclass__',
 '__le__',
 '__lt__',
 '__module__',
 '__ne__',
 '__new__',
 '__reduce__',
 '__reduce_ex__',
 '__repr__',
 '__setattr__',
 '__sizeof__',
 '__str__',
 '__subclasshook__',
 '__weakref__',
 'fullname',
 'name',
 'surname']

최상위 클래스: object

λ†€λžκ²Œλ„ Person 클래슀λ₯Ό μ„ μ–Έν•  λ•Œ λͺ…μ‹œλœ 속성과 λ©”μ„œλ“œ 이외에 μΆ”κ°€λ‘œ λ§Žμ€ 이름이 보인닀. μ΄μœ λŠ” λ‹€μŒκ³Ό κ°™λ‹€.

  • 파이썬의 λͺ¨λ“  ν΄λž˜μŠ€λŠ” objectλΌλŠ” 클래슀λ₯Ό μƒμ†ν•œλ‹€.
  • ν•˜λ‚˜μ˜ 클래슀λ₯Ό μƒμ†ν•˜λ©΄ ν•΄λ‹Ή 클래슀의 속성과 λ©”μ„œλ“œλ„ λͺ¨λ‘ ν•¨κ»˜ μƒμ†λ°›λŠ”λ‹€.
  • object ν΄λž˜μŠ€μ—λŠ” μœ„μ—μ„œ μ–ΈκΈ‰λœ, 이쀑 λ°‘μ€„λ‘œ 감싸인 속성과 λ©”μ„œλ“œκ°€ μ„ μ–Έλ˜μ–΄ μžˆλ‹€.

μƒμ†μ˜ λŒ€μƒμΈ 클래슀λ₯Ό μƒμœ„ 클래슀(superclass) λ˜λŠ” λΆ€λͺ¨ 클래슀(parent class), μƒμ†ν•˜λŠ” 클래슀λ₯Ό ν•˜μœ„ 클래슀(subclass) λ˜λŠ” μžμ‹ 클래슀(child class) 라고 λΆ€λ₯Έλ‹€. 이런 μ˜λ―Έμ—μ„œ objectλŠ” μ΅œμƒμœ„μ— μœ„μΉ˜ν•œ ν΄λž˜μŠ€μ΄λ‹€. object ν΄λž˜μŠ€μ— ν¬ν•¨λœ 속성과 λ©”μ„œλ“œλŠ” λͺ¨λ‘ 양끝이 이쀑 λ°‘μ€„λ‘œ 감싸이며, object 클래슀의 λ©”μ„œλ“œλ₯Ό νŠΉλ³„νžˆ 맀직 λ©”μ„œλ“œ(magic method)라 λΆ€λ₯Έλ‹€. λ”°λΌμ„œ μž„μ˜μ˜ ν΄λž˜μŠ€λŠ” object ν΄λž˜μŠ€μ—μ„œ μ„ μ–Έλœ 맀직 λ©”μ„œλ“œμ™€ 속성을 λͺ¨λ‘ μƒμ†λ°›λŠ”λ‹€.

Person 클래슀λ₯Ό μ—„λ°€νžˆ μ •μ˜ν•˜λ €λ©΄ λ‹€μŒκ³Ό 같이 μƒμ†ν•˜λŠ” object 클래슀λ₯Ό λͺ…μ‹œν•΄μ•Ό ν•œλ‹€. ν•˜μ§€λ§Œ 상속 λŒ€μƒμ΄ object 클래슀 뿐인 경우 λͺ…μ‹œν•˜μ§€ μ•Šμ•„λ„ 되며, κ΄„ν˜Έλ„ μƒλž΅ν•œλ‹€.


class Person(object):
    def __init__(self, name, surname):
        self.name = name
        self.surname = surname

    def fullname(self):
        return f"{self.name} {self.surname}"

주의: 파이썬 2μ—μ„œλŠ” objectλ₯Ό λ°˜λ“œμ‹œ λͺ…μ‹œν•΄μ•Ό ν•œλ‹€. 상속에 λŒ€ν•΄μ„œλŠ” 일단 이 μ •λ„λ§Œ μ•Œκ³  있으면 되며, 보닀 μžμ„Έν•œ μ„€λͺ…은 μΆ”ν›„ 닀룬닀.

object 클래스의 속성과 매직 메서드 기능

초기 μ„€μ • λ©”μ„œλ“œ __init__λŠ” ꡳ이 선언될 ν•„μš”κ°€ μ—†λ‹€κ³  μ•žμ„œ μ–ΈκΈ‰ν•˜μ˜€λŠ”λ°, κ·Έ μ΄μœ κ°€ λ°”λ‘œ μƒμ†ν•˜λŠ” λΆ€λͺ¨ 클래슀의 __init__ λ©”μ„œλ“œκ°€ μžλ™μœΌλ‘œ μ‚¬μš©λ˜κΈ° λ•Œλ¬Έμ΄λ‹€. ν•˜μ§€λ§Œ μœ„μ™€ 같이 __init__ λ©”μ„œλ“œλ₯Ό μ„ μ–Έν•˜λ©΄ μƒˆλ‘œ μ •μ˜λœ ν•¨μˆ˜κ°€ μ‚¬μš©λœλ‹€.

μ°Έκ³ : object 클래슀의 __init__ λ©”μ„œλ“œμ˜ κΈ°λ³Έ κΈ°λŠ₯은 아무 일도 ν•˜μ§€ μ•ŠλŠ” 것이닀.

λͺ¨λ“  맀직 λ©”μ„œλ“œλŠ” 고유의 κΈ°λŠ₯을 μˆ˜ν–‰ν•œλ‹€. λ”°λΌμ„œ, νŠΉλ³„ν•œ μ‚¬μœ κ°€ μ—†μœΌλ©΄ 맀직 λ©”μ„œλ“œλ₯Ό λ‹€μ‹œ μ •μ˜ν•˜λŠ” 일은 ν”Όν•΄μ•Ό ν•œλ‹€. μ—¬κΈ°μ„œλŠ” μ£Όμš” 맀직 λ©”μ„œλ“œμ˜ κΈ°λ³Έ κΈ°λŠ₯을 κ°„λž΅ν•˜κ²Œ ν™•μΈν•œλ‹€.

__repr__ 메서드와 __str__ 메서드

숫자, λ¬Έμžμ—΄, 리슀트, νŠœν”Œ, 사전 등을 ν™•μΈν•˜κ±°λ‚˜ 좜λ ₯ν•˜λ©΄ μš°λ¦¬μ—κ²Œ 맀우 μΉœμˆ™ν•œ λ°©μ‹μœΌλ‘œ 보여진닀. 예λ₯Ό λ“€μ–΄, 리슀트의 κ²½μš°λŠ” 값을 ν™•μΈν•˜λŠ” 거와 좜λ ₯ν•˜λŠ” 데에 차이가 μ—†λ‹€.

In [8]:
[1, 2, 3]
Out[8]:
[1, 2, 3]
In [9]:
print([1, 2, 3])
[1, 2, 3]

λ°˜λ©΄μ— λ¬Έμžμ—΄μ˜ κ²½μš°λŠ” 쑰금 λ‹€λ₯΄λ‹€.

  • κ°’ 확인: 인용 λΆ€ν˜Έκ°€ λΆ™μŒ
In [10]:
"파이썬이 μ΅œκ³ μ—μš”!"
Out[10]:
'파이썬이 μ΅œκ³ μ—μš”!'
  • ν™”λ©΄ 좜λ ₯: 인용 λΆ€ν˜Έ 뢙지 μ•ŠμŒ
In [11]:
print("파이썬이 μ΅œκ³ μ—μš”!")
파이썬이 μ΅œκ³ μ—μš”!

μ΄λ ‡κ²Œ μž‘λ™ν•˜λŠ” μ΄μœ λŠ” λ¬Έμžμ—΄ 클래슀의 λ‚΄λΆ€μ—μ„œ __repr__ κ³Ό __str__ 두 λ©”μ„œλ“œκ°€ 쑰금 λ‹€λ₯΄κ²Œ μ •μ˜λ˜μ–΄ 있기 λ•Œλ¬Έμ΄λ‹€. 값을 확인할 λ•ŒλŠ” __repr__ λ©”μ„œλ“œκ°€ 호좜되고, 좜λ ₯ν•  λ•ŒλŠ” __str__ λ©”μ„œλ“œκ°€ ν˜ΈμΆœλœλ‹€.

In [12]:
"파이썬이 μ΅œκ³ μ—μš”!".__repr__()
Out[12]:
"'파이썬이 μ΅œκ³ μ—μš”!'"

μž‘μ€ μΈμš©λΆ€ν˜Έκ°€ ν¬ν•¨λœ λ¬Έμžμ—΄μ΄ 리턴값이닀. λ°˜λ©΄μ— __str__ λ©”μ„œλ“œμ˜ 경우 μž‘μ€ μΈμš©λΆ€ν˜ΈλŠ” ν¬ν•¨λ˜μ§€ μ•Šμ€ λ¬Έμžμ—΄μ΄ μ‚¬μš©λ˜μ—ˆλ‹€.

In [13]:
"파이썬이 μ΅œκ³ μ—μš”!".__str__()
Out[13]:
'파이썬이 μ΅œκ³ μ—μš”!'

이제 jane 객체λ₯Ό ν™•μΈν•˜κ³  좜λ ₯ν•΄λ³΄μž.

In [14]:
jane
Out[14]:
<__main__.Person at 0x7fa7ace4d210>
In [15]:
print(jane)
<__main__.Person object at 0x7fa7ace4d210>

두 경우 μ•„μ£Ό 쑰금 λ‹€λ₯΄κΈ°λŠ” ν•˜μ§€λ§Œ 기본적으둜 λ™μΌν•œ 정보λ₯Ό 보여쀀닀. λ³΄μ—¬μ§€λŠ” μ •λ³΄λŠ” jane이 Person 클래슀의 객체λ₯Ό κ°€λ¦¬ν‚¨λ‹€λŠ” 사싀과 ν•΄λ‹Ή 객체가 μ €μž₯λ˜μ–΄ μžˆλŠ” λ©”λͺ¨λ¦¬μ˜ μ£Όμ†Œμ΄λ‹€. μ΄λ ‡κ²Œ λ‚˜μ˜€λŠ” μ΄μœ λŠ” Person ν΄λž˜μŠ€μ—μ„œ __repr__, __str__ λͺ¨λ‘ μ •μ˜λ˜μ–΄ μžˆμ§€ μ•ŠκΈ° λ•Œλ¬Έμ΄λ‹€.

이제 Person ν΄λž˜μŠ€μ—μ„œ __repr__, __str__ 두 λ©”μ„œλ“œλ₯Ό λ‹€μŒκ³Ό 같이 μž¬μ •μ˜ν•΄λ³΄μž. 두 λ©”μ„œλ“œ λͺ¨λ‘ 리턴값은 λ¬Έμžμ—΄μ΄μ–΄μ•Ό ν•œλ‹€.

In [16]:
class Person(object):
    def __init__(self, name, surname):
        self.name = name
        self.surname = surname

    def fullname(self):
        return f"{self.name} {self.surname}"
    
    def __str__(self):
        return f"μ„±: {self.surname}, 이름: {self.name}"

    def __repr__(self):
        return f"Person(μ„±: {self.surname}, 이름: {self.name})"

jane = Person("Jane", "Smith")

이제 λ‹€μ‹œ jane을 ν™•μΈν•˜κ³  좜λ ₯ν•΄λ³΄μž.

In [17]:
# __repr__ λ©”μ„œλ“œ μ‚¬μš©λ¨.
jane
Out[17]:
Person(μ„±: Smith, 이름: Jane)
In [18]:
# __str__ λ©”μ„œλ“œ μ‚¬μš©λ¨.
print(jane)
μ„±: Smith, 이름: Jane

주의: __str__ λ©”μ„œλ“œκ°€ μž¬μ •μ˜λ˜μ–΄ μžˆμ§€ μ•Šμ€ 경우, __repr__ λ©”μ„œλ“œμ˜ μ •μ˜λ₯Ό μ‚¬μš©ν•œλ‹€.

In [19]:
class Person(object):
    def __init__(self, name, surname):
        self.name = name
        self.surname = surname

    def fullname(self):
        return f"{self.name} {self.surname}"
    
    def __repr__(self):
        return f"Person(μ„±: {self.surname}, 이름: {self.name})"

jane = Person("Jane", "Smith")
In [20]:
jane
Out[20]:
Person(μ„±: Smith, 이름: Jane)
In [21]:
print(jane)
Person(μ„±: Smith, 이름: Jane)
차이점

__repr__ λ©”μ„œλ“œμ™€ __str__ λ©”μ„œλ“œλŠ” 기본적으둜 λΉ„μŠ·ν•œ μš©λ„λ‘œ μ‚¬μš©λœλ‹€. 차이점은 __str__ λ©”μ„œλ“œλŠ” 객체듀을 μ μ ˆν•˜κ²Œ 화면에 좜λ ₯ν•˜λŠ” 데에 μ‚¬μš©λ˜λ©°, __repr__ λ©”μ„œλ“œλŠ” 객체듀을 μ’€ 더 ν˜•μ‹μ„ κ°–μΆ”μ–΄ 전달 ν•  λ•Œ μ‚¬μš©ν•œλ‹€. 전달 λ˜λŠ” 값은 λ‹€λ₯Έ ν•¨μˆ˜ 등에 μ˜ν•΄ ν™œμš©λ˜κΈ° λ•Œλ¬Έμ— μ μ ˆν•œ 정보λ₯Ό λ‹΄κ³  μžˆλŠ” 게 μ’‹λ‹€.

예λ₯Ό λ“€μ–΄, μ‹œκ°„κ³Ό κ΄€λ ¨λœ datetime λͺ¨λ“ˆμ˜ date ν΄λž˜μŠ€μ™€ datetime 클래슀 객체λ₯Ό μ΄μš©ν•˜μ—¬ 두 ν•¨μˆ˜κ°€ λ‹€λ₯΄κ²Œ μž‘λ™ν•˜λŠ” 것을 μ‚΄νŽ΄λ³΄μž. μ•„λž˜ μ˜ˆμ œμ—μ„œλŠ” repr와 str 두 ν•¨μˆ˜λ₯Ό μ†Œκ°œν•˜λ©΄μ„œ 두 λ©”μ„œλ“œμ˜ 차이점을 μ„€λͺ…ν•œλ‹€. repr ν•¨μˆ˜λ₯Ό ν˜ΈμΆœν•˜λ©΄ 인자둜 μ‚¬μš©λœ κ°’μ˜ __repr__ λ©”μ„œλ“œκ°€, λ°˜λ©΄μ— str ν•¨μˆ˜λ₯Ό ν˜ΈμΆœν•˜λ©΄ 인자둜 μ‚¬μš©λœ κ°’μ˜ __str__ λ©”μ„œλ“œκ°€ ν˜ΈμΆœλœλ‹€.

In [22]:
import datetime

# datetime 객체: ν˜„μž¬ μ‹œκ°„ 정보 μ €μž₯
now = datetime.datetime.now()

# date 객체: ν˜„μž¬ λ‚ μ§œ 정보 μ €μž₯
today = datetime.date.today()

repr ν•¨μˆ˜λŠ” 훨씬 ν˜•μ‹μ„ κ°–μΆ˜ ν‘œν˜„μ„ μ‚¬μš©ν•œλ‹€.

In [23]:
repr(now)
Out[23]:
'datetime.datetime(2020, 6, 2, 17, 17, 34, 413169)'
In [24]:
repr(today)
Out[24]:
'datetime.date(2020, 6, 2)'

λ°˜λ©΄μ— str ν•¨μˆ˜λŠ” κ°„μ†Œν™”λœ ν‘œν˜„μ„ μ‚¬μš©ν•œλ‹€.

In [25]:
str(now)
Out[25]:
'2020-06-02 17:17:34.413169'
In [26]:
str(today)
Out[26]:
'2020-06-02'

__lt__ 메서드

μ½”λ“œ 좔상화: ν΄λž˜μŠ€μ™€ 객체 1λΆ€μ—μ„œ ν™œμš©ν•œ datetime λͺ¨λ“ˆμ˜ date 클래슀의 객체듀에 λŒ€ν•΄ 크기 비ꡐλ₯Ό ν•˜μ˜€λ‹€.

이전에 μ‚¬μš©λ˜μ—ˆλ˜ μ½”λ“œμ˜ μΌλΆ€λŠ” λ‹€μŒκ³Ό κ°™μœΌλ©°, 1992λ…„ 3μ›” 12일 μƒμ˜ λ§Œλ‚˜μ΄λ₯Ό κ³„μ‚°ν•˜κ³  μžˆλ‹€. 그런데 if μ‘°κ±΄λ¬Έμ—μ„œ 였늘 λ‚ μ§œμ™€ μ˜¬ν•΄ 생일 λ‚ μ§œμ˜ 크기 비ꡐλ₯Ό ν•˜κ³  μžˆλ‹€.

In [27]:
import datetime

birthdate = datetime.date(1992,3,12)
today = datetime.date.today()
age = today.year - birthdate.year

if today < datetime.date(today.year, birthdate.month, birthdate.day):
    age -= 1

print(f"만 {age}μ„Έ")
만 28μ„Έ

date 클래슀의 객체듀 μ‚¬μ΄μ˜ 크기 비ꡐ가 κ°€λŠ₯ν•˜λ©°, 일반적인 λ‚ μ§œ κΈ°μ€€μ˜ 크기λ₯Ό μ‚¬μš©ν•˜κ³  μžˆλ‹€. ν•˜μ§€λ§Œ λͺ¨λ“  클래슀의 κ°μ²΄λ“€μ˜ 크기 비ꡐ가 항상 κ°€λŠ₯ν•œ 것은 μ•„λ‹ˆλ‹€. ν•΄λ‹Ή ν΄λž˜μŠ€μ— __lt__ λ©”μ„œλ“œκ°€ κ΅¬ν˜„λ˜μ–΄ μžˆμ„ λ•Œλ§Œ κ°€λŠ₯ν•˜λ‹€. (ltλŠ” less than의 μ€„μž„λ§μž„)

예λ₯Ό λ“€μ–΄, Person 클래슀의 객체듀은 μ„œλ‘œ 비ꡐ할 수 μ—†λ‹€.

In [28]:
scotty = Person("Scotty", "Wing")
In [29]:
scotty.fullname()
Out[29]:
'Scotty Wing'

'scotty'와 'jane'의 크기λ₯Ό λΉ„κ΅ν•˜λ©΄ 였λ₯˜κ°€ λ°œμƒν•œλ‹€.

In [30]:
scotty < jane
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-30-ea72389fb6af> in <module>
----> 1 scotty < jane

TypeError: '<' not supported between instances of 'Person' and 'Person'

이제 Person 클래슀의 객체λ₯Ό 이름 순으둜 크기비ꡐλ₯Ό ν•˜λ„λ‘ λ§Œλ“€κΈ° μœ„ν•΄ __lt__ λ©”μ„œλ“œλ₯Ό κ΅¬ν˜„ν•˜μž. 크기 λΉ„κ΅λŠ” 이름 순으둜 μ •ν•œλ‹€. λ¬Έμžμ—΄λ“€μ˜ 비ꡐ가 μ•ŒνŒŒλ²³ μˆœμ„œλ‘œ κ²°μ •λ˜λŠ” 것을 ν™œμš©ν•œλ‹€.

  • 성을 λ¨Όμ € 비ꡐ.
  • 성이 κ°™μœΌλ©΄ 이름 비ꡐ.

μ°Έκ³ :, __lt__ λ©”μ„œλ“œλŠ” μžμ‹ κ³Ό λ‹€λ₯Έ Person 클래슀의 κ°μ²΄μ™€μ˜ λΉ„κ΅μ΄λ―€λ‘œ self 이외에 μΆ”κ°€λ‘œ ν•˜λ‚˜ 더 λ§€κ°œλ³€μˆ˜λ₯Ό μ‚¬μš©ν•œλ‹€. 동일 클래슀의 객체λ₯Ό κ°€λ¦¬ν‚€λŠ” λ³€μˆ˜λŠ” κ΄€μš©μ μœΌλ‘œ otherλΌλŠ” 이름을 μ‚¬μš©ν•œλ‹€. λ¬Όλ‘  μ˜λ¬΄μ‚¬ν•­μ€ μ•„λ‹ˆλ‹€.

In [31]:
class Person(object):
    def __init__(self, name, surname):
        self.name = name
        self.surname = surname

    def fullname(self):
        return f"{self.name} {self.surname}"
    
    def __repr__(self):
        return f"Person(μ„±: {self.surname}, 이름: {self.name})"
    
    def __lt__(self, other):
        if self.surname < other.surname:
            return True
        elif self.surname == other.surname:
            if self.name < other.name:
                return True
            else:
                return False
        else:
            return False

jane = Person("Jane", "Smith")
scotty = Person("Scotty", "Wing")

이제 Person 클래슀의 두 객체의 크기λ₯Ό 비ꡐ할 수 μžˆλ‹€. 크기 λΉ„κ΅λŠ” 일반적으둜 μ•Œλ €μ§„ < μ—°μ‚°μžλ₯Ό μ‚¬μš©ν•œλ‹€.

In [32]:
jane < scotty
Out[32]:
True

μœ„ μ½”λ“œλŠ” μ‹€μ œλ‘œ μ•„λž˜ μ½”λ“œμ˜ 일을 ν•œλ‹€.

In [33]:
jane.__lt__(scotty)
Out[33]:
True

__lt__ κ°€ μ •μ˜λ˜λ©΄ > μ—°μ‚°μžλ„ μžλ™μœΌλ‘œ μ‚¬μš©μ΄ κ°€λŠ₯ν•˜λ‹€. > μ—°μ‚°μžλŠ” __gt__ λ©”μ„œλ“œμ— ν•΄λ‹Ήν•œλ‹€. (gtλŠ” greater than의 μ€„μž„λ§μž„)

In [34]:
jane > scotty
Out[34]:
False

ν•˜μ§€λ§Œ 아직은 <= 와 >= λŠ” μ‚¬μš©ν•  수 μ—†λ‹€. μ΄μœ λŠ” 두 객체 μ‚¬μ΄μ˜ λ™μΉ˜μ„±(equivalence)이 μ •μ˜λ˜μ§€ μ•Šμ•˜κΈ° λ•Œλ¬Έμ΄λ‹€. 즉, __eq__ λ©”μ„œλ“œκ°€ κ΅¬ν˜„λ˜μ–΄ μžˆμ§€ μ•Šλ‹€. 등식에 λŒ€ν•œ μ„€λͺ…은 μ•„λž˜ μ—°μŠ΅λ¬Έμ œμ—μ„œ λΆ„μˆ˜ 클래슀λ₯Ό μ„ μ–Έν•  λ•Œ μžμ„Ένžˆ μ‚΄νŽ΄λ³Ό 것이닀.

__class__ 속성

객체의 μžλ£Œν˜•μ„ 확인할 λ•Œ type ν•¨μˆ˜λ₯Ό μ‚¬μš©ν•œλ‹€. 그러면 type ν•¨μˆ˜λŠ” ν•΄λ‹Ή 객체의 __class__ 속성을 ν™•μΈν•˜μ—¬ μ „λ‹¬ν•œλ‹€.

In [35]:
jane.__class__
Out[35]:
__main__.Person
In [36]:
type(jane)
Out[36]:
__main__.Person
In [37]:
[1, 2, 3].__class__
Out[37]:
list
In [38]:
type([1, 2, 3])
Out[38]:
list

__dict__ 속성

__dict__ 속성은 μΈμŠ€ν„΄μŠ€ 속성듀을 μ‚¬μ „μœΌλ‘œ λͺ¨μ•„ λ‘”λ‹€.

In [39]:
jane.__dict__
Out[39]:
{'name': 'Jane', 'surname': 'Smith'}

즉, __dict__λŠ” ν•΄λ‹Ή μΈμŠ€ν„΄μŠ€μ˜ λͺ¨λ“  속성을 사전 μžλ£Œν˜•μœΌλ‘œ ν¬ν•¨ν•œλ‹€. ν‚€λŠ” 속성 λ³€μˆ˜, 값은 속성 값을 μ‚¬μš©ν•œλ‹€.

주의: λ¦¬μŠ€νŠΈλŠ” μΈμŠ€ν„΄μŠ€ 속성을 μ „ν˜€ 갖지 μ•ŠλŠ”λ‹€.

In [40]:
[1, 2, 3].__dict__
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-40-1c5e42f4d9b3> in <module>
----> 1 [1, 2, 3].__dict__

AttributeError: 'list' object has no attribute '__dict__'

__iter__ 메서드

리슀트, νŠœν”Œ, λ¬Έμžμ—΄, 사전 등은 μ΄ν„°λŸ¬λΈ”(iterable) μžλ£Œν˜•μ— μ†ν•œλ‹€. μ΄ν„°λŸ¬λΈ” μžλ£Œν˜•μ€ for 반볡문과 ν•¨κ»˜ μ‚¬μš©λ  수 μžˆλŠ” μžλ£Œν˜•μ΄λΌκ³  μƒκ°ν•˜λ©΄ μ‰½κ²Œ 이해할 수 μžˆλ‹€. 보닀 μ—„λ°€νžˆ λ§ν•˜λ©΄ __iter__ λ©”μ„œλ“œκ°€ μ •μ˜λ˜μ–΄ μžˆλŠ” μžλ£Œν˜•μ΄ μ΄ν„°λŸ¬λΈ” μžλ£Œν˜•μ΄λ‹€. μ‹€μ œλ‘œ λͺ¨λ“  리슀트, νŠœν”Œ, λ¬Έμžμ—΄μ€ ν•΄λ‹Ή λ©”μ„œλ“œλ₯Ό ν¬ν•¨ν•˜κ³  μžˆλ‹€.

주의: __iter__ λ©”μ„œλ“œλŠ” object에 ν¬ν•¨λ˜μ–΄ μžˆμ§€ μ•Šλ‹€.

In [41]:
[1, 2, 3].__iter__()
Out[41]:
<list_iterator at 0x7fa7acd74310>
In [42]:
(1, 2, 3).__iter__()
Out[42]:
<tuple_iterator at 0x7fa7acd6c2d0>
In [43]:
'1, 2, 3'.__iter__()
Out[43]:
<str_iterator at 0x7fa7acd6c450>

__iter__ λ©”μ„œλ“œμ˜ 역할은 ν•΄λ‹Ή μžλ£Œν˜•μ„ μ΄ν„°λ ˆμ΄ν„°(iterator)둜 λ³€ν™˜μ‹œν‚€λŠ” 일이닀. μ΄ν„°λ ˆμ΄ν„°λŠ” __next__ λ©”μ„œλ“œλ₯Ό ν¬ν•¨ν•˜λŠ” μžλ£Œν˜•μ΄λ‹€. ν•΄λ‹Ή λ©”μ„œλ“œλŠ”, 예λ₯Ό λ“€μ–΄, for 반λͺ©λ¬Έμ—μ„œ 각 ν•­λͺ©μ„ ν™œμš©ν•  λ•Œ μ‚¬μš©λœλ‹€.

리슀트 [1, 2, 3]을 μ΄μš©ν•˜λŠ” 예λ₯Ό μ‚΄νŽ΄λ³΄μž.

In [44]:
aList = [1, 2, 3]

iter ν•¨μˆ˜λ₯Ό μ‚¬μš©ν•˜λ©΄ __iter__ λ©”μ„œλ“œκ°€ μ‚¬μš©λœλ‹€.

In [45]:
aIterator = iter(aList)

이제 next ν•¨μˆ˜λ₯Ό ν˜ΈμΆœν•˜λ©΄ __next__ λ©”μ„œλ“œκ°€ ν˜ΈμΆœλœλ‹€.

In [46]:
next(aIterator)
Out[46]:
1

next ν•¨μˆ˜λ₯Ό ν˜ΈμΆœν•  λ•Œλ§ˆλ‹€ 리슀트의 λ‹€μŒ ν•­λͺ©μ΄ ν™•μΈλœλ‹€.

In [47]:
next(aIterator)
Out[47]:
2
In [48]:
next(aIterator)
Out[48]:
3

λ§ˆμ§€λ§‰ ν•­λͺ©μ΄ ν™•μΈλœ ν›„μ—λŠ” StopIteration 였λ₯˜κ°€ λ°œμƒν•œλ‹€.

In [49]:
next(aIterator)
---------------------------------------------------------------------------
StopIteration                             Traceback (most recent call last)
<ipython-input-49-02382458df68> in <module>
----> 1 next(aIterator)

StopIteration: 

next ν•¨μˆ˜λ₯Ό λ‹€μ‹œ μ‚¬μš©ν•˜λ €λ©΄ μ΄ν„°λ ˆμ΄ν„°λ₯Ό λ‹€μ‹œ 생성해야 ν•œλ‹€. μ΄λ²ˆμ—λŠ” __iter__와 __next__ λ©”μ„œλ“œλ₯Ό 직접 μ‚¬μš©ν•œλ‹€.

In [50]:
aIterator = aList.__iter__()
In [51]:
aIterator.__next__()
Out[51]:
1
In [52]:
aIterator.__next__()
Out[52]:
2
In [53]:
aIterator.__next__()
Out[53]:
3
In [54]:
aIterator.__next__()
---------------------------------------------------------------------------
StopIteration                             Traceback (most recent call last)
<ipython-input-54-31285d98d373> in <module>
----> 1 aIterator.__next__()

StopIteration: 

μ‹€μ œλ‘œ μ΄ν„°λŸ¬λΈ” μžλ£Œν˜•μ„ for 반볡문과 ν•¨κ»˜ μ‚¬μš©ν•˜λ©΄ λ‚΄λΆ€μ μœΌλ‘œλŠ” μ•žμ„œ μ„€λͺ…ν•œ λŒ€λ‘œ μ΄ν„°λ ˆμ΄ν„°λ‘œ λ³€ν™˜ν•œ ν›„ next ν•¨μˆ˜λ₯Ό 반볡적으둜 μ‚¬μš©ν•œλ‹€.

예λ₯Ό λ“€μ–΄, μ•„λž˜ for λ°˜λ³΅λ¬Έμ„ μ‚΄νŽ΄λ³΄μž.

In [55]:
for item in [1, 2, 3]:
    print(item)
1
2
3

μœ„ λ°˜λ³΅λ¬Έμ„ 파이썬 해석기가 μ²˜λ¦¬ν•˜λŠ” μ‹€μ œ λ‚΄μš©μ€ λ‹€μŒκ³Ό κ°™λ‹€.

In [56]:
aIterator = iter([1, 2, 3])

# λ¬΄ν•œ 반볡문
while True:
    try:
        # λ‹€μŒ ν•­λͺ© κ΅¬ν•˜κΈ°
        item = next(aIterator)
        print(item)
    except StopIteration:
        # 더 이상 확인할 ν•­λͺ©μ΄ μ—†μœΌλ©΄ 반볡문 μ’…λ£Œ
        break
1
2
3

μ΄ν„°λŸ¬λΈ” μžλ£Œν˜•κ³Ό μ΄ν„°λ ˆμ΄ν„° μžλ£Œν˜•μ— λŒ€ν•΄μ„œλŠ” μš°μ„  이 정도 μ•Œμ•„λ‘λ©΄ μ’‹λ‹€. 보닀 λ§Žμ€ ν™œμš© μ˜ˆμ œμ— λŒ€ν•œ μ„€λͺ…은 programiz.com: Python Iterators을 μ°Έκ³ ν•  것을 μΆ”μ²œν•œλ‹€.

__len__ 메서드

리슀트, νŠœν”Œ, λ¬Έμžμ—΄, range λ“±μ˜ μžλ£Œν˜•μ²˜λŸΌ ν•­λͺ©λ“€μ˜ μˆœμ„œμ™€ 쀑볡이 ν—ˆμš©λ˜λŠ” μžλ£Œν˜•μ„ 순차 λ˜λŠ” μ‹œν€€μŠ€(sequence) μžλ£Œν˜•μ΄λΌ ν•œλ‹€. λ°˜λ©΄μ— 집합, 사전 λ“±μ˜ μžλ£Œν˜•μ²˜λŸΌ μˆœμ„œλ₯Ό λ¬΄μ‹œν•˜κ³  쀑볡이 ν—ˆμš©λ˜μ§€ μ•ŠλŠ” μžλ£Œν˜•μ€ λ‹¨μˆœνžˆ λͺ¨μŒ(collection) μžλ£Œν˜•μ΄λΌ λΆ€λ₯Έλ‹€. κ²½μš°μ— 따라 순차 μžλ£Œν˜•λ„ λ‹¨μˆœνžˆ λͺ¨μŒ μžλ£Œν˜•μœΌλ‘œ κ°„μ£Όν•˜κΈ°λ„ ν•œλ‹€. μˆœμ°¨μ™€ λͺ¨μŒ μžλ£Œν˜•μ€ λͺ¨λ‘ ν•­λͺ©μ˜ 개수λ₯Ό ν™•μΈν•΄μ£ΌλŠ” __len__ λ©”μ„œλ“œλ₯Ό κ°–κ³  μžˆλ‹€.

주의: __len__ λ©”μ„œλ“œλŠ” object에 ν¬ν•¨λ˜μ–΄ μžˆμ§€ μ•Šλ‹€.

In [57]:
[1, 2, 3].__len__()
Out[57]:
3
In [58]:
"Python".__len__()
Out[58]:
6

jane의 __dict__ μ†μ„±μ—λŠ” 두 개의 ν•­λͺ©μ΄ ν¬ν•¨λ˜μ–΄ μžˆλ‹€.

In [59]:
jane.__dict__.__len__()
Out[59]:
2

이와 같이 __len__ λ©”μ†Œλ“œλ₯Ό ν¬ν•¨ν•œ μžλ£Œν˜•μ— λŒ€ν•΄μ„œ len ν•¨μˆ˜λ₯Ό μ‚¬μš©ν•˜μ—¬ ν•­λͺ©μ˜ 개수λ₯Ό 확인할 수 μžˆλ‹€. 즉, len ν•¨μˆ˜λ₯Ό μ•„λž˜μ™€ 같이 μ •μ˜λœ κ²ƒμœΌλ‘œ 생각할 수 μžˆλ‹€.

def len(s):
    return s.__len__()
In [60]:
len([1, 2, 3])
Out[60]:
3
In [61]:
len("Python")
Out[61]:
6
In [62]:
len(jane.__dict__)
Out[62]:
2

매직 메서드 재정의

Person ν΄λž˜μŠ€μ—μ„œ __init__ λ©”μ„œλ“œλ₯Ό μƒˆλ‘œ μ •μ˜ν•˜μ˜€λ‹€. 이와 같이 λ©”μ„œλ“œλ₯Ό μƒˆλ‘œ μ •μ˜ν•˜λŠ” 것을 λ©”μ„œλ“œ μž¬μ •μ˜ λ˜λŠ” λ©”μ„œλ“œ μ˜€λ²„λΌμ΄λ”©(method overriding)이라 λΆ€λ₯Έλ‹€. λ”°λΌμ„œ λͺ¨λ“  맀직 λ©”μ„œλ“œλŠ” μž¬μ •μ˜ λ˜λ“ κ°€, μ•„λ‹ˆλ©΄ λΆ€λͺ¨ ν΄λž˜μŠ€μ—μ„œ μ„ μ–Έλœ κ·ΈλŒ€λ‘œ μ‚¬μš©λœλ‹€.

μ—¬κΈ°μ„œλŠ” μ•žμ„œ 닀루지 μ•Šμ•˜μ§€λ§Œ μ€‘μš”ν•œ 맀직 λ©”μ„œλ“œ λͺ‡ 개λ₯Ό μž¬μ •μ˜ν•˜λŠ” 것을 톡해 μœ μš©ν•œ μžλ£Œν˜•μ„ μ •μ˜ν•˜λŠ” 방법을 μ†Œκ°œν•œλ‹€.

Fraction 클래스

λΆ€λ™μ†Œμˆ˜μ μ„ λ‹€λ£¨λŠ” float μžλ£Œν˜•μ— μ†ν•˜λŠ” 값듀은 μ‹€μˆ˜λ₯Ό 닀루기 μœ„ν•΄ κ΅¬ν˜„λ˜μ—ˆλ‹€. ν•˜μ§€λ§Œ μ»΄ν“¨ν„°μ˜ ν•œκ³„λ‘œ 인해 μ‹€μ œλ‘œ λ‹€λ£° 수 μžˆλŠ” κ°’λ“€μ˜ μ†Œμˆ˜μ  μ΄ν•˜ μžλ¦Ώμˆ˜κ°€ μ œν•œλœλ‹€. 즉, μ»΄ν“¨ν„°λŠ” 기본적으둜 μ œν•œλœ λ²”μœ„μ˜ 유리수만 λ‹€λ£° 수 μžˆλ‹€. 이런 이유둜 ν•΄μ„œ 유리수 연산을 100% μ •ν™•ν•˜κ²Œ κ³„μ‚°ν•˜κΈ° μ–΄λ €μš΄ κ²½μš°κ°€ λ°œμƒν•˜λ©°, 이런 문제λ₯Ό μ „λ¬Έμ μœΌλ‘œ 맀우 μ‘°μ‹¬μŠ€λŸ½κ²Œ 닀루어야 ν•˜λ©°, λ§Žμ€ 곡학 λΆ„μ•Όμ—μ„œ κ²½μš°μ— 따라 맀우 ν•΄κ²°ν•˜κΈ° μ–΄λ €μš΄ 문제λ₯Ό λ°œμƒμ‹œν‚€κΈ°λ„ ν•œλ‹€.

예λ₯Ό λ“€μ–΄, μ•„λž˜ μ½”λ“œλŠ” 10의 1000승 λΆ„μ˜ 1이 0κ³Ό κ°™λ‹€κ³  νŒλ‹¨ν•œλ‹€. 10의 1000승 λΆ„μ˜ 1이 맀우 μž‘μ€ 수이긴 ν•˜μ§€λ§Œ 0은 μ•„λ‹Œλ° μ»΄ν“¨ν„°λŠ” κ·Έλ ‡λ‹€κ³  νŒλ‹¨ν•œλ‹€.

$$x = \text{1.0e-1000} = 1.0 \times 10^{-1000} = \frac{1}{10^{1000}}$$

주의: 이런 λ¬Έμ œλŠ” 파이썬의 λ¬Έμ œκ°€ μ•„λ‹ˆλΌ, μ»΄ν“¨ν„°μ˜ κΈ°λ³Έ ν•œκ³„ λ•Œλ¬Έμ— λ°œμƒν•˜λŠ” λ¬Έμ œμ΄λ‹€. λ‹€λ₯Έ ν”„λ‘œκ·Έλž˜λ° μ–Έμ–΄λ₯Ό μ‚¬μš©ν•΄λ„ λΉ„μŠ·ν•œ λ¬Έμ œκ°€ λ°œμƒν•œλ‹€.

In [63]:
x = 1.0e-1000
x == 0
Out[63]:
True

이와 같은 문제λ₯Ό μ–΄λŠ 정도 ν•΄κ²°ν•˜κΈ° μœ„ν•΄ νŒŒμ΄μ¬μ„ μœ λ¦¬μˆ˜λ“€μ˜ 클래슀인 Fraction을 μ œκ³΅ν•œλ‹€. Fraction μžλ£Œν˜•μ— μ†ν•œ 값은 μ •μˆ˜λ“€μ˜ λΆ„μˆ˜μ— ν•΄λ‹Ήν•˜λ©°, λͺ¨λ“  연산은 λΆ„μˆ˜λ“€μ˜ μ—°μ‚°μœΌλ‘œ μ²˜λ¦¬λœλ‹€. Fraction ν΄λž˜μŠ€λŠ” fractions λͺ¨λ“ˆμ— μ„ μ–Έλ˜μ–΄ μžˆλ‹€.

In [64]:
from fractions import Fraction

예λ₯Ό λ“€μ–΄ $\frac{1}{2}$, $\frac{1}{3}$, $\frac{2}{4}$에 ν•΄λ‹Ήν•˜λŠ” μˆ˜λŠ” 각각 μ•„λž˜μ™€ 같이 ν‘œν˜„ν•œλ‹€.

In [65]:
F12= Fraction(1, 2)
F13= Fraction(1, 3)
F24= Fraction(2, 4)

사칙연산이 κ°€λŠ₯ν•˜λ‹€. κ²°κ³ΌλŠ” __repr__ λ©”μ„œλ“œμ˜ κΈ°λŠ₯을 톡해 λ‹€μ‹œ Fraction 클래슀의 객체둜 보여진닀.

In [66]:
# 1/2 + 1/3 = 5/6

F12 + F13
Out[66]:
Fraction(5, 6)
In [67]:
# 1/3 - 1/2 = -1/6

F13 - F12
Out[67]:
Fraction(-1, 6)
In [68]:
# 1/2 * 1/3 = 1/6

F12 * F13
Out[68]:
Fraction(1, 6)
In [69]:
# (1/3) / (1/2) = 2/3

F13 / F12
Out[69]:
Fraction(2, 3)

λΆ„μˆ˜κ°€ 기본적으둜 μ•½λΆ„λ˜μ–΄ μ²˜λ¦¬λœλ‹€. 예λ₯Ό λ“€μ–΄, 1/2 + 1/2λ₯Ό 2/2κ°€ μ•„λ‹Œ 1/1, 즉 1둜 μ²˜λ¦¬ν•œλ‹€.

In [70]:
F12 + F12
Out[70]:
Fraction(1, 1)

Fraction 객체와 μ •μˆ˜λ“€κ³Όμ˜ 연산도 κ°€λŠ₯ν•˜λ‹€.

In [71]:
Fraction(1, 6) + 2
Out[71]:
Fraction(13, 6)
In [72]:
Fraction(1, 6) * 2
Out[72]:
Fraction(1, 3)

print ν•¨μˆ˜λ₯Ό μ‚¬μš©ν•˜λ©΄ μ΅μˆ™ν•œ ν‘œν˜„μœΌλ‘œ 보여진닀. 즉, __str__ λ©”μ†Œλ“œκ°€ μ μ ˆν•˜κ²Œ μ •μ˜λ˜μ–΄ μžˆλ‹€.

In [73]:
print(F12)
1/2
In [74]:
print(F13)
1/3
In [75]:
print(F13 / F12)
2/3

크기 비ꡐ도 κ°€λŠ₯ν•˜λ‹€.

In [76]:
F12 < F13
Out[76]:
False

λͺ¨λ“  연산은 약뢄을 μ „μ œλ‘œ 이루어진닀. λ”°λΌμ„œ 두 λΆ„μˆ˜μ˜ 동일성도 μ‰½κ²Œ νŒλ‹¨ν•œλ‹€.

In [77]:
F12 == F24
Out[77]:
True

연습: 유리수 클래스 선언하기

Fraction ν΄λž˜μŠ€μ™€ μœ μ‚¬ν•˜κ²Œ μž‘λ™ν•˜λŠ” 클래슀λ₯Ό 직접 μ •μ˜ν•˜λ©΄μ„œ λ‹€μŒ 맀직 λ©”μ„œλ“œλ“€μ„ μž¬μ •μ˜ν•˜λŠ” μ‹€μŠ΅μ„ ν•΄λ³΄μž. μž¬μ •μ˜ λŒ€μƒ λ©”μ„œλ“œλŠ” λ‹€μŒκ³Ό κ°™λ‹€.

  • __repr__
  • __str__
  • __eq__
  • __lt__
  • __le__
  • __add__
In [78]:
import math

class myFraction:
    """λΆ„μˆ˜ 클래슀
    μ •μˆ˜λ“€μ˜ λΆ„μˆ˜λ₯Ό μ •μˆ˜λ“€μ˜ 쌍으둜 κ΅¬ν˜„ν•¨.
    λΆ„μˆ˜λŠ” 기본적으둜 μ•½λΆ„ μ²˜λ¦¬ν•˜μ—¬ μ €μž₯.
    """

    def __init__(self, numerator, denominator):
        
        # λΆ„λͺ¨κ°€ 0이면 였λ₯˜ λ°œμƒ
        if denominator == 0: 
            raise ZeroDivisionError(f"myFraction({numerator}, 0)")
            
        # λΆ„λͺ¨λŠ” 항상 μ–‘μ˜ μ •μˆ˜λ‘œ μœ μ§€
        elif denominator < 0:
            denominator = -denominator
            numerator = -numerator

        # μ΅œλŒ€κ³΅μ•½μˆ˜λ‘œ λΆ„λͺ¨ λΆ„μž λ‚˜λˆ„κΈ°
        g = math.gcd(numerator, denominator)
        numerator //= g
        denominator //= g
        
        # μΈμŠ€ν„΄μŠ€ 속성: λΆ„μžμ™€ λΆ„λͺ¨
        # μ•½λΆ„ ν›„ μ €μž₯
        self._numerator = numerator
        self._denominator = denominator
                
    def __repr__(self):
        return f"{self.__class__.__name__}({self._numerator},{self._denominator})"
    
    def __str__(self):
        if self._denominator == 1:
            return str(self._numerator)
        else:
            return f"{self._numerator}/{self._denominator}"

    # κ°™λ‹€(equal)
    def __eq__(self, other):
        return (self._numerator * other._denominator) \
                == (other._numerator * self._denominator)

    # μž‘λ‹€(less than)
    def __lt__(self, other):
        return (self._numerator * other._denominator) \
                < (other._numerator * self._denominator)

    # μž‘κ±°λ‚˜ κ°™λ‹€(less than or equal)
    def __le__(self, other):
        return self < other or self == other

    # λ”ν•˜κΈ°. myFraction 객체λ₯Ό μƒμ„±ν•˜λ©΄μ„œ μžμ—°μŠ€λŸ½κ²Œ κΈ°μ•½λΆ„μˆ˜λ‘œ λ§Œλ“¦.
    def __add__(self, other):
        numerator = self._numerator * other._denominator + \
                    self._denominator * other._numerator
        denominator = self._denominator * other._denominator
        return myFraction(numerator, denominator)

μ°Έκ³ : λΆ„μˆ˜λ“€μ˜ 크기 비ꡐ와 λ§μ…ˆμ€ μ•„λž˜μ™€ 같이 이루어진닀.

  • 크기 비ꡐ $$\frac{b}{a}\, <\, \frac{d}{c} \quad\Longleftrightarrow\quad b\, c\, <\, a\, d$$

  • λ§μ…ˆ $$\frac{b}{a} + \frac{d}{c} \quad =\quad \frac{b\, c + a\, d}{a \,c}$$
In [79]:
R14 = myFraction(1, 4)
In [80]:
R14
Out[80]:
myFraction(1,4)
In [81]:
print(R14)
1/4
In [82]:
Rm14 = myFraction(1, -4)
In [83]:
Rm14
Out[83]:
myFraction(-1,4)
In [84]:
print(Rm14)
-1/4
In [85]:
R34 = myFraction(3, 4)
In [86]:
Rm34 = myFraction(-3, 4)
In [87]:
R28 = myFraction(2, 8)
In [88]:
print(R14)
1/4
In [89]:
print(R28)
1/4
In [90]:
R14 == R28
Out[90]:
True
In [91]:
R14 < R34
Out[91]:
True
In [92]:
R14 <= R34
Out[92]:
True
In [93]:
Rm34 < Rm14
Out[93]:
True
In [94]:
Rm34 <= Rm14
Out[94]:
True
In [95]:
Rm34 >= Rm14
Out[95]:
False
In [96]:
print(R14 + R34)
1
In [97]:
print(Rm14 + R34)
1/2

PythonTutor 활용 4

μœ„ μ˜ˆμ œλ“€μ„ PythonTutor: 유리슀 ν΄λž˜μŠ€μ—μ„œ μ‹€ν–‰ν•˜λ©΄μ„œ 확인할 수 μžˆλ‹€.

연습문제

  1. myFraction 클래슀의 객체가 사칙연산을 λͺ¨λ‘ μ§€μ›ν•˜λ„λ‘ μ•„λž˜ λ©”μ„œλ“œλ₯Ό κ΅¬ν˜„ν•˜λΌ. 그러면 -(λΆ€ν˜Έ λ°”κΎΈκΈ°), -(λΊ„μ…ˆ), *, / λ“±μ˜ μ—°μ‚° 기호λ₯Ό μ‚¬μš©ν•  수 μžˆλ‹€.
    • __neg__: λΆ€ν˜Έ λ°”κΎΈκΈ° (μ–‘μˆ˜ <=> 음수)
    • __sub__: λΊ„μ…ˆ
    • __mul__: κ³±μ…ˆ
    • __truediv__: λ‚˜λˆ—μ…ˆ

  2. myFraction 클래슀의 객체가 μ •μˆ˜μ™€μ˜ 사칙연산도 κ°€λŠ₯ν•˜λ„λ‘ μ•„λž˜ λ©”μ„œλ“œλ₯Ό μˆ˜μ •ν•˜λΌ.
    • __add__: λ§μ…ˆ
    • __sub__: λΊ„μ…ˆ
    • __mul__: κ³±μ…ˆ
    • __truediv__: λ‚˜λˆ—μ…ˆ