2019.03.09 TIL

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

# 질문에 답하기

  1. 32비트 컴퓨터에는 램슬롯이 4기가 넘어가면 아예 인식을 못하게 된다. 왜 그럴까?

32bit / 64bit

원래 램은 없어도 된다. 바로 하드 or SSD와 cpu가 직접 연결되어 있어도 가능하다.

하지만 cpu의 작업 요청 속도와 하드 ,SSD에서 작업에 대해 응답해주는 속도가 너무나도 차이가 나기 때문에 그 중간자의 역활로 ram이 필요하게 되었다.

Ram은 random access memory를 줄인 말로 cpu가 요청할 것으로 예상 되는 자료들을 SSD 또는 하드에서 불러와 미리 저장소에 저장해 놓는다. 따라서 cpu가 어떤 자료를 요청했을 때 빠르게 반환시켜주고 만약에 그 자료가 ram에 올라와 있지 않다면 다시 램에서 SSD 혹은 저장소로 요청을 하게 된다. 우리는 이럴 때 랙이 걸린다고 표현한다.

스크린샷 2019-04-06 오후 3 15 05

기본적으로 데이터의 이동은 메모리 게층 순차적으로 이루어져야 한다. 따라서 하드디스크에 있는 데이터는 메인 메모리를 거쳐 캐시 그리고 레지서트로 이동하게 된다. 하지만 여기에는 엄청난 속도의 차이가 존재한다.

cycles for data

  • register
    • 1 cycle
  • cache
    • 3 cycles
  • memory
    • 20-100 cycles
  • hard disk
    • 500000-5000000 cycles

위와 같이 각 데이터의 이동속도는 천차만별이다.

그럼 32bit와 64bit는 무엇일까?

32bit는 cpu에서 ram에 한번에 요청할 수 있는 데이터의 양이다.

컴퓨터는 0과 1로 표현할 수 있고 하나의 0과 1은 1bit를 표현한다. 따라서

32bit는 0000 0000 0000 0000 0000 0000 0000 0000 총 32자리의 bit를 표현할 수 있고

이것은 2의 32승이다.

cpu는 최대 2의 32승 갯수의 특정 데이터를 ram에 요청할 수 있게 되는 것이다.

그럼 이제 다시 ram으로 가보자

SSD또는 하드에서 가져온 자료를 저장해놓는 메모리에서 특정 자료를 찾기 위해서는 메모리 주소가 필요하다. 이러한 메모리 주소는 1개당 1바이트를 가르킨다. (1바이트는 8비트이다) 즉 8비트로 메모리 1개의 주소를 명확하게 설정할 수 있다는 것이다.

우리는 cpu에서 최대 2의 32승 bit로 2의 32승 개 만큼의 주소를 요청할 수 있고 메모리에서 주소 1개는 1바이트를 나타냄으로 ram은 2에 32승 바이트를 표현할 수 있다. (#이제 이게 의문이 들기 시작한다. 왜 주소 1개는 1바이트를 나타낼까?. 그렇게 표현하기로 결정했다. ) 2에 32승 바이트는 4,294,967,294바이트고 이는 4 x 1024 x 1024 x 1024 byte로 풀어 쓸 수 있고 byte - kbyte - mbyte - gbyte 등을 거쳐서 4G로 쓸 수 있다. 따라서 32bit 컴퓨터에서는 최대 4G 램까지 밖에 가르킬 수 없다.

32bit 컴퓨터에 8G램을 설치한다 하더라도 32bit가 요청할 수 있는 최대 램의 주소는 4G이므로 램의 나머지 부분들은 아예 주소를 배정받을 수도 없게 되는 것이다.

CPU가 램에 자료를 요청하는데 램의 공간이 아무리 넓어도 CPU가 요청할 수 있는 공간이 한정되어 있다면 의미가 없는 것이다.

하드웨어 추상화/ 언어 추상화

c 와 c++ 은 하이레벨 언어이다.

로우레벨은 어셈블리어 밖에 없다.

ammembly lan만 로우레벨 언어이다.

  1. 컴퓨터와 1:1로 대응해야 하므로 cpu각각의 이름을 다 알아야 한다. cpu에 있는 레지스터의 이름까지 다 알아야 쓸 수 있다.

  2. 하드웨어 의존적 언어이다. cpu로 바뀌면 새롭게 다시 다 짜야 한다.

high- level lan

  • cpu , 즉 하드웨어를 몰라도 쓸 수 있게 되었다. 하드웨어 의존적이지 않아짐. 이것을 하드웨어 추상화라고 한다.
  1. c언어 - 코드를 짜놓았으면 어떤 하드웨어에 상관없이 제조사가 제공하는 컴파일러를 통해 각 cpu에 작동하는 어셈블리어로 바꾸어 작동시킬 수 있다.
    • 하이레벨 언어 중에서 추상화가 가장 덜 된 케이스
    • int (내가 4바이트를 쓰겠다.) short(16byte를 쓰겠다.) 등등 아직 메모리와 바인딩이 되어 있음

이후에 python,javascript 등으로 넘어오면

  • a = 10 일 때 메모리 차원에서 보다 바인딩이 떨어짐 . 언어 추상화가 더 잘 일어남.

하드웨어 추상화 계층(HAL, Hardware Abstraction Layer)은 컴퓨터의 물리적인 하드웨어와 컴퓨터에서 실행되는 소프트웨어 사이의 추상화 계층이다. 이것은 하드웨어(cpu)의 차이를 숨겨서 응용 프로그램이 작동할 수 있는 일관된 플랫폼을 제공한다.

아직은 이해 안되는 참고사항

char --> 1byte이다. 0 ~ 2** 8/ 2 0 ~ 255 ---> -128 ~ 127 까지 사용가능

4byte ---? 2 ** 32 / 0 ~

컴파일러 언어를 한개씩은 한다. 컴파일러 언어가 뭔가?

c 언어 (integer overflow) --->

char num = 126; char의 범위가 범위가 -128 ~ 127까지 인데 그 수를 넘어가므로 overflow가 일어난다.

num ++; num ++;

if (num>0) { print("%d \n" , num); } else { print("wrong1"); }

2019.03.08 TIL

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

오늘의 한마디:

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

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

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

기수법

프로그램에서 쓰는 수는
10진수(사람을 위한 수) 0,1,2,3,4,5,6,7,8,9
2진수( 컴퓨터를 위한) 0,1
16진수 ( 2진수를 더 쉽게 쓰기 위해) 0,1,2,3,4,5,6,7,8,9,10, a, b, c, d, e, f,
8진수(리눅스 등에서 씀)
각각 변환하는 것을 먼저 잘 할 수 있어야 한다.

계산기에 프로그래머 계산기가 있음
dec - 10진수
hex - 16진수
oct - 8진수
bin - 2진수

1. 10진수를 2진수로 바꾸는 방법

예를 들어서 설명해보자
50을 2진수로 바꾸어 보자

먼저 2의 제곱 중에 50에 가장 가까운 수를 찾는다. 2 ** 5 = 32

이후 남은 18에 가장 가까운 2의 제곱을 찾는다. 2 ** 4 == 16

남은 2에 대해 가장 가까운 2의 제곱 2 ** 1 = 2

이상 해결

이걸 직관적으로 적어보면

32 + 16 + 2 -----> 220010 (2)

ex)

  • 47 ---> 2 ** 5 (32) + 2 ** 3 (8) + 2 ** 2(4) + 2 ** 1(2) + 1 ===> 101111(2)

a = 0b1010 ---> 2진수 표현 방법 hex(a) ---> a를 16진수로 변환

c = 0xaf ---> 16진수 표현 방법 bin(c)

2. 2진수를 10진수로 바꾸는 방법

2진수 위에 

6543210 이렇게 숫자를 적는다. 

1010110(2)   -------> 64 + 16 + 4 + 2 = 86

예)

543210

110010(2)    --------> 32 + 16 + 2 ===> 50
  1. 16진수를 2진수로 바꾸는 방법
    16진수

0123456789abcdef

외워야 하는 것

a = 1010(2)
b = 1011(2)
c = 1100(2)
d = 1101(2)
e = 1110(2)
f = 1111(2)

16진수가 나오면 꼭 2개로 나누어서 2진법을 표현하자!!

ex ) fa(16) ---> 1111 1010(2)

ex) 74(16) ---> 0111 0100(2) #제일 앞에 의미 없는 0도 적어주면 좋다.

ex) 8d(16) ----> 1000 1101(2)

4. 2진수를 16진수로 바꾸는 방법

뒤에서 부터 4개씩 끊어준다. 그리고 그것을 하나씩 16진수로 바꾸어 준다.

제일 앞에 부족한 것은 00으로 채워준다.

1101110001(2) ----> 0011/0111/0001 -----> 3 7 1 (16)

1011110101(2) -----> 0010/1111/0101 -----> 2 f 5 (16)

100011110111(2) -----> 1000/1111/0111 -----> 8 f 7(16)

2019.08.07 파이썬 인자 및 매개변수

# 질문에 답하기

1.

파이썬 매개변수(parameter)와 실행인자(argument)

  • 매개변수 : 함수를 정의할 때 사용하는 이름
  • 실행인자 : 함수를 실행할 때 사용하는 변수 혹은 값
def fucntion(a, b)
    print("""{a}, {b}""".format(a=a, b=b))

function('byeonguk', 'hi')
  • 여기서 a와 b는 매개변수이고
  • 'byeonguk', 'hi'는 실행인자이다.

파이썬 함수 인자 (Arguments)

  • default value(기본값)을 주어서 값을 넘겨 줄 수 있다.
  • 기본 default값을 활용하여 2개의 매개변수가 필요해도 1개의 인자만 받을 수도 있다.
def fucntion(a, b='hello')
    print("""{a}, {b}""".format(a=a, b=b))

fucntion('byeonguk')

byeonguk, hello

fucntion('byeonguk', 'hi')

byeonguk, hi

파이썬 위치 인자(Position argument)

  • 기본적으로 매개변수에서 실행인자를 받는 것은 순서대로 받는다.
  • 해당 순서를 지키고 싶지 않을 때는 키워드 인자를 지원한다.

파이썬 키워드 인자(keyword argument)

  • 해당 키워드 인자는 특정 매개변수에 특정 인자를 사용하겠다고 지정해주는 것이다.
  • 위의 경우를 예로 들어서 추가 설명해보면
    function(b="byeonguk", a="hi")

hi, byeonguk

  • 위처럼 순서를 지키고 싶지 않거나, 그러기가 힘든 경우 키워드 인자를 활용해서 특정 인자를 지정해서 넣어줄 수 있다.

함수의 인자 수와 입력하는 매개변수의 수가 다르면 에러가 발생

  • 3개의 매개변수를 필요로 하는데, 2개의 인자만 입력해주면 에러 발생

따라서 만약에 매개변수에 2개 이상의 None이 섞이게 되면..

  • 꼭 넘겨 줄 때 키워드 인자를 활용해서 특정 부분을 지정해주자!

2019.08.07 파이썬 에러메시지(try, escept, else, finally

# 질문에 답하기

1.

try, except를 통한 에외처리

  • 파이썬에서 특정 구문을 실행하고 싶은데, 혹시라도 오류가 날까 두려울 때.
  • 어떤 오류가 날지 모를 때
  • try, except를 통해서 해당 문제를 해결할 수 있다.

try, except

try:
    print('이것부터 실행해봅니다.')

except:
    print('위에 try 구문이 안될 시 이것을 실행합니다')
  • 원래는 except 뒤에 일어날 에러명을 적어주면 된다.
  • 하지만 단순히 예외처리를 위한 것이라면 굳이 적어주지 않아도 되는 것 같다.

특정 경우 에러 일으키키 (raise)

  • raise를 통해 에러를 일으킬 수 있다.
  • try, except을 통해서 좀 더 효율적으로 쓸 수 있다.
try:
    if err_code != 0:
        raise Exception

except:
    print('err_code가 0이 아닙니다')
  • 해당 부분이 아니면 raise Exception 에러를 일으켜서 except로 넘어 간다.

파이썬 에러와 예외에 대한 좋은 자료

try와 except, else, finally를 통한 예시

def divide(x, y):
    try:
        result = x / y
    except:
        print('0으로 나눌수가 없어요')
        return False
    else:
        print('결과: ', result)
        return True
    finally:
        print('나누기  연산 종료')
  • try를 해보고 가능하면 else로 넘어가서 해당 결과값에 대해 출력한다.
  • finally는 항상 끝나기 전에 무조건 실행된다.
  • finally를 잘 활용하면 왠지 더 좋은 결과물을 낼 수 있을 것 같다.

12A9EF95-FE55-4858-AAAE-AD6579CB8370

2019.04.29 CSV파일 및 TXT파일 읽기, 저장하기, 쓰는 방법

# 질문에 답하기

  1. CSV파일 다루는 방법
  2. TET파일 다루는 방법

기본 전제

기본적으로 CSV와 TXT의 형태가 비슷하다.

CSV 다루기

  • CSV는 comma-separated value(쉼표로 구분된 텍스트)
  • 데이터를 저장하고 공유하는 매우 간편한 포맷이다.
  • 데이터가 일반 텍스트로 저장되어 어디서나 데이터를 쉽게 다룰 수 있다.
  • 단 따로 자료형이 없는 원시데이터이다.

CSV 열기(파일이 없다면 자동생성된다)

  • f = open('파일명', 'w', newline='')
  • w는 나는 write할 것이다를 알려줌
  • newline은 write할 때 줄 바꿈이 2줄씩 일어나지 않도록 해줌

CSV 데이터 입력하기

  • f.write('1,2,3\n')
  • f.write('4,5,6')
  • 마지막에 \n을 해주지 않으면 덮어쓰게 된다.

CSV 파일 저장하기

  • f.close()

csv.py

csv

1.csv

txt

TXT 다루기

  • 가장 간단한 에디터이다.

TXT 열기 및 생성

  • f = open('파일명', 'w', encoding = 'utf-8')
  • w는 나는 write할 것이다를 알려줌
  • w : 쓰기 모드
  • r : 읽기 모드
  • a : 추가모드 (마지막에 새로운 내용을 추가할 때 사용)
  • encoding은 한글이 깨지지 않도록 해준다.

TXT 데이터 입력하기

  • f.write('안녕하세요\n')
  • f.write('처음 연습합니다.')

TXT 파일 저장하기

  • f.close()

TXT 파일 읽기(read 모드)

  • readline : 한 줄 읽어오기
    f = open('text.txt', 'r' )
    line = f.readline()
    print(line)
    f.close()
  • readlines : 모든 줄 읽어오기
    f = open('text.txt', 'r' )
    lines = f.readlines()
    for line in lines:
      print(line)
    f.close()

새로운 내용 추가하기

  • 모드를 a로 연다.
    f = open('text.txt', 'a')
    for i in range(11, 20):
      data = "%d번째 줄입니다.\n" % i
      f.write(data)
    f.close()
  • 11번째줄부터 20번째 줄까지 데이터 삽입

open, 데이터 삽입 , close까지 한번에 처리하기

  • with로 처리하기
    f = open("foo.txt", 'w') 
    f.write("Life is too short, you need python") 
    f.close()
    이것을
with open("foo.txt", "w") as f:
    f.write("Life is too short, you need python") 

이렇게 한번에 적어줄 수 있다.
단 파이썬 2.5부터 지원한다.

2019.04.25 동적크롤링(Ajax, JSON)

# 질문에 답하기

  1. 동적크롤링
  2. Ajax
  3. JSON

동적크롤링의 큰 그림

  • 자바스크립트로 동작되는 애들은 지속적으로 데이터를 가지고 와서 업데이트 한다.
  • 따라서 현재 보이는 데이터를 기준으로 하면 안되고 그 원본 데이터의 주소를 찾아야 한다.
  • 먼저 해당 데이터를 불러오는 url을 찾는 것이 중요한데 그것은 network에서 확인할 수 있다.
  • XHR을 선택하면 아이젝스로만 활동하는 것을 확인할 수 있고, 거기서 해당 내용을 불러오는 url을 구성한다.
  • 그리고 그 url을 통해서 크롤링을 진행해야 하며 동적데이터이기 때문에 이럴 때 이용해야 하는 것은 json이다.

Introduction

  • 브라우저(웹클라이언트)에서 웹페이지를 요청하거나 링크를 클릭하면 화면전환이 발생하는데 이것은 브라우저와 서버와의 통신에 의한 것이다.
  • 서버는 요청받은 페이지(HTML)를 반환하는데 이때 HTML 뿐만 아니라 CSS 및 javascript파일들도 함께 반환한다.
  • 웹페이지가 반환되면 브라우저(웹클라이언트)는 page를 reload하여 화면을 보여준다.
  • 서버는 요청받은 페이지가 무엇이냐에 따라 정적인 파일들만 반환할 수도 있고, javascript가 섞인 동적인 파일들을 반환할 수도 있다.

Ajax(Asynchronous javascript and XML)

  • Ajax는 자바스크립트를 이용하여 비동기적으로 서버와 브라우저가 데이터를 교환하는 통신 방식이다.
  • 서버에서 한번 페이지를 반환했을 때 거기에는 HTML와 같은 정적인 파일들 뿐만 아니라 javascript와 같이 동적인 파일들도 함께 반환한다.
  • 만약 페이지에서 일부만 지속적으로 갱신해야 한다면 굳이 모든 파일을 다시 반환할 필요가 없다.
  • 따라서 브라우저(웹클라이언트)는 Ajax request를 통해 동적인 부분을 요청하고 서버는 json을 통해 reload하는데 필요한 정보들을 반환 해준다.
동적크롤링

Ajax의 요청과 처리

  • 웹브라우저는 XMLHttpRequest 객체를 이용하여 Ajax요청을 보낸다.
  • 서버는 JSON형태로 요청온 정보만을 담아 순수한 텍스트로 구성된 데이터 문자열을 반환한다.
  • 이 문자열을 객체화하는 것을 역직렬화라고 하고 역직렬화를 위해 JSON.parse()를 이용한다.
동적크롤링1

크롤러

크롤러 : 인터넷에 있는 데이터를 자동 수집하는 프로그램

  • 웹 브라우저를 흉내내는 프로그램

정적 크롤링(크롬에서 페이지 소스보기가 가능할 때)

  • request: 웹 사이트에 접속, 데이터를 받아오는 역할
  • BeautifulSoup: 데이터를 HTML로 해석하는 역할

동적 크롤링

  • Ajax : 웹클라이언트에서 서버에 데이터를 요청하는 역할
  • JSON : 웹서버로부터 반환되는 데이터
  • JSON.parse() : 반환된 데이터를 사용할 수 있도록 객체화 해주는 역할

실습하기

  • 목표
    • 지속적으로 변하는 인기검색 10위까지의 순위 & 이름 & 현재시가 크롤링해보기
동적크롤링예제

코드

import requests
import json            #json import하기

#custom_header을 통해 아닌 것 처럼 위장하기
custom_header = {
    'referer' : 'http://http://finance.daum.net/quotes/A048410#home',
    'user-agent' : 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.103 Safari/537.36'  }

#해당 접속 사이트가 아닌 원본데이터가 오는 url 추적. network에서 가지고 온다.
url = "http://finance.daum.net/api/search/ranks?limit=10"

req = requests.get(url, headers = custom_header)    #custom_header를 사용하지 않으면 접근 불가

if req.status_code == requests.codes.ok:    
    print("접속 성공")
    stock_data = json.loads(req.text)        #json에 반환된 데이터가 들어가 있다.
    for rank in stock_data['data']:         #stock_data는 'data' key값에 모든 정보가 들어가 있다.
        print(rank['rank'], rank['symbolCode'], rank['name'], rank['tradePrice'])

else:
    print("Error code")

접속 성공
1 A001000 신라섬유 2350
2 A068270 셀트리온 211500
3 A048410 현대바이오 12650
4 A005930 삼성전자 44650
5 A207940 삼성바이오로직스 338500
6 A020560 아시아나항공 6460
7 A002210 동성제약 20150
8 A007460 에이프로젠 KIC 4000
9 A066570 LG전자 77000
10 A000660 SK하이닉스 80200

2019.04.25 정적크롤링(request,beautifulsoup4)

# 질문에 답하기

  1. 정적 크롤링
  2. request
  3. beautifulSoup

크롤링 : 웹 페이지에 있는 자료를 자동으로 수집하는 프로그램

  • robots.txt : 검색엔진에게 어디까지 검색을 허용할 것이냐?
  • 선행지식 : HTML에 대한 이해 CSS Selection을 만드는 방법
  • 정적페이지 크롤링(페이지 소스보기에서 보여질 때)
    • request와 beautifulsoup4를 이용해서 크롤링 및 해석한다.
  • 동적페이지 크롤링(실시간으로 데이터가 바뀔 때)
    • selenum혹은 ajax, josn을 이용하여 크롤링 및 해석한다.
  • 정적페이지 동적페이지 확인 방법
    • 본인이 크롤링 할려는 데이터가 페이지소스보기 했을 때 있는지 확인한다.
    • 검사 및 셋팅(f1)에 들어가서 DEBUGGER의 disable javascript를 한후 새로고침을 해본다.
      • 데이터가 나오지 않으면 javascript를 통해 데이터를 받아온다.
disablejavascript
  • 오른쪽 하단에 위치

브라우저가 혹은 크롤러가 어떤식으로 서버에 접근해서 데이터를 가져오는가?

  1. 주소를 입력하면 해당 서버로 접근한다.(url 필수)
  2. 웹서버 프로그램이 해당 주소에 맞는 내용을 전달한다.(by source code)
    1. request라는 모듈을 이용한다.
    2. request(urllib의 wrapper 클래스)
  3. 웹 브라우저는 받은 소스코드를 해석해서 화면에 보여준다.
    1. 크롤러는 받은 내용을 해석해서 내가 원하는 데이터를 뽑아낸다.
      1. BeautifulSoup 모듈 이용
      2. 해석해서 : html 코드의 해석, CSS Selecter 만드는 방법
    2. 웹브라우저와 크롤러의 차이

정적 크롤링 필요 모듈 설치

  1. requests : 웹페이지에 접근해서 데이터를 가져온다.
    1. pip3 install requests
    2. requests는 aiax로 받아온 데이터를 실시간으로 반영할 수 없다.
      1. 페이지소스코드 보기까지 가능하다.
      2. 내가 원하는 자료를 계속 못 받아온다고 하면 aiax로 구성되서 그런가보다 하고 알아야 한다.
      3. 해결방안으로 selenium : 웹 브라우저를 원격 조작하는 방식의 크롤러이다.
      4. 크롤링 모듈 : request, selenium, scrapy등 상황에 맞는 크롤링 모듈 정리
  2. BeautifulSoup4 : 가져온 데이터를 HTML로 해석한다.
    1. pip3 install BeautifulSoup4
  3. 사용하기
import requests
from bs4 import BeautifulSoup

크롤러의 이용

웹 브라우저를 그대로 따라하기 때문에

  1. 명절표 예매
  2. 콘서트 티켓 예매 등을 할 수 있다.
  3. 빠른 정보 취득
  4. 정보 취득 및 삽입

크롤러자체는 불법이 아니나, 내가 크롤러로 무료로 취득한 정보를 바탕으로 돈을 벌면 불법이다.

만약에 자바스크립트와 같은 것을 통해서 지속적으로 업데이트 되는 웹사이트라면

  • selenum보다는 request가 훨씬 빠르다.
  • selenum : 웹 브라우저 자체를 컨트롤해서 크롤링
    • 요소를 선택해서 사용자의 동작을 흉내낸다. : 클릭, 키보드 입력
    • 선택자 xpath, css
    • xpath : //*[@id="main_content"]/div[2]/div/dl/dt[2]/a
    • css : #main_content > div.list_body.newsflash_body > div > dl > dt:nth-child(2) > a

2019.04.25 크롤링 기본(HTML과 선택자 이해하기)

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

# 질문에 답하기

  1. 크롤링의 그림
  2. HTML이란?
  3. 선택자란?

크롤링의 큰 그림

  1. 우리가 가지고 오고 싶은 부분을 눈으로 확인하고 ( 첫 번째 줄을 가지고 와보자)
  2. HTML 구조를 분석해서 선택자를 선별하고 (태그는 div 이고 class는 1234 이네!)
  3. 선택자를 컴퓨터가 알아먹게 변환 (div.1234)

HTML의 개념과 구조 이해하기

  • HTML은 hypertext markup language로서 hypertext는 링크로서 한 문서에서 다른 문서로의 이동을 이야기하며 markup langage는 태그를 이용해서 문서를 작성한다는 이야기입니다. 즉 웹페이지를 만들기 위한 언어로서 문서간의 이동을 목적으로 만들어졌고 태그들을 이용하여 문서를 작성하는 언어입니다.
  • 기본적으로 태그들은 각각의 고유한 역할을 가지며 들여쓰기를 통해 내부 단계를 구현합니다.
  • 들여쓰기를 통해 부모와 자식구조를 표현하며 이를 도식화하면 나무와 닮아있기 때문에 트리구조라고도 한다.
  • 너무 좋은 내용이 있어서 가지고와 본다. Stage 1 - HTML에 대해 알아보자 - 네이버 함께 정복하는 크롤링 스터디

HTML 구조 연습하기

제품리스트

출처 : Stage 2 - 선택자 대해 알아보자 - 네이버 함께 정복하는 크롤링 스터디

  • 제품리스트
    • 첫번째 줄
      • 첫번째 상품
        • 상품명
        • 최저가
        • 상품평
        • 판매처
      • 두번째 상품
        • 상품명
        • 최저가
        • 상품평
        • 판매처

    • 두번째 줄
      • 첫번째 상품
        • 상품명
        • 최저가
        • 상품평
        • 판매처
      • 두번째 상품
        • 상품명
        • 최저가
        • 상품평
        • 판매처

데이터를 특정짓기 위한 선택자

  • html은 반복적으로 태그들이 쓰이기 때문에 컴퓨터는 특정 태그에 대해서 혼동이 올 수 있다.
  • 따라서 태그에 특정 별명을 붙이는데 이 별명을 선택자라고 하며 ID와 클래스가 가장 많이 쓰인다.
  • id는 고유한 식별자로서 한 ID는 html 문서에 하나밖에 존재하지 않는다.

컴퓨터에게 선택자 알려주기

  • 태그 : 그냥 태그명을 적어주면 된다.
  • ID가 1234 인 div 태그 : div#1234
  • class가 1234인 div 태그 : div.1234
  • 우리가 가지고 오고 싶은 부분을 눈으로 확인하고 그 부분의 html 태그를 해석하여 선택자를 선택하고 컴퓨터에게 선택자를 알려준다.

연습하기

크롤링2

출처 : Stage 2 - 선택자 대해 알아보자 - 네이버 함께 정복하는 크롤링 스터디

  1. 자식선택자
    1. 바로 밑의 선택자를 찾아야 할 때 사용
    2. 우측의 영상제목들만 가지고 오기
    3. div.cds>span.title
  2. 자손 선택자
    1. 자식선택자를 건너띄고 자손선택자를 가지고 오기
    2. div#container span.title
  3. 위의 2개 모두 불러오는 것은 같으나 불러오는 상황이 다르다.

선택자 선택하기

  1. 간결성
    1. 결국 선택자 경로상에서 가장 중요한 것은 우리가 원하는 데이터가 있는 마지막 선택자이다.
  2. 정확성
    1. 해당 태그가 그 태그만 속한 것이 맞는지 확인이 필요하다.
      1. <span class ="hit"> ===> 컨트롤 + F 를 통해 span.hit 을 검색해본다.
  3. 세분화
    1. 어디까지의 조상을 결정 할지를 잘 확인하자.

2019.04.24 TIL

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

# 질문에 답하기

  1. 랜덤 숫자 추출 방법
  2. key값 기준으로 정렬하기
  3. value값 기준으로 정렬하기

랜덤 숫자 추출

import random

  1. random.randrange()
    1. random.randrange(1, 7) : 1이상 7 미만의 난수 생성
    2. random.randrange(1, 101, 2) : 1부터 100사이의 임의의 짝수
  2. random.randint()
    1. random.randint(1, 100) : 1부터 100사이의 임의의 정수

자료 섞기

  1. abc = ["a", "b", "c", "d", "e"]
  2. random.shuffle(abc)

아무 원소나 하나 뽑기

  1. abc = ["a", "b", "c", "d", "e"]
  2. random.choice(abc)

key값 기준으로 정렬하기

  • import operator
  • sorted(dic.items(), key=operator.itemgetter(0))
import operator
numbers_count = {1: 122, 2: 112, 3: 111, 4: 115, 5: 119, 6: 107, 7: 114, 8: 118, 9: 88, 10: 120, 11: 120, 12: 123, 13: 121, 14: 122, 15: 116, 16: 109, 17: 123, 18: 121, 19: 118, 20: 124, 21: 112, 22: 91, 23: 99, 24: 114, 25: 111, 26: 116, 27: 126, 28: 104, 29: 106, 30: 105, 31: 118, 32: 96, 33: 121, 34: 133, 35: 105, 36: 115, 37: 117, 38: 110, 39: 116, 40: 125, 41: 103, 42: 108, 43: 123, 44: 112, 45: 121}

# 오름차순 정리
sorted_num = sorted(number_count.items(), key=operator.itemgetter(0))
# 역순으로 정리
sorted_num = sorted(number_count.items(), key=operator.itemgetter(0), reverse=True)  

value값 기준으로 정렬하기

  • import operator
  • sorted(dic.items(), key=operator.itemgetter(1))
import operator
numbers_count = {1: 122, 2: 112, 3: 111, 4: 115, 5: 119, 6: 107, 7: 114, 8: 118, 9: 88, 10: 120, 11: 120, 12: 123, 13: 121, 14: 122, 15: 116, 16: 109, 17: 123, 18: 121, 19: 118, 20: 124, 21: 112, 22: 91, 23: 99, 24: 114, 25: 111, 26: 116, 27: 126, 28: 104, 29: 106, 30: 105, 31: 118, 32: 96, 33: 121, 34: 133, 35: 105, 36: 115, 37: 117, 38: 110, 39: 116, 40: 125, 41: 103, 42: 108, 43: 123, 44: 112, 45: 121}

# 오름차순 정리
sorted_num = sorted(number_count.items(), key=operator.itemgetter(1))
# 역순으로 정리
sorted_num = sorted(number_count.items(), key=operator.itemgetter(1), reverse=True)  

2019.03.28 TIL

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

# 질문에 답하기

  1. iterator
  2. generator

전체적인 그림

iterator generator

Iterator

  1. next()함수에 의해서 값을 하나씩 반환한다. (next()함수 호출시점)
  2. Stopiteration

list의 iterator

>>>    li = [1,2,3,4,5]
>>>    it = item(li)        # list의 내장함수 dir(list)를 통해 알 수 있다.
>>>    print(type(it))
list_iterator
>>>    next(it)         #li의 값을 1부터 하나씩 반환한다.
>>>
>>>    for e in li:
>>>        print(e)            #이거를 실행하는 순간 내부적으로 iter(li)가 만들어져서 하나씩 반환한다.
  • 실제로 list는 미친듯이 느리다.
  • 따라서 필요에 의해 list를 iterator로 바꾸어 사용한다.

나만의 iterator 만들기

>>>
>>>    class Myiter:
>>>        def __init__(self.li):            
>>>            self.continer = li
>>>            self.index = 0
>>>
>>>        def __iter__(self):
>>>            return self
>>>
>>>        def __next__(self):
>>>            if self.index >= len(self.continer):
>>>                raise StopIteration
>>>            ret = self.container[self.index]
>>>            self.index += 1
>>>            return ret
>>>
>>>    it_obj = Myiter([1,2,3,4,5])
>>>
>>>    next(it_obj)
1
>>>    next(it_obj)
2
>>>    it = iter(it_obj)    #중간에 iter을 만들어도 기존에 것 해당x
>>>    type(it)
__main__.Mylter
>>>    next(it)
3

파일을 불러와 iterator 추출하기

>>>
>>>    class Reader:
>>>        def __init__(self, filename):
>>>            self.f = open(filename, 'rt')
>>>        
>>>        def __iter__(self):
>>>            return self
>>>
>>>        def __next__(self):
>>>            a = self.f.readline()
>>>            if a == '':
>>>                self.f.close()
>>>                raise StopIteration
>>>            print(a)
>>>            

Generator

제너레이터는 특별한 종류의 이터레이터이다.

  • 제너레이터 역시 함수이다. 독특한 함수이다.

  • 함수 body안에 yield라는 키워드가 있어야 한다.(이게 나오면 자동으로 generator이다)

  • next와 yield이 서로 서로에게 실행을 넘겨주며 호출한다.

  • yield는 양보하다 라는 뜻을 가지고 있다.

    • 제너레이터 에러 막기
    • 제너레이터로 피보나치 만들기
    • coroutine function
    • send()
    • yield from

제너레이터 에러 막기

>>>    def gen():
>>>        print('gen start')
>>>        yield 1
>>>        print("a")
>>>        yield 2
>>>        print("b")
>>>        yield 3
>>>        return 'done'
>>>
>>>    g = gen()    #제너레이터 객체가 생성된다. 실행x
>>>    print(g)
<generator object gen at 0x1060a85c8>
>>>
>>>    next(g)   # 함수를 위에서 내려오며 yield을 만날 떄까지 진행
gen start    # yield가 1을 value로 반환한다.
>>>
>>>    print(next(g))
a           # yield 1을 반환하며 blocking되고 그 이후부터 실행
2              # yield 2를 만나면서 2를 반환하다.
>>>    
>>>    next(g) #b를 print하며 3을 반환하고 blocking
b
>>>
>>>    #next(g)를 하게 되면 StopIteration error가 발생
>>>    #에러를 막기 위해 try exception을 이용
>>>    
>>>    try:
>>>        next(g)
>>>    except StopIteration as exc:
>>>        print exc.value
>>>        pass
>>>    
c
done
>>>

제너레이터로 피보나치 구현하기

>>>    def fib():
>>>        prev, curr = 0, 1
>>>        while True:
>>>            yield prev    #prev 넣으면 0부터 curr 을 넣으면 1부터
>>>            prev, curr = curr, prev+curr
>>>
>>>    f = fib()
>>>    for i in range(10):
>>>        print(next(f), end = ' ')
0 1 1 2 3 5 8 13 21 34
>>>

coroutine function

  • 원래 함수는 실행할 때 스텍프레임이 생성되었다가 종료되면 스텍프레임이 없어진다. 하지만 제너레이터를 보면 실행 중에 멈추어서 다른 일을 하다가 또 호출을 해도 그 이전의 진행했던 사항들을 기억하고 있다. 이것을 가능하게 해주는 것이 coroutine function이다.

함수 실행 도중에 실행 주도권을 다른 함수에 넘길 수 있고, 내가 원하는 시점에 다시 실행 주도권을 가져올 수 있는 함수!!

함수 실행 도중이라는 것은 스텍프레임을 쌓아놓고 다른 곳에 실행주도권을 주는데 그렇게 하기위해 어디에 스텍프레임이 살아 있어야 한다.
명시적으로 실행주도권을 넘긴다.

원래는 os의 스케줄러가 라운드 로빈을 통해 선점형으로 실행되는데 내가 원하는 때 준다는 것은 비선점형이다.

중요한 것은 파이썬의 스텍 프레임이 heap에 할당된다는 것이다. 파이썬의 스텍프레임이 실행주도권을 넘기더라도 살아있을 수 있다. (힙에 저장되어 있으므로)
다른 언어에는 coroutine이 없는가라는 질문이 있을 수 있다.

c나 c++에서는 언어차원에서는 지원하지 않으나,

c : finite state machine기법 -> FSM

  1. 엔트리포인트의 위치를 적절하게 저장하여서 구현(실제로는 스텍프레임이 사라지지만 실행의 순서가 이어지듯이 구현

c++ : stack swapping

  1. 이미 스텍프레임에 쌓인 것은 어쩔수 없지만 heap영역에 마음대로 쌓을 수 있다. stack와 heap이 서로 오가며 스텍프레임을 쌓음

위와 같은 방법을 통해 해결한다.

파이썬의 코루틴은 generator 기반으로 만들어졌고 파이썬의 스텍프레임이 heap에 저장되므로 가능하다.

send()

  • send()는 제너레이터의 내장함수이다.
  • next는 실행주도권을 넘기는꺼지 가능하지만 send를 통해 실행주도권뿐만 아니라 데이터까지 함께 넘길 수 있다.

코드를 통해 send를 알아보자.

>>>    def gen():
>>>        print('gen start')
>>>        deta1 = yield 1  #yield 1이 실행되면 blocking이 걸리므로 아직 data1 변수는 만들어지지 않는다.(오른쪽에서 왼쪽으로 이동)
>>>        print(f"gen[1] : {data1}")
>>>        data2 = yield 2
>>>        print(f"gen[2] : {data2}")
>>>        return 'done'
>>>    g = gen()
>>>    g
generator object gen at 0x1060a8a40
>>>
>>>    first = g.send(None)     #보통 시직할 때는 None을 넣어준다. 
gen start
>>>
>>>    first       #yield 1로부터 1을 받아온다.
1
>>>
>>>    second = g.send('world')     #yield 1에 다시 'world'를 보내준다.
gen[1] : world
>>>    second # yield 2로부터 2를 받아온다.
2 
>>>
>>>    try:
>>>        g.send('hello')
>>>    except StopIterator as exc:
>>>        print(exc.value)
gen[2] : Hello
done
>>>

yield from

  • delegate(중계)만을 한다.
  • 아직까지 명확하게 어떻게 사용해야 할지는 감이 오지 않는다.
>>>    def gen():
>>>        print('gen start')
>>>        deta1 = yield 1
>>>        print(f"gen[1] : {data1}")
>>>        data2 = yield 2
>>>        print(f"gen[2] : {data2}")
>>>        return 'done'
>>>    
>>>    def delegate():
>>>        g = gen()
>>>        print('start')
>>>        ret = yield from g   #g는 따로 관여하지 않고 delegate만 한다. gen()
>>>        print('end')
>>>        print(f'return value:{ret}')
>>>        return ret
>>>
>>>    g = delegate()
>>>    print(g)
generator object delegate 
>>>    first = g.send(None)       #yield from g를 받게 되면 gen으로 이동하며 gen실행이 끝날 때까지 다시 delegate로 내려 올 수 없다.
start
gen start
>>>    print(first)
1
>>>    second = g.send('world')
gen[1] : world
>>>    second
2
>>>    g.send('hello')
gen[2] : hello
end
return value : done

StopIteration  Traceback (most recent call last)
<ipython-input-130-40ccd6fc328d> in <module>
----> 1 g.send('hello')

StopIteration: done
>>>
  • yield from에 대해서는 어떤 상황에 쓰는지 좀 더 공부를 해봐야 할 것 같다.

2019.03.23 TIL

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

# 질문에 답하기.

  1. 단순 객체 복사/ 얕은 복사 / 깊은 복사에 대해 설명하기
  2. 단순 객체 복사/ 얕은 복사 / 깊은 복사 만드는 방법

코드를 통해 알아보는 복사

단순 객체 복사

>>>    #단순 객체 복사
>>>    li = [1,2,3,4]
>>>    li2 = li1
>>>    li2   #[1,2,3,4]
>>>    li2.append(5)    
>>>    li2   #[1,2,3,4,5]
>>>    li   #[1,2,3,4,5]
>>>

li라는 변수는 [1,2,3,4]라는 메모리에 올라온 객체를 가르키고 있다.
li2 = li을 통해 li2는 li의 변수를 복사해온다.
따라서 li2와 li은 같은 객체를 가르키고 있으므로 li2와 li1 가르키는 객체는 완벽하게 동일하다. 따라서 li2를 통해 변경을 하게 되면 li 객체 역시 완벽하게 변경된다.

얕은 복사

>>>    #얕은 복사
>>>    li = [1,2,3,4]
>>>    li3 = li.copy() # or copy.copy(li)도 가능
>>>    li3     #[1,2,3,4]
>>>    li3.append(5)
>>>    li3      #[1,2,3,4,5]
>>>    li       #[1,2,3,4]
>>>

위의 내용을 이해하기 위해서는 단순히 객체가 [1,2,3,4]의 형태처럼 메모리에 올라가 있다고 생각하면 안된다.
실제적으로 파이썬에서 list를 생성한다고 하더라도 모두 모여서 메모리에 올라가지 않는다. [* , * , * , * ]와 같은 형태로 생성되어 첫번째 * 은 1을 가르키고 두 번째 * 은 2를 가르키고 이런 형식이다. 얕은 복사는 [* , * , * , * ]를 복사해 온 것이다. 따라서 li3는 li의 [* , * , * , * ]를 복사해온 것이다. 따라서 li3에서 append를 통해 새로운 요소를 추가하게 되면 li3의 [* , * , * , * ]가 [* , * , * , * , * ]가 되어서 마지막 * 가 새로운 요소를 가르키게 된다. 따라서 li3가 변경되더라도 li는 변경되지 않는다.
하지만 여기에서도 예외는 있다.

>>>    #얕은 복사
>>>    li = [1,2,3,[4,5]]
>>>    li3 = li.copy()
>>>    li3    #[1,2,3,[4,5]]
>>>    li3[3].append(6)
>>>    li3    # [1,2,3,[4,5,6]]
>>>    li     # [1,2,3,[4,5,6]]
>>>

위에서 보면 알 수 있듯이 원래 얕은 복사에서라면 li3를 바꾼다고 해서 li가 바뀌어서는 안된다. 하지만 li는 변경되었다. 그렇다면 li3는 왜 변경되었을까? 위에 언급한대로 li3는 li의 [* , * , * , * ]를 복사해 온 것이다. 위에 예시에서 마지막 * 은 3번째 인덱스 [4,5]을 가르키고 있다. li3와 li 모두 [4,5]을 가르키고 있는 것이다. 따라서 li3를 통해 [4,5]를 변경하게 되면 마지막 *가 가르키는 것이 변경되는 것이므로 li 역시 변경되게 된다.(즉 li와 li3의 내부리스트는 각은 객체를 참조하기 떄문이다.)

이러한 것을 맡기 위해서는 깊은 복사가 필요하다.

깊은 복사

>>>    import copy     #deepcopy를 위해서는 copy를 import 해주어야 한다.
>>>    li = [1,2,3,4]
>>>    li4 = copy.deepcopy(li)
>>>    li4        # [1,2,3,4]
>>>    li4.append(5)
>>>    li4        # [1,2,3,4,5]
>>>    li         # [1,2,3,4]
>>>    li = [1,2,3,[4,5]]
>>>    li4 = copy.deepcopy(li)
>>>    li4        # [1,2,3,[4,5]]
>>>    li4[3].append(6)
>>>    li4        # [1,2,3,[4,5,6]]
>>>    li         # [1,2,3,[4,5]]

깊은 복사를 통해서는 완전히 동일한 새로운 객체를 생성하는 것이므로 li4에 어떤 요소를 추가해도 li는 전혀 영향을 받지 않는다. 따라서 상황에 맡게 단순 객체 복사, 얕은 복사, 깊은 복사를 사용해야 한다.

2019.03.23 TIL

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

# 질문에 답하기.

  1. 객체지향이란?
  2. 객체지향의 특징 3가지

OOP를 시작하기 전에 보면 좋은 것을 지난 번에 TIL한 것에서 가져와보았다.

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

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

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

파이썬에서의 객체

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

object란?

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

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

절차지향 vs 객체지향

###procedural vs object-oriented

객체지향 절차지향

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

  • 추상화란?
    • 상세한 정보는 무시하고 필요성에 의해 있어야할 정보만을 추림. why?
    • 우리가 구현하고 싶은 것들을 설계하기 위해 멤버와 메서드를 뽑아내는 일

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

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

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

  • 현실세계에서 객체를 나타내려면 변수와 함수만 있으면 된다. 현실 세계를 모델링하거나 프로그램을 구현하는데 이처럼 변수와 함수를 가진 객체를 이용하는 패러다임을 객체지향이라고 한다.
  1. 현실 세계(에 존재하는 객체(object)를 어떻게 모델링(modeling)할 것인가?
    • 객체(object) : 사람, 동물, 자동차 등을 어떻게 프로그램으로 모델링 할 것인가?
  2. 사람과 비슷한 사고 방식
  3. 객체를 이용

OOP의 3대 특징

  • 캡슐화
  • 정보은닉
  • 다형성

1. Encapsulation(캡슐화)

정의 : 관련 있는 멤버(변수=데이터)와 메서드(함수=행동)를 하나의 단위(대부분 클래스)로 묶는 것

  • 관련 있는 멤버와 메서드가 중요하다.

  • 하나의 단위 : java, python, c++ 에서는 모두 클래스로 묶지만 꼭 클래스가 아니라도 하나의 단위로만 묶을 수 있다면 OOP로 볼 수 있다.

  • 정보 은닉을 포함

    • 멤버와 메서드를 묶으면서 어떤 멤버를 공개 혹은 공개 하지 않을 것인가 결정
    • 어떤 메서드를 공개 혹은 비공개 할 것인가?
    • 멤버과 메서드를 공개하지 않을 것을 정보 은닉이라고 한다.
    • 정보은닉은 캡슐화 안에서 일어나므로 캡슐화에 포함되었다고 생각

2. 정보은닉

정의 : 특정 멤버와 메서드를 공개하지 않을 것

  1. 파이썬에서는 완벽한 정보은닉을 지원하지 않는다.

    • 그럼 완벽한 정보은닉은 무엇이냐?
      1. c에서는 private를 통해 정보를 은닉시킴
      2. 접근조차 못하게 한다. java, c#, c++모두 지원
  2. 하지만 약간의 기법을 통해 특정 멤버와 메서드가 정보은닉을 바라는 것인지 알 수 있다.

    1. name-mungling
      • self.__ balance = balance —> acnt.__ balance를 하면 실행이 안됨
        • __ balance 를 _Account__balance로 바꾸어서 저장하는 것이다.
      • 함수에도 __를 붙여서 외부에서 쉽게 호출하지 못하도록 한다.
        • 지원을 할려고 노력은 하되 100%지원은 하지 않음
    2. property
      • OOP 호출 전제 조건
        • OOP에서는 외부에서 변수(멤버)에 직접 접근하는 것은 절대 금지이다.
        • 객체의 멤버에 접근이나 수정할 떄는 반드시 메서드를 이용해야 한다.
        • 따라서 객체의 멤버에 접근하기 위해서는 get_ or set_등과 같은 함수를 이용해야 한다.
      • 하지만 property를 이용하여 접근을 일관되게 할 수 있다.
        • 멤버를 호출하면 내부적으로는 함수를 호출하도록 설계한다.
        • 함수를 설계할 때 특정한 정보은닉을 할 수 있다.
  • name-mungling과 멤버에 대한 접근
>>> class Account:
>>>     def __init__(self, name, money):
>>>            self.user = name
>>>            self.__balance = money
>>>    
>>>        def get_balance(self):
>>>            return self.__balance
>>>    
>>>        def set_balance(self, money):
>>>            if money < 0:
>>>                return
>>>            self.__balance = money
>>>    
>>> if __name__ == '__main__':
>>>        my_acnt = Account("greg", 5000)
>>>        my_acnt.__balance = -3000
>>>    
>>>        print(my_acnt.get_balance())
>>>        print(my_acnt.set_balance(6000))
>>>        print(my_acnt.get_balance())
>>>        print(my_acnt.__dict__)
>>>
>>> 5000
>>> None
>>> 6000
>>> {"user" : "greg", '_Account_balance' : 6000, '__balance : - 3000'}
  • name-mungling을 통해 바로 접근할 수 없어졌고, 클래스안에서 멤버 앞에 언더바 2개를 붙이게 되면 이후에 이 객체가 만들어 질 때 멤버의 이름은 _ 클래스 이름 __ 멤버 로 설정된다.
  • 객체의 멤버에 접근하기 위해 get_balance와 set_balance 함수를 통해 접근하였다.
  • property를 활용해 이를 해결 할 수 있다.
>>>    class Person:
>>>    def __init__(self, name, money):
>>>       self.name = name
>>>            self.money = money
>>>
>>>        @property
>>>        def money(self):
>>>            print('getter executed')
>>>            self._money = money
>>>
>>>        @money.setter
>>>        def money(self, money):
>>>            print('setter executed')
>>>            if money < 0:
>>>                print("음수보다 큰 값을 넣어주세요")
>>>                return
>>>            
>>>            self._money = money    
>>>            
>>>    if__name__ == "__main__":
>>>        john = Person('john' , 5000)
>>>        print(john.__dict__)
>>>        john.money = -5000
>>>        print(john.money)    
>>>
>>> setter excuted
>>> {'name': 'john', '_money': 5000}
>>> setter excuted
>>> 음수보다 큰 값을 넣어주세요
>>> getter excuted
>>> 5000
  • \ __ init __ 을 실행하여 객체를 만들 때 money 멤버에 오면 setter 메서드가 실행되면서 _money로 저장이 된다.
  • 이러한 방법을 통해 우리는 정보를 은닉하고 멤버에 대한 접근을 일관되게 할 수 있게 된다.
  1. 멤버는 어느 메서드에서도 생길 수 있다!!!!

    1. 파이썬에서는 다른 어떤 함수에서도 멤버가 생성될 수 있다.
    2. 따라서 안쓰더라도 모든 멤버들은 __init__에 넣어줘라!
    3. 인스턴스 멤버를 만든다고 하면 되도록이면 생성자에서 None으로 두고 만들어라
  2. setter를 통해 특정 조건에 대해 멤버 생성을 조절 할 수 있다.

    1. -가 들어오는게 막고 싶다면(특정 조건) 그 기능을 한 함수에 모아서 setter에 집약시킨다.

생성자에 모든 인스턴스 멤버들을 만들기 위해 노력하되 특정 생성자에 대한 생성 조건이 있다고 하면 그것은 하위에 한 함수를 통해 조절한다.

정보은닉은 name-mungling과 property를 함께 사용하여 확실한 은닉을 하기도 한다.

3. polymorphism(다형성)

정의 : 상속받은 같은 인스턴스 메서드를 호출하는데 호출의 주체인 object가 서로 다른 클래스에서 온 인스턴스기 때문에 다른 behavior을 나타낼 때 다형성이라고 한다.

  • 상속을 전제로 한다.
  • 다형성은 추상클래스로 부터 오기 때문에 추상클래스에 대해 먼저 알아야 한다.
  • 추상클래스를 통해 추상메서드를 만들어내면 이후에 만들어지는 객체에서는 추상메서드를 오버라이딩하여 새로운 인스턴스 메서드를 만들어내고, 각각의 메서드들은 다른 behavior을 나타내게 되는데 이를 다형성이라고 한다.
>>>from abc import ABCMeta, abstractmethod
>>>
>>>    class Animal(metaclass=ABCMeta):
>>>        @abstractmethod
>>>        def say(self):
>>>
>>>            print('noting')
>>>
>>>
>>>    class Lion(Animal):
>>>        # def say(self):
>>>        #     print("어흥")
>>>        pass  # 이렇게 say를 빼먹으면 상속받은 메서드를 쓰지 않으면 Lion도 추상 클래스가 된다.
>>>
>>>    class Duck(Animal):
>>>        def say(self):
>>>            print("꽥꽥")
>>>
>>>    class Dog(Animal):
>>>        def say(self):
>>>        print("멍멍")
>>>
>>>    class Deer(Animal):
>>>        def say(self):
>>>            print("사슴")
>>>
>>>    if __name__ == "__main__":
>>>        animals = []
>>>        animals.extend((Lion(), Duck(), Deer(), Dog(), Duck()))
>>>
>>>        for animal in animals:
>>>            animal.say()                 #"어흥", "꽥꽥", "멍멍", "사슴"
>>>
>>>        ani = Animal()  # 이 세상에 그냥 동물은 없으므로 이것을 못되게 해야 한다.
>>>        ani.say()
  • from abc import ABCMeta (abstract base class)
  • 추상클래스는 매개변수로 metacalss = ABCMeta를 받아야 한다.
  • 추상매서드를 1개 이상 포함하고 있어야 한다.
  • 추상클래스를 상속받아 생성된 클래스는 모두 추상매서드를 오버라이딩해야한다.

추상클래스

정의 : 동물은 고양이과, 개과로 나눌 수 있다. 또 고양이과에는 사자, 고양이, 호랑이 등등 더 세분화 되어 들어갈 수 있다. 하지만 단순히 고양이과의 객체를 만들어서는 그 객체를 구체화 시킬 수 없다. 따라서 객체를 만들기에 너무나 추상적이라 객체를 만들 수 없도록 한 것이 추상클래스이다..

  • [참고] (https://itewbm.tistory.com/entry/%EC%B6%94%EC%83%81%ED%81%B4%EB%9E%98%EC%8A%A4abstract-class%EC%9D%98-%EC%A1%B4%EC%9E%AC-%EC%9D%B4%EC%9C%A0) : 추상클래스의 존재 이유

  • abstract class의 특징

    1. 인스턴스를 만들 수 없다.
    2. abstract method를 가져야 한다.
      1. 추상 메서드 (= 순수 가상 함수)는 몸체가 없어야 한다.
      2. 파생 클래스는 추상메서드를 반드시 overriding을 해야 한다.!!!!!!!!!!
      3. 상속받은 메서드를 사용하지 않으면 그 클래스도 추상 클래스가 된다.(상위)—> 구현을 강제 할 수 있다.
      4. 함수 시그니처는 함수 인터페이스 , 함수 사용법이다.
        1. 객체는 공개된 메서드만 가져와서 사용한다.
        2. 유저가 사용할 수 있는 메서드의 목록 및 사용방법을 보여주는 것을 인터페이스라고 한다.
        3. 객체에서 인터페이스라고 하면 유저프로그래머는 내부 프로그램을 알 필요가 없다. 공개되어 있는 메서드만 가져다 쓰면된다.
        4. 이게 객체에서의 인터페이스이다.
        5. 인터페이스를 공개한다는 말은
          1. 유저에게는 인터페이스를 제공하고
          2. 클래스를 상속받아 쓰는 사람들에게는 다형성을 제공해준다.
  • 메서드 오버라이딩(메서드의 재정의)

    1. 상속 일때만 해당된다.
      1. 정의 : 상속을 하는데 자식 클래스가 어떤 함수(이미 상속을 받아서 이미 있는 함수)를 replace한 경우
      2. 부모로부터 부여 받은 함수를 자신에게 맞추어 메서드를 재정의 하는 것
    2. 메서드 오버라이딩을 통해 구현 가능 한 것
      1. 객체에 따라 함수 결과 값을 다르게 바꿀 수 있다.
        1. 함수를 호출하는 객체가 다르므로
    3. 다형성을 구현하기 위한 기술적인 특징
    4. 이것을 기반으로 다양한 패턴이 나온다.
    5. 메서드 오버라이딩을 하는 순간 다 가상 함수라고 생각해도 된다.
  • 연산자 오버로딩(메서드의 중복정의)
    1. 다형성의 한 종류
    2. 직접 정의한 클래스에서 생성된 객체는 기본적으로 연산이 불가능하다.
    3. 직접 정의한 클래스의 객체에 +,-,와 같은 일반 연산을 적용하려면 *연산자 오버로딩**을 통해 객체를 연산 가능한 상태로 만들어야 한다. (기존에 있던 연산자의 기능을 바꾸어 중복으로 정의)
    4. 파이썬에서는 다른 자료형에 적용되는 모든 연산을 사용자가 정의하는 클래스에서 동작할 수 있도록 구현할 수 있다.

2019.03.22 TIL

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

# 질문에 답하기

  1. is-a / has -a에 관해 이야기 해보시오

궁극적으로 코딩이란 클래스 간의 계층 구조를 만들고 설계하는 것

좋은 설계와 나쁜 설계

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

클래스와 클래스 사이의 관계 설정 방법

  • 클래스와 클래스 사이의 관계에는 Is -A 와 Has - A 가 있다.
  1. 클래스와 클래스 사이의 관계 설정 방법
    1. is-a ( is - a는 상속이용)
      1. 첫번째 조건 모든 멤버와 메서드가 중복이 되어야 한다.
        1. 부모가 가진 멤버와 메서드 중 단 한개라도 가지지 않으면 사용할 수 없다.
      2. 상속 받은 모든 멤버와 메서드를 가지게 되어 재사용성이 가능해진다.
        1. is-a가 아닌데 재사용만하고 싶으면 composition을 통해 구현해야 한다.
      3. 언제 상속을 쓰나?(1번 조건을 전제하에)
        1. 멤버가 추가될 때
        2. 메서드가 추가 될 때
    2. has - a (논리적으로 조금 차이가 있다)
      1. 객체의 멤버로서 다른 객체를 포함해서 사용한다.(composition, aggregation 둘다 대전제)
      2. 상속이 아니다. 서로 has-a관계 일 때 composition 혹은 aggregation을 활용
      3. composition(합성)
        1. 코드 재사용을 위해서 관계를 구축하는 거라면 상속보다 컴포지션을 이용한다.
        2. same life cycle
          1. 객체들의 생성 시점과 소멸 시점이 같다.
        3. strong coupled
          1. 객체들의 생성 시점이 같다.
          2. 객체들의 소멸 시점이 같다.
          3. cpu, ram, computer는 강하게 커플링 되어 있다.
      4. aggregation(통합)
        1. Not same life cycle
        2. weak coupling
        3. 내가 필요할 때만 받아서 쓰고 필요없으면 반납한다.

정리

IS-A

  1. A laptop IS - A computer
  2. ~클래스은 ~클래스의 한 종류이다.
  3. inheritance(상속)으로 구현

HAS-A(합성).

  1. A policeman has a gun.
  2. ~이 ~을 가지고 있다. or 포함하고 있다.
  3. composition(합성) or aggregation(통합)

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(" ") 
>>>

+ Recent posts