Blog Full Notice
back to main page

28 분 소요

motivation: 네이버 블로그

#파이썬 문법 정리 : 네이버 블로그

강의영상 출처: 혼자 공부하는 파이썬 개정판 유튜브. 강의영상 1-91 내용.

1. 스네이크케이스: 단어와 단어 사이에 _를 붙이는 것. 단어는 모두 소문자로 시작

캐멀케이스: 모든 단어의 앞글자를 대문자로 해서 붙이는 것. 첫번째 글자도 대문자로 하는 이유는 클래스와 변수/함수를 구분할 수 있기 때문이다. 파이썬에서는 클래스를 첫번째 글자 대문자로, 변수는 소문자로, 스네이크케이스로, 함수는 소문자, 스네이크케이스, 괄호로 표시하기 때문이다.

2. print("안녕하세요.", 10) #뛰어쓰기 하고 print됨, print하고 들여쓰기(newline) 됨.

print()는 newline.

3. 파이썬의 자료형은 문자열, 정수, 부동소수점(float), bool이 있다. 각각을 변환해주는 함수는

str(), int(), float(), bool()이 있다.

print(type(a)) -> 자료형이 나온다.

4. 문자열 만들 때는 ""와 "" 또는 """ """을 사용해도 된다. 이때 마지막은 여러줄 문자열이라고 한다. 또한 여러줄 문자열을 사용할 때면 처음과 마지막에 newline을 생략되게 만들 수 있는데, """\ \"""를 통해서 newline을 씹을 수 있다. 또한 익스케이프 문자는 \ (역슬레쉬)인데, newline, tab, backslash를 쓸 때 붙여써준다. print\ (newline)(1,2,3) 해도 실행이 된다.

5. 문자열 연산자에는 * + [인덱스:인덱스:스탭]이 있다. * 는 몇번 반복해준다. +는 뒤에 더해준다. [시작인덱스:포함하지 않는 인덱스: 2개씩 보는 수.] (e.g. str="hello", str[4:2:-2] 하면 o 만 출력된다.) (str 끝까지 순회하고 싶을 때에는 list의 두번째 파라미터를 빈칸으로 남겨두면 된다. str[0::] 하면 처음부터 끝까지 나온다. 세번째 파라미터인 스탭을 빈칸으로 두면 1이 default로 들어간다.) (리스트, 문자열에서는 순방향으로 순회할 때 두번째 파라미터에 0이 들어가면 안된다. 그냥 빈칸으로 두어야 한다. 마찬가지로 거꾸로 순회할 때 두번째 파라미터에 -1이 들어가면 안된다. )

6. str[숫자]를 인덱싱, str[숫자:숫자]를 슬라이싱이라고 한다. 문자열 하나를 선택하는 것을 인덱싱, 문자열 두개 이상을 선택하면 슬라이싱이라고 한다. 인덱싱에는 hello기준으로 -5부터 4까지만 들어가야 한다. 그 초과/미만을 넣게 되면 IndexError가 발생한다. 슬라이싱의 경우 어떤 값을 넣어도 상관없는데, 명확하게 하기 위해서 len(str)함수를 사용한다.

7. 정수와 정수의 연산자 중 / 만 결과가 부동소수점으로 나오고 나머지는 정수로 나온다. 부동소수점끼리의 연산은 결과가 다 부동소수점이 나온다. 부동소수점과 정수의 연산은 결과가 다 부동소수점이 나온다. 문자열과 정수와의 연산자는 *빼고 다 불가능, typeError가 나온다.

8. 연산자 우선순위가 있는데, 그냥 괄호를 써라. 8**(1/3) 이런 식으로 말이다.

9. a = input("prompt: ") 프로그램의 입력방법 중 표준입력 방법이다. a에 할당되는 것은 사용자가 입력한 것이 들어가는데, 항상 string으로 들어간다. 그래서 다른 자료형으로 사용하려면 명시적 형변환을 사용하면 좋다. 만약 int("23.23")같은 것이 들어가면 ValueError가 나온다. 바꿀 수 없는 것은 error가 나온다.

10. 프로그램 실행단계를 보고 싶을 때에는 https://pythontutor.com/ 에서 파이썬 누르고 코드 올려서 확인하면 된다.

11. format() 함수는 print(a, " + ", b ," = ", a+b)의 수고를 줄이고자, "{}월 {}일".format(month, day) 형식으로 사용할 수 있다. 이때 괄호 안에 {:=+10d} 하면 =는 부호를 왼쪽에다 표시하고, + 부호도 표시하고(왼쪽에), 10개의 공백을 가지고 정수를 print하겠다는 의미이다. 반면 "{:10.2f}".format(52.777)하면 10개의 공백을 가지고 소수점 2번쨰 자리에서 반올림하겠다는 의미이다. "{:.2f}".format(52.777) 는 그냥 소수점 2번째 자리에서 반올림하겠다는 의미이다.

12. split()함수가 있다. a = "10-20-30-40".split(-) 하면 a에는 list로 10 20 30 40이 들어간다. 이때 string으로 들어가겠다 당연히. split() 이렇게 actual parameter 없이 넣으면 newline, tab, space 모두 다 짤라서 리스트로 만들어준다.

13. f문자열은 기능은 format과 똑같은데 형식이 조금 다르다. python 3.7부터 추가되었다. 형식은 print(f"{a} + {b} = {a+b}") 로 바로 사용할 수 있다. 여러줄 문자열에도 사용할 수 있다. f""" {} {} """

14. 비파괴적 연산은 +와 같이 실제 변수에 할당된 값을 변형시키지 않는 연산자다. e.g. a+b 하면 a와 b의 값 둘다 바뀌지 않는다. 파괴적 연산은 반면 피연산자의 값을 바꾼다. a = b 하면 a의 값이 바뀐다. 이와 같이 대부분의 함수, 연산자들은 비파괴적 함수와 비파괴적 연산자다. 예를 들어서 a = "안 녕 하 세 요" 에서 a.split() 하고 print(a) 하면 값이 변하지 않는다.

15. a.upper(), a.lower() 함수. 모두 대문자, 소문자로 만들어준다.

16. a.strip() 도 비파괴적, 어쨋든 왼쪽 오른쪽 공백을 제거해준다. a.lstrip() a.rstrip()도 있다.

17. isXX()함수는 대부분 True, False를 return해준다. a.isalpha()

18. str = "abcdabcd"에서 str.find("b") 하면 1 출력, str.rfind("b")하면 5 출력해준다.

19. in. str[-1] in "02468" 해서 return 값은 True 아니면 False다. not (str[-1] in "02468") 하면 되긴 하는데, 이 문법이 생각보다 많이 쓰여서 str[-1] not in "02468"로도 쓰일 수 있다고 한다.

20. bool 자료형에는 True, False만 있는데, 둘다 대문자로 써야 한다는 특징이 있다. 비교연산자에는 6가지가 있는데, 특이한 점은 파이썬은 x < y < z < k 형태로 사용할 수 있다는 점이었다. 이게 가능한 이유는 x < y and y < z and z < k 형태로 해석한다고 한다. 그래서 x < y > z는 문법상으로는 쓸 수 있다고 한다. x < y and y > z 로 해석되기 때문이다.

(출처: https://stackoverflow.com/a/43677846

Multiple comparison operators in single statement (chaining comparison operators)

Does this do what I think it does? assert 1 < 2 < 3 I couldn't find any reference to this in the docs but I saw it in a high rep answer. It seems to work but it could be luck, like the lef...

stackoverflow.com

)

21. 파이썬에서 현재 날짜와 시간을 구하는 방법은 구글에 'python timezone datetime seoul'이라고 치면 알 수 있다고 한다. 그래도 형식을 적자면

import datetime

import pytz

seoul = pytx.timezone("Asia/Seoul")

now = datetime.datetime.now(seoul)

now.year (year대신 month, day, minute, second 활용 가능.)

22. if 조건: suite. 형식으로 조건문이 형성된다. suite는 파이썬에서 들여쓰기로 구분한다. 만약 indentation을, 들여쓰기를 잘못하면 indentation error가 발생한다. elif 조건: suite , else: suite 로 if elif elif .... elif else 구문을 만들 수 있다.

23. False로 반환되는 값에는 none, 0, 0.0, 빈 컨테이너가 있다. True로 반환되는 값은 false로 반환되는 값을 제외한 모든 값이다.

24. 빈 문자열을 사용하지 않는다면... 을 코드로 표시하면 if (i != ""): 가 된다. 그러나 이거를 if (i): 로 바뀔 수 있다. if (i): exit()해서 아예 프로그램을 종료하지 않는 방법도 존재한다.

25. pass 구문은 조건문의 suite부분에 반드시 어떤 문장 하나라도 있어야 해서, 그냥 pass쓰면 오류 없이 사용할 수 있다. 그러나 아직 구현하지 않았는데, 프로그램을 다 구현했다고 생각해서 그냥 배포해버릴 수도 있다. 그것을 방지하기 위해서 그 if 구문 내로 들어가면 오류를 내주는 raise NotImplementedError를 pass 자리에 넣어주면 그 suite에 들어가는 경우 오류를 만들어준다. raise는 오류를 강제로 발생시켜주는 키워드다.

26. 리스트 기본 문법

a = [1,2,3]

a + a

a * 5

a[::-1]

27. 중첩 리스트

b = [a,a,a]

b[0][0] 하면 1 나옴.

28. 리스트 요소 추가 함수 3개, 요소 제거 함수 4개, 요소 정렬 함수 1개, 요소 존재 확인 연산자 2개

list는 굉장히 메모리를 많이 잡는 data type이라서 대부분의 함수가 파괴적이다. + 빼고는 거의 다 파괴적인듯

a.append(10) # 마지막에 요소 하나 추가

a.insert(0,20) # 0번째 index에 20 추가

a.extend([4,5,6,7]) # a = a + [4,5,6,7]

del a[0] # 제거하고 싶은 인덱스 입력

a.pop(0) # 제거하고 싶은 인덱스 입력, 안적으면 -1 default로 들어감.

a.remove(4) # 제거하고 싶은 요소를 입력한다. 이때 앞에서부터 검사하고 처음에 발견된, 4만 제거된다.

a.clear() # 모든 요소 제거하낟.

a.sort() a.sort(reverse=True) # 오름차순, 내림차순 정렬

1 in a, 2 not in a # 요소가 a안에 있는지.

29. 2차 중첩 리스트에서 요소를 접근하려면 어떻게 해야 하는가?

for loop을 두번 돌려야 한다. 이중 for loop을 사용해야 한다. 왜냐하면 for loop이 결국에 하나의 리스트 중첩을 제거해주기 때문이다. for i in range a: 하면 한번 loop을 돌 때마다 a의 각 요소에 접근해주기 때문이다.

30. 항등원, identity element 개념.

*의 항등원은 1이 된다. 어떤 operator의 항등원의 정의는 A operator k = A 를 만드는 k를 의미한다. +의 항등원은 0이다. for loop에서 곱/합을 누적해야 할 때 항등원은 초기값으로 설정해준다.

31. 전개연산자

*[1,2,3,4] 는 1,2,3,4 형태로 나오게 된다. 그래서 [2022, 1, 23] 이면 "{}{}{}".format(*[2022, 1, 23]) 형태로 사용하면 편하다.

32. 딕셔너리의 정의와 값 참고,수정, 제거, 추가, 키 존재유무 확인 방법

정의: dictionary1 = {"key1": 1, "key2": 2,} #dictionary의 키 값은 숫자, 문자열, 불, 튜플을 사용할 수 있다. #값에는 모든 자료형을 쓸 수 있다.

참고: dictionary1["key1"] 하면 1이 나온다. for i in dictionary1: print(dictionary[i]) # dictionary에 for loop을 돌리면 key값이 순차적으로 접근된다.

수정: dictionary1["key1"] = "ㄻㅇㄴ"

제거: del dictionary1["key1"]

키 존재유무 확인: dictionary1.get("key1") != None: 또는 if "key1" in dictionary1:

하면 된다. get()함수는 return값이 key의 값이다.

33. 어떤 변수 a의 자료형이 list냐, dictionary냐 판단할 수 있는 문법

if (type({}) is dict:

if (type([]) is list:

34. range() 함수 사용법.

range(10, 0 -1, -1) #range가 list와 다른 점은 거꾸로 순회할 때 두번째 parameter에 -1을 넣어도 된다는 것이다. list는 안됨. 마찬가지로 순방향으로 순회할 때 두번째 parameter에 0을 넣어도 된다. range에서는. list와 문자열에서는 그러면 안된다.)

range(0,10 + 1)

35. list1.append(10)은 너무 느리다. 그러면 어떤 방법을 써야 하는가?

a = [0] * 10000 #먼저 초기화. 후 수정.

36. 여러줄 입력 받기

여러번 input() 받으면 된다.

a = input().split()

$$ 백준의 단계별로 풀어보기 반복문 숙제, 5번에서 for문 문제를 풀 때 입출력 방식이 느리면 여러 줄을 입력받거나 출력할 때 시간초과가 날 수 있어서 input() 대신 import sys 하고 sys.stdin.readline()을 써야 한다고 한다. strip()함수도 써줘야 한다. 그 이유는 input은 \n을 제거해주는데 sys.stdin.readline()는 \n도 같이 오기 때문.

37. 거꾸로 list, range를 만들어주고 iterator type으로 return해주는 함수. 그리고 중요 특징 한가지.

list(reversed([1,2,3,4,5])) 하면 뒤집어져서 나옴.

보통 for i in reversed(range(1,10)): 으로 쓴다.

근데 reversed는 iterator type으로 return해주기 때문에 한번만 사용이 가능하다.

예를 들어 a = reversed(range(0,11)

for i in a: print(i)

for i in a: print(i)

하면 첫번쨰 줄만 실행이 된다. 그 이유는 a가 iterator type이라서 그렇다.

38. numbers = [1,2,3,3,4,5,6,6,7,8,8,9,9,3,4,5,1]에서 빈도수 체크하는 코드.

counter = {}

for i in numbers:

if ( i not in counter): counter[i] = 0

counter[i]+=1

하면 dictionary 에 있는 숫자들을 key 값으로 몇번 숫자가 나왔는지 알 수 있다. 이 형식은 유명해서 암기할 수 있으면 암기. 또한 key값으로 숫자가 쓰이는 use case다.

39. while (True):

break: 해당 loop을 즉시 벗어남

continue: 해당 loop의 밑의 logic을 수행하지 않고 그냥 바로 다시 loop의 시작줄부터 그냥 수행한다.

40. python에서는 while loop으로는 구현 가능한데 for loop으로는 구현이 불가능한 것이 있다고 한다. 그 이유는 python에는 for loop에 종료 조건이 없어서 그런 것 같다.

41. list에 적용할 수 있는 함수 4가지, min, max, sum, enumerate 함수.

a = [1,2,3,4,5]

max(a) max(1,2,3,4,5)도 가능. min, sum도 가능하다. 신기한 점은 min max는 리스트의 요소의 data type이 str이어도 가능하다.

enumerate()의 use case를 보자. fruits = ["사과","딸기","귤"]

일 때 a = enumerate(fruits) 하고,

print(a)하면 리스트 안의 튜플로 [(0, ‘사과’), (1, ‘딸기’), (2, ‘귤’)] 만들어준다.

그래서 원래 for i in fruit: print(k,i) 하고 k = k+1 했어야 했는데,

for i in enumerate(fruits): print(i[0], i[1]) 하거나

for i,fruit in enumerate(fruits): print(i,fruit) 이 가능하다.

그러나 중요한 점은 enumerate도 return type이 iterator라서 단 한번만 사용해야 한다.

42. dictionary에 쓸 수 있는 items()함수.

use case를 보자.

dictionary1.items()함수도 [(키1, 값1), (키2, 값2),...] 형태로 만들어준다.

그래서 for i in dictionary1: print(i, dictionary1[i]) 했어야 했는데,

for i in dictionary1.items(): print(i[0], i[1]) 또는

for key, value in a.items: print(key, value) 로 사용이 가능하다.

# items() 함수의 return value는 dict.items이다. iterator는 아닌 듯 싶다.

43. 리스트 내포

a = [표현식 for반복문]

a = [표현식 for반복문 if조건문]

형태의 문법이 허용된다.

44. join 함수의 필요성과 사용방법

",".join(["A","B","C"] 하면 A,B,C 나온다.

"".join(["A","B","C"] 하면 ABC 나온다.

join함수를 어떨때 쓰면 좋은 이유: 한 줄이 너무 길어질 때에는, 여러줄 문자열을 써야 한다. 근데 여러줄 문자열의 단점은 쓰다 보면 indentation이 꼬여서 보기 안좋다. 그래서 어떻게 쓰냐면,

print(",".join([str(component) for component in a])) 형태로 쓴다.

45. n진수에서 10진수로 / 10진수에서 2진수, 8진수, 16진수로 바꾸는 방법

n진수에서 10진수로 바꾸는 방법: int("n진수 숫자", n)

int("1010", 2) int("47", 8) int("ac", 16)

10진수에서 n진수로 바꾸는 방법: f"{10:b/o/x}"

f"{10:b}" f"{10:o}" f"{10:x}"

46. count("str")함수

"1010".count("1") 하면 2 나온다.

count() 함수를 사용하면 문자열, 리스트, 범위 등의 반복 가능한 객체에서 몇개 있는지 알 수 있다.

47. 카운터 코드

A = [1,2,3,1,1,2,3,4,1,2,3,3]

카운터 = {}

for i in A:

if a not in 카운터: 카운터[a] = 0

카운터[a] += 1

48. 복잡도

복잡도는 시간복잡도와 공간복잡도가 있다. 시간복잡도는 내가 어떤 data를 가지고 있는데 단위 연산이 얼만큼 사용되는지 알아보는 개념이다. 데이터의 크기를 n이라고 할 때, n의 식으로 계산 횟수가 나타난다. bigO 표기법으로 알고리즘의 복잡도를 나타낸다.

49. 함수, parameter(매개변수), argument(인수)

함수 정의 문법

def function(var1, var2, ...): suite. #formal parameter

함수 실행 문법

function(1,2) #actual parameter

parameter(매개변수)는 formal(형식) parameter를 의미한다. argument(인수)는 actual(실질) parameter를 의미한다.

이때 suite에 return값이 없으면, None이 return된다. 그냥 순수하게 return keyword를 사용하면, None이 return이 된다.

50. 가변(variable) 매개변수(parameter) 함수

가변 매개변수 함수는 어떻게 만드는가?

def function(var1, *list1) #* 전개연산자를 사용하여 만든다.

for i in list1: suite # 전개연산자를 사용하여 만들어진 리스트를 for 구문으로 처음부터 끝까지의 요소에 접근한다.

근데

def function(*list1, var1)

for i in list1: suite

함수를 이렇게 만들면, 모든 argument가 가변매개변수로 들어가게 된다. 그래서 이렇게 가변 파라미터 함수를 만들면 안된다. (그러나 키워드 인자를 사용하면 되기는 하는데, 이렇게 안만드는 것이 좋다. function(1,2,3,var1=4))

51. 키워드 인자(keyword argument), 위치 인자(positional argument)

키워드 인자는 actual parameter에 'formal_parameter_name=value' 형식으로 쓰는 것을 의미한다. 변수에 값을 명시적으로 지정하는 문법이다. 이게 왜 나왔는가? 원인 1. 가변 매개변수 함수에서 *list1이 var1보다 먼저 나오는 경우를 대비해서, 원인 2. 기본 매개변수 여러개 중 한개만 고치고 싶을 때, 사용하기 위해서.

키워드 인자가 아닌 인자를 위치 인자라고 한다. 키워드 argument를 사용할 때에는, 항상 위치 인자 오른쪽에 사용해야만 한다. 이유는 없다. 그냥 문법이 그렇다. 항상 위치 인자가 먼저, 그 다음에 키워드 인자를 사용해야 한다.

52. 기본(default) 매개변수(parameter) 함수

기본 매개변수 함수는 어떻게 만드는가?

def function(var1, var2, var3=10, var4=1.4): suite

# 이때 기본 매개변수 함수의 특징은 기본 매개변수의 parameter가 와야 하는 지점은 끝부터 와야한다는 것이다. 오른쪽부터 와야 한다. 이 이유는 기본 매개변수가 앞에 있으면 의미가 없기 때문이다. var1이 기본 매개변수라고 하더라도 결국 뒤에 있는 매개변수를 쓸려면 매개변수를 적어주어야 하기 때문이다.

# 근데 기본 매개변수는 보통 키워드 인자로 입력해야 한다. 왜 그런가? 우선 기본 매개변수는 함수 정의에서 맨 뒤에 오게 된다. 즉 기본 매개변수가 아닌 parameter들은 어떤 값들을 적어주어야 한다. 그러나 그 값을 적어주고 난 다음, 위의 예에서 var4만 바꾸고 싶은데, 키워드 인자를 사용하지 않는다면 var3도 적어주어야 한다. 따라서 두개의 인수를 적어주고 var4=4 이런 식으로 키워드 인자로 적게 된다.(즉 마지막 기본 파라미터만 쓸려고 하는데, 키워드 인자를 사용하지 않는다면, 처음부터 끝까지 기본 파라미터에 대한 인수 값을 적어야 한다. 이 경우는 가변 파라미터가 없는 경우를 말한다. 만약 가변 파라미터가 있다면, 반드시 기본 파라미터는 키워드 인자를 사용해야 한다.)

# 따라서 기본 매개변수(var4=4)와 가변 매개변수(*list1)가 같이 사용되는 경우, 그리고 일반 매개변수(var, 기본도 가변도 아닌 매개변수)도 같이 사용되는 경우, 순서는 def func(var, *list1, var4=4): 형태로 적는다. def func(var, var4=4, *list1): 이렇게 적지 않는 이유는, 기본매개변수는 무조건 맨 뒤로 와야 한다(그렇지 않으면 기본 파라미터에 대한 인수를 다 적어줘야 한다. 기본파라미터를 쓰는 이유가 없음). 일반매개변수는 무조건 앞에 와야 한다. 그리고 가변 매개변수는 일반매개변수 앞에 오는 것이 좋기 때문이다.

( def func( *args, **kwargs):

이런 문법도 있다. kwargs의 의미는 keyword argument의 약자로, 키워드 인수로 적으면, 딕셔너리로 들어간다. 예를 들어 func("1","1","1","1",abc1=1, hi1hi=2,_dsf=3) 이렇게 적으면 ['1', '1', '1', '1'] {'abc1': 1, 'hi1hi': 2, '_dsf': 3} 이렇게 들어간다.

)

53. 메모리 구조

파이썬에서 변수를 참조할 때에는, 자신이 소속한 스택부터 참조하고, 가까운 것부터 참조한다. 즉 자신의 wrapper 함수쪽부터 참조한다는 뜻.

기본자료형은 stack에 바로 그 값을 저장한다.

그러나 복합자료형은 stack에 어떤 자료를 가르키는 heap 주소를 저장하고, 그 heap 주소 위치에 자료를 저장한다. 그 이유는 프로그램 동적으로 많이 바뀌는 자료형이기 때문이다.

function의 경우 정의를 할 때 function code가 위치해 있는 주소를 전역 stack에 저장을 한다.

그리고 function이 호출 될 때, 그 function의 stack이 새로 만들어진다. 여기에서는 parameter가 stack에 만들어지고, return할 때 return value에 대한 stack 변수가 하나 만들어진다.

54. UnboundLocalError 의 원인과 그 해결책

# 할당과 할당연산자는 stack의 값을 아예 바꾸어버린다.

a라는 전역변수가 있다고 하자.

UnboundLocalError가 뜨는 이유는 함수 안에서 지역변수 a를 할당 연산자를 통해서 할당을 했는데, 그 전에 함수의 지역공간에서 그 a를 접근하는(append, print, 등등) 함수들이 있으면, UnboundLocalError가 발생한다. 원래 할당만이라도 하지 않으면, 지역공간에서 전역변수를 접근하는 다양한 연산들을 수행해도 된다(전역공간의 혹은 자신을 감싸고 있는 위 지역 공간의 stack만 변경시키지만 않는다면). 특히 복합자료형, 즉 heap을 쓰는 자료형의 경우 dict,list같은 자료형들은 할당연산자를 쓰지 않으면서, 즉 전역 공간의 stack만 변경시키지 않으면서, 즉 해당 자료가 저장되어 있는 heap 주소만 건들이지 않으면, 얼마든지 자료를 수정할 수 있다.

이를 해결하는 것이 global keyword다. global a,b 이런 식으로 쓰면 된다. 이거는 실제로 똑같은 이름의 전역변수가 없어도 쓸 수 있는데, 동작 방식은, 그냥 global stack에 a라는 식별자로 변수가 하나 배정이 된다. 즉 global 키워드를 통해 function 내부의 scope에서도 전역변수를 정의할 수 있는 것이다. 만약 식별자 b에 대한 전역변수가 있었다면, 그냥 지역 공간에 있는 변수 b를 전역변수로 쓰겠다. 라고 명시하는 것이다.

b = [1,2,3,4]

def function():

b = [5,6,7,8]

print(b) # 5,6,7,8

print(b) # 1,2,3,4

b = [1,2,3,4]

def function():

global b

b = [5,6,7,8]

print(b) # 5,6,7,8

print(b) # 5,6,7,8

55. 일반자료형과 복합자료형의 할당 연산자(=)를 통한 복사에서의 차이

a = 1

b = 1

일반자료형은 복사를 하게 되면 값이 1로, stack의 값이 바뀐다.

a = [1,2,3]

b = a

복합자료형은 복사를 하게 되면 값이 그 주소값으로 바뀐다. 그래서 b의 주소값은 a의 주소값이 되므로 값은 객체를 가르키게 된다.

56. 재귀함수

자기 자신을 호출하는 함수다.

수열의 점화식을 이용한다.

57. 재귀함수의 속도를 개선하는 방법과, 재귀함수의 실사용 례: 리스트 평탄화

조기리턴 패턴이란 것도 있다고 한다. 함수 끝에서 리턴하는 것이 아니라 뭐 조건문 중간에서 조기 리턴하는 패턴이라고 한다. 그냥 코딩 트랜드라고 한다. 요즘에는 코더에게 친화적인 코드가 중요시되는 것 처럼.

재귀함수로 하면 n차 리스트도 평탄화 할 수 있다고 한다.

재귀함수는 사실 정의대로 구현하면 느리다. 왜냐하면 같은 함수의 호출을 반복하게 되기 때문이다. 그 이유는 일반식이 아닌 점화식을 사용한다는 점과, 그 점화식이 두개 이상의 이전항을 요구하게 되면, 똑같은 함수를 반복적으로 호출해야 한다. 그래서 재귀함수의 속도를 빠르게 하는 방법이 있는데, 그 방법은, 메모화, memoization을 하면 된다고 한다. 어떤 딕셔너리에 초항을 정의하고, 그 다음부터의 n차항을 계산하게 되면 그 딕셔너리에 저장하는 방식이다.

58. 튜플.

튜플 정의하는 방법

a = (1,) #일차 튜플은 , 를 꼭 붙여주어야 함. 안 그러면 그냥 숫자로 인식함.

b = (1,2,3)

튜플의 특징 2개: 1. 외관이 간단하다 2. 요소를 변경할 수 없다.

#1. 외관이 간단하다. -> 괄호를 생략해도 될 것 같으면 괄호를 생략해도 된다. 그래서 [a,b] = [1,2]로 정의할 수 있지만, (a,b) = (1,2)로 각각의 변수를 정의할 수 있지만, 그냥 a,b,c = 1,2,3 으로 정의할 수 있다.

또한 함수에서 튜플로 여러 값을 return할 수 있다.

list의 enumerate 함수(print i, element), dictionary의 items 함수(key, value) 에서 튜플로 쉽게 표현할 수 있다.

# 2. 튜플은 복합자료형이지만, 즉 주소값을 stack에 저장하고 실제 data는 heap에 저장하지만, 값을 수정할 수 없다는 특징이 있다.

59. 이뮤터블 자료와 뮤터블 자료, 그리고 이 분류의 dictionary에서의 중요성

뮤터블 자료: 변수에 넣었을 때 스택에 있는 값을 변경하지 않아도, 값을 변경할 수 있는 자료. 숫자, 불, 문자열, 튜플이 이에 해당

이뮤터블 자료: 변수에 넣었을 때 스택에 있는 값을 변경해야만, 값을 변경할 수 있는 자료. 딕셔너리, 리스트가 이에 해당

이뮤터블 자료만 딕셔너리의 키로 사용할 수 있다.

사실 구체적으로는 이뮤터블 자료면 hashable하고, hashable이면 dictionary의 키로 사용할 수 있는 것.

(https://stackoverflow.com/a/14535739 All of Python’s immutable built-in objects are hashable, while no mutable containers (such as lists or dictionaries) are. Objects which are instances of user-defined classes are hashable by default; they all compare unequal, and their hash value is their id().)

60. 콜백함수

정의: 1. 다른 함수의 인자(변수)로 사용되는 함수 and 2. 자신을 호출하는 wrapper함수 마지막에 호출되는 함수.(called at the back)

파이썬에서는 콜백함수가 가능하다. 파이썬은 변수에 함수를 저장할 수 있다. 콜백함수가 어떻게 호출이 되냐면, wrapper함수의 stack으로 갈 때, 매개변수에 callback function의 주소가 넘어간다. 그래서 그 주소를 통해서 함수를 호출할 수 있는 것이다.

61. 콜백함수의 활용 예 함수 2개, map과 filter 함수.

이터레이터 = map(함수, 리스트), 이터레이터 = filter(함수, 리스트)

이때 함수는 리스트의 원소 하나에 대한 data type만을 매개변수로 가지는 함수다. filter는 return 값이 True 또는 False여야 한다.

def power(숫자):

return 숫자 ** 2

A = [1,2,3,4,5]

이터레이터 = map(power,A)

print(list(이터레이터))

print([숫자 ** 2 for 숫자 in range(1,5+1) ]) #이걸로 구현이 가능하다.

리스트의 요소를 함수에 전달했을 때, 결과로 True가 나오는 녀석을 모아서 새로운 이터레이터를 만든다.

def odd(숫자): return 숫자 % 2 ==1

이터레이터 = filter(odd,A)

print(list(이터레이터))

print([숫자 for 숫자 in range(1,6) if 숫자%2 == 1]) #이걸로 구현이 가능하다.

62. lambda function과 key 키워드 매개변수

def power(숫자): return 숫자 ** 2

power = lambda var1: var1 ** 2

는 같은 함수다. 매개변수가 많은 경우, lambda var1, var2: var1 ** 2 이렇게 쓰면 된다.

lambda가 효과를 발휘하는 위치는 함수 호출에 바로 꽂아놓을 때다.

e.g.

A = [1,2,3,4,5]

이터레이터 = map(lambda var1: var1 ** 2, A)

min, max, sort 함수는 list에 사용할 수 있는 함수였다. 근데 여기에, key 키워드파라미터가 있다. 이 키워드파라미터는 언제 쓰냐면, 리스트 안에 딕셔너리와 같은, 비교가 안되는 data가 있을 때, 그 자료들을 서로 비교할 수 있도록 그 자료 안에 있는 sub자료형을 이용해 비료할 때 사용한다.

min(리스트, key=callback_func) 형태로 쓰인다.

A = [{"가격": 100, "크기": 400,} , {"가격": 200, "크기": 200,} , {"가격": 400, "크기": 100,}]

def func(list_element):

return list_element["가격"]

min(A, key=func)

형식으로 쓰는 것이다.

이때 func대신, lambda function을 쓸 수 있다.

min(A, key=lambda dictionary: dictionary["가격"])

이렇게 쓸 수 있는 것이다.

A.sort(key=lambda dictionary: dictionary["가격"]) #sort는 이렇게 쓰면 된다.

63. 기본파일 처리

1. 스트림 연결

파일 = open("경로", "모드, r 또는 a 또는 w")

2. 스트림을 통해 data 통신

문자열 = 파일.read() #파일의 모든 내용 가져오는 것.

print(문자열)

3. 스트림 해제

파일.close()

형식으로 파일처리가 이루어진다.

또다른 문법도 있는데,

with open("test.txt", "r 또는 w 또는 a") as 파일: suite #이 문법은 suite 안에 있을 때에만 경로에 해당하는 file을 open하고, suite를 나갈 때, 파일을 자동으로 close해준다.

#read() 대신 한줄씩 읽는 방법.

for one_line in 파일: print(one_line) #one_line에는 \n이 이미 포함되어 있어 print(one_line) 하면 newline이 두번 나온다.

64. random module

import random

random.choice(리스트) 하면 리스트에서 랜덤으로 요소 하나 추출해준다.

random.randrange(시작, 끝) 사이의 숫자로 랜덤으로 뽑아준다.

65. 이터러블, 이터레이터, 제너레이터, 제너레이터 함수와 제너레이터 표현식

for i in 반복할수있는것

여기서 반복할수있는것이 파이썬에서는 '이터러블'이라고 한다.

이터러블은 리스트, 튜플, 딕셔너리 등이 있다.

이터러블을 만들려면 이터레이터를 만들면 된다. 즉 이터레이터는 이터러블이지만, 이터러블은 이터레이터는 아니다.

이터레이터를 만들려면 제너레이터를 만들면 된다. 즉 제너레이터는 이터레이터지만, 이터레이터는 제너레이터가 아니다.

이터레이터를 만드는 방법은 3가지가 있다. 1. 제너레이터 표현식 2. 제너레이터 함수 3. 이터레이터 클래스(나중에 알아서 더 알아보자)

1. 제너레이터 표현식은 리스트 내포의 문법과 같은데, 괄호를 소괄호(parenthesis)를 쓰면 된다.

범위 = range(0,100)

list_comprehension = [i*i for i in 범위]

generator_expression = (i*i for i in 범위)

2. generator 함수는 함수처럼 만들면 되는데, return 대신 yield 키워드를 통해 만든다.

def gen_func():

for i in range(0,100):

yield i * i

generator_function = gen_func()

#1. list_comprehension

for 요소 in list_comprehension:

print(요소)

#2. generator_expression

for 요소 in generator_expression:

print(요소)

#3. generator_function

for 요소 in generator_function:

print(요소)

하면 3개가 다 list만든 것과 똑같이 출력된다.

앵? 근데 list로 만든 것과 똑같이 출력되는 것이면 굳이 제너레이터를 쓸 필요가 있는가?

그걸 알아보기 전에 next()함수를 알아야 한다.

66. next()함수.

print(next(generator))

print(next(generator))

print(next(generator))

print(next(generator))

하면

1

4

9

출력되고 오류가 뜬다. 이것을 통해서, next 함수는 매개변수로 넣은 이터레이터 내부의 요소를 하나씩 꺼낼 수 있고, 다 꺼내고 더이상 안남아있으면 오류를 출력한다는 것을 알 수 있다.

제너레이터를 리스트 내포 대신에 사용하는 이유는 성능상의 이점 때문이다. 리스트 내포를 사용하게 되면, 하나의 리스트를 하나 새로 완성시킨 다음에 for 요소 in [리스트 내포]: 구문을 수행한다. 즉 새로운 리스트를 만들어야 하니깐, cpu, memory 등을 많이 쓰게 된다.

그러나 제너레이터 표현식을 쓰면, 그 iterate되는 부분 모두를 미리 생성하지 않는다. 그래서 메모리 사용량이 적다고 한다.

제너레이터 표현식/함수의 실용예는 큰 csv 파일을 읽을 때 유용하다고 한다. 또는 infinite_sequence를 사용해야 할 때 유용하다고 한다. ( https://realpython.com/introduction-to-python-generators/ )

def csv_reader(file_name):

for row in open(file_name, "r"):

yield row

def infinite_sequence():

num = 0

while True:

yield num

num += 1

for i in infinite_sequence():

pass

(https://stackoverflow.com/a/1995585 , 제너레이터 함수가 제너레이터 표현식보다 약간 더 빠르다고 한다. 근데 뭐 상관은 없는 것 같긴 하다. 둘 간의 속도 차이는.)

제너레이터는 기저에서 어떻게 실행되는 것일까? 표현식을 실행하는 순간에, next 함수고 호출될 때, 연산이 하나씩 일어나게 된다. 그래서 과부하가 분산이 된다. 그래서 일반적으로 리스트 내포보다는 제너레이터표현식을 훨씬 많이 사용하게 된다.

67. 가독성과 유지보수성

가독성: 코드를 쉽게 읽고 이해할 수 있는 성질.

가독성과 유지보수성을 높이기 위해서 변수와 함수를 많이 쓴다. 함수를 사용하면 변경사항을 한꺼번에 쉽게 반영할 수 있다.

68. 68-69강의 문제, 하노이탑

총 인원수 n이 주어졌을 때, 하나의 테이블에는 2 이상 10 이하의 사람만 앉을 수 있다. 이때, 100명을, 앉힐 수 있는 모든 조합의 경우의 수를 구하시오. (i.e. n = 6일 때에는 2+2+2, 2+4, 3+3, 6 의 총 4가지 경우의 수가 있다.) 답은 437420이 나오면 된다.

이 문제 푸는 방식은 나중에 공부하고 보자.

하노이탑도 마찬가지. 굳이 지금 공부 안해도 될 것 같다.

69. 파이썬 설치, vscode 설치, python powershell에서 실행하는 방법

파이썬 설치할 때, 반드시 add python.exe to PATH 체크 해야 한다.

powershell에 들어가서 python test.py 하면 수행된다.

70. 기본적인 쉘 명령어

ls 현재 폴더의 내용물 출력

mkdir 이름 현재 폴더에 폴더를 만듬

cd 폴더이름 / 경로 폴더이름으로 가거나, 해당 경로로 간다

rmdir 폴더 해당폴더 이름을 지운다

explorer . 현재 directory에서 윈도우 탐색기가 열린다.

d: D 드라이버로 이동한다.

powershell에 가서, python test.py 하면 실행된다. vscode에서 ctrl + ` 하면 terminal이 열린다. 거기서 실행해도 된다.

71. 오류, 구문 오류, 예외, 예외 처리

오류: 1. 구문오류와 2. 예외로 나뉜다.

1. 구문오류, syntax error는 프로그램 실행 전에 발생하는 오류다. compiler error라고도 불린다.

2. 예외, runtime error, exception은 프로그램 실행 중에 발생하는 오류다.

기본적인 예외처리 방법은 조건문을 사용한다.

입력 = input()

if 입력.isdigit():

처리

else:

print(“정수를 입력하지 않았습니다.”)

이런 방식으로, 정수로 입력한 경우에만 실행할 수 있게 만들면 예외 발생을 막을 수 있다.

근데 세상에 있는 예외들은 조건이 복잡하거나, 조건을 예측하는 것이 힘들 수 있다. 그래서 만들어진 것이 try except 구문이다.

72. try except 구문

try:

except:

는 반드시 같이 쓰여야 한다.

if 예외처리를해줘야하는조건:

처리

else:

처리

이렇게 해줘야 하는데, 예외가 발생할 수 있는 부분을 찾기 쉽지 않다. 그래서 예외 처리를 해줘야 하는 조건 대신에, 예외가 발생할 수 있는 부분을 try로 감싸주면 된다. 그래서 예외조건을 확인하는 로직을 따로 구현하지 않고도, 예외를 처리할 수 있다.

try:

오류가날확률이있는코드

except:

pass

구문이 굉장히 유용한 때가 많다. try: except: pass 의 use case로는 리스트에서 float만 list에 append하는 방법이다. 이때 try: 출력리스트.append(float(요소)) except: pass 하면 float로 변환되는 값만 append되고, 아닌 요소는 append되지 않는다.

이걸 함수로 def: isfloat(입력): try: float(입력) return True except: return False

해서, 실제 for문에서는 if isfloat(요소): 하면 된다.

try except else finally 구문까지 살펴보자.

try:

예외가 발생할 때 가능성이 있는 코드

except:

예외가 발생했을 때 실행할 코드 (없는 경우 pass 쓰면 된다.)

pass

else:

예외가 발생하지 않았을 때 실행할 코드

finally:

무조건 실행하는 코드

5개의 경우의 수가 존재하는데, 그냥 try except, try except finally만 사용하면 된다.

왜 else는 사용되지 않는가? try에 넣은 것이랑 똑같다. 논리적으로 사용 과정이 똑같다. 그래서 잘 안쓰인다.

그러나 finally는 많이 쓰인다. 그냥 finally 없이 해도 출력되지 않는가? 맞다. 함수 내부, 반복문을 제외하고는 그냥 try except 구문 밑에 놓으면 실행이 된다. finally가 강력한 부분은 함수 내부, 반복문에 있다. 함수 내부에서 try, except, finally 구문이 있는데, try 또는 except가 일어난다고 하더라도, finally는 반드시 실행이 되고 return된다.

그러면 except에서 오류가 발생하면 어떻게 되는가? finally에서 무조건 실행이 되는가? (답변: https://stackoverflow.com/a/34249285 ) 무조건 finally는 실행이 된다. handled 되지 않은 오류처리가 발생하면, 오류는 일시적으로 저장된다. 그리고 나서 finally가 실행되고 오류가 나온다.

반복문의 경우, for i in range(10): try: except: finally: 구문이 있는데, 이때 try에서 break/continue가 발생하더라도 무조건 finally에 있는 코드는 수행된다.

73. 예외 객체와 예외 객체를 통한 except 구문 활용법

예외 객체:

try:

pass

except 예외의종류 as 예외객체의변수이름1:

pass

except 예외의종류 as 예외객체의변수이름2:

pass

....

보통 예외객체의변수이름 을 e 또는 exception이라고 쓴다.

try:

pass

except NameError as e:

pass

except IndexError as exception:

pass

except ValueError: # 자신의 suite에서 예외 객체의 변수를 사용하지 않는 경우 굳이 'as e' 쓸 필요는 없다.

pass

except NameError: # except (NameError, ValueError) as e: 도 가능하다고 한다.

pass

except Exception as e:

if type(e) == NameError

print(e)

#모든 예외들의 부모가, Exception 예외 객체다. 그래서 어떤 에러를 입력해야할 지 모르면 그냥 Exception을 씀녀 된다. 그래서 다른 세부적인 예외 객체를 쓰는 경우 가장 밑에 Exception 객체를 사용해야 한다. 왜냐하면 try except 구문도 위에서 아래로 실행되기 때문이다. 또한 except 구문은 elif와 같아서 하나의 except 구문으로 들어갔으면 다른 except 구문은 들어가지도 않는다.

74. 예외 강제 발생 문법

raise 예외객체

raise 예외객체(메세지)

raise 예외객체()

raise "정수를 입력하지 않았습니다"

raise Exception("예외를 강제로 발생시킨다")

raise NotImplementedError("명시적 구현되지 않음 명시")

깃허브에서 코드 살펴보기는, 구글에 Github Flask 와 같이 이미 많이 사용되는 라이브러리 이름 앞에, Github 붙여서 검색하면 된다.

그리고 들어가서 왼쪽 상단에 raise라고 검색하면 많이 나온다. 사용례들을 보면 된다. finally 찾아보면, return/yield가 사용되는 것을 볼 수 있다.

75. 객체지향 프로그래밍 이념

프로그래밍 이념이 있다.

1. 절차형 프로그래밍. goto, jump 구문을 사용하지 말고, 함수를 사용하자.

2. 함수형 프로그래밍. 함수는 수학적 개념이어서, 순수 함수를 사용해야 한다.

3. 명령형 프로그래밍. 명령문을 사용해서 프로그램이 어떤 동작을 할지 순서대로 나열해서 프로그램을 만들자고 하는 것.

4. 객체지향 프로그래밍. 좋은 프로그램이란 규모가 큰 프로그램, 이 기준에서, 함수를 묶어서, 하나의 묶음으로 만들면, 규모가 큰 프로그램을 쉽게 만들 수 있다.

파이썬은 이 4가지 이념이 모두 들어가있다.

- 명령형 프로그래밍 이념. 명령문을 사용해서 프로그램을 만드는 방식, 동사와 목적어만 사용하는 방식. how.

love(you), print("k")

- 선언형 프로그래밍 이념. 명령형의 반대 이념. html같은, 그냥 존재를 작성하면 코드를 실행하면 만들어지는 그런 프로그래밍 방식.

에서 파이썬은 명령형 프로그래밍 이념이다. 근데 이 이념을 기반으로, 함수를 무진장 많이 만들었는데, 어떤 함수가 존재하는지 알기 어려웠다. 그래서 대규모로 프로그램 만드는 것이 너무 어려웠다. 그래서 주어를 기반으로, I.Love(you)처럼, 카테고리를 만들면 어떨까? 해서 변수와 함수를 모은 것이 객체지향 프로그래밍이다.

관심사로 카테고리 구분을 한 것은 모듈이고, 객체지향 프로그래밍은 클래스를 사용한다.

class를 사용하게 되면 함수들의 관리가 편해진다.

76. 클래스 문법

class ParentClass:

def __init__(self)):

print("__init__, parent")

class ChildClass(ParentClass): #상속 문법.

def __init__(self, arg1, arg2):

if value < 0:

raise TypeError("typeerror다.")

if (arg1.isdigit()):

self.__private_member_data = arg1

self.public_member_data = arg2

def __private_method(self):

return self.__private_member_data

def public_method(self):

return self.__private_member_data

def __eq__(self, other):

if self.__private_member_data == other.__private_member_data:

return True

else: return False

# class의 인스턴스를 이용해서 두 대상이 같다는 ==를 활용하고 싶으면, 클래스 내부에 __eq__ 함수를 정의하면 된다. (__eq__, equal 함수 __ne__, not equal 함수 __gt__, greater than, > __ge__, greater than or equal, >= __lt__, less than, < __le__, less than or equal, <=)

def __add__(self, other):

pass

#__add__ (+) __sub__ (-) __mul__ (*) __truediv__ (/) __floordiv__ (//, 정수 나누기)

# 특히 이런 연산자들은 비파괴적 연산자들이다. 그래서 이런 연산자들은 비파괴적으로 연산자가 정의되기를 바란다. 따라서 이런 함수도 비파괴적으로 만들어야 한다. 그럴려면 clone 함수가 있어야 한다.

def clone(self):

output = ChildClass(self.__private_member_data, self.public_member_data)

return output

#이 함수는 자기 자신을 복제해서 같은 자료형의 다른 인스턴스를 만들어내는 함수다. 이 함수를 이용해서 __add__ 같은 비파괴적 연산자를 구현하면 된다.

def __add__(self, other):

output = self.clone()

# output 변수에 add 하는 함수로 기능을 추가해야 함.

return output

def __str__(self):

return 문자열

# print(instance) 형태로 print 함수를 사용할 수 있게 만들어준다. 왜냐하면 print()함수는 내부에서 어떤 자료형을 문자열로 강제 변환해주고, 그 강제 변환한 문자열을 출력하는데, 이 함수가 강제적으로 문자열로 변환해주는 '표준' 함수라고 보면 된다. print() 함수가 __str__ 함수를 계속 사용하고 있는 것.

# 따라서 반드시 문자열로 return해주어야 한다.

def get_private_member_data(self): #getter 함수

return self.__private_member_data

@property #프로퍼티, 게터 함수에서 get_를 빼면 instance.private_member_data 로 사용할 수 있다.

def private_member_data(self):

return self.__private_member_data

def set_private_member_data(self, value): #setter 함수

if value < 0:

raise TypeError("typeerror다.") #오류 생성은 생성자에서도, 세터에서도 할 수 있다.

self.__private_member_data = value

@private_member_data.setter #세터 함수의 프로퍼티, instance.private_member_data = 10 형태로 사용 가능

def private_member_data(self, value): #setter 함수

if value < 0:

raise TypeError("typeerror다.") #오류 생성은 생성자에서도, 세터에서도 할 수 있다.

self.__private_member_data = value

def func1(self):

super().__init__() # ParentClass.__init__(self) 와 같은데, 대부분 부모의 함수를 호출시 그냥 super()를 쓴다.

print("func1, child")

instance = ChildClass(1,2) #instance 정의

ChildClass.public_method(instance,1,2) #함수호출 방법1, 잘 안사용되는 방법

instance.public_method(1,2) #함수호출 방법2, 대부분 이 방법

- 원래는 게터의 경우

instance.get_private_member_data()

세터의 경우

instance.set_private_member_data(10)

이렇게 작성했어야 했는데,

프로퍼티로 작성하면 게터의 경우

instance.get_private_member_data

세터의 경우

instance.set_private_member_data = 10

형태로 작성할 수 있게 된다.

- 상속의 경우, 부모의 모든 코드가 나한테 왔다고 생각하면 된다. 먼저 child class의 생성자를 호출하려고 한다. 근데 없으면 부모 클래스의 생성자를 호출한다. 이때 부모 class에 있는 함수를 반드시 재정의, override하도록 선언하고 싶으면, 자식클래스가 반드시 재정의해야하는 함수들은 모두 raise 키워드로 오류를 발생시켜주면 된다. 그러면 자식 클래스는 그 함수들을 재정의할 수밖에 없다.

child class의 instance로 class member function을 호출하게 되면 먼저 child class 내부에 있는 member function들을 뒤져본다. 그리고 없으면 부모 class로 찾아가서 찾는다.

- class scope 내부에서, __private_member_data 계속 접근할 수 있다. 무슨 말이냐면

def clone(self):

output = ChildClass(self.__private_member_data, self.public_member_data)

return output

def __add__(self, other):

output = self.clone()

output.__private_member_data = output.__private_member_data + other.__private_member_data

return output

이런 코드가 있다고 해보자. 이떄 output은 같은 클래스의 다른 instance임에도 불구하고, instance의 name mangling된 member data를 접근하고 있다. 따라서, 그냥 class scope 내부에서는 "private" 이건 "public" 변수이건 그냥 상관없이 접근할 수 있는 것 같다. (https://stackoverflow.com/a/56354236 self.__var 가 자식클래스 또는 class scope 외부에서는 self._BaseClassName__var 가 된다.)

- super().부모함수이름() 하면 자식 클래스에서 부모 클래스의 함수를 사용할 수 있다.

77. 상속과 컴포지션

사실 상속은 굉장히 위험한 도구다. 그래서 요즘에는 상속을 최대한 사용하지 말고, 컴포지션을 사용하라고 한다.

컴포지션은 무엇인가? has-a 관계라고 한다. has-a 관계는 무엇인가? 어떤 기능을 부모 클래스에서 상속받지 말고, 그 기능을 가진 변수를 class의 member variable, member data, field로 사용하라는 것이다.

그렇다면 왜 상속이 위험한가? 어떤 안전한 프로그램을 만들기 위해서는 상속받으려고 하는 부모 클래스의 함수를 모두 override해야 하기 때문이다. 즉 이거는 할일이 너무 많다. 블랙리스트가 너무 많아진다. 화이트리스트를 구현하는 것이 더 좋다.

따라서 프레임워크가 상속을 강제하는 경우만 상속을 쓰고, 프레임워크가 상속을 강제하지 않는 이상 반드시/대다수의 경우에만 컴포지션을 사용하는 것이 좋다고 한다.

78. 스택과 큐 구현

스택: FILO, FOLI 구조를 갖는 자료구조.

class Stack:

def __init__(self):

self.__list = []

def push(self, value)

self.__list.append(value)

def pop(self):

output = self.__list[-1]

del self.__list[-1]

return output

큐는 FIFO 구조를 갖는다.

class Queue:

def __init__(self):

self.__list = []

def enqueue(self, value)

self.__list.append(value)

def dequeue(self):

output = self.__list[0]

return self.__list.pop(0)

79. 모듈을 읽어 들이는 기본 문법

1. import 모듈

묘듈.함수()

variable = 모듈.class()

varaible = 모듈.variable

2. import 모듈 as nickname

nickname.function()

nickname.Class()

nickname.variable

3. from 모듈 import AnyClass, any_function, any_variable

AnyClass()

any_function()

any_variable

1. import math

math.sin(1)

math.cos()

2. import math as m

m.tan(0)

m.floor()

m.ceil()

3. from math import sin, cos, tan

sin(1)

cos(1)

tan(0)

기본적으로는 첫번째 방법을 사용하지만, 특정 모듈에 따라서 이 3가지 방법 중에 사람들이 많이 사용하는 법이 정해져있다. 그것을 바탕으로 맞추면 된다.

import pandas as pd

import numpy as np

import tensorflow as tf

이런거는 2번째 방법 쓰면 된다.

인터넷에 있는 데이터를 읽어들여서 분석할 때 사용하는 BeautifulSoup이라는 모듈이 있는데, 이 모듈은 사실 bs4 모듈 안에 있어서, 책, 유튜브, 공식 문서. 다 from bs4 import BeautifulSoup을 쓴다.

80. 기본 내장 모듈

파이썬이 기본적으로 제공하는 기본 내장 모듈은, 구글에서 '파이썬 모듈 문서'라고 검색하면 나온다. 그러면 한글로 정리된 파이썬 표준 라이브러리가 나온다(https://docs.python.org/ko/3/library/index.html).

지금부터 설명할 모듈들은, 어디에 쓸지는 모르겠지만 일단 이런 것이 있다는 것을 인지해두자. 나중에 필요할 때 표준 라이브러리 설명을 통해서 공부하면 된다.

1. '파이썬 시간 구하기' 구글에 쳐보기

from datetime import datetime

2. random module

random.uniform(10,20) 10, 20 사이의 float값을 랜덤하게 만들어준다.

random.randrange(10,20) 10, 20 지점 사이의 정수를 랜덤하게 만들어준다.

random.choice(반복가능한것), 리스트의 요소 중 하나를 뽑아서 return해준다. [1,2,3] 이면 2 return해준다.

3. sys module

import sys

명령 매개변수를 받아준다. 이 뜻은, python lesson.py 라고 터미널에서 입력하면 sys.argv, argument value에서 리스트로 sys.argv[0] 에 lesson.py가 들어간다. 이게 유용한 이유는, 앞으로 프로그램 실행할 때 url 을 python lesson.py 옆에 치면, 프로그램을 매번 수정하지 않아도 된다.

4. os module

운영체제에 대한 정보 추출, 운영체제가 제공하는 파일처리 기능 활용할 때 쓴다.

import os

print(os.name)

print(os.listdir(".") # 특정한 dir 내부에 있는 요소들을 확인 가능

os.mkdir("h")

os.rename("변경전", "변경후")

os.remove("파일")

os.system("시스템 명령어")

5. datetime module

앞에서 시간 출력할 때 사용해서 넘어간다.

6. time module

import time

time.sleep(5)

특정 시간동안 대기해준다.

7. urllib module

import urllib 가 아니라, from urllib import request를 한다. 그 이유는, 이 모듈은 인터넷에 있는 data와 상호작용할 때 사용할 수 있는 모듈이다. 일반적으로, 이 모듈은 큰 모듈이라서, 내부에 여러 sub module이 있는데, 그 중에서 request라는 모듈을 활용하기 때문이다.

from urllib import request

target = request.urlopen("https://google.com")

이렇게 입력하면 구글.com에 있는 데이터를 긁어오게 된다.

print(target.read()) 할 수 있다.

( https://stackoverflow.com/q/18857355 왜 math module은 파이썬이 설치된 곳에 저장이 되어 있지 않은지. 답변: 이미 내장되어 있다. print()함수처럼. )

모듈은 어떻게 활용해야 하는가?

# 모듈의 기본적인 활용 방법

1. 내가 무엇을 해야겠다고 인지

2. 구글에서 ‘무엇을 하려면 어떻게 해야하나요?” 찾은 뒤

3. 그 코드를 복사해서 사용

4. 자주 사용되는 함수가 있다면 이는 외워서 활용

1번 정했으면 그냥 구글 찾아서 써라.

81. 모듈 만들기

파이썬 실행기는 import 모듈이름 이라는 코드가 실행되면 어떻게 동작할까?

# 현재 실행하고 있는 파일이 있는 위치에서, “모듈이름”이라는 파일/폴더가 존재하는지 확인한다.

#존재한다면, 이를 읽어들이게 되고,

#존재하지 않는다면, 환경변수에 등록되어 있는 추가적인 위치에서 모듈을 찾게 된다.

우리는 지금까지 import sys, import math라는 코드를 활용해서 모듈을 읽어들였다.

import sys

print(sys.path) 치면, 첫번째 문자열은 이 파일이 위치하고 있는 경로를 의미하고, 이후의 문자열은 환경변수라고 하는 곳에 등록되어 있는 모듈의 경로를 의미한다. zip은 건너뀌고 이 문자열을 탐색기를 통해서 들어가면 우리가 활용했던 여러 모듈들을 확인할 수 있다. 그러면 모듈은 폴더거나, .py 파일로 되어 있는 것이라는 것을 알 것이다.

우선 모듈을 만들기 위해서 한 폴더에서 main.py랑 module.py를 만들자.

그럼 module.py에 클래스, 변수, 함수를 적고 저장하고, main.py로 와서

import module 하면 클래스, 변수, 함수를 쓸 수 있다.

import module as m

from module import 클래스, 변수, 함수

로 해서도 사용할 수 있다.

82. if __name__ == "__main__": suite 코드의 의미는?

이것을 보자. 이 코드를 module.py에 넣어보자. main.py는 module.py를 모듈로 import 시키는 코드다.

이떄 module.py를 실행하면, suite가 실행된다. 그러나 main.py를 실행해서 module.py를 모듈로써 실행시키면 suite가 실행이 되지 않는다. 그 이유는 module로 실이 되면, __name__이 module이라고 나오기 때문이다. 즉 모듈로 실행이 될 때에는 __name__이 자기 자신의 파일 이름이 되어 suite 부분이 실행이 되지 않는다.

그럼 이 코드는 왜 쓰는가? module.py를 수정하고 그 효과를 보고 싶을 때, main.py를 실행해야 한다. 그러지말고, 그냥 module.py 단에서 실행을 하고 그 효과를 보고 싶을 때, 이 코드를 사용한다.

즉 test를 하는 것이다. 이 test는 test module이 나중에 따로 있게 된다. 이 test 모듈은 if __name__ == "__main__": suite 내부에서 진행하게 된다.

83. 패키지 기본

패키지는 모듈의 규모가 커졌을 때, 그 모듈을 나누는 방법이다.

1. 폴더 내부에 있는 모듈을 읽어들이는 방법

2. 폴더 자체를 모듈로 읽어들이는 방법

3. 이 둘을 섞는 방법

1. 폴더 내부에 있는 모듈을 읽어들이는 방법

folder라는 이름을 가진 폴더에, module1.py, module2.py를 만들었다고 하자.

그리고 'folder' 폴더와 같은 level에 있는 main.py가 있다고 하자.

from import 구문을 반드시 사용해야 한다. import (as) 사용 못한다.

from folder.module1 import 클래스, 변수, 함수 (module1에 있는)

from folder.module2 import 클래스, 변수, 함수

이런 형태로 사용해야 한다.

그럼

class_var = 클래스()

class_var.print()

등등 다 할 수 있게 된다.

참고로 코드가 많이 없으면,

from folder.module1 import *

from folder.module2 import *

하면 된다.

2. 폴더 자체를 모듈로 읽어들이는 방법

import folder #main.py에서

이렇게 폴더 자체를 import 시키면, folder 폴더 안에 있는 하나의 .py 파일, 즉 하나의 모듈만 읽힌다. 그 하나의 파일은 __init__.py 폴더다.

그럼

folder.init에저장되어있는함수()

folder.init에저장되어있는클래스()

folder.init에저장되어있는변수

형태로 사용할 수 있다고 한다.

3. 이 둘을 섞는 방법

그러면 __init__.py 파일에 from module1 import AnyClass1 과 from module2 import AnyClass2를 하면(1번 방법), 그냥 import folder하면 그 folder 안의 원하는 모든 모듈에 대한 기능을 다 쓸 수 있을 것 같다.

근데 오류가 뜬다. ModuleNotFoundError라고 한다. 그 이유는, __init__.py 파일일 내부에서 module1 module2 모듈을 찾을 수 없다는 오류가 발생하기 때문이라고 한다. from module1 import AnyClass1에서 오류가 발생한 것이다.

그렇다면 어떻게 해결해줄 것인가? 패키지 내부 단에서, 패키지 내부에서의 서로의 모듈을 읽어들이기 위해서는, 이 모듈이 상대적인 경로에 있다는 것을 나타내주어야 한다고 한다.

따라서 이떄는 __init__.py 파일에, from import 구문에서 module 앞에 점을 붙이게 된다.

from .module1 import AnyClass1

from .module2 import AnyClass2

그러면 현재 이 init 파일이 있는 위치를 기준으로 상대적으로 모듈을 찾게 된다고 한다. 이렇게 코드를 입력하고 실행하면 코드가 문제 없이 돌아간다.

그러면 import folder

print( folder.AnyClass1 )

print( folder.AnyClass2 )

이렇게 정상적으로 사용할 수 있게 된다.

84. 패키지에서 __all__ 사용법

근데 일반적으로 패키지를 활용할 때에는, from school import * 를 활용해서 모듈을 읽어들이는 경우가 많다고 한다. 그러나, 이 경우에는 class 캡슐화에서 본 것처럼, 너무 많은 기능을 main.py에 접목시키게 된다.

이런 위험한 상황을 방지하기 위해서, 이 패키지에도 캡슐화 비슷한 기능이 있다.

__all__ = [] 이라는 변수를 만들게 되면, 패키지에서 읽어들일 것을 선택적으로 지정할 수 있다.

__init__.py 파일 맨 위에, __all__ = ["AnyClass1", "AnyClass2"] # 각각 module1, module2에 정의된 class들

이렇게 문자열로 넣으면, 이들을 제외한 모든 변수, 클래스, 함수는 읽어들여지지 않는다.

만약에 __init__.py 파일 맨 위에, __all__ = ["module1", "module2"] 라고, 모듈 이름을 지정하게 되면,

from folder import * 로 모든 것을 읽어들였을 때, 이 파일들을, submodule로 읽어들이게 되어서,

module1.AnyClass1()

module2.AnyClass2() 형태로

submodule (module1, module2가 실제 모듈처럼 문법상 동작하고 있음) 처럼 사용되게 된다.

- class 문법: stackoverflow

https://stackoverflow.com/a/12409963

buffer

댓글남기기