[day-10] 클래스, lambda응용, 매직 메서드, match, repr/str

Joohyung Park·2024년 1월 14일
0

[모두연] 오름캠프

목록 보기
10/95
post-thumbnail

함수, 클래스 주석 다는 방법

def hello():
    '''
    DocString
    주석은 여기에 답니다
    '''
    pass

함수와 클래스의 주석은 위처럼 답니다.

함수도 클래스이다.

<class ‘function’>

함수의 이름은 변수이다.

l = [print, print, print]
l[2]('hello world')    # hello world

hojun = print
hojun('hello world') # hello world

# id(hojun)과 id(print)는 같다. 즉 주소가 같다는 말이다.

print = 10    # 과 같은 코드도 가능하다. 

# 결론
# print는 변수 이름일 뿐이고 실제로는 코드를 가르키고 있다.

lambda의 응용 - map

1. map에서 많이 사용한다.

- 람다 기본형 => add = lambda x, y: x+y
- lambda + 3항 연산자 많이 사용함
	- x = 10일때, y = True if x%2 == 0 else False    # True
- 비슷한 코드를 재사용할 예정이라면?
  - list(map(함수명, [1, 2, 3, 4]))
  - list(map(lambda x: x**2, [1, 2, 3, 4]))
- 마스킹?
  - 하단과 같은 코드를 마스킹이라고 하는데..
  - list(map(lambda x: True if x % 2 == 0 else False, [1, 2, 3, 4]))
  - 알고리즘 문제에서 점과 점사이의 거리, 에라토스테네스의 체(소수 구하기) 등에 유용함
  - 위 코드를 실행하면 [False, True, False, True] 이런식으로 나오는데 여기서 True값의
	  차이를 구하든 count를 하든 해서 쉽게 구할 수 있다.

lambda의 응용 - filter

2. filter에서 많이 사용한다.

- filterTrue인 것만 출력하며 기본형은 다음과 같다. => list(filter(함수, 데이터))
- list(filter(lambda x: True if x % 3 == 0 or x % 5 == 0 else False, range(100)))
	- 위와 같이 3,5의 배수만 출력할 수도 있고.. 홀수만 출력할 수도 있다.
- list(filter(lambda x:x % 2, range(100)))    # 홀수만 출력
	# x == 0일때 => lambda x: x % 2 => 0 => False로 평가
	# x == 1일때 => lambda x: x % 2 => 1 => True로 평가
	# x == 2일때 => lambda x: x % 2 => 0
	# x == 3일때 => lambda x: x % 2 => 1

lambda의 응용 - sorted

3. sorted에서 많이 사용한다.

- sort()는 원본을 변화시키기에 보통 변화시키지 않는 sorted()를 사용
- sorted에는 key 값을 넣어 세부적인 정렬이 가능하다!
- 알고리즘에서 단골 문제이고 거의 1문제는 출제된다고 한다.
- 최댓값, 최솟값, 우선순위, 가장 거리가 짧은 쌍 등에서 사용한다.
- sorted는 보통 0번째 값을 기준으로 정렬
- key값을 주어 특정 칼럼의 값으로 정렬 가능
	- sorted(데이터, key=lambda x: x[3])
- 각 점수들의 평균을 정렬도 가능
	- sorted(반별점수, key=lambda x: x[1] + x[2] + x[3])
- key값을 2개 주면..?
	- sorted(반별점수, key=lambda x: (x[1] + x[2] + x[3], x[3])
	- 위와 같은 경우에는 x[1] + x[2] + x[3]으로 우선 정렬하고 값이 같다면 x[3]으로 정렬함
# 딕셔너리에서 key값으로 비교하는 예제
# 기본적으로 오름차순
우편번호 = [
    {
        '주소': '경기도 용인시',
        '우편번호': 321
    },
    {
        '주소': '경기도 수원시',
        '우편번호': 355
    },
    {
        '주소': '제주특별자치도 제주시',
        '우편번호': 333
    }
]

# sorted(우편번호) #error, 비교를 할 수 없다!?
sorted(우편번호, key=lambda x: x['우편번호'])
# 점과 점 사이의 가장 짧은 점의 쌍 출력하는 문제
# 입력값 : s = [1, 3, 4, 8, 13, 17, 20]
# 출력값 : (3, 4)

s = [1, 3, 4, 8, 13, 17, 20]
s[1:]    # [3, 4, 8, 13, 17, 20]
# zip을 사용한다!
x = list(zip(s, s[1:]))    # [(1, 3), (3, 4), (4, 8), (8, 13), (13, 17), (17, 20)]
# 튜플 안의 원소의 차이로 정렬한다(sorted)
sorted(x, key=lambda x: x[1] - x[0]    
# [(3, 4), (1, 3), (17, 20), (4, 8), (13, 17), (8, 13)]
# 가장 차이가 작은 쌍이 앞에오므로 정답은 sorted(x, key=lambda x: x[1] - x[0][0]    
# 조금 다른 예시인데 문자끼리 zip을 하면?
list(zip('hello', 'world'))    
# [('h', 'w'), ('e', 'o'), ('l', 'r'), ('l', 'l'), ('o', 'd')]
# 각 단어의 한 글자씩 불러온다

built-in function(내장함수) 중 자주 사용하는 것

- chr()    # 아스키코드를 문자로 바꿔줌
- ord()    # 문자를 아스키코드로
- sum()    # 더하는 기능, 이어붙이는 기능, 리스트 평탄화 작업시에 사용됨
# sum의 기능중 하나인 평탄화 작업의 예시를 보자.
sum([[1, 2, 3], [4, 5, 6]], [])

# [] + [1, 2, 3] == [1, 2, 3]
# [1, 2, 3] + [4, 5, 6] == [1, 2, 3, 4, 5, 6]
# 결과적으로 [1, 2, 3, 4, 5, 6] 이 된다.

조금 다른 얘기긴 하지만 질문 나왔던 내용이라 정리하고 넘어가겠다. 파이썬을 어디까지 이해하고 어디까지 암기해야할까? 라는 질문은 모두가 하고 있을 것이다. 강사님은 암기할 것은 암기하고, 버릴 것은 버리면서 부지런히 가면 된다고 하셨다.


매직 메서드?

# 강사님이 클래스 부분을 조금 설명하셨는데 이후에 또 나오므로 후에 살펴보도록 하자.
a = 10
print(a)    # a.__str__
a   # a.__repr__
repr(a) # a.__repr__
# 잠깐 현업에서 사용하는 코드의 맛을 보았다.
# 실제 Django에서 사용하는 코드

class Notice:
    '''
    게시물 클래스
    '''
    def __init__(self, title, contents):    # 객체가 생성될 때 자동으로 호출되는 메서드
        self.title = title
        self.contents = contents
    
    def __str__(self):    # 객체를 문자열로 표현할 때 사용되는 메서드
        return self.title

    def __repr__(self):    # 객체를 개발자가 보기 위한 형태의 문자열로 표현하는데 사용
        '''
        repr이라는 값은 제가 임의로 만든 값이 아닙니다!
        정해져 있는 이름입니다.
        '''
        return f'{self.title}, {self.contents}'
    
    def __len__(self):    # len()함수를 호출할 떄 사용되는 메서드
        '''
        repr이라는 값은 제가 임의로 만든 값이 아닙니다!
        정해져 있는 이름입니다.
        '''
        # return 100
        return len(self.title + self.contents)

게시물1 = Notice('파이썬 이렇게 어려운줄 몰랐다!', '내가 알던 파이썬이 아니야....')
게시물2 = Notice('Django는 이렇게 재미있을 줄 몰랐다!', '파이썬을 잘 못해서 내가 할 수 있을까 생각이 들었는데...')

len(게시물1)    # 게시물1의 title + 게시물1의 contents의 글자수 합
# print()함수로 게시물 1을 문자열로 출력. 
print(게시물1)  # 파이썬 이렇게 어려운줄 몰랐다! / 문자열로 출력하므로 str부분에 정의되어 있는
                # title의 값만 출력됨
게시물1 # 파이썬 이렇게 어려운줄 몰랐다!, 내가 알던 파이썬이 아니야.... / 게시물1 객체 자체를 출력
repr(게시물1)   # 그 자료형을 대표하는 문자열 취급 / 파이썬 이렇게 어려운줄 몰랐다!, 내가 알던 파이썬이 아니야....

아직 매직 메서드에 대해 자세히 다뤄보지 않았으므로 후에 정리하겠다.


match문법

3.10버전에서 나온 문법이기에 현업에서 거의 사용 하지 않는다고 한다. 예를 들어 속도가 매우 빨라야 하는 프로젝트를 하게 되었다. 그렇다면 python 최신 버전을 사용할텐데, 그러면 match 문법을 사용할 수는 있다. 다만 권고하기 어렵다고 한다. 왜냐하면 match 문법이 동료들이 익숙하지 않기 때문이다. 그렇지만 2024년에는 중요도가 별표 0.5개 정도 되고 2 ~ 3년 후에는 중요도가 3 ~ 4개 정도로 올라갈 것으로 보인다고 한다.

# 예시1
요일 = 100
match 요일:
    case 0:
        print('일요일')
    case 1:
        print('월요일')
    case 2:
        print('화요일')
    case 3:
        print('수요일')
    case 4:
        print('목요일')
    case 5:
        print('금요일')
    case 6:
        print('토요일')
    case _:
        print('요일없음')
# 예시2
text = '1'
match text:
    case '1' | '2':
        print('1, 2')
    case _:
        print('No Match')
# get()을 사용하는 것을 권장
def 요일반환(요일):
    return {
        0: '일요일',
        1: '월요일',
        2: '화요일',
        3: '수요일',
    }.get(요일, '요일없음')

요일반환(2)
요일반환(100)

클래스 만들어보기

# 클래스 예시 
class 클래스이름 => 인스턴스
class Post => 게시물1, 게시물2, 게시물3...
class Dot =>1,2,3...
class Matrix => 행렬1, 행렬2, 행렬3...
class User => 유저1, 유저2, 유저3...
class Cart(장바구니) => Cart1, Cart2, Cart3...
class comment(댓글) => 댓글1, 댓글2, 댓글3
Character => Licat(주인공), Lion(빌런), Mob1(몬스터)
class Product => 상품1, 상품2, 상품3...
class Class_ => 강의1, 강의2, 강의3...
class Teacher => 강사1, 강사2, 강사3
# 0번: 기초 상식
# 0.0 __init__ 매직메서드는 없어도 되는가?
# 실제로 이런 코드를 짜실 일이 생긴다고 한다.
class 우편번호:
    제주도 = 100
    서울시 = 101
    경기도 = 102
    전라남도 = 103

우편번호.제주도
# 왜 이걸 딕셔너리로 만들지 않고 class로 만들었을까?
# 재사용성 + 확장성

# 0.1 클래스 꼭 사용해야 하는가?
# 일부 자료형에서는 클래스는 꼭 사용해야 한다.

# 아래와 같이 저장한다고 해서 문제가 생기는가?
# 데이터의 양(속도), 확장성, 다양성, 자료의 형태
# 기본 클래스(list, tuple, dict(속도 개선이 많이 되었음에도), set)는 매우 느리다.

# 제목: python 기초
# 저자: 이호준

{'제목': 'python 기초', '저자': '이호준'}

# 제목: html 기초
# 저자: 한재현

{'제목': 'html 기초', '저자': '한재현'}

[{'제목': 'python 기초', '저자': '이호준'}, {'제목': 'html 기초', '저자': '한재현'}]
# 위의 경우처럼 리스트로 구현하게 되면 이 데이터를 추가 작업을 할때 어려움이 있음
# 따라서 클래스를 사용함

Post 클래스 만들어보기

# 1번
# 클래스를 설계할 때
# 첫 번째로 - 모든 인스턴스에서 공유해야할 변수(클래스 변수)나 메서드 설정
# 두 번째로 - 인스턴스 고유의 영역
# in_crerate_at(스네이크 표기법) - 파이썬 권고사항
# inCreatedAt(카멜 표기법) - 파이썬에서는 잘 사용하지 않습니다.(JavaScript에서는 많이 사용합니다.)
# InCreatedAt(파스칼 표기법) - 파이썬 클래스

class Post:
		'''
		1. init메서드로 매개변수의 초기값을 설정하고, self.~~로 인스턴스 변수를 선언하였다
		2. print문을 사용할 때 출력하는 부분을 str메서드로 설정했고 해당 객체 고유의
			 title값을 출력하도록 한다.
		3. repr은 str과 동일하게 작성한다고 한다.
		4. add는 next로 다음 객체를 받아 현재 객체와 합쳐서 출력한다.
    5. eq는 각 객체간의 타이틀을 비교한다. 
		5-1. eq메서드를 선언하지 않는다면 같은 인스턴스인지 확인함
    6. update 메서드는 내용을 받아서 객체의 내용을 업데이트하는 기능
    6-1. 게시물1.update()를 print()하면 역시 update메서드의 return값 출력
		'''
    def __init__(
            self, 
            in_title='', 
            in_contents='', 
            in_count=0, 
            in_crerate_at='', 
            in_updated_at='', 
            in_author=''
        ):
        self.title = in_title
        self.contents = in_contents
        self.count = in_count
        self.created_at = in_crerate_at
        self.updated_at = in_updated_at
        self.author = in_author

    def __str__(self):
        return self.title

    def __repr__(self): # str 매지메서드를 선언하면 repr도 같이 나오고 일반적으론 동일하게 한다.
        return self.title 

    def __add__(self, next):
        # return self.count + next.count  # 게시물 1의 카운트 + 게시물 2의 카운트
        return self.title+next.title

    def __eq__(self, next):
        return self.title == next.title

    def update(self, in_contents):  
        '''
        in_ => 실무에서 쓰는 접두사가 아닙니다!
        '''
        self.contents = in_contents
        return '게시물 수정에 성공했습니다! 200'    # return값은 print()해야만 출력

게시물1 = Post(
    '파이썬은 참 깊군요!', 
    '오늘은 파이썬 클래스를 배웠는데 너무 어렵기도 하고, 신기했습니다.',
    0,
    '2024-01-11',
    '2024-01-11',
    '이호준'
    )

게시물2 = Post('2', '22',0,'2024-01-11','2024-01-11','이호준')

print(게시물1)    # 파이썬은 참 깊군요! => str에서 title만 정의되어 있기에 그거 반환
print(게시물1.title)    # 파이썬은 참 깊군요! => 게시물1 객체의 고유 title
print(게시물1 + 게시물2)    # 파이썬은 참 깊군요!2 => add메서드 출력
게시물1.update('hello world')    # 게시물 수정에 성공했습니다! 200
게시물1.contents    # hello world => update로 contents내용을 수정했기 때문

게시물1 == 게시물2  # eq 매직메서드로 정의했기에 title끼리만 비교함

repr이란 무엇일까?

a = 10
print(a) => 10    # 이거는 str 메서드
repr(a) => '10'    # 이거는 repr 메서드

# print() 함수는 값을 콘솔에 출력. 위 코드에서는 정수 10을 문자열로 반환하여 콘솔에 출력함
# 이 경우 print() 함수는 값을 출력한 이후엔 아무것도 반환하지 않아 반환값은 None임
# 이애하기 쉬운 결과 제공을 목적으로 함

# repr() 함수는 객체의 공식적인 문자열 표현을 반환. 위에선 문자열 '10'을 반환.
# repr() 함수는 값을 콘솔에 출력하지 않고 문자열 '10'을 반환함
# 객체 디버깅이나 로깅에 특이 유용함

repr vs str 비교

class Person:
		'''
		str 입니다
		'''
    def __init__(self, name, age):
        self.name = name
        self.age = age
 
    def __str__(self):
        return f'{self.name}{self.age}살이다.'
 
john = Person('John Doe', 30)
print(str(john))  # 출력: John Doe는 30살이다.

-----------------------------------------------------------

class Person:
		'''
		repr 입니다
		'''
    def __init__(self, name, age):
        self.name = name
        self.age = age
 
    def __repr__(self):
        return f'Person(name={self.name}, age={self.age})'

john = Person('John Doe', 30)
print(repr(john))  # 출력: Person(name=John Doe, age=30)

# 이런식으로 디버깅, 로깅 하기위한 유용한 정보를 적는식
# repr은 eval()을 사용하여 객체를 재생산 할 수 있어야 한다고 하는데 
# repr객체를 eval()로 씌우면 원래의 값을 얻을 수 있다는 것이다!

Dot 클래스 만들어보기

class Dot:
		'''
		매직메서드에서 next는 현재 객체와 다음 객체를 연계해서 무언가 할때 쓴다.
		'''
    def __init__(self, x, y):    # x,y를 파라미터로 받아 인스턴스 변수 생성
        self.x = x
        self.y = y

    def __add__(self, next):    # + 표시를 만나면 각 인스턴스의 좌표끼리 더한다
        return (self.x + next.x, self.y + next.y)
    
    def __mul__(self, next):    # * 표시를 만나면 각 인스턴스 좌표끼리 곱한다
                return (self.x * next.x, self.y * next.y)

    def distance(self, next):    # distance를 하면 좌표를 구한다
        dx = self.x - next.x
        dy = self.y - next.y
        return (dx**2 + dy**2) ** 0.5   # root는 **0.5로 많이 사용합니다.

dot1 = Dot(20, 10)    # dot1 객체 생성, 고유의 x,y 인스턴스 변수 생성
dot2 = Dot(30, 5)    # dot2 객체 생성, 고유의 x,y 인스턴스 변수 생성

dot1 + dot2    # add 메서드 반환값, (50, 15)
dot1.distance(dot2)    # distance 메서드 반환값, 11.180339887498949

Character 클래스 만들어보기

class Character:
    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
    
    def move(self):
        self.x += 100
    
    def attack(self, next):
        next.hp -= self.power

# 캐릭터 생성
주인공1 = Character('주인공1', '불꽃의 검', 100, 50, 20, 0)
주인공2 = Character('주인공2', '물의 창', 120, 60, 15, 200)

# 상태 출력
print(f"{주인공1.name}의 현재 체력: {주인공1.hp}")
print(f"{주인공2.name}의 현재 체력: {주인공2.hp}")

# 주인공1이 주인공2를 공격
주인공1.attack(주인공2)

# 상태 출력
print(f"{주인공1.name}이(가) {주인공2.name}을(를) 공격했습니다!")
print(f"{주인공2.name}의 현재 체력: {주인공2.hp}")

과제

연습문제


피드백

어제 말했다 시피 오늘부터는 이호준 강사님이 파이썬 심화과정을 진행하기로 했다. 전에 진환 강사님이 아프셔서 호준 강사님이 한번 강의를 대신 해주신 적이 있었는데 강의 내공이 있으셔서 그런지정말 템포가 빠르긴 했는데 집중은 더 잘 되었던 것 같다. 오늘도 비슷한 느낌으로 진행했다. 클래스의 이론은 조금만 다루고 실습 위주로 했고 어디에서 보기 힘든 실무에서 사용하는 문법, 궁금증을 유발하는 어떠한 개념의 유래 등 꽤 흥미로웠다고 생각한다. 나같은 경우는, 클래스를 살짝 보기만 하고 실제로 써본적은 없다시피 한데 클래스를 그냥 계속 1개씩 만들다 보니까 이해가 되는 것 같기는 하다 ㅋㅋ. 이렇게 배운 지식들로 무언가 프로그램? 서비스?를 만들어봐야 내것이 될 수 있을 것 같은데 고민이 되는 부분이다. 아 그리고.. 어제 학습을 진행하며 애매한 부분이 있었는데 나만 모르는 것이 아니라 다른 수강생들도 모르는 부분이 있었어서 같이 공부하는 입장이라는 것을 다시금 깨달았다. 앞으로도 잘 부탁드립니다.

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

0개의 댓글