Naver Ai Boostcamp

[DAY 4] 파이썬 기초 문법 III

잡담연구소 2021. 1. 21. 23:53

class에 대해서 사용할 줄 알지만 , 누군가 나에게 객체가 뭐야? 라고 질문한다면 어,,,,만 하다 끝날만큼 정확한 개념을 잘 모르고 있었다. 확실히 이해하면서 넘어가는 하루가 되도록!

 

Python Object Oriented Programming

📌 class를 왜 사용하나요? 

 

- 코드의 반복을 줄일 수 있다. 

   : 하나의 클래스를 통해서 여러개의 객체를 생성할 수 있다.
     많은 예시들도 있겠지만, 가장 와닿았던 예시로는 만약 내가 색깔,스피드를 가지고 있는 '자동차'라는 클래스를 만들었다고 하자. 그런데 색깔,스피드,높이제한을 가지고 있는 '트럭'이라는 클래스를 만들어야한다.

트럭과 자동차는 색깔과 스피드라는 두 속성이 중복된다. 

만약 함수라면 자동차 함수 따로, 트럭 함수 따로 함수를 짜줘야 했을 것이다.

하지만 클래스를 사용할 경우 자동차를 상속해와서 '높이제한' 이라는 속성만 따로 추가해주면 반복을 최소화할 수 있다. 

- 재사용이 간편하다. 

 

📌 객체 지향 프로그래밍 (OOP)

  • 객체 : 어떤 속성(attribute)과 행동(action)을 가지고 있는 데이터

  • 속성은 변수 (variable) , 행동은 함수 (method) 로 표현됨 

🤔🤔❓   class에는 꼭 속성, 행동 다 있어야 하나?

처럼 굳이 속성, 행동 다 있을 필요는 없다. 

하지만 저렇게 class를 짜야할 일이 있을까..? 

 

 

📌 class와 instance 

  • 붕어빵틀 (class) 와 붕어빵 같은 존재 (instance)

    Car 라는 자동차 설계도 역할을 하는 class가 있으면 설계도를 통해 만들어진 차들이 instance이다. 
    class : sonata 설계도
    intsance : 소나타 78가 9037 , 소나타 123허 2284 등..

  • Class 작명 : CamelCase 사용 

    - 띄어쓰기 부분에서 대문자를 사용

    - 함수명(snake_case) 처럼 띄어쓰기 부분에 "_"를 추가하는 방식과 다름 

  • Attribute 추가는 __init__, self와 함께 !

    - __init__은 객체 초기화 예약함수

  • method는 반드시 self를 추가해줘야 class 함수로 인정됨

    self란? 생성된 instance 자기 자신을 의미 

  • object이름 선언과 함께 초깃값 입력하기

    __init__함수에 맞게 초기값을 주어야함 

# class 예약어 , class 이름 , 상속받는 객체명(자동상속이라 object 작성 굳이 안해도 됨) 
class SoccerPlayer():
    def __init__ (self, name, position, back_number):
        self.name = name
        self.position = position
        self.back_number = back_number

    def change_back_number(self, new_number):
        print(f"선수의 등번호를 변경합니다. from{self.back_number} to {new_number}")
        self.back_number = new_number

    def __str__(self):
        return f"Hello my name is {self.name} , mu back num is {self.back_number}"

korea = SoccerPlayer("son" , "FW", 7)
spain = SoccerPlayer("messi" , "FW" ,10)


print(korea is spain)
# >> false

america = SoccerPlayer("chris" , "PW")
>> TypeError: __init__() missing 1 required positional argument: 'back_number'

👉 SoccerPlayer 라는 class 를 만들어주자 .

이 클래스는 이름, 포지션, 등번호 라는 속성을 가지고 있고,  초기화 역할을 해주는 init 함수와 등 번호를 바꾸어주는 역할을 하는 change_back_number 함수 , 축구선수의 정보를 출력해주는 str함수로 구성된다.

SoccerPlayer라는 class를 통해서 korea, spain이라는 instance를 생성할 수 있다.

두 instance는 같은 class에서 나왔지만 , a is b 를찍어보면 False가 나오듯이 서로 다른 instance (객체) 이다.

 

현재 init은 이름, 포지션, 등번호로 이루어져있다. 

인스턴스를 만들때는 init에 맞추어 초기값을 인수로 넣어줘야하는데,

SoccerPlayer("chris" , "PW") 이렇게 등번호를 인수로 넘겨주지 않고, 인스턴스를 만드려고 하면 typeerror가 발생한다. 

꼭 init에 맞추어 초기값을 넣어주어야한다. 

 

__str__함수는 매직메소드로 출력 예약 함수이다. 

korea.change_back_number(20)
print(korea)

>>선수의 등번호를 변경합니다. from7 to 20
>> Hello my name is son , mu back num is 20

korea.back_number = 50
print(korea)

>> Hello my name is son , mu back num is 50

 korea.change_back_number(20) 과 같이 인스턴스명.클래스 내 함수 로 메소드를 이용할 수도 있다. 

꼭 메소드를 거치지 않아도 korea.back_number로 속성(attribute)를 변경할 수도 있다. BUT 권장하지 않음

 

 

🤔🤔❓ __ (이중 밑줄 접두사)의 의미

- 특수한 예약 함수나 변수 그리고 함수명 변경(맨글링)으로 사용함  

ex) __main__ , __str__ 등등 매직메소드

 

🤔🤔❓ 맨글링이란?

- 이중 밑둘 접두사는 서브클래스에서 이름 충돌(변수의 재정의)을 피하기 위해 파이썬 인터프리터가 속성 이름을 다시 쓰도록 한다. 클래스가 확장될 때, 충돌이 발생하기 어렵게 만드는 방식

 

 

📌 Lab : class로 notebook 만들어보기

🙋‍♀️스스로 CODE 

note , notebook을 만들려면 필요한 속성과 함수가 무엇인지 생각해보고 스스로 코드를 짜보자.

notebook에 어떻게 note를 어떻게 삽입하지?

# note class를 만들려면? : 
# 속성 : 내용
# 함수 : content 삽입(초기화할 때 내용을 넣어주면 안되나?), content 제거 

class Note():
    def __init__(self, content):
        self.content = content
        self.page = 1
    
    def remove_content(self):
        self.content = " "

# notebook class를 만들려면?
# 속성 : note class, page , 
# 함수 : note가 추가되면 페이지 제한에 걸리는 지 확인 후 페이지를 추가

class Notebook ():
    def __init__(self,page):
        self.page = page

    def add_note(self, other):
        if self.page + other.page <= 300:
            self.page = self.page + other.page

 

🤦‍♀️ 교수님 CODE

생각치도 못한 부분까지 엄청 자세하게 구현하셨다.

스스로 notebook을 구현할 때, note를 어떻게 삽입하지? 고민이 많았는데 저렇게 딕셔너리를 사용하니 깔끔한 거 같다.

또한 문제에서 출력문을 만들라는 얘기는 없었지만, str을 넣어주어 출력할 수 있게 하는 것도 센스 있다. 배워가자

# note class를 만들려면? : 
# 속성 : 내용
# 함수 : content 삽입, content 제거 

class Note():
    """[summary]
        init : content가 들어온다면 content를 저장하는 note 생성 
               content가 없다면 그냥 빈 note 생성 
        write_content : 내용을 작성  
        remove_content : 내용 삭제
        add : 내용 추가 
        str : note의 내용 출력 
    """    
    def __init__(self, content = None):
        self.content = content
    
    def write_content(self,content):
        self.content = content

    def remove_content(self):
        self.content = " "

    def __add__(self , other):
        return self.content + other.content

    def __str__(self):
        return self.content
        

# notebook class를 만들려면?
# 속성 : 내용, 제목, 페이지 수
# 함수 : note 추가 , note 삭제 , 페이지 추가

class Notebook ():
    """[summary]
    init : notebook의 page number 
    title : notebook의 제목 
    notes : notebook을 구성하는 note들과 notebook 내 위치한 page 번호
    add_note : 
    remove_content: 특정 페이지에 있는 content 삭제 
                    만약 특정 페이지가 노트북 내에 없다면 문구 출력 
    get_numbers_of_pages : 노트북의 페이지 수 출력 해줌 
    """    
    def __init__(self,title):
        self.page_number = 1
        self.title = title
        self.notes = {}

    def add_note(self, note, page=0):       
        if self.page_number < 300:
            if page == 0 :
                self.notes[self.page_number] = note
                self.page_number += 1
            else :
                self.notes[page] =  note
                self.page_number += 1
        else :
            print("page가 모두 채워졌습니다.")

    def remove_content(self, page_number) :
        if page_number in self.notes.keys():
            return self.notes.pop(page_number)
        else :
            print("해당 페이지는 존재하지 않습니다.")
        
    def get_numbers_of_pages(self):
        return len(self.notes.keys())

my_notebook = Notebook("나는 오혜린")
print(my_notebook.title)
 >> 나는 오혜린    

new_note = Note("oop어려운 데 재밌다.")
print(new_note)
>> oop어려운 데 재밌다.

new_note_2 = Note("배고프다 밥먹고싶다.")
my_notebook.add_note(new_note)
my_notebook.add_note(new_note_2 , 100)

print(my_notebook.notes)
>> {1: <__main__.Note object at 0x0000025216580D88>, 100: <__main__.Note object at 0x0 000025216580DC8>}  
print(my_notebook.get_numbers_of_pages())
>> 2

 

📌 OOP의 특징 

: 실제 세상을 모델링한다. 

1. 상속 (inheritance)

  • 부모 클래스로부터 속성과 method를 물려받은 자식 클래스를 생성하는 것

  • 원래 class를 작성할 때 object를 상속받아야하는데, 파이썬 3이상부터 자동상속이 되어 필요 없어졌다.

  • 교수님과 같이 person class를 만들어보고 나혼자 위의 예시로 말했던 car,truck class를 상속받아 만들어보았다. 

class Car() :
    def __init__(self, color,speed):
        self.color = color
        self.speed = speed

    def speed_up(self, target_speed):
        print(f"속도가 {self.speed}에서 {target_speed}으로 증가했습니다.")
        self.speed = target_speed
        
    def speed_down(self, target_speed):
        print(f"속도가 {self.speed}에서 {target_speed}으로 감소했습니다.")
        self.speend = target_speed

class Truck(Car): # 부모클래스 Car로부터 상속
    def __init__(self , color , speed , load):
        super().__init__(color , speed) #부모객체의 초깃값 그대로 적용
        self.load = load # 속성값 추가 
    
    def load_more(self, add_load): # 새로운 메소드 추가
        if self.load + add_load > 100 :
            print("더 이상 실을 수 없습니다.")
        else : 
            self.load += add_load
            print(f"현재 적재량은 {self.load}입니다.")
    
    def speed_up(self , target_speed) : #부모 클래스 함수 재정의 
        super().speed_up(target_speed) #부모 클래스 함수 사용
        if target_speed > 80:
            print("속도 제한 구역입니다. 속도를 낮추십시오.")
        else : 
            print(f"속도가 {self.speed}에서 {target_speed}으로 증가했습니다.")
            self.speed = target_speed

sonata = Car('red' , 60)
sonata.speed_up(120)
sonata.speed_down(90)
>>속도가 60에서 120으로 증가했습니다.
>>속도가 120에서 90으로 감소했습니다.


coupang = Truck('white' , 80 , 75)
coupang.speed_up(200)
coupang.load_more(20)
coupang.load_more(50)

>>속도가 80에서 200으로 증가했습니다.
>>속도 제한 구역입니다. 속도를 낮추십시오.
>>현재 적재량은 95입니다.
>>더 이상 실을 수 없습니다.
  • class  명 뒤 괄호 안에 상속받고싶은 클래스명을 넣어준다. Truck이라는 클래스가 자식 , Car라는 클래스가 부모

  • super().__init__( ) : 부모의 초기화를 그대로 받아온다. 괄호 안에는 부모클래스를 초기화할 때 사용했던 attribute들을 넣어준다. 
    혹시 truck이 부모의 color는 받지 않고 , speed만 받고싶다면 ? super.__init__(speed) 이렇게 써도 될까?
    안된다. 부모가 초기화할 때 썼던 attribute들을 다 넣어주도록!

  • def ~~ 해서 부모클래스와는 다른 새로운 메소드들을 추가해줄 수도 있다. 

  • 만약 부모클래스에서 사용하였던 함수를 사용하고싶다면 ? super().메소드(인수) 로 불러와주면 된다.
    나같은 경우에는 속도 조건문을 추가해주었다. 
    출력문으로 부모클래스에서는 "속도가 80에서 200으로 증가했습니다 " 
    자식 클래스에서는 "속도 제한 구역입니다. 속도를 낮추십시오" 가 출력되는 것을 볼 수 있습니다. 

부모의 것을 가져다 쓰고 싶으면 super(). 메소드(self를 제외한 부모 메소드의 인수들 )

 

2. 다형성 

  • 같은 이름 메소드의 내부 로직을 다르게 작성 (같은 모양의 코드가 다른 동작을 하는것)
    함수명은 같은데 인터페이스와 코드에 따라 달라짐 ?
    개념적으로는 같은 일이지만 , 세부적으로 다른 기능을 할 때 사용
    이름은 같지만 하는 행위(호출되는 코드)는 다르다.

  • 동적 타이핑의 특성으로 같은 부모 클래스의 상속에서 주로 발생함.

  • 코드의 양을 줄이고 , 여러 객체 타입을 하나의 타입으로 관리가 가능하다.

  • 메소드 재정의 (오버라이딩)도 다형성의 한 예이다. 

class Animal:
    def __init__ (self, name):
        self.name = name
    def talk(self):
        raise NotImplementedError("Subclass must implement abstarct method")

class Cat(Animal):
    def talk(self): 
        return "Meow!"
class Dog(Animal):
    def talk(self):
        return "woof! woof!"

animals = [Cat("missy") , Dog("lassie")]
for animal in animals : 
    print(animal.name+':'+animal.talk())
    
 >> missy:Meow!
 >> lassie:woof! woof!

🤔🤔❓ Raise NotImplementedError 란 ?

자식 클래스에서 해당 method를 구현하지 않는다면, 자동으로 부모클래스의 method를 호출하고, notimplentedError가 뜨게 된다. 

만약 부모 클래서에서 해당 method를 pass만 해놓도록 구현했다면 추후에 이로 인한 side effect가 생길 수 있다. 

이런 불상사를 피하기 위헤서 에러를 호출하는 거다.

 

3. visibility overview

  • 객체의 정보를 볼 수 있는 레벨을 조절하는 것. 누구나 객체 안에 모든 변수를 볼 필요가 없음 -> 가시성 조절

  • 캡슐화 (encapsulation)
    - class를 설계할 때, 클래스 간 간섭/정보공유의 최소화 
    - 캡슐을 던지듯, 인터페이스만 알아서 써야함

  • 예시 )
    product 객체를 inventory 객체에 추가
    invectory 에는 오직 product객체만 들어감
    inventory에 product가 몇 개인지 확인 필요 
    inventory는 product items에 직접적인 접근 불가

  • priviate 변수 '__' 를 사용해주자

❓ private 변수가 없다면 ? 

class Product():
    pass

class Inventory():
    def __init__(self):
        self.items = [] # __ : private 변수로 선언 타객체가 접근 못함
        self.test = "abc"

    def add_new_item(self, product):
        if type(product) == Product:
            self.items.append(product)
            print("new item added")
        else :
            raise ValueError("Invalid Item")

    def get_numbers_of_items(self):
        return len(self.items)

myinventory = Inventory()
myinventory.add_new_item(Product())
myinventory.add_new_item(Product())
myinventory.items.append("abc")
print(myinventory.items)

################################################################
new item added
new item added
[<__main__.Product object at 0x00000250C9613508>, <__main__.Product object at 0x00000250C9613588>, 'abc']

Inventory에는 product만 전급할 수 있고 Inventory의 items에는 product들만 담겨야한다. 

하지만 위의 코드에서 items.append로 직접 추가를 해주는 경우, 접근이 가능해져 items에 product 이외의 'abc'가 담긴 것을 확인할 수 있다. 

 

하지만 아래와 같이 __를 사용해 private 변수로 만들어주면 접근을 할 수 없게 막을 수 있다. 

또 함부로 __items 리스트에 추가할 수 없게 된다. 

class Product():
    pass

class Inventory():
    def __init__(self):
        self.__items = [] # __ : private 변수로 선언 타객체가 접근 못함
    def add_new_item(self, product):
        if type(product) == Product:
            self.__items.append(product)
            print("new item added")
        else :
            raise ValueError("Invalid Item")

    def get_numbers_of_items(self):
        return len(self.__items)

myinventory = Inventory()
myinventory.add_new_item(Product())
myinventory.add_new_item(Product())
print(myinventory)
print(myinventory.__items)

########################################################33
new item added
new item added
<__main__.Inventory object at 0x00000167554D33C8>

Traceback (most recent call last):
  File "c:\Users\HR-OH\workspace\command_analyzer.py", line 21, in <module>
    print(myinventory.__items)
AttributeError: 'Inventory' object has no attribute '__items'

Inventory의 items의 갯수가 필요할수도 있고, items의 정보를 누군가는 가져다 써야하는 경우가 생길수도 있다. 

하지만 items를 함부로 수정하면 안되는 경우가 있을수도 있다. 

이럴 때는 @property 사용하면 된다. 

  • 예시 ) 
    product 객체를 inventory 객체에 추가 
    invectory 에는 오직 product객체만 들어감
    inventory에 product가 몇 개인지 확인 필요 
    inventory는 product items에 접근 허용

class Product():
    pass

class Inventory():
    def __init__(self):
        self.__items = [] # __ : private 변수로 선언 타객체가 접근 못함
    
    @property
    def items(self):
        return self.__items
    
    def add_new_item(self, product):
        if type(product) == Product:
            self.__items.append(product)
            print("new item added")
        else :
            raise ValueError("Invalid Item")

    def get_numbers_of_items(self):
        return len(self.__items)

myinventory = Inventory()
myinventory.add_new_item(Product())
myinventory.add_new_item(Product())
print(myinventory)
print(myinventory.items)
print(myinventory.__items)

###########################################################
new item added
new item added
<__main__.Inventory object at 0x000002144ED00588>
[<__main__.Product object at 0x000002144ED005C8>, <__main__.Product object at 0x000002144ED00648>]
Traceback (most recent call last):
  File "c:\Users\HR-OH\workspace\command_analyzer.py", line 27, in <module>        
    print(myinventory.__items)
AttributeError: 'Inventory' object has no attribute '__items'

@property : 숨겨진 변수를 반환하게 해줌.

외부에서는 접근이 안되지만 , 내부에서 접근 후 copy 하여 반환해준다. 

items로 접근 시 반환되지만 , __items로 접근하면 반환이 되지 않는다

 

📌 decorate와 친구들

1. first-class objects

  • 변수나 데이터 구조에 할당이 가능한 객체 

  • 파라미터로 전달이 가능 + 리턴값으로도 사용이 가능 

  • 파이썬의 함수는 일급함수 -> 함수가 파라미터로 전달이 되거나 , 리턴값으로 함수가 나올 수도 있다! 

    ex ) map(f,ex) 혹은 재귀함수의 return f(x-1) 

def cube(x) :
    return x*x*x

def square(x): 
    return x*x

def formula(method , argument_list): #함수를 파라미터로 사용 
    return [method(value) for value in argument_list]

print(formula(cube , [1,2,3,4,5]))
print(formula(square , [1,2,3,4,5]))

##########################################################
print(list(map(lambda t : t*t*t , [1,2,3,4,5])))
print(list(map(lambda t : t*t , [1,2,3,4,5])))

위와 같이 함수는 일급함수이기때문에 파라미터로 사용할 수 있다. 

아래 map은 연습할 겸 이렇게 해도 될 거 같아서 한 번 해보았다. 재사용할 게 아니라면 확실히 map+lambda 콤보 짱

 

2. inner fuction

  • 함수 내에 또 다른 함수가 존재 

def print_msg(msg) :
    def printer():
        print(msg)
    printer()

print_msg("hello~~~~")

################################################

def print_msg(msg) :
    def printer():
        print(msg)
    return printer

another = print_msg("비가 온다 주륵주륵")
another()

두 코드를 비교해보자. 

위의 코드는 print_msg라는 코드를 실행하면 내부에 있는 printer이라는 함수가 실행이 된다.

priter이라는 함수가 실행되면 msg인자로 받은 문구가 출력이 된다. 

 

아래의 코드는 print_msg라는 코드를 실행하면 내부에 있는 printer라는 함수가 반환된다. 

즉 print_msg라는 코드를 실행하면 msg를 출력하는 기능을 하는 함수가 나오는 것이다. 

another에는 그럼 printer이라는 함수가 할당된다. 

그렇기 때문에 another() 이라고 코드를 치면 할당된 함수 printer이 실행되어 문구가 출력된다.

 

이렇게 내부함수가 반환이 되도록 하는 것을 closer이라고 한다.

이걸 언제 유용하게 사용할 수 있을까? 아래와 같은 코드를 보자. 

def tag_func(tag, text):
    text = text
    tag = tag

    def inner_func():
        return '<{0}>{1}<{0}>'.format(tag, text)

    return inner_func

h1_func = tag_func('title' , 'this is python class')
p_func = tag_func('p' , 'data academy')

print(h1_func())
print(p_func())

##################################################################
>> <title>this is python class<title>
>> <p>data academy<p>

fuction을 목적에 맞춰서 같은 이름으로도 다양한 목적을 가진 함수들을 만들어낼 수 있다. 

 

3. decorater fuction

- 복잡한 클로저 함수를 간단하게!

def decorator(func):
    def wrapper(*args, **kwargs):     
        print('전처리')   
        print(func(*args, **kwargs))
        print('후처리')
    return wrapper

@decorator
def example():
    return '함수'
    
example()

@decorator를 하면 그 아래에 있는 함수문이 decorator의 func의 인자로 들어간다.

wrapper , func의 인자로는 무엇이 들어올 지 모르니까 *args , **kwargs로 가변인자를 준다. 

 

- 함수 말고 class에서는 어떻게 사용할까? 

class Decorator:

    def __init__(self, function):
        self.function = function

    def __call__(self, *args, **kwargs):
        print('전처리')
        print(self.function(*args, **kwargs))
        print('후처리')
        
@Decorator
def example():
    return '클래스'
example()

 

-decorator 가 여러개라면? 

def star(func) : 
    def inner(*args , **kwargs) : 
        print("*" * 10)
        func(*args , **kwargs)
        print("*" * 10)
    return inner

def percent(func) : 
    def inner(*args , **kwargs) : 
        print("%" * 10)
        func(*args , **kwargs)
        print("%" * 10)
    return inner

@star
@percent
def printer(msg):
    print(msg)

printer("Hello")


####################################
**********
%%%%%%%%%%
Hello
%%%%%%%%%%
**********

처음에는 결과가 이해가 잘안됐다. 피어세션에 조원들에게 질문해 해결했다. 

@star 때문에 맨 처음 star라는 함수에 

@percent
def printer(msg):
    print(msg)

이만큼이 func에 할당된다.

star 함수가 실행되면서 " ********** "을 출력한 후 , func를 실행한다. 

@percent때문에 percent라는 함수에 다시 아래만큼이 func에 할당된다.

def printer(msg):
    print(msg)

 

Module & Package 

여러의 모듈들을 모아 패키지를 만들고 이 패키지들이 모여 하나의 프로그램이 됨.

 

📌 Module 

  • Module == py를 의미
    같은 폴더에 Module에 해당하는 .py파일을 import를 사용해 호출

  • import 모듈명 를 하면 모듈에 해당하는 메모리가 로딩된다. 그 후 모듈명.함수명으로 모듈 내 함수를 사용할 수 있다.

  • import시, pycahe라는 폴더 생성 -> 더 빠르게 가져오기위해 모듈을 컴파일 한 폴더

  • namespace : 모듈을 호출할 때, 범위를 정하는 방법

    from과 import를 사용해 필요한 내용만 골라서 호출할 수 있음

    - alias 설정 : 모듈명 별칭으로 사용 ex) import baseball_game as bg

    - 특정 함수 또는 클래스만 호출 : from 모듈명 import 클래스/함수명 

    - 모듈에서 모든 함수 또는 클래스 호출 : from 모듈명 import *

 👀✨ 파이썬에는 다양한 모듈이 존재한다. 이 모듈을 다 외울 순 없으니 핵심키워드를 기억해 구글링해보자. 

 

📌 package 

  • 하나의 대형 프로젝트를 만드는 코드의 묶음 

    다양한 모듈들의 합, 폴더로 연결 

    __init__,  __main__등 키워드 파일명 사용 

  • __init__ : 현재 폴더가 패키지임을 알리는 초기화 스크립트 

    - 하위폴더와 py파일을 모두 포함한다.

    - import 와 __all__ keyword를 사용 __all__에는 사용할 module명을 담아준다. 

  • __main__ :  

  • 패키지 내 다른 폴더의 모듈을 부를 때 상대참조로 호출가능 : 상위폴더로 이동하는 . 을 사용해서 쉽게 가능

 

📌 오픈 소스 라이브러리 

- 두 개의 프로젝트 서로 충돌이 일어는 것을 방지하기 위해 가상환경 설정 

  • virtualenv + pip : 가장 대표적인 가상환경 관리 도구 
    - python이 c로 이루어져있어서, 컴파일을 해와야하는 경우가 있는데, pip라는 저장공간에는 컴파일된 파일이 안들어가져있는 경우가 있어서 conda가 윈도우에서 장점이 있음. 

  • conda : 상용 가상환경도구
    가상환경을 새로 만들때마다 항상 구글링해서 코드를 찾았었는데 이참에 정리해놓자. 
    - 가상환경 새로 만들기 : conda create -n 가상환경 이름  python = 파이썬 버전
    - 가상환경 실행하기 : conda activate 가상환경 이름 
    - 가상환경 탈출하기 : conda deactivate 가상환경 이름 
    - 가상환경 내 패키지 설치하기 : conda install 패키지 명 

 

피어세션 
  • 피어세션 

    - 다형성과 부모클래스 재정의가 왜 다른건지? 에 대해 질문하였다. 피어세션 + 구글링으로 해결했다.

      결론은 내가 잘못 이해한 것 부모클래스를 재정의하는 액션 자체가 파이썬의 다형성의 한 예이다. 

    - decorator가 여러 개 있을 경우 

    위와 같은 예시일때 print라는 함수가 star에 들렸다가 나오고 , 그 후 다시 percent라는 들어갈 거라고 생각했는데, 그게 아니었다. 충분한 이해 후 학습정리를 하며 다시 정리해보았다. 

 

느낀 점 (코드 리뷰) 

운이 좋게 친구들과 같이 캠프에 붙어서, 서로 짠 코드를 리뷰해줬다.

사소하다고 생각했던 것들도 , 별 거 아닌 거 같아보였던 것들도 많이 배웠다.

 

1.  어제 배운 map, list comprehension,generator등의 개념을 활용해서 문제를 풀고 싶었는데, 결국 잘 쓰지 못했다. 
  난 이렇게 짠 코드를 

 for k , v in list(get_morse_code_dict().items()): 
        if v == morse_character : return k

내 친구는 generator comprehension을 사용해 이렇게 짰다.....

result = next(key for k,v in morse_code_dict.items() if v == morse_character)

generator comprehension에 대해서 list comprehension도 이해했으니까, 이것도 이해했지. 쉽지.하고 넘어갔는데

막상 사용을 못하는 걸 보고 많은 생각이 들었다. 

자만하지않고 정말 내가 사용할 수 있는만큼 이해하고 익혔는지 꼭 확인해보자.

그리고 다른 사람들의 코드를 보면서 파이써닉한 코드에 대해서 많이 연습해봐야겠다. 

 

2. ['-' , '.' , ' '] -> list(".- ") 일일히 작성하는 대신 문자열형태로 list에 넣어주면 된다. 

 

3. list(string.digits) -> ['0', '1' ,,,,] 0~9까지에 해당하는 숫자가 하나씩 string으로 리스트에 담긴다.

 

4. len(set(user_input) - set([""," ",".","!",",","?"])) ==0 : set으로 바꾸어버리면 중복없이 비교가 가능해진다. 

   user_input이 공백 ,  . ! ? 로만 이루어져있다면 0이 되어 어떤 결과값을 반환하게 된다. 

 

 

오늘은 class에 대해서 배웠다. 
이전 프로젝트를 할 때는 self가 언제 쓰여야되는지도 잘 모르면서 남들이 쓰니까 쓰고, 오버로딩과 오버라이딩의 차이도 정확히 모르고 상속도 잘 몰랐다. 
오늘 1시간짜리 강의를 5시간 내내 곱씹으며 들었더니 이전에는 불확실했던 개념들이 머릿속에 자리잡았다. 
오늘이 4일동안 가장 알찼던 강의인 거 같다.