[day-11] map, 클래스, 이터레이터, 상속, 오버로딩, 매직메서드

Joohyung Park·2024년 1월 14일
0

[모두연] 오름캠프

목록 보기
11/95

함수명은 변수다

# 이런식으로 사용도 가능하다
l = [lambda x, y: x + y, lambda x, y: x - y, lambda x, y: x / y, lambda x, y: x * y]
l[0](10,20)

map 추가 정리

- map 함수는 자료형을 바꾸는 것 말고도 다른 기능이 있다.
	- list(map(int, ['1', '11', 100, '77']))    # [1, 11, 100, 77]
	- list(map(str.isdigit, 'he1llo w2orl3d'))  # [False, False, True, ...]
	- list(filter(str.isdigit, 'he1llo w2orl3d'))    # ['1', '2', '3']

딕셔너리명.get()

요일 = {
    0: '일요일',
    1: '월요일',
    2: '화요일',
    3: '수요일',
    4: '목요일',
    5: '금요일',
    6: '토요일',
    }

def 함수(x):
    return 요일.get(x, '요일없음')    # 요일에 해당하지 않으면 요일없음

list(map(함수, [1, 5, 6, 3, 4]))    # ['월요일', '금요일', '토요일', '수요일', '목요일']

Character 클래스 만들기

class Character:

    character_count = 0

    def __init__(self, name, skill, hp, mp, power, x):
        self.name = name
        self.skill = skill
        self.hp = hp
        self.mp = mp
        self.power = power
        self.x = x
        Character.character_count_increase()    # 예를 들어 한 화면에 몹이 100개 이상 나오지 못한다. 

    @classmethod    						   # 클래스의 어떤 변수를 변경할때 선언 => 데코레이터
    def character_count_increase(cls):
        '''
        캐릭터가 100개 이상 생성되면 경고 메시지 출력
        cls는 class입니다. self는 instance입니다.
        2개가 다릅니다!
        '''
        cls.character_count += 1

    def move(self):
        self.x += 100

    def attack(self, next):
        next.hp -= self.power

주인공1 = Character('licat', '질풍검', 100, 100, 10, 10)1 = Character('licat', '질풍검', 100, 100, 10, 10)

주인공1.attack(1)     # attack 메서드에서 self = 주인공 객체, next = 몸1 객체
					  # 몹1의 hp가 주인공의 power만큼 감소하였다!1.hp                # 100 => 90으로 감소1.character_count   # character_count_increase 메서드에 의해 객체 1개당 +1 해서 
                     # 2가 된다

Character 예제에서 상속 맛보기

class Character:
    def __init__(self, name, skill, hp, mp, power, defence):
        self.name = name
        self.skill = skill
        self.hp = hp
        self.mp = mp
        self.power = power
        self.defence = defence

    def attack(self, next):
        next.hp -= self.power

class Item:
    엑스칼리버 = {'name': '엑스칼리버', 'power': 1000}
    가죽갑옷 = {'name': '가죽갑옷', 'defence': 10}
    철갑옷 = {'name': '철갑옷', 'defence': 100}
    HP물약 = {'name': 'HP물약', 'hp': 10}
    MP물약 = {'name': 'MP물약', 'mp': 10}

    def repair(self):
        print('수리합니다!')

class Hero(Character, Item):
    def __init__(self, name, skill, hp, mp, power, defence, level=1):
        # self.name = name 										# 이렇게 일일이 하지 않고, 아래와 같이 하면 됩니다.
        super().__init__(name, skill, hp, mp, power, defence)   # super()는 상속받을 때 사용. self 사용 안함
        self.level = level
        self.item = []
    
    def add_item(self, item):
        self.item.append(item)
        self.power += item.get('power', 0)
        self.defence += item.get('defence', 0)
        self.hp += item.get('hp', 0)
        self.mp += item.get('mp', 0)

    def show_item(self):
        print(self.item)

class Mob(Character):
    pass

# 주인공은 아이템이 있는데!?, 몹은 아이템이 없어요.
# 주인공은 Lv이 있는데!?, 몹은 Lv이 없어요.
# 몹은 Drop하는 아이템이 있는데!?, 주인공은 없어요.
주인공1 = Hero('licat', '질풍검', 100, 100, 10, 0, 1)1 = Mob('licat', '질풍검', 100, 100, 10, 0)

주인공1.attack(1)1.hp

주인공1.add_item(Item.엑스칼리버)
주인공1.repair()
주인공1.show_item()

위의 코드에서 주인공1 객체에는 attack이라는 메서드가 없지만 Hero 클래스의 인자로 Character 클래스를 상속받겠다고 선언했기에 Character 클래스의 attack 메서드를 쓸 수 있었다! 또한 Item과 Repair()메서드도 마찬가지이다.

Matrix 클래스 만들어보기

# 우리가 원하는 형태
# [1, 2, 3] + [1, 2, 3] == [1, 2, 3, 1, 2, 3]
# [1, 2, 3] + [1, 2, 3] == [2, 4, 6] 
# [1, 2, 3] * 3 == [1, 2, 3, 1, 2, 3, 1, 2, 3]
# [1, 2, 3] * 3 == [3, 6, 9]    

# 행렬 1번 예제(비추천)
class Matrix:
    def __init__(self, value):
        self.value = value

    def __add__(self, next):    			# 수학연산에선 next, equal같은 경우는 other을 쓴다고 한다.
        result = []
        for i in range(len(self.value)):    # 여기서 len()을 하면 2번 연산하기에 비추
            result.append(self.value[i] + next.value[i])
        return result

a = Matrix([1, 2, 3])
b = Matrix([1, 2, 3])

a + b
# 행렬 2번 예제(추천)
class Matrix:
    def __init__(self, value):
        self.value = value
    
    def __add__(self, next):    # 수학연산에선 next, equal같은 경우는 other을 쓴다고 한다.
        result = []
        for i, _ in enumerate(self.value):
            result.append(self.value[i] + next.value[i])
        return result

    def __mul__(self, next):    # 곱셈연산
        for i, _ in enumerate(self.value):
            self.value[i] *= next
        return self.value
    
    def __str__(self):    # 문자열 출력
        return str(self.value)
    
    def __repr__(self):   # str과 똑같이 설정, 개발자가 보기 편한 형태로 반환해줌
        return str(self.value)

a = Matrix([1, 2, 3])
b = Matrix([1, 2, 3])

a * 3
print(a)    # <__main__.Matrix object at 0x7c1b6a973b20> 라고 나오는 것을 str메서드로 a의 행렬이 나오도록 했음. 

# 파이썬에서 사용하지 않는 변수는 _로 할당한다.
# 위 코드에서 for i in a:
#                 print(i)하면 오류납니다(반복 불가능한 객체 오류). 

행렬 2번 예제 심화버전

# 행렬 2번 예제 심화과정. 지금 당장 만들기는 힘들어요
# 나중에 다시 할겁니다
class Matrix:
    def __init__(self, value):
        self.index = 0
        self.value = value

    def __add__(self, next):
        result = []
        for i, _ in enumerate(self.value):
            result.append(self.value[i] + next.value[i])
        return result

    def __mul__(self, next):
        for i, _ in enumerate(self.value):
            self.value[i] *= next
        return self.value

    def __str__(self):
        return str(self.value)

    def __repr__(self):		# str과 똑같이 생성
        return str(self.value)

    def __iter__(self):    # 이 iter메서드와 next 메서드 때문에 for문이 정상 출력함
        return self

    def __next__(self):
        if self.index >= len(self.value):
            raise StopIteration
        idx = self.index
        self.index += 1
        return self.value[idx]

a = Matrix([1, 2, 3])

for i in a:
    print(i) 
# 1
# 2
# 3

이터레이터

# 심화과정이지만 중요한 내용이기에 설명해주셨다.
# 한 번 더 호출?!
# 왜 0, 1, 2, 3, 4가 2번 출력되지 않는가?
class MyIterator:
    def __init__(self, stop):
        self.currentValue = 0
        self.stop = stop    # 5

    def __iter__(self): # for문을 처음만나면 iter메서드가 가장 먼저 실행됨, 이후에는 실행 x
        self.currentValue = 0
        return self     # 변수를 초기화 시키려면 iter메서드에서 해야한다!

    def __next__(self): # for문을 돌리면 iter 다음으로 next메서드가 실행됨
        if self.currentValue >= self.stop:
            raise StopIteration # 예외처리로 이것 때문에 for문이 멈춤
        result = self.currentValue  # result = 0, 1, 2, 3,...
        self.currentValue += 1
        return result

my_iterator = MyIterator(5)

for i in my_iterator:
    print(i)

for i in my_iterator:
    print(i)

# map()도 같은 원리로 for문을 돌 때, 초기화를 시켜주지 않아 for문 1개치의 결과만 출력됨
# map, zip ,reversed, filter가 동일하게 적용되며 sorted만 재순회 가능하다!

이터레이터 - zip

a = [1, 2, 3]
b = ['a', 'b', 'c']

z = zip(a, b)
print(list(z)) # [(1, 'a'), (2, 'b'), (3, 'c')]

# zip 객체는 한 번 사용되었으므로 빈 리스트가 반환됩니다.
print(list(z)) # []

클래스 이론 - 상속

# 상속
# 클래스 끼리는 2줄 공백을 준다.
class Car(object):
    maxSpeed = 300
    maxPeople = 5

    def move(self):
        print('출발하였습니다.')

    def stop(self):
        print('멈췄습니다.')


class HybridCar(Car):   # Car를 상속합니다.
    battery = 1000
    batteryKM = 300


class ElectricCar(HybridCar):
    battery = 2000
    batteryKM = 600

modelx = HybridCar()
print(modelx.maxSpeed)  # maxSpeed가 modelx에 없는데요? 그래도 출력됩니다~
modelx.move()           # 출발하였습니다.

클래스 이론 - 다중 상속

# MRO(Method Resolution Order) = 다중 상속을 받을 때 클래스 상속에서 메서드 찾는 순서를 정의
class A:
    def method(self):
        print("A method")

class B(A):
    def method(self):
        print("B method")

class C(A):
    def method(self):
        print("C method")

class D(B, C):
    pass

obj = D()
print(D.mro()) # 출력: [<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
obj.method()  # 출력: B method

위의 출력을 보면 D => B => C => A 순으로 메서드를 찾는 모습을 볼 수 있다

# 같은 함수명은 피하는게 좋다
# 부모의 메서드를 재구현 : 메서드 오버라이딩

class A:
    def hello():
        print('helllo world A')

class B(A):
    def hello(self): => 부모의 hello메서드를 재정의 하였음 = 메서드 오버라이딩
        print('hello world B')

class C(A):
    pass

a = A()
b = B()
c = C()

a.hello()    # helllo world A
b.hello()    # hello world B
c.hello()    # helllo world A

오버로딩

# 파이썬은 오버로딩이 없습니다!! 그럼에도 오버로딩이 무엇인지 설명은 해드립니다.
# 자주나오는 용어이니까.. 이해할 필요는 없습니다.

class A:
    def hello(self, a):
        return a ** 2

    def hello(self, a, b):
        return a + b

a = A()
print(a.hello(10))  # JAVA는 이렇게 아규먼트의 갯수에 따라 호출되는 함수를 다르게 할 수 있습니다.
print(a.hello(10, 20)) # 아규먼트가 1개이면 자동으로 첫번째 hello의 인자가 1개이므로 첫번째 함수값 반환, 2개이면 두번째 hello
# 위 코드는 파이썬에서 오류납니다.

클래스 대안 자료 형태

1. 딕셔너리 => 가능은 하지만 확장성, 재사용 등의 이유로 클래스를 안쓸 이유가 없다.
# 간단한거면 딕셔너리 써도됨
2. 클로저 => JavaScript에서 간혹 사용하고 파이썬에선 사용하지 않는다고 봐도 될듯

매직메서드 이론

### 2.1. **생성자와 소멸자**

- __init__(self, ...): 객체가 생성될 때 호출되는 생성자 메서드.

### 2.2. **문자열 표현**

- __str__(self): str() 함수 출력 결과와 같습니다. 
	- 이는 print로 출력되는 결과와도 동일합니다. 
	- 이 출력은 "공식적이지 않은" 또는 "좋게 보이는" 문자열 표현을 반환합니다. 
	- 객체의 공식적인 표현은 repr입니다.
- __repr__(self): 
	- repr() 함수에 출력 결과와 같습니다. 
	- 객체의 "공식적인" 문자열 표현을 반환합니다.

### 2.3. **산술 연산**

- __add__(self, other): 덧셈 연산
- __mul__(self, other): 곱셈 연산

### 2.4. **비교 연산**

- __eq__(self, other): 동등 연산 (==)
- __ne__(self, other): 부등 연산 (!=)
- __lt__(self, other): "작다" 연산 (<)
- __le__(self, other): "작거나 같다" 연산 (<=)
- __gt__(self, other): "크다" 연산 (>)
- __ge__(self, other): "크거나 같다" 연산 (>=)

### 2.5. **컨테이너 타입 연산**

- __len__(self): len() 함수의 출력 결과와 같습니다.
- __getitem__(self, key): 인덱싱 연산 (obj[key])의 출력 결과와 같습니다.

### 2.6. **호출 가능 객체**

- __call__(self, ...): 객체를 함수처럼 호출할 때 사용됩니다.

call() 메서드는 이럴때 쓰입니다

class Counter:
    def __init__(self):
        self.count = 0

    def __call__(self):
        self.count += 10
        print(f'현재 count 값: {self.count}')

    def __getitem__(self, key):
        return key*10

# 객체 생성
counter = Counter()

# 객체를 직접 호출합니다. 이때 __call__ 메서드가 실행됩니다.
counter()  # 출력: 현재 count 값: 10
counter()  # 출력: 현재 count 값: 20
counter()  # 출력: 현재 count 값: 30

counter[5] # 출력: 50 => getitem메서드 때문에

서비스 만들때 실제로 필요한 매서드

1. init
2. str
3. repr
4. eq
5. len

클래스를 이용한 크롤링 예제

import requests # 통신을 해서 서버에서 제공해주는 값을 가져올 수 있다.
from bs4 import BeautifulSoup   # 가져온 값을 '파싱'(태그별로 나눠준다) 해주는 모듈

response = requests.get('https://paullab.co.kr/bookservice/')
soup = BeautifulSoup(response.text, 'html.parser')

book_name = soup.select('.book_name')
book_info = soup.select('.book_info')
# 마음에 안드는 포인트들
# 1. 가격이 int형이 아니다!
# 2. 가격, 저자가 한 번 더 들어있다!
# 3. 공백도 들어가 있다!

books = []
class Book:
    def __init__(self, name='', price=0, author='', info=''):
        self.name = name
        if price.replace('가격: ', '').replace('원', '').replace(',', '') == '무료':
            self.price = 0
        else:
            self.price = int(price.replace('가격: ', '').replace('원', '').replace(',', ''))
        self.author = author
        self.info = info

    def __str__(self):
        return self.name

    def __repr__(self):
        return self.name

for i, _ in enumerate(book_name):
    books.append(Book(
        book_name[i].text,
        book_info[(i*3)].text,
        book_info[(i*3)+1].text,
        book_info[(i*3)+2].text
    ))

books
books[0].name
books[0].price

위의 코드는 book_name과 book_info로 사이트의 내용을 가져오고 이를 Book 클래스를 활용하여 books 리스트에 저장한다. init메서드로 이름, 가격, 저자, 정보를 초기값을 설정해 주었다. 가격 부분은 조건문으로 문자를 수정해주었다는 특징이 있다. 이후 str 메서드로 print()를 찍으면 이름이 나오도록 하였으며 repr 메서드도 str 메서드와 똑같이 설정해주었다.


과제

연습문제


피드백

오늘 오전에는 특이하게 (주)퍼즐벤처스에서 CTO를 하시는 김태성님의 특강을 들었다. 스타트업 관련 얘기를 해주셨고 기술 채용 담당자의 입장에서 신입의 포트폴리오 이야기를 해주셨다. 쉽게 듣지 못하는 이야기인 만큼 필기하면서 들었던 것 같다. 자세한 이야기는 하단의 페이지를 보면 된다.

24/01/12 세미나 내용 정리

수업으로 넘어와서.. 클래스를 며칠째 하고 있다. 실무에서 쓰는 코드를 배우고 잠깐 딴길로 새서 보안관련 얘기도 듣고 역사도 듣고 하는데 그래도 재미있게 들었던 것 같다. 클래스 예제를 계속 타이핑 하고 매직메서드, 상속까지 이론을 추가로 듣고 나니 과제정도는 잘 풀게 된 것 같다. 클래스, 메서드 마스터까지 화이팅!!

profile
익숙해지기 위해 기록합니다

0개의 댓글