2019.03.21 TIL

(TIL은 스스로 이해한 것을 바탕으로 정리한 것으로 오류가 있을 수 있습니다)

# 질문에 답하기.

  1. 아래의 코드를 따라가면서 #에 따라 답해보기

코드를 통해 보는 class

>>>
>>>    def sum(a,b):            #1
>>>        return(a+b)
>>>
>>>    class Account:
>>>        
>>>        interest_rate = 0.08        #2         
>>>        num_of_account = 0                    
>>>        
>>>
>>>        @staticmethod                #3
>>>        def func(a,b):
>>>            return(a+b)
>>>
>>>        @classmethod                    #4
>>>        def string_constructor(cls, string):
>>>            data = string.split('_')
>>>            clnt_name = data[0]
>>>            balance = int(data[1])
>>>            return cls(clnt_name, balance)
>>>
>>>        @classmethod                        #5
>>>        def get_num_of_account(cls):
>>>            """
>>>            Account.get_num_of_account() ---> interger
>>>            return : 개설된 계좌 수
>>>            """
>>>            return cls.num_of_account
>>>
>>>        def __init__(self, clnt_name, balance):      #6
>>>            self.clnt_name = clnt_name              #7
>>>            self.balance = balance
>>>            Account.num_of_account += 1             
>>>
>>>        def deposit(self, money):                 #8
>>>            """
>>>            a.deposit(money) --> boolean
>>>            만약에 money > 0 입금성공
>>>            아니면 에러 메시지 출력 후 실패!
>>>            """
>>>            if money < 0:
>>>                print("입금은 0원 초과부터 가능합니다.")
>>>                return False
>>>            self.balance += money
>>>            return True
>>>
>>>        def withdraw(self, money):
>>>            """
>>>            a.withdraw(money) --> integer
>>>            return : 인출된 돈
>>>            만약 잔고가 모자라면 None
>>>            """
>>>            if money > self.balance:
>>>                print("인출할 돈이 잔액보다 많습니다.")
>>>                return None
>>>            self.balance -= money
>>>            return money
>>>
>>>        def transfer(self, other, money):           #9
>>>            self.balance -= money
>>>            other.deposit(money)
>>>
>>>        def __str__(self):                             #10
>>>            return f"{self.clnt_name} : {self.balance}"
>>>
>>>    if __name__ == "__main__"":
>>>        my_acnt = Account("greg", 5000)            #11
>>>        your_acnt = Account("john", 2000)
>>>    
>>>        print(Account.interest_rate)               #12
>>>        print(Account.get_num_of_account())        #13
>>>        print(my_acnt.interest_rate)               #14
>>>        print(my_acnt.get_num_of_account())        #15
>>>        print(Account.func(4,5))                   #16
>>>        print(type(Account.interest_rate))         #17
>>>        print(type(Account.get_num_of_account()))  #18
>>>        print(type(Account.get_num_of_account))    #19
>>>        print(type(my_acnt.deposit))               #20
>>>        print(type(Account.deposit))               #21
>>>        print(type(Account.func))                  #22
>>>
>>>        s = 'james_6000'                           #23
>>>        his_acnt = Account.string_constructor(s)
>>>        print(his_acnt)
>>>
>>>        my_acnt.deposit(7000)                      #24
>>>        print(my_acnt)
>>>
>>>        result1 = my_acnt.withdraw(3000)           #25
>>>        result2 = your_acnt.withdraw(3000)       
>>>        print(result1)
>>>        print(result2)
>>>
>>>        print(my_acnt)
>>>        print(your_acnt)
>>>
>>>
0.08
2
0.08
2
9
<class 'float'>
<class 'int'>
<class 'method'>
<class 'method'>
<class 'function'>
james : 6000
greg : 12000
인출할 돈이 잔액보다 많습니다.
3000
None
greg : 9000
john : 2000
>>>
>>>
>>>

#에 따라 정리하기

  1. #1 전역 함수

    • 어느 클래스에서도 속하지 않는다.
    • oop에서 제일 싫어하는 행동이다.
    • 최대한 관련있는 class에게 넣어주자.
  2. #2 클래스 멤버(class member)

    • 모든 객체가 공유한다.
    • 전역 변수(global variable)를 대체한다.
    • 클래스 바로 밑에 아무것도 적지 않고 적어주면 된다.
    • 모든 객체가 가지고 있을 필요는 없으나 모든 객체가 함께 공유해야 하는 것
      • ex: 이자율
    • 객체를 통해서도 클래스 멤버에 접근 가능하다.
    • 접근 방법 :
      • my_acnt.interest_rate : 객체를 통한 접근 방법
      • Account.interest_rate : 클래스를 통한 접근 방법
  3. #3 static method

    • 매서드처럼 보이지만 대놓고 함수이다. 전역함수
    • 전역 함수를 대체할 때 클래스메서드와 하나의 해결책이 될 수 있다.
    • 인자로 클래스나 객체를 받지 않는다. 함수의 정의만 클래스 A의 네임 스페이스에 있을 뿐 일반 함수와 같다.
  4. #4 클래스메서드(class method == @데코레이터)

    • 객체가 하나도 없는 상태에서도 호출이 가능하다.
    • cls가 self 대신 메모리 주소를 받는다.
    • 전역함수를 대체한다.
      • 전역함수를 대체할 때는 static method를 쓸 수 있다.
        • 아무것도 받지 않는다. 순수하게 함수다.
        • 사용방법(Account.func(5,4))
    • 관용적으로 cls를 받는다.
    • cls는 class를 받으므로 _init_ 와 같다.
  1. #5 클래스메서드(class method) + 대체 생성자

    • 객체가 하나도 없는 상태에서도 호출이 가능하다.
    • 전역 함수를 대체할 수 있다.
    • 원래 파이썬은 하나의 생성자만을 허용하지만 클래스메서드를 통해 해결 가능하다.
      • 대체 생성자(alternative constructor)가 될 수 있다.
      • 파이썬에서는 생성자를 하나만 가능하기때문에 여러가지 형태의 입력에 대응 할수 없다.
      • ex) 단체가입 "이름"_"초기값" ---> s = 'james_6000'
    • 접근방법
      • Account.get_num_of_account()
      • 객체를 통해서도 클래스 메서드에 접근이 가능하다.
  2. #6 생성자(constructor)

    • 파이썬에서는 오직 하나 존재한다.
    • _init_ : 객체(object)가 생성될 때 반듯이!! 한번 호출된다.
    • __ __ 은 파이썬에서 미리 쓰기로 해놓은 것이다.
  3. #7 인스턴스 멤버(instance member)

    • 인스턴스 멤버 == 상태정보 관리이다.
    • 각자의 객체가 가지는 값(값 = 상태정보)
    • init에 보면 변수가 나와 있다.
    • 생성된 object들은 같은 속성을 가지고 있으나 속성 값이 다르다.
    • Account.num_of_account ---> class멤버에 접근하는 방법
  4. #8 인스턴스 매서드(instance method)

    • object의 메서드로 봐도 된다.
    • class 입장에서는 function/ object입장에서는 메서드이다.
    • 객체가 할 수 있는 행동, 기능을 이야기 한다.
    • 인스턴스 메서드는 self를 꼭 써줘야 한다.
    • self는 해당 객체가 저장된 첫번째 주소를 가르킨다.
      • 즉 self는 생성된 객체 자기 자신을 가르킨다.
      • my_account.deposit(5000)이 실행될 떄 my_account를 self로 던져준다. self는 객체의 메모리 주소를 참조한다. 즉 자체참조이다.
  5. #9 message passing(메시지 패싱)

    • 나의 상태 정보와 함께 다른 객체의 상태 정보를 함께 변경해야 할 때
    • 다른 객체의 상태 정보(인스턴스 멤버)를 변경할 때는 반드시 상대 객체가 가진 메서드를 이용해야 한다.
    • 절대 하면 안되는 예)
      • self.balance -= money
      • other.balance += money
      • 다른 객체가 가진 메서드를 이용하지 않았다.
  6. #10 _str_(self)

    • my_acnt의 반환 포맷을 적어준다.
    • 이게 없으면 _main_.Account object at 0x10f512438처럼 반환
  7. #11 객체 생성

    • my_acnt(변수) = Account("greg", 5000) ==> class(매개변수)
    • 변수 = class(매개변수) 형태로 생성
    • 해당 객체는 해당 class의 인스턴스이다.
  8. #12 클래스 멤버 접근 방법

  9. #13 클래스 메서드 접근 방법

    • 클래스 메서드는 클래스 입장에서는 메서드지만(클래스의 행동 특정)
    • class입장에서 deposit과 같은 것은 함수이다.(클래스에 영상 x)
    • 하지만 객체 입장에서 deposit은 메서드이다.
    • print(type(my_acnt.deposit)) --- class 'method'
    • print(type(Account.deposit)) --- class 'function'
  10. #14 객체를 활용한 클래스 멤버 접근 방법

  11. #15 객체를 활용한 클래스 메서드 접근 방법

  12. #16 static method 접근 방법

  13. #17 클래스 멤버의 type

  14. #18 클래스 메서드 실행 했을 때의 type ---> int

  15. #19 클래스 메서드의 type

  16. #20 객체에서 메서드의 type

  17. #21 클래스에서 인스턴스 메서드의 type

  18. #22 static method의 타입

  19. #23 class method를 통해 대체 생성자를 만든 이유

    • 상황에 따라 전혀 예상하지 못한 형태의 값이 주어질 수도 있음
  20. #24 instance method 호출 방법(객체.method())

    • 객체에 self 인자는 넣어 줄 필요 없다.
    • class를 만들 때는 꼭 self를 써줘야 하지만 method를 호출하거나 객체를 생성할 때는 쓸 필요가 없다.
  21. #25 함수와 method의 차이점을 보여주는 예

    • 원래 withdraw라는 함수라면 입력이 3000원으로 같으므로 똑같이 나와야 한다.
    • 하지만 메소드는 내부에 가지고 있는 상태 정보가 다르기 때문에는 다르게 나온다.
    • 클로저에는 매개변수는 free variable이라고 부르는데
    • OOP에서는 인스턴스 멤버라고 부르는 것을 통해 상태정보를 관리한다.
    • 메서드 : 입력 + 인스턴스 멤버(상태정보, 데이터)에 의해 결과 값이 결정
      • 인스턴스 멤버(상태정보)를 바꾸는 역할을 하기도 한다.
    • 함수 : 입력에 의해 출력이 결정
      • 메서드처럼 사용하기 위해서는 call by reference 혹은 다른 변수에 할당
    • 메시지 패싱
      • 객체 간의 상호 작용 --> 객체가 가지고 있는 멤버 값(데이터, 상태 정보)의 변화
      • 내가 가진 건 1000이 적어지면 너에게 1000이 늘어난다.
      • interaction이 반드시 메서드에 의해 이루어져야 한다.
      • .은 속성을 가지고 온다는 뜻이다. 속성에는 인스턴스 멤버와 인스턴스 메서드가 있다.

절차지향 vs 객체지향

###procedural vs object-oriented

2개다 추상화를 하는 기법이다.

procedural : 함수를 이용해 추상화를 했다. 추상화의 도구로 함수를 이용 (절차지향)

  1. 이 프로그램은 어떤 일을 하는가?
  2. 기계와 비슷한 사고 방식
  3. 함수를 이용

object-oriented : 객체를 통해 추상화를 했다.

  1. 현실 세계(에 존재하는 객체(object)를 어떻게 모델링(modeling)할 것인가?
    1. 객체(object) : 사람, 동물, 자동차 등을 어떻게 프로그램으로 모델링 할 것인가?
  2. 사람과 비슷한 사고 방식
  3. 객체를 이용

객체 vs 인스턴스

메모리 상으로는 완벽히 같다.
하나를 볼 때는 객체로 보고
이 객체는 특정 클래스의 인스턴스라고 부른다.
why? 어떤 클래스에서 나왔는지 알고 싶을 때 - 클래스에 집중해서 말할 때

object란?

  1. 관련 있는 변수와 함수를 한곳에 모아(bundling) 놓은 곳(메모리의 영역)
    1. 변수 - 상태정보
    2. "관련 있는" 변수, 함수" "한곳에 모아" 가 중요하다.
  2. class를 통해 생성되므로 class의 속성을 가지고 있어야 한다.
  3. 속성
    1. instance member(변수)
      1. 각자의 객체가 가지는 값(값 = 상태정보)
      2. init에 보면 변수가 나와 있음
      3. 같은 속성을 가지고 있으나 속성 값이 다르다.
    2. instance method(함수)
      1. 객체가 할 수 있는 행동, 기능

—> 절차지향에서는 전체 프로그램을 다 봐야하지만.
—> 객체지향에서는 단위로 프로그램을 유추해볼 수 있다.

중요한 이해

클래스의 메모리와 객체의 메모리는 모두 다르다.
객체마다 모두 다른 메모리 공간을 가지고 있다.
각 객체가 인스턴스 멤버를 따로 가지고 있고 메서드 역시 각자 가지고 있다고 생각하는게 좋다.
my_acnt
balance / clnt_name / deposit / withdraw / transfer
your_acnt
balance / clnt_name / deposit / withdraw / transfer

미션 : 클래스를 쓰지 않고 OOP 구현

클래스를 쓰지 않고.
(클래스 멤버, 메소드 제외).
어떤 객체를 만들 수 있어야 하고
그 객체에 대해 함수를 호출하여 돈을 입금
init / deposit / withdraw / transfer.
하나의 unit을 딕셔너리로 고정한다.
class라는 유닛 대신에 딕셔너리로 묶어라.

def person_init(name, money):
    obj = {'name': name, 'money': money}
    obj['give_money'] = Person[1]
    obj['get_money'] = Person[2]
    obj['show'] = Person[3]
    print(obj)
    # --->    obj = {'name' : name, 'money' : money , 'give_money' : Person[1], 'get_money' : Person[2], 'show' : Person[3]}
    return obj


def give_money(self, other, money):  # 3
    self['money'] -= money
    other['get_money'](other, money)  # 4


def get_money(self, money):
    self['money'] += money


def show(self):
    print('{} : {}'.format(self['name'], self['money']))


Person = person_init, give_money, get_money, show

if __name__ == "__main__":
    # 객체 생성
    g = Person[0]('greg', 5000)  # 5
    j = Person[0]('john', 2000)

    g['show'](g)
    j['show'](j)
    print('')

    # 메시지 패싱
    g['give_money'](g, j, 2000)  # 6

    g['show'](g)
    j['show'](j)

2019.03.20 TIL

(TIL은 스스로 이해한 것을 바탕으로 정리한 것으로 오류가 있을 수 있습니다)

# 질문에 답하기.

  1. if name == "main" 의 의미에 대해 설명

openpyxl

openpyxl은 python에서 엑셀을 불러오기 위해 설치해야 하는 모듈이다.

openpyxl 기본 용어 정리

  1. Workbook 관련

    • openpyxl.Workbook() ---> 새 엑셀 임시 파일 생성
    • openpyxl.load_workbook("파일명") ---> 기존 엑셀 파일 로드
    • Workbook.save("파일명") ---> 엑셀 파일 저장
  2. Worksheet 타입 관련

    • wb.active ---> 현재 활성화된 시트 반환
    • wb.create_sheet("시트명") ---> 새 시트 생성
    • wb['시트명'] ---> 이름으로 시트 접근
  3. cell 타입 관련 접근

    • ws['b1'] ---> <Cell 'Sheet1'.B1>
    • ws['b1'].value ---> b1의 실제 값 반환
    • ws.cell(row=2, column=1) ---> <Cell 'Sheet1'.A2>
    • ws.cell(row=2, column=1).value ---> 2번째 행의 첫번째 열 값 반환(A2)
    • ws.append(['이름', '나이', '직업') ---> 시트의 마지막 행에 새행 추가
    • g = ws.rows ---> 그 줄에 있는 값을 g에 넣음 (lazy로 next를 통해 값 실행)

OPENPYXL 모듈로 데이터 읽어 들이기

해당 엑셀 데이터의 모습(exam.xlsx)

openpyxl데이터

원하는 데이터 출력 모습

student_data = [ {'name' = greg, 'math' = 95, 'literature' = 65, 'science' = 75} ,
{'name' = greg, 'math' = 25, 'literature' = 30, 'science' = 55}, .... ]

실행코드

>>> import openpyxl
>>> wb = openpyxl.load_workbook("exam.xlsx")
>>> ws = wb.active
>>> wr = ws.rows
>>> g = next(wr)    #제일 위에 wr 한줄을 가져옴
>>> li = []
>>> for i in g:
>>>     li.append(i.value)
>>> # li = ['name', 'math', 'literature', 'science']
>>> keys = []
>>>     for j in wr:
>>>        dic = { k : v.value for k, v in zip(li, j) }
>>>        keys.append(dic)
>>>
>>> print(keys)

OPENPYXL을 통해 평균 및 분산, 표준편차 구하기

해당 데이터 모습(class_1)

평균, 분산, 표준편차

원하는 것

  1. 평균
  2. 분산
  3. 표준편차
  4. 인터프리터로 실행하여 뽑아내기

실행코드

>>> import openpyxl
>>> import math
>>>
>>> def get_data_from_excel(filename):
>>> """
>>> get_data_from_excel(filename) --> {'name1': 'scores1', 'name2':'scores2' ....}
>>> """
>>> dic = {}
>>> wb = openpyxl.load_workbook(filename)
>>> ws = wb.active
>>> wr = ws.rows
>>>
>>> for name, score in wr:
>>>     dic[name.value] = score.value
>>>
>>> return dic
>>>
>>> #scores = list(dic.values())
>>> #scores = [95, 25, 50, 15, 100, 10, 25, 80, 95, 20]
>>>
>>> def average(scores):
>>>     s = 0
>>>     for score in scores:
>>>         s += score
>>>     aveg = round(s/len(scores), 1)
>>>     return aveg
>>>
>>> def variance(scores, aveg):
>>>        s = 0
>>>     for score in scores:
>>>         s += (score - aveg)**2
>>>         var = round(s/len(scores), 1)
>>>        return var
>>>
>>> def std_dev(var):
>>>    return round(math.sqrt(var),1)
>>>
>>> if __name__ == "__main__":
>>>        raw_data = get_data_from_excel("class_1.xlsx")
>>>    scores = list(raw_data.values())
>>>         avrg = average(scores)
>>>    variance = variance(scores, avrg)
>>>    standard_deviation = std_dev(variance)
>>>    print("평균 : {}, 분산:{}, 표준편차: {}".format(avrg, variance, standard_deviation))

초보몽키님 실행코드 참고하기(by reduce, map)

# 평균
def average(scores):
    return reduce(lambda a, b: a + b, scores)/len(scores)

# 분산
def variance(scores, avrg):
    return reduce(lambda a, b: a + b, map(lambda s:(s-avrg)**2, scores))/len(scores)

import openpyxl에서 자료 불러오기

  1. 불러 올 excel파일을 작성 중인 파이썬 자료의 폴더안에 함께 넣어놓는다.
  2. 사이드패키지
    • excel파일이 python3의 site-package안에 들어가면 해당 폴더에 없어도 실행된다.
  3. 경로지정해주기
    • export PYTHONPATH = "해당 자료의 경로" 를 통해 해당경로를 지정해준다.

새롭게 알게 된 사실

파이썬에서의 for문

  1. for i in range(1,11) ---> 1부터 10까지의 숫자가 하나하나 i에 들어간다.
  2. for i in li ---> li의 0번째 인덱스부터 하나하나 들어간다.
    • javascript에서는 인덱스 0부터 포문에 들어갔던거 같은데 그 생각을 버려야 겠다.
  3. 파이썬에서의 구구단 설계 (단 4줄로 설계가능하다)
>>>for i in range(2,10):
>>>        for j in range(1, 10):
>>>            print(i * j, end = " ") #각 항마다 실행
>>>        print(" ") 
>>>

2019.03.20 TIL

(TIL은 스스로 이해한 것을 바탕으로 정리한 것으로 오류가 있을 수 있습니다)

# 질문에 답하기.

  1. 객체란? (클래스, 객체, 인스턴스, 프로퍼티, 메소드에 대해)

class - 객체를 만들어 내기 위한 틀

class는 객체를 만들어내기 위한 약속이다.
class는 객체의 속성(property)과 동작(method)을 정의한다.

object - class라는 틀을 가지고 만들어낸 실물(ex. 자동차)

파이썬에서의 객체

  • 컴퓨터가 보는 객체 : 클래스를 이용해 만들어진 변수와 함수를 가진 메모리 공간
  • 우리가 보는 객체 : 현실 세계의 사물을 모델링 한 것

object란?

  1. 관련 있는 변수와 함수를 한곳에 모아(bundling) 놓은 곳(메모리의 영역)
    1. 변수 - 상태정보
    2. "관련 있는" 변수, 함수" "한곳에 모아" 가 중요하다.
  2. class를 통해 생성되므로 class의 속성을 가지고 있어야 한다.
  3. 속성
    1. instance member(변수)
      1. 각자의 객체가 가지는 값(값 = 상태정보)
      2. __init__에 보면 변수가 나와 있음
      3. 같은 속성을 가지고 있으나 속성 값이 다르다.
    2. instance method(함수)
      1. 객체가 할 수 있는 행동, 기능

—> 절차지향에서는 전체 프로그램을 다 봐야하지만.
—> 객체 단위로 프로그램을 유추해볼 수 있다.

컴퓨터 프로그램은 정보가 어떤 경로(네트워크, 디스크, 센서, 사람의 손 등)로 입력되든지, 결국은 그 정보를 메모리 위에 올린 뒤에 처리한다. 음식을 어떻게 준비하든지 결국은 식탁 위에 올려서 먹는 것처럼 말이다. 메모리는 그냥 비트(숫자)를 나열한 단순하고 넓은 판이기 때문에, 메모리 위에 올려 둔 정보를 의미 있는 덩어리로 묶어두기 위한 단위가 필요하다. 파이썬에서는 객체(object)라는 단위로 메모리 위의 정보를 관리한다.
객체에는 값(value)·유형(type)·정체성(identity)이라는 세 특성이 있다. 값은 메모리에 기록된 내용이다. 가변 객체는 값이 바뀔 수 있지만 불변 객체는 값이 바뀌지 않는다. 유형은 데이터의 종류로, 유형에 따라 그 값을 어떻게 읽고 다루어야 할지가 결정된다. 정체성은 각각의 객체를 식별하기 위한 고유번호로, 객체가 메모리 속에서 위치한 주소 값이기도 하다. 값과 유형이 동일한 데이터가 데이터가 메모리 공간에 여러 개 존재할 수 있지만, 이들은 서로 별개의 객체이며 정체성이 서로 다르다.

ex)

a = 10
b = 10
a == b (객체의 값 비교)
type (a) == type(b) (두 객체의 유형 비교)
id(a) == id(b) (두 객체의 정체성 비교)

추가 개념정리

a = 10
b = a
a += 10
print(b) 

위를 계산하게 되면 a라는 이름은 10이라는 값 객체를 가르키게 되고 b=a라고 할당해주었을 때 b는 a를 가르키는게 아니라(이중 할당 x) a의 값 객체를 가르킨다.
따라서 a += 10을 해주었을 때 integer은 immutable이므로 a는 새로운 값객체 20을 만들어 가르킨다. 따라서 10을 가르키고 있는 b는 변하지 않는다. 하지만 call by reference에서는 b는 a의 주소를 참조하므로 b = 20이 된다.

추가 개념정리

a = [1,2,3]
b = a
a += [4,5,6]
print(b)

일 때 b의 값
a 는 리스트를 가르키고 있고 리스트는 대표적인 mutable이므로 a가 가르키는 값 객체가 [1,2,3,4,5,6]으로 바뀌고 그에 따라 b역시 바뀜

def func(a)
     a[2] = 0
a = [1,2,3]
func(a)
print(a).  #[1,0,3]     

개념 정리

  • 객체: 메모리에 존재하는 개별 데이터를 가리키는 개념
  • 객체의 값: 객체를 통해 구할 수 있는 정보 그 자체. 가변 객체는 값이 바뀔 수 있다.
  • 객체의 유형: 객체가 어떤 범주에 속하고 어떻게 다뤄야 하는지를 구별하기 위한 분류. 파이썬에서는 클래스로 유형을 나타내며, type() 함수로 구할 수 있다.
  • 객체의 정체성: 객체를 다른 객체와 구별하기 위한 고유번호이자, 메모리 상의 위치. id() 함수로 구할 수 있다.
  • 변수: 객체에 붙인 이름. 한 객체에 여러 개의 이름을 붙일 수도 있다.

https://python.bakyeono.net/chapter-8-2.html 참고

property - object의 속성 (ex. 블랙박스, 오디오, 쿨시트 등등)

obj = {} 라는 빈 객체를 만들고
여기서 obj.a = 10 을 해주면 (in javascript)
a라는 property에 10이라는 값이 들어가게 된다. 따라서 obj이라는 객체는 10이라는 속성을 가지고 있고
그 값에 접근가능한 이름이 a이고 이 a는 활용 가능한 10이라는 값을 지니게 된다.

method - object의 동작을 나타낸다. (ex, 간다, 멈춘다, 가속, 후진)

메소드는 객체가 가지고 있는 동작이다. 함수와 메소드는 기본적으로 일련의 동작을 실행한다는 점에서 동일하나,
메소드는 특정 객체가 가지고 있는 동작을 가지고 있고, 메소드를 실행하기 위해서는 객체를 통해야만 실행가능하다.
그에 비해 함수는 함수 자체가 그 동작을 정의한 객체이다.

instance - 객체가 어떤 class에 속할 때 그 객체를 그 class의 instance라고 부른다.

파이썬에서 객체라고 하면 메모리에 올려져 있는 단위라고 볼 수 있다.

year = 2019 라고 하면
2019라는 값을 가지고 있는 객체가 메모리에 올라가고
year은 변수로서 2019라는 객체를 가리키고 있는 것이다. (즉 객체에 이름을 붙여 준 것이다.).
이 year이라는 이름을 가진 객체는 2019라는 프로퍼티를 가지고 있다.
그리고 2019는 int라는 클래스의 인스턴스이다.

'Python' 카테고리의 다른 글

파이썬 14. 코드를 통해 보는 class  (0) 2019.12.28
파이썬 13. openpyxl  (0) 2019.12.28
파이썬 11. decorator  (0) 2019.12.28
파이썬 10. filter, map, reduce  (0) 2019.12.28
파이썬 09. lambda  (0) 2019.12.28

2019.03.15 TIL

(TIL은 스스로 이해한 것을 바탕으로 정리한 것으로 오류가 있을 수 있습니다)

# 질문에 답하기

  1. 데코레이터에서 대해 이야기 해보기

데코레이터

데코레이터는 쉽게 기능을 추가할 수 있다.
누가 만들어 놓은 함수를 내가 만들고 있는 함수에 갖다 붙이면
그 기능이 추가되어 버린다.

ex)

from fancy import decofunc
@decofunc
def func(a,b): return a + b
  • 이렇게 하면 기능이 그냥 붙어 버림 decofunc의 기능이 실행되고 이후에 def func의 기능이 실행

데코레이터 기본 형태

def outer(org_func):       #org_func는 내가 만들고 있는 함수
    def inner(*args, **kwargs):  # 여기서는 가변인자이고 *과 **을 통해 패킹시킴
        print("added functionalty")
        return org_func(*args, **kwargs)  #실행에서는 *, ** 은 튜플과 딕셔너리로 들어오는 것을 unpacking한다는 것이다.
    return inner    

def func(a,b):
        return a+b

func=outer(func) -----> outer(func)는 return inner가 되고
org_func인자가 func로 됨에 따라서 inner내에서 func가 한번 더 리턴된다.

따라서 여기서 부터 func.name == inner가 된다.

func(10,2)을 실행하면
added functionalty가 되고
리턴값으로 12를 받는다. (inner함수 내부에서 실행)

이것을 나타내기 위해서는 print(func(10,2))가 되어야 한다.
매번 func= outer(func)를 적어줄 수는 없으므로

@outer
def func(a,b):
    return a+b    # 이것 같이 적어주어도 된다.

overloading , overriding

def f(a,b):
    return a+b
def f(a,b):
    return a*b
f(2,7) ===> 14

python에서는 덮어씌어지지만 java같은 것은 인자만 다르면 인정해준다. 이를 overloading이라고 한다. 하지만 파이썬에서는 overloading을 인정하지 않는다.

decorator

여기서 out 9가 나온것은 실제 출력값이 아니라 함수 내부적으로의 return 값이다.

미션 함수 실행 시간 계산하기

import time
def henchmarker(org_func):
    def inner(*args, **kwargs):
        start = time.time()
            result = org_func(*args, **kwargs)
            elapsed = time.time() - start
            print(f"elapsed time : {elapsed: .2f}")  #{elapsed: .2f}는 무엇일까?
            return result
        return inner



@henchmarker
def something(a,b):
    time.sleep(5) ----> 5초 동안 있다가 실행
    return a+b
something(1,2)

미션 callcounter

어떤 함수를 호출한 횟수를 보여준다.

g_call_num = 0
def callcounter(org_func):
    def inner(*args, **kwargs):
        result = org_func(*args, **kwargs)
        global g_call_num
        g_call_num += 1
        print(f"{g_call_num}번 호출되었습니다.")
        return result
        return inner


@callcounter
def func(a,b):
    return (a+b)


for _ in range(10):
    print(func(1,3))

주의사항 만약에 함수를 2개 연속으로 사용할 때

decorator2

from functools import wraps와 @wraps를 꼭 적어주어야지만

함수가 중복적으로 계산되지 않는다.

하나의 공식으로 알아두자.!!

'Python' 카테고리의 다른 글

파이썬 13. openpyxl  (0) 2019.12.28
파이썬 12. 객체와 클래스  (0) 2019.12.28
파이썬 10. filter, map, reduce  (0) 2019.12.28
파이썬 09. lambda  (0) 2019.12.28
파이썬 08. 클로저  (0) 2019.12.28

2019.03.14 TIL

(TIL은 스스로 이해한 것을 바탕으로 정리한 것으로 오류가 있을 수 있습니다)

# 질문에 답하기

  1. filter, map, reduce에 대해 이야기 해보기

filter

기본 문법

filter(f, iterable) 
  • filter는 여러개의 값들이 있을 때 특정한 기준에 대해 블리언을 적용하여 뽑아내는 것이다.

ex)

li = [-3, 5, 1, 2, -5, -4, 14] # 이 리스트 중에서 음수를 제거하고 싶다라고 하면
f = filter(lambda a: a>0, li) # filter을 활용하여 간단하게 끝낼 수 있다. but 하나씩 꺼낼 수 있다
  • 여기서 lazy evaluation이 나와야 한다.

lazy evaluation

lazy evaluation은 내가 원할 때 마다 결과값을 가지고 오는 것이다.
즉 함수의 실행시기를 내가 결정하게 된다.
이것이 filter, map, reduce, generator의 큰 특징이다.

filter, map, reduce는 모두 generator의 일종이다.

다시 돌아와서 위에

f = filter(lambda a: a>0, li) # next(f)라는 것으로 f에 해당하는 요소를 하나씩 뽑아낸다.

# 이것을 다 뽑아 보기 위해서는

for e in f:
    print(e) 

# 이것을 리스트로 만들기 위해서는

li = []

for e in f:
    li.append(e) 

# 혹은 한번에 적어줄 수도 있다.

result = []

for elem in li:
    if elem>0:
        result.append(elem)

# 같이 한번에 적어줄 수도 있게 되었다.
# 또 한줄로 적어줄 수도 있다. 

# 홀수 리스트로 한번에 뽑아내기

li2 = list(filter(lambda x: x%2, li)) # 한번에 리스트로 뽑아낸다.

# 짝수 리스트 한번에 뽑아내기

li2 = list(filter(lambda x:x%2==0, li))
  • filter는 특정한 f(x)에 대해 bloolean값을 필터하여 뽑아낸다.
  • 내가 다른 작업을 하다가 next(f)를 통해 하나를 뽑아내고 또 이후에 next(f)를 함으로서 그 다음 값들을 뽑아 올 수 있다. 메모리를 따로 쓰지 않는다는 장점이 있다.
  • 내가 함수의 실행시간을 결정할 수 있다는 것과, 메모리를 따로 저장하지 않고 바로 가지고 올 수 있다.
  • 내가 원하는 시점에 그때 작용(lazy evaluation)
  • 컴퓨터 프로그래밍에서 느긋한 계산법(Lazy evaluation)은 계산의 결과값이 필요할 때까지 계산을 늦추는 기법이다. 두 가지 관련된 항목들이 있는데 지연 계산법과 최소 계산법이다.
  • 느긋하게 계산하면 필요없는 계산을 하지 않으므로 실행을 더 빠르게 할 수 있고, 복합 수식을 계산할 때 오류 상태를 피할 수 있고, 무한 자료 구조를 쓸 수 있고, 미리 정의된 것을 이용하지 않고 보통 함수로 제어 구조를 정의할 수 있다.
  • 느긋한 계산법을 사용하는 언어들은 "이름으로 호출"하거나 "필요할 때 호출"하는 계산 전략을 사용하는 것으로 나눌 수 있다. 하스켈과 같은 대부분의 실제 느긋한 언어들은 성능상의 이유로 "필요할 때 호출" 전략을 사용한다. 그러나 느긋한 계산법을 이론적으로 표현한 것들은 간편하게 "이름으로 호출"하기도 한다.

map

map 기본 문법

map(f, iterable) #mapobject를 반환한다. map은 함수와 iterable을 받는다.
  • 특정한 f(x)를 적용하여 새롭게 값을 뽑아내는 것. 이것을 선형대수에서는 사상이라고 한다.

ex)

li = [2, 3, 4, 5] # 여기에 있는 값을 제곱을 하여 하나씩 뽑아내보자.
m = map(lambda x: x**2, li)
next(m)
  • 맵에서 나온것 맵객체 필터에서 나온건 필터객체 이건 generator 객체에 포함되고 이건 또 iterator에 포함된다. 따라서 generator로 순회할 수 있다. iterator는 포문을 쓸 수 있으므로 map과 filter 역시 포문을 쓸 수 있다.

filter와 map을 함꼐 사용하여 풀어야 하는 문제

li = [ 2, 3, -5, 6, -2, 1, -10] # 양수를 골라내어 제곱한 값을 리스트로 만드시오

a = list(map(lambda x: x**2 , filter(lambda y: y>0, li)))

reduce

자료구조 (list,tuple)를 연산을 통해서 단 하나의 값으로 만드는 함수이다.
reduce는 내장함수가 아니므로 import를 해서 불러줘야한다.

from functools import reduce
help(reduce)     #함수는 기본적으로 이름, 인자 , 반환값을 꼭 적어줘야한다.
    reduce(function, sequence[, initial]) -> value
    """
    여기서 function 에는 람다가
    sequence에는 자료구조(iterable)이 들어갈 것이고 initial은 초기값이 들어갈 것이다.
    그리고 return 값으로는 하나의 value가 나와야 한다.
    """

# reduce는 기본적으로 2개의 인자를 받는다.

li = [2, 3, -5, 6, -2, 1, -10]

result = reduce(lambda x,y : x+y, li)
result = reduce(lambda x,y: x+y, li, 100) # 100이 초기 x에 들어가도록 초기값 설정이 가능하다.

list의 최대 요소 구하기

li = [3,6,8,-10,2,1, 100, 50, 46, -47]
from functools import reduce
result = reduce(rambda x,y : x if x>y else y, li) (삼차다항식이용)

문자수 세기

li =["a", "b", "a", "b", "b", "a", "c", "a"]가 주어졌을 때
dic ={'a':4, 'b':3, 'c':1} ---->크롤링해서 좋아요 혹은 싫어요 핸들링 할 때 사용

위와 같이 dictionary를 뽑아내자.

  • 첫번쨰 참고 : 파이썬의 함수는 식이므로 무조건 값을 반환(None도 반환)

  • 두번째 참고 : 논리 연산을 할 때 마지막으로 참조한 값을 반환한다.[1,2] or [] ---> [1,2]라는 값을 반환한다.

  • dic.get("a",0) + 1 ---> value로 활용하고

  • dic.update({"a":3, "b" : 4})

  • 초기값 딕셔너리

import reduce
li =["a", "b", "a", "b", "b", "a", "c", "a"]
result  = reduce(lambda dic,cha : dic.update({cha : dic.get(cha,0)+1}) or dic, li, {}) 

dic.update는 None를 반환하므로 false이다 or dic을 통해 dic이 다시 위로 올라가도록 한다.

2019.03.13 TIL

(TIL은 스스로 이해한 것을 바탕으로 정리한 것으로 오류가 있을 수 있습니다)

# 질문에 답하기

  1. 람다함수에 대해 이야기해보기

lambda

lambda : 익명함수

즉 이름이 없다.
함수를 재사용하지 않고 몇 번 정도만 쓸 때 사용한다.(바로 문장 내에서 함수를 바로 실행할 때)

파이썬에서 함수는 "식"이다.
그리고 식은 (반환)값이 있다. 따라서 파이썬에서 모든 함수는 반환값을 가지고 있다.

10 역시 단항식이다.
'i am your father' 역시 식이다.

def func(a,b):
    return a+b         # 함수는 리턴을 하니깐 식이다 
def func(a,b):
    a+b              # 이것 역시 식이다. None을 반환한다.

print(print(func(10,20)) # None, None 


func 
<function __main__.func(a, b)>

lambda a,b: a+b         #같은 펑션이 메모리에는 생겼으나, 변수에 따로 할당해놓지 않으면 접근할 수 없다.  
<function __main__.<lambda>(a, b)>

lambda 기본 문법

lambda 한칸 띄우고 바로 쓴다. lambda a, b: return a + b #람다는 무조건 값을 반환하기 때문에 return을 적어주지 않는다. ---> lambda a,b: a+b --->이게 맞는 것이다.

f2 = lambda a,b:a+b #return이 명시되지 않았지만 암묵적으로 무조건 리턴이 있다.

lambda 사용법

  • 정렬에 사용하기
li = [5, 2, 3, 1, 7, 10]  # 짝수는 앞에 홀수는 위로 배열하고 싶다면

li.sort(key =  ,reverse=True) # sort에는 key가 들어갈 수 있고 정렬에 쓰이는 키를 pred key라고 한다.

# 구현
def pred(x):
    return x % 2 -----> 0과 1을 반환하는데 오름차순으로 하면 0이 먼저 오므로 짝수가 먼저 반환
li.sort(key=pred)
print(li)
[2, 10, 5, 3, 1, 7]

# 홀수 먼저 오고 싶다고 하면

li.sort(key=pred, reverse=True)
li
[5, 3, 1, 7, 2, 10]

#이제 이것을 람다를 활용해서 한번에 적어보자.

li.sort(key=rambda x: x%2, reverse =True)
li
[5, 3, 1, 7, 2, 10]

이 람다를 바탕으로 3가지를 추가적으로 배운다.

  • filter
  • map
  • reduce

###2019.03.12 TIL

클로저

클로저(closure)은 닫혀있다는 의미다.
우리가 일반적으로 배운 함수는 output이 input에 의존하여 나와야 하나, 이제는 함수에 초기 값을 넣어주면
그게 상태정보가 되고 그 상태정보에 따라 내부에서 함수를 호출했을 때 새로운 input과 내부의 상태정보를 함께 연산하여 새로운 출력값을 나타낼 수 있게 된다.
즉 함수 내부에 상태 정보를 클로징하고 있다하여 클로저라고 부른다.
원래 함수는 실행 이후에 스택프레임이 사라지면서 사라져야 하지만 클로저는 주도권만 주고 그대로 살아 있다.

전제조건

파이썬은 first class function을 지원한다.

first class function

  1. 매개변수로 함수를 줄 수 있다.
  2. return 값으로 함수를 줄 수 있고 함수 내부에 함수 정의 가능
  3. 함수를 변수에 할당할 수 있다.

따라서 함수 내부에 새로운 함수를 정의할 수 있다.
이것을 기본 전제조건을 가지고 클로저를 만들기 위한 조건이 있다.

클로저 생성 조건

그럼 이제 파이썬에서 클로저를 만들기 위한 조건을 정리해보자.

  • 중첩 함수(Nested Function)를 갖는다.
  • 중첩 함수는 자신을 감싸고 있는 함수 영역(부모함수)의 변수를 참조하고 있다.
    • 만약 부모 함수의 변수를 변경하고 싶으면 nonlocal로 참조한다고 설정해주어야 한다.
  • 부모함수는 중첩 함수(자식 함수)를 반환한다.
  • 새로운 변수에 부모함수를 할당해주어야 한다.
    • 새로운 변수에 새로운 값을 넣게 되면 내부 상태정보와 함께 새로운 출력값을 나타낸다.

클로저 예시)

def account(clnt_name, balance):  
    def change_money(money):   #매개변수도 지역변수 이다.
        nonlocal balance            #부모 함수의 변수를 변경하기 위해 nonlocal을 사용한다.
        balance += money      #balance 는 account의 지역변수이다. global도 아니다 
        return (clnt_name, balance)
    return change_money
my_acnt=account("greg", 5000)    #원래라면 함수 호출 이후에 스택프레임이 사라져야 하지만
my_acnt(1000)   # ('greg', 6000)   #내부의 상태정보를 가지고 있다가 새로운 출력값을 나타낸다.

클로저는 class를 사용할 수 없을 때 쓴다????

파이썬에서는 class를 지원하기 때문에 클로저의 효용가치가 많이 떨어진다.
왜냐하면 굳이 클로저로 구현하지 않고 클래스를 만들어서 클래스 내부에 함수를 정의해 놓으면 언제든지 객체를 만들어서 해당 인스턴스를 불러올 수 있기 떄문이다.
따라서 클로저 같은 경우 class를 지원하지 않는 자바스크립트와 같은 언어에서 class를 재현하기 위해 사용한다. 즉 프로토타입 기반의 언어인 자바스크립트는 클로저를 통해서 클래스 기반 언어처럼 캡슐화, 모듈화 작업을 수행할 수 있다.

참조 : 자바스크립트 클로저(Closure)

2019.03.08 TIL

(TIL은 스스로 이해한 것을 바탕으로 정리한 것으로 오류가 있을 수 있습니다)

오늘의 한마디:

지금 내 상황을 객관적으로 바라보고, 반성하자.

지금 온전히 집중할 수 있는 지금 이 순간이 내 인생의 최고이다.

if you think you can or if you think you can't you are right.

First class function

first class function란 프래그래밍 언어가 함수를 first class citizen으로 취급하는 것을 뜻한다. 그렇게 취급을 해주면 뭐가 좋을까?

일단 기본적으로 first class function은 3가지 기능을 충족해야 한다.

  1. 함수를 인자로 쓸 수 있는가?

    • 매개변수는 parameter 할당인자는 argument이다. 이 둘을 혼동해서 쓰기도 하는데 명확한 것은 argument은 함수 안에 인자로 전달되는 명확한 값이고 매개변수는 초기 함수에 지정되는 인자이다.
  2. 함수가 변수에 할당될 수 있는가?

    • a = func() 처음 함수가 변수에 직접 할당될 수 있는가에 대한 문제
  3. 함수를 리턴할 수 있는가?

    • 함수 안에서 return func() 처럼 특정함수를 리턴할 수 있는가에 대한 문제

위의 3가지 조건을 모두 만족하게 되면 first class function을 특정언어에서 지원한다고 할 수 있다.

파이썬은 first class function을 지원한다.

그럼 이제 각각의 예를 들어서 설명해보자.

1. 함수를 매개변수(인자)에 할당할 수 있는가?

def f(a,b):
    return (a+b)

def g(func, c, d):
    return func(c,d)

a = 10
b = 20
g(f , a, b) # 해주면 과연 값이 나올까?

30

값은 30을 반환한다. ---> 이를 통해서 우리는 func가 parameter로 쓰인다는 것을 알 수 있다.

2. 함수를 변수에 직접 할당 할 수 있는가??

def f(a,b):
    return (a+b)

g_var = f()
g_var(20, 30) 

값은 50을 반환한다. ----> 이를 통해서 우리는 runc가 변수에 직접 할당될 수 있는 것을 확인할 수 있다.

3. 함수를 리턴할 수 있는가?

firstclassfunction

2019.03.07 TIL

(TIL은 스스로 이해한 것을 바탕으로 정리한 것으로 오류가 있을 수 있습니다)

# 질문에 답하기.

  1. 잘못된 정보를 입력했을 때 지속적으로 새롭게 실행하도록 만드는 방법

재귀함수

  1. 자기가 자기 자신을 호출하는 함수 ---> 함수 내부에서 자기 자신을 또 호출

  2. 기저 조건(base case) 필요 ---> 탈출 조건

  3. 해결방안 - 점화식을 먼저 세우고 기저조건을 세운다.

항상 다시 시작될 자신을 호출 할 때는 return 값으로 호출 해줘야 한다.

예)

def func(num):
    func(num -1)

func(5) ---> 스텍프레임이 계속 쌓여 가므로 스텍이 터지게 된다. stack over flow

위의 예를 기저조건이 없다. 해결하기 위해 기저조건을 설정

def func(num):
    if num <= 0:
        return
    print(num)
    func(num -1)

func(5)
5
4
3
2
1

재귀함수 응용

factorial
5! = 1 x 2 x 3 x 4 x 5

5! = 4! x 5
= 3! x 4 x 5
= 2! x 3 x 4 x 5
= 1! x 2 x 3 x 4 x 5

factorial

전제조건 :

  1. 점화식 -- > fac(num) = fac(num -1 ) x num

  2. 기저조건 ---> if num == 1 then return 1

def factorial(num):
    if num == 1:
        return 1

    return factorial(num-1) * num     #여기에서 return이 안들어가면 실행이 안됨

factorial(5) ===> 120

for num in range(1,10):
    print(factorial(num), end=" ")   ===> 뒤에 " "을 붙여서 뛰어쓰게 해줌

1 2 6 24 120 720 5040 40320 362880

fibonacci

fibonacci

  1. 점화식 ---> fibo(num) = fibo(num-2) + fibo(num-1)

  2. 기저조건 num = 1 or num = 2 then return 1

예 1)

def fibo(num):
    if num == 1 or num == 2:
        return 1
    return fibo(num-2) + fibo(num-1)

예2)

def fibo_iteration(n):
    # 기저 조건 li에 설정
    li = [0, 1]
    a = 0
    while len(li) < n:  # 반복문으로 구현
        result = li[a]+li[a+1]
        li.append(result)
        a = a + 1
    return li[n-1]

예3)

def fibo_iteration_2(n):
   first = 0
   second = 1
   if n==1:
       return first
   elif n==2 :
       return second
   else :
       for i in range(2,n):
           first, second = second, first+second
   return second

위와 같이 하면 fibonacci 수열이 만들어 지게 된다.

for i in range(1, 11):
    print(fibo(i), end = "   ")

와 같이 하면 피보나치 수열의 나열을 볼 수 있다.

하지만 피보나치 수열은 재귀적으로 구현하게 되면 지속적으로 계산해서 나왔던 것을 또 구하고 또 구하는 비효율이 발생한다. 이것을 위해 스택을 활용할 수 있다.

스택을 활용한 피보나치 구현

  • 피보나치 함수를 재귀함수를 통해 구현하되 내부에 캐시로 사용할 자료구조를 두고
    한번 호출되었던 값은 캐시에 저장해두었다가 다음번에 다시 같은 매개변수가 전달되면
    연산하지 않고 캐시에 저장된 값을 바로 반환하도록 구현하십시오.
def make_fibo():
    cache = [0, 1]

    def fibo_recursion(n):
        if fibo_recursion(n) in cache:
            return fibo_recursion(n)
        elif n == 1:
            return 0
        elif n == 2:
            return 1
        return fibo_recursion(n-2) + fibo_recursion(n-1)
        cache.append(fibo_recursion(n))
    return fibo_recursion


if __name__ == "__main__":
    fibo = make_fibo()
    for i in range(1, 11):
        print(fibo(i), end="  ")

하노이 타워

하노이 타워는 개인적으로 풀지 못해 굉장히 아쉬운 문제이다.

내가 그만큼 직접 해보면서 이것을 느꼈었는데 이런 걸 해결하지 못한게 아쉬울 따름이다.

하노이 타워에서 중점적으로 봐야하는 것은

  1. num == 1 일 때 어떻게 해야 하는지

    1. num == 1 일 때는 시작지점에서 도착지점으로 가야 한다.
  2. 마지막 n이 나오기 전에 타워는 어디로 이동해야 하는지

    1. 마지막 n은 항상 도착지점으로 가야 하므로 n-1의 타워는 중간 지점으로 가야 한다.
  3. 마지막 n을 어디에 이동시켜야 하는지

    1. 마지막 n은 항상 바로 시작지점에서 도착 지점으로 가야 한다.
  4. 마지막 n을 옴긴 이후에 n-1은 어떻게 해야 하는지

    1. n-1의 중간지점을 다시 시작점으로 하여 by를 거쳐 end로 가야 한다.

위를 바탕으로 하노이타워를 만들어보자

def hanoi(num , start, by, end):
    if num == 1:
        print(f"{num}번째 도형을 {start}에서 {end}로 옴겨주세요")  #1번조건
        return ---->굉장히 중요한 조건이다.
    hanoi(num -1, start, end, by)   #2번 조건
    print(f"{num}번째 도형을 {start}에서 {end}로 옴겨주세요") #3번 조건
    hanoi(num -1, by, start, end)  #4번 조건 

hanoi(5, "a", "b", "c")

1번째 도형을 a에서 c로 옴겨주세요
2번째 도형을 a에서 b로 옴겨주세요
1번째 도형을 c에서 b로 옴겨주세요
3번째 도형을 a에서 c로 옴겨주세요
1번째 도형을 b에서 a로 옴겨주세요
2번째 도형을 b에서 c로 옴겨주세요
1번째 도형을 a에서 c로 옴겨주세요
4번째 도형을 a에서 b로 옴겨주세요
1번째 도형을 c에서 b로 옴겨주세요
2번째 도형을 c에서 a로 옴겨주세요
1번째 도형을 b에서 a로 옴겨주세요
3번째 도형을 c에서 b로 옴겨주세요
1번째 도형을 a에서 c로 옴겨주세요
2번째 도형을 a에서 b로 옴겨주세요
1번째 도형을 c에서 b로 옴겨주세요
5번째 도형을 a에서 c로 옴겨주세요
1번째 도형을 b에서 a로 옴겨주세요
2번째 도형을 b에서 c로 옴겨주세요
1번째 도형을 a에서 c로 옴겨주세요
3번째 도형을 b에서 a로 옴겨주세요
1번째 도형을 c에서 b로 옴겨주세요
2번째 도형을 c에서 a로 옴겨주세요
1번째 도형을 b에서 a로 옴겨주세요
4번째 도형을 b에서 c로 옴겨주세요
1번째 도형을 a에서 c로 옴겨주세요
2번째 도형을 a에서 b로 옴겨주세요
1번째 도형을 c에서 b로 옴겨주세요
3번째 도형을 a에서 c로 옴겨주세요
1번째 도형을 b에서 a로 옴겨주세요
2번째 도형을 b에서 c로 옴겨주세요
1번째 도형을 a에서 c로 옴겨주세요

와 같이 하면 하노이 문제를 풀 수 있다.

참고 사이트

###2019.03.07 TIL

(TIL은 스스로 이해한 것을 바탕으로 정리한 것으로 오류가 있을 수 있습니다)

대답해보기

========================

  • 전역변수, 지역변수
  • 함수에서 mmutable, immutable에 대해서

개발자가 하는 일

컴퓨터 사이언스 부트캠프 with 파이썬이라는 책을 쓰신 양태환 강사님께서 요즘 강의를 해주시고 계신다. 그리고 나는 TIL에 양태환 강사님께 배운 내용들을 바탕으로 글을 적고 있다.

양태환 강사님이 말씀하신 프로그래머가 하는 단 하나의 일은 추상화(abstraction)된 것(예를 들면 기획, 생각 등)을

인터페이스로 어떻게 설계할 것인지 그리고 그것을 어떻게 implementation(구현) 할 것인지이다.

인터페이스(interface)는 서로 다른 두 개의 시스템, 장치 사이에서 정보나 신호를 주고받는 경우의 접점이나 경계면이다. 즉, 사용자가 기기를 쉽게 동작시키는데 도움을 주는 시스템을 의미한다.

아직 명확히 글의 뜻은 이해 못하겠지만 구글링으로 관련 자료를 가지고 와 보았다.

인터페이스와 구현

객체 지향 디자인의 목표 중에 하나는 소프트웨어를 더 유지보수 가능하게 만드는 것이다. 다시 말해서 시스템의 다른 부분이 변경됐을 때나 프로그램을 새로운 요구사항에 맞춰 수정했을 때도 프로그램이 동작해야 한다는 뜻이다.

이러한 목표를 이루기 위한 디자인 원칙은 인터페이스와 구현을 분리하여 유지하는 것이다. 객체라면 클래스에서 제공하는 메서드가 속성의 표현 방식에 의존하지 않아야 한다는 뜻이다.

예를 들어 이 장에서 우리는 시간을 나타내는 클래스를 개발했다. 이 클래스가 제공하는 메서드에는 time_to_int, is_after, add_time이 있다.

이들 메서드는 몇 가지 방법으로 구현할 수 있다. 세부 구현은 시간을 어떻게 표현하는가에 따라 다르다. 이 장에서 Time 객체의 속성은 hour, minute, second가 있다.

대안으로 이들 속성을 자정부터의 경과 시간을 초로 표현한 정수 하나로 대체할 수 있다. 이러한 구현은 is_after 같은 메서드를 더 쉽게 작성할 수 있지만, 어떤 메서드는 구현하기가 더 어려워지기도 한다.

새 클래스를 배포한 후에 더 나은 구현을 발견할 수도 있다. 프로그램의 다른 부분에서 이 클래스를 사용하고 있다면 이 인터페이스를 변경하는 데 시간이 오래 걸리고 오류가 발생할 수 있다.

인터페이스를 주의 깊게 설계한다면 인터페이스를 변경하지 않아도 구현을 변경할 수 있다. 즉, 프로그램의 다른 부분은 변경하지 않아도 된다.

출처 https://thebook.io/006878/ch17/10/ [바로가기] (https://thebook.io/006878/ch17/10/)

아무튼 인터페이스 설계에서 한 가지의 예를 들면 함수 시그니쳐를 들 수 있다.

함수 시그니쳐

  1. 함수의 이름 - 함수의 기능을 명확하게 표현하는 함수 이름을 설정해야 한다.

  2. 파라미터 - 매개 변수를 어떻게 설정하고 받을 것인지

  3. 결과값 - 어떤 결과값을 반환해줄 것인지

위와 같은 정보가 함수를 설계할 때 꼭 들어가야 한다는 것이다.

예)

함수 시그니처

뒤늦게 정리하다 보니 선생님이 말씀해주신 좋은 설계와 나쁜 설계가 떠오른다.

open-closed

###확장에 대해서는 open 되어 있고 기존 코드의 수정에 대해서는 closed되어 있다.

클래스 간의 계층 구조를 만들고 설계하는 것

좋은 설계와 나쁜 설계

  • 다 크게 만들어 놨는데 클라이언트가 특정 기능을 추가해달라고 했는데 미친듯이 클래스가 변경해야되면 나쁜 설계이다.
  • 좋은 설계는 (궁극적인 목표) 기능이 추가되는 그 클래스 한개만 변경이 되고 나머지는 완벽히 닫혀져 있는 상태(Open-closed)
  • 이것을 위해 design pattern이 필요하고 이게 GOF(gang of Four) - 20개 이렇게 하면 open-closed에 근접해 갈 수 있다.
  • 이것을 가기 위해 가장 밑바닥의 지식이 상속이다.

packing와 unpacking

container = 1, 2, 3, 4, 5 라고 예를 들어보자.

type(container) == tuple 로 나온다.

container라는 이름에 여기에 할당된 값 객차가 5개가 있어 자연스럽게 튜플로 묶어 준다. 이를

packing이라고 한다.

이것을 a와 b라는 이름에 나누어 다시 할당하기 위해서는 특별한 기호가 필요하다

a, *b = container 라고 해주면

a = 1 ===> type(a) == int

b = [2,3,4,5]가 들어가게 된다. === > type(b) == list (한개를 제외한 나머지 전부, 뒤에 c가 있으면 [2,3,4]만 받게 된다.

2가의 타입은 다르지만(왜 다르게 들어가는지는 알 수 없지만) 나누어 할당이 가능하다.

그럼 이것은 내부적으로 어떻게 되는 것인지? 새로운 값 객체가 할당되는 것인가?

이부분에 대해서는 선생님도 새로운 값 객체가 형성되는 것인지 아니면 그냥 값 객체를 나누게 되는지는 잘 알 수 없다고 하신다.

단 한가지 알게 된 것은

container = 1,2,3

a = container 이라고 하면 파이썬에서 이중 할당이 되지 않기 때문에

a도 1,2,3이라는 값 객체를 가르키게 되는 것이다.

a, b, *c = container 역시 가능하다. ===> a=1, b=2, c=[3,4,5]가 된다.

이를 unpacking (다시 풀어준다) 이라고 한다.

unpacking의 한가지 예를 더 들어보면

>>>    dic = {"a" : 1, "b" : 2}
>>>    for k, v in dic.items():
>>>        print(k,v)
>>>    # a 1
>>>    # b 2
>>> 

이것 역시 unpacking의 한 종류로 볼 수 있다.

함수에서 가변인자를 받도록 인터페이스 설계하기

  • 만약에 a,b 2개가 아닌 정해져 있지 않은 숫자의 인자를 받아서 모두 더하게 하려면 어떻게 해야 할까?
>>>    def sum_int(a,b):
>>>        s = a+b
>>>        return s        
>>>    

즉 위와 같이 단순한 형태가 아닌 여러개의 parameter를 받을 때는 어떻게 해야할까?
ex) sum_int(1,2,3) / sum_int(1,2,3,4) 등.

이것을 가능하게 하기 위해서는 가변인자를 사용해야 한다.

>>>    def sum_int(*args):
>>>        print(args)                
>>>
>>>    sum_int(1,2,3,4,5)         
>>>    #(1,2,3,4,5) 즉 튜플로 된 (1,2,3,4,5)를 얻게 된다. 따라서 우리가 원하는 위의 문제를 해결하기 위해서는
>>>    # 튜플로 된 것을 unpacking 작업을 거쳐야 한다.
>>>
  • 해결방안
>>>    def sum_int(*args):
>>>        s = 0
>>>        for i in args:
>>>            s += i
>>>        return s
>>>
>>>    sum_int(1,2,3,4)
>>>    # 10이라는 원하는 값을 얻을 수 있다.
>>>

만약에 li = [1,2,3,4] 를 만들어서 sum_int(li)를 하게 되면 어떻게 될까?

>>>    li = [1,2,3,4]
>>>    def sum_int(*args):
>>>        s = 0
>>>        for i in args:
>>>            s += i
>>>        return s
>>>    sum_int(li)
>>>
>>>    #TypeError: unsupported operand type(s) for +=: 'int' and 'list'

위와 같이 테입에러가 발생하게 된다. 왜냐하면 위의 식을 다시 가져와

>>>    def sum_int(*args):
>>>        print(args)                
>>>
>>>    print(li)   #를 해보면 args가 ([1,2,3,4])로 packing 된 것을 볼 수 있다.
>>>

위의 문제를 해결하기 위해서는 li를 다시 unpacking하도록 해줘야하는데 굉장히 간단하다.

>>>    li = [1,2,3,4]
>>>    def sum_int(*args):
>>>        s = 0
>>>        for i in args:
>>>            s += i
>>>        return s
>>>    sum_int(*li)                            *li를 unpacking해주면 된다.
>>>    #10

위와 같이 li에 * 하나만을 추가하여 해결 할 수 있다.

그럼 여기서 sum_int(1, 2, 3, age = 100, weight = 100) 와 같이 특수 한 경우를 처리하기 위해서는 어떻게 해야 할까?

실행 해보면 TypeError: sum_int() got an unexpected keyword argument 'age'

타입에러가 나오게 되면서 기대하지 않았던 keyword argument인 'age'가 나왔다는 메시지를 접하게 된다.

이 방법을 해결하기 위해 keyword argument를 받아 줄수 있는 가변인자를 한개 더 설정해줘야 한다. **kwargs로 가변인자를 설정해준다.

>>>    def sum_int(*args, **kwargs):
>>>        print(args)
>>>        print(kwargs)
>>>
>>>    sum_int(1,2,3, age = 100, weight = 100)
>>>    #(1,2,3)
>>>    #{"age" = 100, "weight" = 100} 로 프린트 해준다.
>>>

다시 위의 식을 가지고 와서

>>>    def sum_int(*args, **kwargs):
>>>        s = 0
>>>        for i in args:
>>>            s += i
>>>        return s
>>>    sum_int(1,2,3, age=100, weight=100)
>>>    #6 을 잘 반환해준다.
>>>        
packing

위에서 보듯이 6을 return 하지만 age와 weight는 사라진 것이 아니라 출력되지 못한 것이다. 함수안에서 print로 kwargs를 해주면 볼 수 있다.

단 예외가 있는데

sum_int(1, 2, 3, age = 100, weight = 100, 4)를 해보면

SyntaxError: positional argument follows keyword argument

센텍스 에러가 뜨면서 positional argument가 keyword argument를 따라왔다는 오류 메시지가 나온다.

  • 추가적으로 고민해보면 좋은 것
>>>    li = [3,4,5,6]
>>>    dic = {"a" : 1, "b" : 2}
>>>    
>>>    def sum_int(*args, **dic):
>>>        print(args)
>>>        print(dic)
>>>
>>>    sum_int(li, dic)
>>>    #([3,4,5,6] ,{"a":1, "b":2}), {}
>>>
>>>    sum_int(*li,*dic)
>>>    # (3,4,5,6) , {"a":1, "b":2}
>>>

왜 위와 같이 되는지 고민해 보면 좋을 것 같다.

주의할 점

sum_int(* li, ** dic) ---> 함수를 호출할 때 * 와 ** 은 언패킹을 의미한다.

삼항 연산자

  • 사용방법

    참인경우 값 if 조건 else 거짓인경우 값

  1. 연산 대상의 개수에 따라 연산자를 분리하면 단항 연산자, 이항 연산자, 삼항 연산자로 분리 합니다.

  2. 단항 연산자는 부호(+, -), not 등이 있으며 +, -, *, / .... 등 대부분의 연산자가 이항 연산자 입니다.

  3. 삼항 연산자는 1개가 존재한다.

[참고] (https://wikidocs.net/20701) (https://wikidocs.net/20701)

  • 말은 어렵지만 예를 보면 간단해진다.
>>>    a = 10
>>>    if a > 10:
>>>        print("good!")
>>>    else:
>>>        print("bad!")
>>>
>>>    # 삼항 연산자
>>>    print("good!") if a>10 else print("bad!")
>>>

###2019.03.06 TIL

(TIL은 스스로 이해한 것을 바탕으로 정리한 것으로 오류가 있을 수 있습니다)

대답해보기

========================

  • 전역변수, 지역변수
  • 함수에서 mmutable, immutable에 대해서

function

  • 예를 바탕으로 설명을 진행한다.
>>>    a = 10
>>>    
>>>    def func():
>>>        a = 20
>>>        def inner():
>>>            global a
>>>            a = 30
>>>
>>>    print(a)           # a = 10 출력 (func()가 실행되지 않음)
>>>
>>>    def func():
>>>        a = 20
>>>        def inner():
>>>            global a
>>>            a = 30
>>>    
>>>    func()                    # func()의 a는 global a를 참조하고 있지 않다.
>>>    print(a)            # a = 10 출력 (func()는 실행되었으나 inner()가 실행되지 않음)
>>>
>>>    def func():
>>>        global a
>>>        a = 20
>>>        def inner():
>>>            a = 30
>>>    
>>>    func()
>>>    print(a)                # a = 20 출력    (func()는 실행되었으나 inner()가 실행되지 않음)
>>>
>>>    def func():
>>>        global a
>>>        a = 20
>>>        def inner():
>>>            a = 30
>>>        inner()
>>>    
>>>    func()                    # inner의 a는 global a를 참조하고 있지 않다.
>>>    print(a)                # a = 20 출력 (inner()역시 실행되나 , global a를 참조하고 있지 않다)
>>>
>>>    def func():
>>>        a = 20
>>>        def inner():
>>>            global a
>>>            a = 30
>>>        inner()
>>>
>>>    func()                    # inner의 a는 global a를 참조하고 있다.
>>>    print(a)                 # a = 30 출력 (func()를 실행하며 inner() 역시 같이 실행 되었다.)
>>>    
>>>    
  • 참조를 위해서는 계속 namespace를 위로 이동가능하다.
  • 하지만 수정을 위해서는 단계에 따라 꼭 nonlocal 혹은 global 이라는 호출이 필요하다.
  • 네임 스페이스는 local namespace ---> global namespace ---> Built - in namespace로 올라간다.

네임스페이스에 관한 예를 더 들어보자

>>>    a = 10
>>>    def outer():
>>>        b = 20
>>>        def inner1():
>>>            b = 30
>>>            def inner2():
>>>                d = 40
>>>                b = 50
>>>                print(b)
>>>            ineer2()
>>>            print(b)
>>>        inner1()
>>>    outer()                        # 50 와 30이 출력된다.
>>>    
>>>
>>>    a = 10
>>>    def outer():
>>>        b = 20
>>>        def inner1():
>>>            b = 30
>>>            def inner2():
>>>                d = 40
>>>                nonlocal b       # local namesapce의 b를 참조한다. 
>>>                b = 50
>>>                print(b)
>>>            inner2()
>>>            print(b)
>>>        inner1()
>>>    outer()                        # 50 와 50이 출력된다.
>>>        
>>>

위의 이야기를 명확하게 이해할 수 있으면 namespace에 대한 개념이 좀 더 명확해졌다고 할 수 있다.

함수의 스텍프레임

함수를 실행하게 되면 메모리에 스텍프레임이라는 공간이 형성되고 스텍프레임 안에 변수와 값들이 저장되게 된다.
이 스텍프레임은 함수 호출이 끝나면 사라진다. 스텍프레임은 메모리를 공부한 이후에 좀 더 깊게 살펴보기로 한다.

>>>    g_var = 20
>>>    def func(val):
>>>        val += 100
>>>        return val
>>>
>>>    func(g_var)                        # 120이라는 return 값을 낸다.
>>>    print(g_var)                        # 20이 출력된다.
>>>
>>>

위의 함수를 실행함으로 g_var는 120으로 바뀌지 않는다. 왜 그럴까?

처음 g_var = 20 이라고 할당되므로서 20이라는 값 객체가 메모리 저장된다. g_var는 그 20이라는 값 객체를 가르키는 변수이다. func(g_var)를 실행하게 되면 func라는 스텍프레임이 생기게 되고 그 스텍프레임 안에서 val라는 새로운 변수가 20을 가르키게 된다. 숫자는 immutable하기 때문에 그 val에 100을 더해 120이라는 새로운 값 객체를 만들어서 가르키게 된다. 그리고 함수 실행이 끝나게 되면 func의 스텍프레임이 사라지게 되므로 val = 120이라는 값도 사라지게 된다.
따라서 g_var는 그대로 20이 출력되게 된다.

stackframe

위의 그림이 100% 정확하진 않지만 이해하는데 도움은 줄 수 있다.(파이썬에서는 값이 저렇게 들어가는 것이 아니라 가리키는 형태로 나와야한다.)

어떤 사람은 g_var가 120으로 바뀌었으면 할 것이고, 또 누군가는 g_var는 그대로 두되 저 함수를 통해서 실행된 값들을 받고 싶을 것이다.

2가지의 해결책을 보자

  1. g_var의 값을 120으로 바꾸기
>>>    g_var = 20
>>>    def func():
>>>        global g_var
>>>        g_var += 100
>>>        return val
>>>
>>>    func()                    
>>>    print(g_var)                    # 120이 출력된다.
>>>

전역 변수를 불러옴으로서 해결할 수 있다.

  1. g_var는 그대로 두되 저 함수를 통해서 실행된 값들을 받자.
>>>    g_var = 20
>>>    def func(val):
>>>        val += 100
>>>        return val
>>>
>>>    a = func(g_var)                        # 120이라는 return 값을 낸다.
>>>    print(a)                            # 120이 출력된다.
>>>    print(g_var)                        # 20이 출력된다.
>>>
>>>

해결책은 간단하다. 새로운 변수 a를 통해 func(g_var)의 값을 받아오면 된다.

그럼 여기서 또 하나의 의문이 생긴다. 과연 mutable한 list나 dictionary가 오게 되면 바뀔까?

한번 해보면 된다 ㅎㅎ.

>>>
>>>    li1 = [1,2,3]
>>>    def func(li, i):
>>>        li.append(i)
>>>        return li
>>>
>>>    func(li1, 4)
>>>    print(li1)             # [1,2,3,4]
>>>
>>>
list stackframe

너무나도 쉽게 변경되는 것을 알 수 있다.

결론을 내리자면 함수에서 immutable한 객체는 새로운 값 객체를 생성하여 할당하며, mutable한 객체는 기존의 객체를 변경하여 할당한다.

###2019.03.06 TIL

(TIL은 스스로 이해한 것을 바탕으로 정리한 것으로 오류가 있을 수 있습니다)

컴프리헨션 & 연산자

========================

대답해보기

  • 컴프리헨션이란?
  • 연산자의 종류 및 각각 간단하게 이야기해보라

어제 복습을 덜 한 슬픔을 뒤로 하고 공부했던 것들을 조금 더 정리해보고자 한다.

오늘의 보너스

  • 프로그램에서 a = 1 은 할당이고 a == 1 이 같다는 표현이다.

  • 파이썬에서의 False

    -False / None / "" / [] / {} / set()

컴프리헨션

컴프리헨션은 하나 이상의 이터레이터(iterater 반복적으로 하나씩 나올 수 있는 객체[list, string, dic. set, tuple 등]로 부터 파이썬의 자료구조를 만드는 컴팩트한 방법이다.

리스트컴프리헨션

li = [i for i in range(1, 10)] ---> 1부터 10전까지의 i로 리스트 만들기

딕셔너리 컴프리헨션

원래 딕셔너리 만들기 위해서는 아래와 같이 만들 수 있다.

>>>    tu = (("a",1), ("b",2))
>>>    dic = {}
>>>    for k,v in tu:
>>>        dic[k] = v
>>>    #위와 같이 만들 수 있다. 하지만 컴프리헨션을 이용하면 한 문장으로 만들 수 있다. 
>>> 
>>>    dic = {key:value for key, value in tu} 

하지만 컴프리헨션을 통해 한 문장으로 딕셔너리를 만들 수 있다.

오퍼레이터(연산자)

bit 연산자

연산자

위의 진리표에 따라 비트 단위로 2진법 비교

  1. bin(0b1010 & 0b1100) --- > 0b1000 그리고

  2. bin(0b1010 | 0b1100) ---> 0b1110 또는

  3. bin(0b1010 ^ 0b1100) ---> 0b110 이거는 공부 더 필요할 듯

    EXCLUSIVE OR 단독이다. 하늘아래 나만 존재 할 수 있다.

bit 연산자는 게임 인벤토리등을 구성할 때 쓸 수 있다.

shift 연산자

프로그래밍 할 때 2의 배수로 나오면 성능이 더 좋다? 이게 맞는 말인가?
실제로 맞는 말이다. shift operator로 인해 성능이 더 좋음

알고리즘에서 2의 배수로 가는 경우 shift 연산자를 직접 써주면 성능이 더 좋아진다.

1<<1 ---> 1은 0001 이고 <<1은 1을 왼쪽으로 한칸 민다는 것이다.

답은 2

1<<2 ---> 1을 왼쪽으로 2칸 밀게 되므로 0100 답은 4

1<<3 ---> 1을 왼쪽으로 3칸 밀게 되므로 1000 답은 8

10>>1 ---> 10은 2진법으로 하면 1010 이므로 이걸 오른쪽으로 1칸 밀면 101이 된다 답은 5

논리 연산자 (logical operation)

논리 연산자는 진리표를 보고 참고하면 되고, 몇가지 예시를 들어보려고 한다.

  1. [1,2] or [] ----> [1,2]

  2. [1,2] and [] ---> []

  3. [1,2] or [3,4] ---> [1,2]

  4. [1,2] and [3,4] ---> [3,4]

  5. [] and [1,2] ---> []

  6. [] or [1,2] ---> [1,2]

여기서 규칙은 제일 마지막에 참조한 객체를 반환이다.

  1. [1,2] or [] ----> [1,2] ㅡㅡ [1,2] 가 참이므로 더 이상 보지 않고 반환

  2. [1,2] and [] ---> [] ㅡㅡ and 이므로 2번까지 봐야하고 []가 마지막으로 참조한 객체이므로 [] 반환

  3. [1,2] or [3,4] ---> [1,2] ㅡㅡ or이라 [1,2]가 참이므로 더 이상 보지 않고 반환

  4. [1,2] and [3,4] ---> [3,4] ㅡㅡ and 이므로 [3,4]까지 봐야하고 [3,4] 반환

  5. [] and [1,2] ---> [] ㅡㅡ and인데 처음부터 false 이므로 더 이상 보지 않고 [] 반환

  6. [] or [1,2] ---> [1,2] ㅡㅡ or이므로 두 번째까지 확인하고 [1,2] 반환

산술연산자(모듈러)

산술연산자는

  • : 더하기

  • : 뺴기

/ : 나누기 (실수형 나누기)

/ : 몫의 정수 부분만 표현 (정수형 나누기)

  • : 곱하기

** : 2 ** 50 ---> 2에 50승 (보통 math.pow(2, 50) 으로 쓴다.)

% : 8 % 3 ---> 8을 3으로 나눈 나머지 표현

+= / -= / = / *= 도 쓸 수 있음

###2019.03.04 TIL

(TIL은 스스로 이해한 것을 바탕으로 정리한 것으로 오류가 있을 수 있습니다)

파이썬 기본 문법

========================
1.기본 문법(Dictionary)


python 기본 문법 2일차이다.

배운거 복습하기

  • dictionary 삽입(insert) 방법 - 3가지 말하기
  • dictionary 검색(search) 방법
  • dictionary 삭제(delete) 방법 - 3가지 말하기

Dictionary

javascript에서 배우던 object와 비슷한 것 같다.

TIL을 진행하기에 앞서서 오늘 배운 꼭 알아야 하는 것을 좀 적어보려고 한다.

  • 변수란 데이터를 저장할 수 있는 메모리 공간이다.

  • 단 파이썬에서는 변수가 이름과 값 객체로 나누어진다. 우리가 파이썬에서 변수라고 부르는 것은 이름이다. 값 객체는 다른 메모리 공간에 있다.

  • a = 10으로 변수를 지정하고 a = 20 으로 새로운 변수를 지정해주면 10 이 20으로 바뀐 것이 아니라, 20이라는 새로운 값 객체가 생성된 것이고 a 는 20이라는 값 객체를 가르키게 된다. 따라서 처음 생성되었던 값 객체 10은 파이썬에서 자동적으로 없애줌(부르는 이름이 할당되지 않으면 삭제된다) 추가적으로 a = 10 이라고 파이썬에서 적어주면 10이라는 값 객체가 먼저 메모리에 생성되고 a라는 변수가 할당된다.

  • list1 = [1,2,3] 에서 list는 다양한 변수들의 모임이므로 1,2,3은 한곳에 저장되어 있지 않고 다 따로 떨어져있다. 이것에 대한 표현을 [ * , * , * ] 라고 생각하면 좀 더 쉽다. 각각의 *가 1과 2와 3을 가르키고 있다.

  • iterable 객체 - 하나씩 돌면서 갈 수 있는 객체 (list, tuple, string, dictionary)

dictionary

그럼 이제 본격적으로 dictionary에 대해 다뤄보고자 한다.

딕셔너리의 정의

딕셔너리는 collection of pairs로 우리가 저장하려는 데이터(item)을 key라는 곳에 분류하여 넣는 것을 말한다. 이제 key와 item(value)는 pair를 이루어 들어가게 된다.

딕셔너리 요소 추가 (3가지)

dic = {} ---> 항상 빈 딕셔너리를 만든 이후에 모든 것이 진행되어야 한다.

  1. dic["a"] = 1 ---> "a"라는 key값이 있으면 value를 1로 바꾸고 없으면 추가 한다.

  2. dic.setdefault()

    • dic.setdefault("e") --- > "e"라는 key값이 있으면 value 추출 / 없으면 "e"에 None 삽입

    • dic.setdefault("e", 1) ---> "e"라는 key값이 있으면 value 추출 / 없으면 "e"에 1 삽입

  3. dic.update({}) #setdefault는 key값이 있으면 value 추출/ update는 value를 바꿈

    • dic.update({"e":40}) ---> "e"라는 key 값이 있으면 바꾸고/ 없으면 "e"에 40 추가

    • dic.update((("e", 1),)) --- > 위와 같으나 튜플로 넣을 시 튜플안에 튜플이 있는 형태로 삽입

    • update안에 들어있는 sequence element #0 has length 1 ---> 2 is required

딕셔너리 요소 삭제 (3가지)

  1. del a[key] ---> 해당 key 값의 value도 함께 삭제

  2. dic.pop(key) ---> 해당 key의 페어를 꺼내서 반환하고 삭제

  3. dic.clear() ---> 모든 key 및 value 삭제

##딕셔너리 서칭

  1. key 추출 -- 3가지

    1. dic.keys() ---> view로 반환// list(dic.keys()) ---> list로 반환

    2. for k in dic.keys() == for k in dic

    3. key in dic ---> 해당 key가 dic에 있는지 확인 후 True or False 반환

  2. value 추출 -- 3가지

    1. dic.values() ---> view로 반환 // list(dic.values()) ---> list로 반환

    2. dic[key] ---> 해당 key의 value 추출 // 없을시 에러 송출

    3. dic.get(key) ---> 해당 key의 value 추출 // 없을시 None이 나옴

      1. dic.get(key, "None") ---> 해당 key가 없을시 "None" 추출
  3. key & value 추출 -- 2가지

    1. dic.items() ---> key와 value를 view로 반환 // list(dic.items()) ----> list로 반환

    2. for k in dic.items() /--->tuple형태로 반환 / for key, value in dic.items() ---> 그냥 반환

딕셔너리 주의 점

  1. key는 immutable만 쓸 수 있음 (tuple, string, num)

  2. value는 모두 사용 가능

  3. key는 중복이 불가능하다

  4. view 객체와 list의 차이점

###2019.03.04 TIL

(TIL은 스스로 이해한 것을 바탕으로 정리한 것으로 오류가 있을 수 있습니다)

파이썬 기본 문법

========================
1.기본 문법(string,list)


python 기본 문법의 시작이다.

오늘은 string/list으로 나누어 기본 문법을 정리해보려고 한다.

String

String은 기본적으로 immutable로서 변경할 수 없다.(tuple와 마찬가지).

string

String은 index를 사용할 수 있으며 "abcdef"[0],

String은immutable하므로 보이는 형태라도 변경하기 위한 방법

  1. slicing

    • b = a[0:2] + "x" + a[3:]
  2. replace ---> 특정 문자 찾아 바꾸기

    • b = a.replace("a", 'x')
  3. a.split()

    • ---> 공백을 뛰어서 list로 바꿈

위와 같은 방법으로 변경할 수 있다.

string에서 빈칸을 없애는 방법

  1. a.lstrip() ---> 왼쪽 공백 제거
  2. a.rstrip() ---> 오른쪽 공백 제거
  3. a.strip() ---> 양쪽 공백 제거
  4. a.replace(" ",'') ---> 띄어쓰기 모두 제거

위의 4가지 방법을 쓸 수 있다.

string의 길이, 요소 파악

  • len(a)를 사용한다

  • a.count("a") ---> 특정 요소의 갯수 파악

string 내에서 특정한 값을 대입 혹은 바꿔야 할 때

  1. format

    • "{},{}".format(a, b)
  2. 포매팅

    • "I love %s" %you

string내에서의 join

  1. "str".join(list)

    • list의 요소마다 str을 넣어서 string으로 변경 . ---> 단 list의 요소는 다 문자로 구성되어야 함
  2. "str".join(str)

    • str의 사이마다 str을 넣어서 변경

list

정의 : python에서 list는 다양한 타입의 변수 모임이라고 본다

list

li = []
li = [ 1, "b", (1,2,3) ]

Number, string, tuple 등 다양한 타입이 모두 올 수 있다.

요소 삭제

  1. li[1:3] = [] ---> index 활용 삭제

  2. del li[0] ---> 특정 인덱스 요소 삭제

  3. li.remove() ---> 특정 요소 직접 삭제

요소 추가

  1. li.append() ---> 제일 뒤에 1개의 특정 요소 추가

  2. li.insert(index, ele) ---> 특정 인덱스에 원하는 요소 추가

  3. li.extend([ ]) ---> list 요소들을 한번에 추가

요소 추출

  1. li[0] ---> 인덱싱

  2. li[0:2] ---> 슬라이싱

  3. li.index("ele") ---> 특정 요소의 인덱스 파악

  4. li.count("ele") ---> 특정 요소의 갯수 파악

  5. li.pop() ---> 특정 인덱스의 요소 추출 및 제거(없을시 가장 뒷 요소)

요소 수정

  1. li[0] = "1" ---> 하나의 값 수정

  2. li[0:2] = [1, 2, 3] ---> 해당 슬라이싱 만큼 특정 값 추가

요소 정렬

  1. li.sort() ---> 원본이 바뀌는 정렬

  2. li.reverse() ---> 원본을 거꾸로

  3. li.sort(reverse = True)

  4. len(a)

  5. sorted(li) ---> 원본이 바뀌지 않는 정렬

특정 기간을 계산해야 하는데, 해당 월을 기준으로 첫째 날과 마지막 날을 얻어야 할 때,

참 쉬울 것 같으면서도 당황스럽게 했다.

구글링을 해보고 여러가지 방법들을 보았지만, 너무 많은 방법들을 제시해주고,

또 생각보다 코드들이 길어져서, 원하는 방법이 아니었다.

그러다가 깔끔하게 해결 할 수 있는 방법을 찾아 공유한다.

예를 들어 나는 해당 전달의 첫째 날과 마지막 날을 얻고 싶을 때를 가정하고 해본다.

from datetime import date, timedelta

today = date.today()

print(today)

datetime.date(2019, 12, 7)

#해당 달의 첫째날 구하기
first_day = today.replace(day=1)

print(first_day)
datetime.date(2019, 12, 1)

#전달의 마지막 날 구하기
last_day_month_ago = first_day - timedelta(days=1)
print(last_day_month_ago)
datetime.date(2019, 11, 30)

#전달의 첫째 날 구하기
first_day_month_ago = last_day_month_ago.replace(day=1)
print(first_day_month_ago)
datetime.date(2019, 11, 1)

 

이렇게 하면 쉽게 구할 수 있다!! 짱 좋은 방법 :)!!!

추가적으로 형태를 "2019-11-1"이런 형태로 얻고 싶다면

last_day_a_month_ago.strftime('%Y-%m-%d')

이렇게 적어주면 해결!

+ Recent posts