파일 입출력에 대하여

Python

with A as B:
	...

A = ‘context manager’가 되는 객체이고 B = ‘A.__enter__메서드의 리턴값’이 된다.

  • with구문이 시작될 때 이 __enter__메서드가 호출되며, with구문이 빠져나가면서 __exit__메서드가 자동으로 호출되는 것이다.
    • with구문에서 open함수를 사용할 때 굳이 file objectclose()헤주지 않아도 되는 이유이다.
with open(...) as f:
	...

에서 open(...)함수가 리턴하는 객체는 file object인데, 이 객체는 __enter____exit__ 메서드를 가지고 있도록 만들어져 있기 때문에 context manager역할하게 된다.

이 객체(open함수가 리턴하는 file object)의 __enter__메서드는 리턴값이 self이다. - file object 자신을 리턴하므로 - with open(...) as f에서 fopen(...)함수가 리턴한 file object가 된다.


json.dump와 json.dumps의 차이 & json.loads에 대하여

Python

json.dump, json.dumps

  • json.dump: Python 객체를 JSON 형식으로 변환하여 파일에 기록한다.
    • 딕셔너리 자료형을 JSON 파일로 생성
  • json.dumps: Python 객체를 JSON 형식의 문자열로 변환한다.
    • dumps함수는 python 객체를 한줄의(직렬화) json 문자열로 변환
    • 리스트나 튜플도 JSON 문자열로 바꿀 수 있다.
  • dumpdumps의 차이는 두번째 인자 fp에 있다.
    • dump는 첫번째 인자를 open한 file 안으로 바로 쓰기를 한다.
    • dumps는 파일로 쓰는 게 아닌 파이썬 내에서 사용하기 위해 json 형식으로 string으로 반환을 한다.
      • 이를 바로 받아서 사용 가능, 파일내로 바로 저장 or print
  • ensure_ascii=False: 데이터를 저장할 때 아스키코드 형태로 변환하지 않겠다는 뜻, 아스키로 바꾸고 싶다면 True로 바꾼다.
  • indent: JSON 문자열을 정렬할 때 쓰인다.

json.load, json.loads

  • json.load함수는 JSON 파일을 읽어들이는 함수이다.
    • 읽은 데이터를 딕셔너리 자료형으로 반환한다.
  • json.loads함수는 JSON 형태의 문자열을 읽어들이는 함수이다.
    • JSON 문자열을 딕셔너리로 변환할때 쓰임.

pandas.read_json

Pandas

  • pandas.read_json 문서
  • pandas.read_json(a valid JSON str, path object or file-like object)를 넣어 pandas object로 만든다.

pandas.json_normalize

  • pandas.json_normalize 문서
  • pandas.json_normalize()는 중첩된 JSON데이터 (딕셔너리 안의 리스트/ list of nested dictionaries)를 평면화하여 dataframe으로 만드는 것
# 예시
ex_data = [
    {"id": 1, "name": {"first": "Coleen", "last": "Volk"}},
    {"name": {"given": "Mark", "family": "Regner"}},
    {"id": 2, "name": "Faye Raker"},
]
pd.json_normalize(ex_data)
  • 출력값

.find, .find_all 함수 이해하기

Python

  • tag 추출하기

find_all(tag, attributes, recursive, text, limit, keyword)

  • tag, attributes
    • .findAll("태그명","dict 형식의 속성")
  • recursive
    • recursiveTrue이면 findAll함수는 매개변수에 일치하는 태그를 찾아 자식, 자식의 자식까지 검색
    • recursiveFalse이면 findAll함수는 문서의 최상위 태그만 검색
  • text
    • text는 태그의 속성이 아니라 텍스트 콘텐츠가 일치하는 값을 찾음
  • keyword
    • 특정 속성이 포함된 태그를 선택할 때 사용됨
  • limit
    • 페이지의 항목 처음 몇개에만 관심이 있을 때 사용
    • 이 매개변수는 페이지에 나타난 순서대로 찾으며 그 순서가 원하는 바와 일치한다는 보장 X

find(tag, attributes, recursive, text, keyword)

  • findAll과 비슷하지만 limit가 없음.
  • findfindAll을 호출하면서 limit가 1인 함수 1개만 출력

import re

Python

  • 파이썬에서 정규 표현식을 사용할 때, 내장 모듈 re를 사용한다.

정규 표현식

  • 특정한 규칙을 가진 문자열의 집합을 표현하는 데 사용하는 형식 언어
  • 복잡한 문자열을 처리할 때 사용하는 기법
  • 주로 문자열의 검색과 치환을 위한 용도로 쓰임

re 모듈의 함수

1. match(패턴, 문자열, 플래그)
  • match()는 문자열의 처음부터 시작해서 작성한 패턴이 일치하는지 확인한다.
import re
 
print(re.match('a','an'))
print(re.match('a','ann'))
print(re.match('a','bba'))
print(re.match('a','ba'))
 
# 출력값
# <re.Match object; span=(0, 1), match='a'>
# <re.Match object; span=(0, 1), match='a'>
# None
# None
  • 예시의 1,2,번의 시작은 a로 시작하여 매칭이 된 것으로 확인되는데, 3,4번은 시작이 b로 시작하여 매칭이 안된 것을 확인할 수 있습니다.
2. search(패턴, 문자열, 플래그)
  • search()match()와 유사하지만 패턴이 문자열의 처음부터 일치하지 않아도 괜찮습니다.
import re
 
print(re.search('a','an'))
print(re.search('a','ann'))
print(re.search('a','bba'))
print(re.search('a','ba'))
 
# 출력값
# <re.Match object; span=(0, 1), match='a'>
# <re.Match object; span=(0, 1), match='a'>
# <re.Match object; span=(2, 3), match='a'>
# <re.Match object; span=(1, 2), match='a'>
  • 패턴과 일치만 한다면 문자열의 시작과는 상관없이 전부 찾아서 결과를 반환해줍니다.
3. findall(패턴, 문자열, 플래그)
  • findall()문자열 안에 맞는 케이스를 전부 찾아서 리스트로 반환합니다.
import re
 
print(re.findall('a','a'))
print(re.findall('a','aba'))
print(re.findall('a','baa'))
print(re.findall('aaa','aaaa'))
print(re.findall('aaa','aaaaaa'))
print(re.findall('\d','숫자123이 이렇게56 있다8'))
print(re.findall('\d+','숫자123이 이렇게56 있다8'))
 
# 출력값
# ['a']
# ['a', 'a']
# ['a', 'a']
# ['aaa']
# ['aaa', 'aaa']
# ['1', '2', '3', '5', '6', '8']
# ['123', '56', '8']
  • 4번째 예시를 보면 패턴은 aaa이고 문자열은 aaaa 4개입니다. 해당 함수는 겹치는 것을 제공하지 않으므로 aaa를 3개 보여주는 형태가 아닌 1개만 보여주게 됩니다.
  • 5번째 예시는 a가 6개 이므로 패턴에 해당하는 문자열이 2개 존재하여 aaa를 2번 리스트에 담아 반환합니다.
4. finditer(패턴, 문자열, 플래그)
  • findall()과 유사하지만 패턴에 맞는 문자열의 리스트가 아닌 iterator 형식으로 반환합니다.
import re
 
re_iter = re.finditer('a', 'baa')
for s in re_iter:
    print(s)
 
# 출력값
# <re.Match object; span=(1, 2), match='a'>
# <re.Match object; span=(2, 3), match='a'>
  • 해당 함수를 사용하는 목적은 패턴에 맞는 문자열과 어느 위치에 존재 하는지를 확인할 때 사용할 수 있습니다.
5. fullmatch(패턴, 문자열, 플래그)
  • fullmatch()는 문자열에 시작과 끝이 정확하게 패턴에 일치할 때 반환합니다. match()는 처음부터 패턴에 맞으면 반환을 하지만 해당 함수는 시작과 끝이 정확하게 일치해야 합니다.
import re
 
print(re.fullmatch('a','a'))
print(re.fullmatch('a','aaa'))
print(re.fullmatch('a','ab'))
print(re.fullmatch('a','ba'))
 
# 출력값
# <re.Match object; span=(0, 1), match='a'>
# None
# None
# None
6. split(패턴, 문자열, 최대 split 수, 플래그)
  • split은 문자열에서 패턴이 맞으면 이를 기점으로 리스트로 쪼개는 함수입니다. 만약 3번째 인자(최대 split 수)를 지정하면 문자열을 지정한 수 만큼 쪼개고 그 수가 도달하면 쪼개지 않습니다.
import re
 
print(re.split('a', 'abaabca'))
print(re.split('a','abaabca',2))
print(re.split('a', 'aaaaaa',3))
 
# 출력값
# ['', 'b', '', 'bc', '']
# ['', 'b', 'abca']
# ['', '', '', 'aaa']
7. sub(패턴, 교체할 문자열, 문자열, 최대 교체 수, 플래그)
  • sub는 문자열에 맞는 패턴을 2번째 인자(교체할 문자열)로 교체합니다. split의 최대 split 수와 동일하게 최대 교체 수를 지정하면 문자열에 맞는 패턴을 교체할 문자열로 교체하고 그 수가 도달하면 더이상 교체하지 않습니다.
import re
 
 
print(re.sub('a','z','ab'))
print(re.sub('a','zxc','ab'))
print(re.sub('a','z','aaaab'))
print(re.sub('a','z','aaaab', 1)) # a를 z로 교체 한번
 
# 출력값
# zb
# zxcb
# zzzzb
# zaaab

※나중에 쓸 일이 있을 때 더 추가


if __name__ == "__main__":

Python

파이썬에서의 네임스페이스(namespace)

  • namespace : 개체를 구분할 수 있는 범위

    • 어떤 객체를 이름으로 구분할 수 있는 범위를 의미
  • 파이썬에서는 모든 것이 객체

    • 객체들은 각자의 이름을 가지고 구분할 수 있다.
    • 하지만, 객체를 이름만으로 구분 ❌
      • 모든 함수, 변수, 클래스등의 이름을 전부 다 다르게 설정해야함
      • 불가능
  • 객체마다 자신의 영향력을 행사할 수 있는 범위를 제한하여 이름의 중복을 허용

파이썬에서 네임스페이스 3가지

  1. 지역 네임스페이스(Local namespace) - 함수(메소드)별로 구분되는 네임스페이스
  2. 글로벌 네임스페이스(Global namespace) - 모듈 단위로 구분되는 네임스페이스
  3. 빌트인 네임스페이스(Built-in namespace) - 내장 함수를 포함한 전체 코드 네임스페이스
  • 어떤 객체를 참조하려고 할 때, 탐색 순서는 지역 글로벌 빌트인 순으로 탐색
    • 역순 참조는 불가능

if __name__ == "__main__"의 기능

  • __name__의 특징
# test1.py
print(__name__)
# 출력값
__main__
  • 파이썬 파일을 커맨드라인이나 인터페이스를 통해 직접 실행한 경우 __name__에는 __main__이라는 값(네임스페이스)이 설정됨
# test2.py
import test1
 
print(test1.__name__)
 
# 출력값
test1
  • 파이썬 파일을 모듈로서 사용, 즉 **다른 파일에서 불러와 사용하는 경우 __name__에는 모듈 이름이 설정됨

  • if __name__ == "__main__"해당 구문이 사용된 파이썬 파일을 직접 실행했을때만 아래 코드를 실행하겠다 라는 뜻

  • 예시

# test1.py
print("메롱")
 
# test2.py
import test1
 
print("ㅎㅇ")
 
# 출력값
메롱
ㅎㅇ
  • test1에 있던 print문이 출력되는 모습이다.
# test1.py
if __name__ == "__main__":
		print("메롱")
  • test1모듈에 위와 같이 __name__을 이용한 구문을 넣어주면 모듈이 import 되면서 실행되는 참사를 막을 수 있다.

텍스트 전처리(정규 표현식)

Python

import re
 
for i in range(len(df_reviews)):
    text = temp_reviews['Review_Text'][i]
    if not isinstance(text, str):
        text = str(text)
    # 1번
    temp = re.sub('[^가-힣a-zA-Z0-9\s]', '',text)
 
	# 2번
    temp = re.sub('[ㄱ-하-ㅣ]', '', text)
 
	# 3번
    temp = re.sub('[^\w\s]', '', text)
 
	# 4번
    temp = re.sub('[ㄱ-ㅎ()]', '',text)
 
	# 5번
    temp = re.sub('[\n]', '', text)
 
	# 6번
    temp = re.sub(r"\n", '', text)
 
	# 7번
    temp = re.sub('[^,.?!\w\s]', '', text)
 
    print(temp)
 
    temp_reviews.loc[[i],['Review_Text']] = temp
  1. re.sub('[^가-힣a-zA-Z0-9\s]', '', text)
    • 한글 문자(가-힣), 영어 알파벳(대소문자), 숫자(0-9), 공백 문자가 아닌 모든 문자를 빈 문자열('')로 대체
    • 이 문자들을 제외한 모든 문자를 제거
    • 제일 많이 쓰임
  2. re.sub('[ㄱ-ㅎㅏ-ㅣ]', '', text)
    • 한글 자음과 모음 문자를 문자열에서 제거
    • ㄱ-ㅎ : 한글 자음 ㄱ부터 ㅎ까지
    • ㅏ-ㅣ: 한글 모음 ㅏ 부터 ㅣ까지
  3. re.sub('[^\w\s]', '', text)
    • 단어 문자(\w) 즉, 알파벳, 숫자, 밑줄(_) 문자와 공백 문자 (\s)가 아닌 모든 문자를 빈 문자열('')로 대체.
    • 구두점이나 특수 문자를 제거 (밑줄은 제거 ❌)
  4. re.sub('[ㄱ-ㅎ()]', '', text)
    • 한글 자음(ㄱ-ㅎ) 및 괄호 문자 (())를 빈 문자열('')로 대체
  5. re.sub('[\n]', '', text)
    • 줄바꿈 문자(\n)를 빈 문자열('')로 대체
    • 줄바꿈 문자(개행 문자)를 제거
  6. re.sub(r"\n", '', text)
    • 줄바꿈 문자 제거
    • r""는 raw string으로, 백슬래시를 있는 그대로 처리
  7. re.sub('[^,.?!\w\s]', '', text)
    • 쉼표,, 마침표., 느낌표!, 물음표?, 단어 문자\w, 공백 문자\s가 아닌 모든 문자를 빈 문자열('')로 대체
    • 지정된 구두점, 단어 문자 및 공백 문자를 제외한 모든 문자를 제거

HTML 태그 제거

  1. re.sub(r'<(.*?)>', '', text)
import re
 
text = "탑텐 남성 쿨에어 코튼 <b>반팔 티</b> 오버핏 MSD2TS1001"
 
# HTML 태그 제거
clean_text = re.sub(r'<(.*?)>', '', text)
 
print(clean_text)
 
# 출력값
탑텐 남성 쿨에어 코튼 반팔 티 오버핏 MSD2TS1001
  1. re.sub('<[^>]+>', '', text)
import re
 
text = "탑텐 남성 쿨에어 코튼 <b>반팔 티</b> 오버핏 MSD2TS1001"
 
# HTML 태그 제거
clean_text = re.sub('<[^>]+>', '', text)
 
print(clean_text)
 
# 출력값
# 문자열로 나옴
'탑텐 남성 쿨에어 코튼 반팔 티 오버핏 MSD2TS1001'

특수 기호 제거

  1. re.sub('[^\w\s]', '', text)
import re
 
text = "과거에서 '교훈'!!!을 얻을 수는 있어도, 과거 속에 살 수는 없다.^.^"
 
# 특수 기호 제거
clean_text = re.sub('[^\w\s]', '', text)
 
print(clean_text)
 
# 출력값
과거에서 교훈을 얻을 수는 있어도 과거 속에 살 수는 없다

맞춤법 확인

py-hanspell

연구원님 py-hanspell github주소
  • 연구원님이 오류를 고치신 버전이다.

  • 사용방법:

    1. 네이버에 네이버 맞춤법 계산기를 친다.
    2. 개발자 도구를 키고 새로고침과 검사하기를 누른다
    3. 개발자도구 맨 위 항목 Network에 들어간다.
    4. 밑에 항목에 SpellerProxy?passportkey=...를 누른다.
    5. requestURL을 확인한 뒤 복사후 코드에 붙인다.
  • requestURL은 계속 바뀌기 때문에 한번 이용할 때마다 위의 사용방법을 한번씩 쓰면 된다.

  • 예시

from hanspell import spell_checker
 
requestURL = "https://m.search.naver.com/p/csearch/ocontent/util/SpellerProxy?passportKey=90ae3cbdae22968de3f12b9095f35ea488ecf40b&_callback=jQuery112409692735818103884_1718943666840&q=%EB%A7%9E%EC%B6%A4%EB%B2%95+%EA%B2%80%EC%82%AC%EB%A5%BC+%EC%9B%90%ED%95%98%EB%8A%94+%EB%8B%A8%EC%96%B4%EB%82%98+%EB%AC%B8%EC%9E%A5%EC%9D%84+%EC%9E%85%EB%A0%A5%ED%95%B4+%EC%A3%BC%EC%84%B8%EC%9A%94.&where=nexearch&color_blindness=0&_=1718943666841"
 
result = spell_checker.check('아녕하세요', requestURL)
 
print(result.checked)
 
# 출력값
안녕하세요

띄어쓰기 확인

pykospacing

  • pykospacing Github 주소

  • 오류 해결 방법

    1. pykospacing폴더 안의 variables안에 내용물이 없어서 에러가 발생하는 경우
      • Github주소에 들어가 variables 폴더 안의 파일들을 다운받는다.
      • site-packages 폴더 안의 pykospacing폴더 안의 variables폴더에 넣는다.
      • 오류 해결
  • 예시

from pykospacing import Spacing
spacing = Spacing()
 
# 오늘도 숭배 우리혁
text = "네번째우승은저희팀을위한것입니다."
spacing(text)

행렬

희소행렬(sparse matrix)

  • 요소가 0이 많은 행렬
  • 메모리, 속도에 매우 비효율적이다.

이상치 찾는 방법

이상치(outlier)란?

  • 보통 관측된 데이터의 범위에서 많이 벗어난 아주 작거나 큰 값을 의미함.

이상치를 탐지하는 방법

  1. 특정 추세를 벗어난 데이터
  • 특정 추세를 벗어난 데이터는 시각화를 통해 확인해보는 것이 좋다.

  • 주로 histogram,scatter plot등으로 확인함

  1. 중앙값을 크게 벗어난 데이터
  • 중앙값을 크게 벗어난 데이터는 IQR(Inter Quartile Range, 75%-25%)로 확인.
  • 주로 boxplot을 통해 확인함

1. Z-Score로 찾는 방법

  • 신뢰구간을 통해 이상치를 탐지 가능
    • 신뢰구간: 모수가 실제로 포함될 것이라고 예측되는 범위
    • 신뢰 수준: 신뢰구간에 모집단의 실제 평균값이 포함될 확률
  • Z-Score: 해당 데이터가 평균으로부터 얼마의 표준편차만큼 벗어나 있는 지를 의미
    • 일반적으로 95% 신뢰수준을 사용함.(Z-Score 1.96)
# z-score를 통해 이상치를 찾는 함수
 
def find_outlier(df_length_reviews, df_reviews):
 
     outlier = df_length_reviews[(abs((df_length_reviews["리뷰 길이"]-df_length_reviews["리뷰 길이"].mean())/df_length_reviews["리뷰 길이"].std())) > 1.96]
 
     clean_df = df_reviews.drop(outlier)
     return clean_df

  • Z-Score는 주로 histogram을 이용하는 편이다.
  • 정규 분포를 통해 를 기준으로 한다.
    • 는 정규분포 평균의 구간 위치에 따라 움직임.
    • 는 정규분포의 분포도에 따라 모양이 바뀐다.
      • 만약 낮은 구간에 데이터가 밀집되어있다면 낮은 곳이 높게 솟아나있다.

2. IQR(Inter Quartile Range)

  • 분위수: 모집단 혹은 표본의 전체 도수를 몇 등분한 뜻
    • 백분위수(percentiles)와 4분위수(quartiles)가 가장 많이 사용됨.
  • IQR : Q3(75%)에서 Q1(25%)를 뺀 값이다.
  • IQR을 사용하여 이상치를 판단하는 기준
# IQR을 이용한 이상치를 찾는 함수
 
def find_IQR(df_length_reviews,df_reviews):
 
    new_df_length = df_length_reviews.copy()
    new_df_reviews = df_reviews.copy()
 
    Q1 = new_df_length["리뷰 길이"].quantile(q=0.25)
    Q3 = new_df_length["리뷰 길이"].quantile(q=0.75)
 
    IQR = Q3 - Q1
 
    # IQR = Q3(3사분위수) - Q1(1사분위수)
    # 제거기준 = (Q3 + IQR * 1.5보다 큰 값) & (Q1 - IQR * 1.5보다 작은 값)
 
    IQR_df = new_df_length[(new_df_length["리뷰 길이"] >= Q3 + 1.5 * IQR) | (new_df_length["리뷰 길이"] <= Q1 - 1.5*IQR)].index
 
    new_df_reviews = new_df_reviews.drop('Unnamed: 0.1', axis = 1)
    new_df_reviews = new_df_reviews.drop('Unnamed: 0', axis = 1)
    new_df_reviews.drop(IQR_df, inplace = True)
 
    new_df_reviews.reset_index(drop=True, inplace = True)
    new_df_reviews.to_csv("./IQRtest.csv")
 
    return new_df_reviews

  • IQR은 주로 Boxplot을 이용한다.
  • Boxplotbox부분의 맨 아래쪽을 Q1 , 맨 위쪽을 Q3라고 하며,
  • IQR은 Q3와 Q1의 차이다.
  • Boxplot 막대기의 제일 아랫부분을 minimum, 제일 윗 부분을 maximum라고 한다.
  • box부분은 정규분포의 제일 튀어나온 부분이다.

파이썬 함수 정의시 화살표, 클론

Python

  • Python 함수의 기본형은 아래와 같다.
def func_name(params):
	return 0
  • Python3부터는 매개변수 뒤에 :표시가 붙거나, 함수명 뒤에 화살표 -> 표시가 붙을 때가 있다.
def func_name(param1 : int, param2 : str) -> int:
	return 0
  • 위와 같은 표현들은 Function Annotation으로 주석의 역할을 한다.
  • 코드상 기능을 하지 않고 보충 설명을 해준다.
  • 함수명 뒤의 화살표->의 경우, 해당 함수의 return 값의 형태에 대한 주석이다.
  • :의 경우 매개변수(parameter)의 형태에 대한 주석이다.
  • 예시: 출처 : 연구원님의 네이버 리뷰 프로젝트
def loading(
        driver: WebDriver, 
        element: Tuple[str, str] = (By.TAG_NAME, "body")
    ) -> None:
    """페이지가 로딩될 때까지 기다리는 함수
    Args:
        driver (WebDriver): Selenium WebDriver 인스턴스
        element (Tuple[str, str], optional) 기다릴 요소를 지정하는 튜플
            첫번째 항목: By 클래스의 속성(ex. By.TAG_NAME, By.CLASS_NAME 등)
            두번째 항목: 첫번째 항목에 대한 값
            기본값: (By.TAG_NAME, "body")
    """
 
def find_element_by_class(
        driver: WebDriver, 
        class_name: str
    ) -> WebElement | None:
    """WebElement를 찾지 못하면 None으로 반환하는 함수
    
    Args:
        driver (WebDriver): Selenium WebDriver 인스턴스
        class_name (str): 찾고자 하는 element의 클래스 이름
    Returns:
        WebElement | None: 찾은 WebElement 객체. 요소가 없으면 None으로 반환
    """
    try:
        element = driver.find_element(By.CLASS_NAME, class_name)
    except:
        element = None
    
    return element 

Logging 사용법

Python

Logging docs

1. Logging이란?

  • 현재 우리의 프로그램이 어떤 상태를 가지고 있는지,, 외부 출력을 하게 만들어서 개발자등이 눈으로 직접 확인하는 것 입니다.

2. logging 모듈 사용하기

  • 파이썬에서 logging모듈은 기본 라이브러리에 포함됨
import logging
 
if __name__ == '__main__':
	logging.error("Something Wrong!!")
 
# 출력값
ERROR:root:Something Wrong!!

3. logging level

  • 파이썬 로깅 level의 기본 설정은 WARNING이다. 따라서 DEBUG < INFO < WARNING < ERROR < CRITICAL
  • 이 순서에서 더 높은 레벨인 ERROR는 출력 되지만, 하위레벨 (INFO,DEBUG)은 출력이 안된다.

3.1 custom log level 설정하기

  • 센서로부터 데이터를 받는 IoT 서비스에서 로깅을 출력할 때 DATA만을 확인해 보고 싶을 경우가 있다. 이 경우 DEBUG보다 한단계 아래에 로깅 설정을 할 수 있다.
CRITICAL = 50
FATAL = CRITICAL
ERROR = 40
WARNING = 30
WARN = WARNING
INFO = 20
DEBUG = 10
NOTSET = 0
  • 위처럼 각 레벨에는 숫자가 10단위로 적용된다.
  • 내부에서는
if level > 세팅된 레벨:
   출력 
  • 식으로 되어 있어서 세팅된 레벨보다 출력 레벨이 클 경우에 한 해서만 출력하고 있다.
if ERROR  > INFO :   세팅이 INFOERROR 는 출력
if DEBUG > INFO :    세팅이 INFODEBUG 는 출력하지 않음 
  • DATA = 15 이렇게 설정해 두면, DEBUG 정보는 출력하지 않고, DATA 레벨만 출력하게 된다.
  • 예시
logging.addLevelName(15, "DATA")
logging.DATA = 15
 
logger.log(logging.DATA, "message")

logging 모듈 사용하기

import logging
 
if __name__ == '__main__':
    mylogger = logging.getLogger("my")				#4-1
    
    mylogger.setLevel(logging.INFO)					#4-2
    
    formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')	# 4-5
    
    stream_hander = logging.StreamHandler()			#4-3
    
    stream_hander.setFormatter(formatter)			#4-5
    
    mylogger.addHandler(stream_hander)				#4-3
    
    file_handler = logging.FileHandler('my.log')	#4-4
    mylogger.addHandler(file_handler)				#4-4
 
    mylogger.info("server start!!!")				#4-3
 
결과: server start!!!									#4-3

4.1 logging 생성

  • my라는 특정 로거를 생성하게 된다.
  • 매개변수 없이 logging.getLogger() or logging.getLogger("") 만들 수 있다.
  • root logger를 리턴하게 되며, root logger는 로깅 시스템의 기본 로거이고 기본 레벨은 WARNING을 가지고 있다.

4.2 logging level 설정

  • setLevel 메소드를 통해서 INFO레벨 이상은 출력하도록 설정함. (DEBUG 출력안함)

4.3 StreamHandler 설정

  • Handler란 내가 로깅한 정보가 출력되는 위치 설정하는 것
  • 콘솔, 파일, DB, 소켓, 큐등을 통해 출력하도록 설정 할 수도 있다.

4.4 FileHandler

  • logging.FileHandler 클래스를 통해 객체를 만들어서 나의 로거에 추가해주면 된다. 현재 디렉토리에 파일(my.log)이 만들어진다.
  • 파일은 a모드로 열리는게 디폴트
  • a모드 : 같은 이름의 파일이 이미 있다면, 파일 끝에 추가하여 쓰고, 없다면 쓰기용으로 파일 만들어라는 뜻

4.5 출력 formatting 설정

  • 메시지말고도, 이메시지가 쓰인 시간, 어떤 모듈에서 쓰였는지 등 정보를 같이 출력할 때, 쓰는 것을 formatting이라 한다.

  • asctime : 시간

  • name : logger 이름

  • levelname: logging레벨

  • message : 메세지

  • 모듈이름, 파일이름, 출력라인넘버등 다양하다.

  • 위의 경우 formatter를 콘솔출력에 한정해서 적용했기에 파일은 기존 그대로 출력된다.

  • 파일도 formatting되게 하고 싶으면 formatter를 FileHandler에도 추가한다.


패키지 __init__.py

Python

Reference

__init__.py 출처1 __init__.py 출처2 __init__.py 출처3

  1. __init__.py이란?
  • 폴더(디렉터리)가 패키지로 인식되도록 하는 역할
  • 패키지가 초기화하는 역할
  1. __init__.py의 필요성
  • Python에서 하나의 Python 파일(*.py)을 모듈(Module)이라고 한다.
  • 모듈의 집합을 패키지(Package)라고 한다.
  • 패키지는 서로 관련된 모듈들을 그룹핑하여 구조적으로 조직화할 수 있게 해준다.
  • 패키지를 사용하기 위해서는 패키지를 import하고 사용할 수 있어야 한다.
  1. __init__.py의 활용
  • 예시 1
  • mypackage라는 이름의 패키지를 구성하려고 할 때:
mypackage/ 
	__init__.py
	module1.py
	module2.py
  • __init__.py파일은 비어 있을 수도 있고, 패키지의 초기화를 위한 파이썬 코드를 포함할 수도 있다.
  • __init__.py파일은 패키지 내에 있는 모든 모듈들이 해당 패키지의 일부로 취급되도록 하며, 패키지의 이름 자체가 패키지를 import하는데 사용될 수 있도록 한다.
import mypackage 
# mypackage 모듈의 함수 사용 
mypackage.module1.some_function() 
 
# mypackage 패키지의 모듈 내의 변수 사용 
print(mypackage.module2.some_variable)
  • 예시 2
  • 3개의 모듈이 있다고 가정
someFolder 
|-- stringLength.py 
|-- stringToUpper.py 
`-- stringToLower.py
# stringLength.py
def stringLength(inStr):
    return len(inStr)
 
# stringToUpper.py
def stringToUpper(inStr):
    return inStr.upper()
 
# stringToLower.py
def stringToLower(inStr):
    return inStr.lower()
  • 스크립트들을 관리하기 쉽게 하나의 폴더에 넣어둔다.
  • 스크립트들을 string_func라는 폴더로 다 넣는다. 그리고 같은 폴더 안에 __init__.py 속내용이 없는 빈 파일을 만든다.
someFolder 
|-- string_func 
| |-- __init__.py 
| |-- stringToUpper.py 
| |-- stringToLower.py 
| `-- strengthLength.py 
`-- example1.py
  • 새로운 example2.py파일을 만든다.
# example2.py
import string_func.stringLength 
import string_func.stringToLower 
import string_func.stringToUpper 
 
some_string = "Hello, Universe!" print(string_func.stringLength.stringLength(some_string)) print(string_func.stringToLower.stringToLower(some_string)) print(string_func.stringToUpper.stringToUpper(some_string))
  • 다른 폴더에서 가져오는 것이 가능해짐

  • 하지만 코딩이 간결해보이지 않음

  • __init__.py에서 imports추가하기

# __init__.py
from .stringLength import stringLength
from .stringToLower import stringToLower
from .stringToUpper import stringToUpper
  • 여기서 모듈네임 이전의 . 은 파이썬 3버젼에서 relative import 가 가능해지도록 변경됨

  • 수정된 예시

# example3.py
import string_func
 
some_string = "Hello, Universe!"
 
print(string_func.stringLength(some_string)) print(string_func.stringToLower(some_string)) print(string_func.stringToUpper(some_string))

클래스 __init__, self

Python

Reference

wikidocs 클래스 init, self

__init__

  • 파이썬에서 클래스의 생성자(constructor) 메소드이다.
  • 클래스의 인스턴스를 생성할 때 자동으로 호출되며, 인스턴스가 생성될 때 초기화 작업을 수행하는 메서드
  • __init__메서드는 클래스 내에 정의된 다른 메소드와 마찬가지로 첫번째 매개변수로 self를 사용한다.
  • self를 통해 인스턴스 변수를 초기화하고, 필요한 경우 다른 초기화 작업을 수행할 수 있다.

self

  • 파이썬에서 클래스의 인스턴스(instance)를 나타내는 예약어이다.

  • 클래스를 정의할 때, 메소드의 첫번째 매개변수로 self를 사용하는 것이 일반적이다.

  • self를 사용하면 인스턴스 변수(instance variable)와 인스턴스 메소드(instance method)에 접근할 수 있다.

  • 예시

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age
 
    def say_hello(self):
        print("안녕하세요! 제 이름은 {}입니다. {}살입니다.".format(self.name, self.age))
 
    def more(self):
        print("one more!")
        self.say_hello()        
person1 = Person("Alice", 20)
person1.say_hello()
 
person1.more()
print(person1.name, person1.age)
  • 위 코드에서 person1Person클래스의 인스턴스이다.
  • Person클래스의 __init__메소드를 호출하여 인스턴스 변수 nameage를 초기화 한다.
  • say_hello메소드를 호출하여 인스턴스 변수 nameage를 출력하고 more메소드를 호출하여 say_hello메소드를 한번 더 사용한다.
  • 마지막 프린트 문에서 nameage가 인스턴스 변수로 저장된 것을 알 수 있다.

클래스 속성(Attribute), 메서드(Method)의 정의

Python

Reference

velog 출처 private,protected 메소드관련 tistory 출처

속성(Attribute)

  • 클래스에 정의되는 공통 요소들을 전문어로 CLASS의 Attribute(성질 혹은 속성)라고 한다.

  • 예를 들어, 모든 자동차는 공통 요소를 가지고 있다.

      1. Maker(현대, Mustang, BMW 등)
      1. Model(BMW 435i, 제네시스 등)
      1. Horse Power - 마력
  • 위의 3개의 속성들을 class에서 정의하기 위해서는 다음과 같이 __init__메소드를 통해 정의한다.

  • (class 안에서 정의해주는 함수(function)은 method라고 한다.)

class Car:
	def __init__(self, maker, model, horse_power):
    	self.maker = maker
        self.model = model
        self.horse_power = horse_power
  • __init__ 메소드는 class가 실체화 될 때 사용하는 메소드이다.
hyundai = Car("현대", "제네시스", 500)
  • 여기서 이미 __init__메소드가 호출되었다.
  • self는 어떠한 실체를 가리키는 단어
    • 클래스에서 self는 클래스의 실체(instance)인 객체(object)를 가리킨다.
    • Car class에서 self는 Car class의 객체인 hyundai 또는 BMW를 가리키는 것이다.

메소드(Method)

  • 클래스 안의 함수를 칭한다.
  • Attribute와 Method의 차이는 명사와 동사의 차이라고 생각하면 된다.
  • 속성(Attribute)는 해당 객체의 이름 등의 정해진 성질
  • 메소드(Method)는 move, eat등 객체가 행할 수 있는 어떠한 Action같은 느낌이라고 생각할 수 있다.
클래스 private와 protected 메소드
  • 파이썬은 이름 맹글링(name mangling)이라는 메커니즘을 사용하여 private 및 protected 메소드에 대한 접근을 제한한다.

  • Private 메소드

    • Private메소드는 클래스 내부에서만 사용되도록 의도된 메소드이다.
    • 파이썬에서 메소드 이름 앞에 두 개의 밑줄(__)을 붙여서 private메소드를 나타낸다.
    • 예시
      • __my_private_method
  • Protected 메소드

    • Protected메소드는 해당 클래스 및 그 서브클래스에서만 사용되어야 한다.
    • 메소드 이름 앞에 하나의 밑줄(_)을 붙여서 protected 메소드를 나타낸다.
    • 예시
      • _my_protected_method
  • 예시1

class MyClass: 
def __init__(self): 
self.__private_method() # private 메소드 호출
self._protected_method() # protected 메소드 호출
 
def __private_method(self): print("This is a private method")
def _protected_method(self): print("This is a protected method")
 
# 클래스 인스턴스 생성
obj = MyClass()
 
# 외부에서 private 및 protected 메소드에 접근 시도
# obj.__private_method() # 오류 발생
# obj._protected_method() # 가능하지만 권장되지 않음
  • 이 코드에서 __private_method는 클래스 외부에서 접근할 수 없다.

  • _protected_method는 접근할 수 있지만, 일반적으로 이렇게 하지 않는 것이 좋다.

  • 예시2

  • 간단한 클래스를 정의하고, 이 클래스 내부에 private 메소드와 protected 메소드를 정의한 후, 이 메소드들을 클래스 내부와 상속받은 서브 클래스에서 어떻게 사용되는지 예시를 보여준다.

class ParentClass:
 
	def __init__(self):
		self.__private_method() # private 메소드 호출
		self._protected_method() # protected 메소드 호출
 
	def __private_method(self):
		print("This is a private method of ParentClass")
 
	def _protected_method(self):
		print("This is a protected method of ParentClass")
 
class ChildClass(ParentClass):
 
	def __init__(self):
		super().__init__()
	# self.__private_method() # 이 코드는 오류를 발생시킵니다.private 메소드에 접근할 수 없음
		self._protected_method() # protected 메소드 호출 가능
	
	def demo_method(self):
	# self.__private_method() # 이 코드는 오류를 발생시킵니다. private 메소드에 접근할 수 없음
		self._protected_method() # protected 메소드 호출 가능
	
# 객체 생성 및 메소드 호출
parent = ParentClass()
child = ChildClass()
child.demo_method()
  • 설명
  1. Private 메소드(__private_method):
  • 이 메소드는 두 개의 밑줄(__)로 시작한다.
  • Private메소드는 클래스 내부에서만 직접 호출할 수 있다. 이 메소드는 클래스 외부나 서브클래스에서 직접 접근할 수 있다.
  • ParentClass 내부의 __init__메소드에서 __private_method가 호출된다. 이는 클래스 내부에서의 사용 예시이다.
  1. Protected 메소드(_protected_method):
  • 이 메소드는 하나의 밑줄(__)로 시작한다.

  • Protected메소드는 클래스 내부와 상속받은 서브클래스에서 접근할 수 있다.

  • ParentClass의 __init__메소드와 ChildClass의 __init__demo_method메소드에서 _protected_method가 호출된다. 이는 클래스 내부와 서브클래스에서의 사용 예시이다.

  • 주의 사항

    • 파이썬은 엄격한 접근 제어를 제공❌
      • 따라서 private 메소드도 특정한 방법(예: 이름 맹글링을 통한 접근)을 사용하면 접근할 수 있다.
      • 하지만 일반적으로 권장❌
    • Protected 메소드는 클래스 외부에서도 접근할 수있다.
      • 일반적으로 이러한 접근은 클래스의 캡슐화를 위반하는 것으로 간주되므로 권장❌

Typing

Python

Reference

Tistory 출처

동적 언어에서의 타입 검사

  • 파이썬은 동적 언어로 잘 알려진 언어
    • 변수의 타입을 일일이 명시하지 않아도 되고, 특정 변수의 타입이 중간에 바뀌어도 된다.
  • 잘못된 타입을 사용하여 예상치 못한 에러를 만들어내고, 이로 인해 프로그램이 망가질 수 있다는 단점이 있다.
    • 코드의 양이 많아지고 복잡해질수록 잘못된 타입으로 인한 에러를 만날 확률이 높다.
  • 최근에는 이런 동적 언어들에도 타입을 명시하고 검사하고자 하는 수요가 늘어나고 있다.
    • 파이썬 같은 경우도 3.5부터 언어 자체에서 Typing(또는 Type Hint)를 지원하고 있다.
  • 동적 언어에 타입을 명시하고 검사하게 되면 인터프리터 언어의 빠른 개발이라는 장점을 살리면서도 상대적으로 안전한 프로그램을 할 수 있다.

파이썬의 Typing 기초

  • 파이썬 3.5버전 이후부터는 아래와 같이 변수나 함수의 인자, 리턴 값에 타입을 명시할 수 있다.
  • 변수나 인자의 경우 :변수: <타입>으로 명시 가능하고
  • 함수의 응답 값의 경우 : :전에 -> <타입>을 붙여주면 된다.
a: int = 3
 
def process_message(msg: str) -> str:
	return msg.strip()
  • 위의 코드를 보면 aint타입을 가지고 있고, process_message함수는 str타입의 msg를 인자로 받아서 str타입을 리턴하는 것을 알 수 있다.

Typing의 장점

  1. 코드의 가독성이 증가
    • 코드에 추가적인 정보 (타입)을 제공함으로써 코드에 대한 이해도 증가
  2. IDE의 도움을 받으면 코드의 생산성 증가
    • vscode와 같은 현대 IDE에서는 명시한 타입을 코드를 작성할 때 보여주거나, 자동 완성해주는 기능을 가지고 있다.
  3. mypypyright와 같은 정적 도구들의 도움으로 사전에 타입 에러를 예방할 수 있다.
    • 파이썬에서 타입을 명시해주는 것만으로는 잘못된 타입을 검사하는 기능을 수행할 수 없다.
    • 가독성 향상에만 도움이 될뿐이다.
    • 하지만, 정적도구와 함께 사용한다면 잘못 사용된 타입을 사전에 알 수 있다.
    • mypy는 가장 많이 사용되고 있는 타입 검사도구이며, pyrightmypy와 유사하지만 mypy에 비해 약 5배 빠른 성능을 가지고 있으며, Microsoft가 운영하고 있는 라이브러리이다.
    • vscode와 같은 에디터를 사용하면 코드를 작성하면서 잘못된 타입에 대한 피드백을 즉각적으로 받을 수 있다. (vscode settings.json에 "python.analysis.typeCheckingMode": "basic" 추가)

기초 타입

  • 파이썬의 빌트인 타입인 int,str만으로는 우리가 원하는 모든 타입을 명확하게 명시할 수 없다. 이런 경우에는 typing모듈의 도움을 받으면 된다.
  • 8개의 기본 타입부터 다뤄보도록 하겠다.
1. typing.Union
  • 하나의 함수의 인자에 여러 타입이 사용될 수 있을 때typing.Union을 사용하면 된다.
from typing import Union
 
def process_message(msg: Union[str, bytes, None]) -> str:
  • 위의 코드는 str 또는 bytes 타입 혹은 None값을 인자로 받는다.
2. typing.Optional
  • Union[<타입>, None]Optional로 대체할 수 있다.
from typing import Optional
 
def eat_food(food: Optional[str]) -< None:
  • 위의 코드에서 food의 인자로는 str타입 또는 None값을 받는다.
참고
  • 파이썬 3.10부터는 Union이나 Optional 타입 대신 |를 사용할 수 있다.
  • Union\[str, bytes, None\]str|bytes|None
  • Opional\[str\]str|None으로 사용할 수 있다.
3. typing.List, typing.Tuple, typing.Dict
  • 리스트(배열) 안에 있는 타입을 나타내 주기 위해서는 typing.List[<타입>]를 사용하면 된다.
  • 튜플의 타입을 나타내주기 위해서는 typing.Tuple[<타입>,<타입>]을 사용하면 된다.
  • 딕셔너리의 타입은 typing.Dict[<Key 타입>, <Value 타입>]를 사용하면 된다.
from typing import List, Tuple, Dict
 
names: List[str]
location: Tuple[int, int, int]
count_map: Dict[str, int]
참고
  • 파이썬 3.9 이상부터는 리스트 , 튜플, 딕셔너리에 대해 typing모듈을 임포트하지 않아도 아래와 같은 타입 명세도 지원한다.
nmaes: list[str]
location: tuple[int, int]
map: dict[str, str]
  • 라이브러리를 만든다면, typing모듈을 사용해서 타입 명세를 해야 3.9 이전 버전의 파이썬에 호환되는 라이브러리를 만들 수 있다.
4. typing.TypedDict
  • 딕셔너리의 경우 value의 타입이 하나로 고정되는 일만 있는 것은 아니다. 이럴 때는 TypedDict를 활용할 수 있다.
  • TypedDict를 상속받은 클래스를 만든 다음 아래와 같이 key와 value의 타입을 매칭 시켜주면 된다. (파이썬 3.8부터 지원)
from typing import TypedDict
 
class Person(TypedDict):
	name: str
	age: int
	gender: str
 
def calc_cost(person: Person) -> float:
	...

또는

from typing import TypedDict
Person = TypedDict("Person", name = str, age = str, gender = str)
Person = TypedDict("Person", "name":str, "age":int, "gender": str)
참고
  • TypedDict는 많은 경우에 dataclass로 대체해서 사용할 수 있고, 데이터 객체를 표현하기 위해서는 더 적절할 수 있다. (파이썬 3.7 버전부터 지원)
from dataclass import dataclass
 
@dataclass
class Person:
	name: str
	age: int
	gender: str
 
def calc_cost(person: Person) -> float:
5. typing.Generator, typing.Iterable, typing.Iterator
  • 어떤 함수가 제너레이터 역할을 하는 경우 리턴 타입으로 Generator[YieldType, SendType, Return Type]를 명시해주면 된다.
def echo_round() -> Generator[int, float, str]:
	sent = yield 0
	while sent >= 0:
		sent = yield round(sent)
	return 'Done'
  • 위의 echo_round() 함수는 float타입을 입력받아서 int타입을 yield하는 제너레이터이며, 음수 값을 받았을 때는 StopIteration 예외와 함께 해당 예외의 값으로 str타입을 리턴한다.
>>> a = echo_round()  
>>> a.send(None) # Start
0
>>> a.send(3.5)
4
>>> a.send(-3)
Traceback (most recent call last):
	File "<stdin>", line 1, in <module>
StopIteration: Done
  • 제너레이터는 입력(Send) 값과 리턴 값이 있는 경우보다 yield만 하는 경우가 더 많다.
  • 이런 경우에는 Generator[yieldType, None, None]으로 타입을 명시하면 된다.
  • 또는 아래에서 설명할 typing.Iterable[YieldType]또는 typing.Iterator[YieldType]을 사용하면 된다.
import random
from typing import Iterator
 
def random_generator(val: int) -> Iterator[float]:
	for i in range(val):
		yield i
참고
  • 파이썬 3.9부터는 typing모듈 대신 collection.abc모듈의 Generator,Iterator,Iterable등을 사용할 수 있다.
  • 제너레이터를 사용할 때 IteratorIterable는 구분할 필요 ❌
    • 다만 , 실제 해당 타입은 __next__매직 메서드를 구현했느냐 여부로 나눌 수 있다.
    • __next__매직 메소드를 구분할 객체에 대해서는 Iterator타입, 아니면 Iterable타입을 명시하면 된다. (두 타입 모두 __iter__매직메소드를 사용하는 것을 전제)
6. typing.Callable
  • 함수를 인자로 가지는 경우에는 Callable[[Arg1Type, Arg2Type], ReturnType] 타입을 활용하면 된다.
def on_some_event_happened(callback: Callable[[int, str, str], int]) -> None:
	...
 
def do_this(a: int, b:str, c:str) -> int:
	...
 
on_some_event_happened(do_this)
참고
  • Callable한 객체 역시 사용 가능하다.
7. typing.Type
  • 일반적으로 클래스의 객체는 해당 타입을 그냥 명시하면 된다.
class Transaction:
	...
 
def process_txn(txn: Transaction):
	...
  • 하지만 클래스 그 자체를 인자로 받을 경우에는 typing.Type[Class명]을 사용해줘야 한다.
class Factory:
	...
 
class AFactory(Factory):
	...
 
class BFactory(Factory):
	...
 
def initiate_factory(factory: Type[Factory]):
	...
  • 간혹 예외를 인자로 받는 함수들이 있다.
  • 이런 경우에도 Type[예외클래스]를 사용해주면 된다.
def on_exception(exception_class: Type[Exception]):
	...
8. typing.Any
  • 어떤 타입이든 관계가 없다면 Any를 사용해주면 된다. (가능하면 지양하는게 좋다.)

os와 pathlib

Python

Reference

파이썬 공식문서 pathlib 파이썬 공식문서 os 위키독스 Tistory 블로그

절대경로와 상대경로

  • 파이썬에서 경로를 지정하는 방식에는 2가지가 있다.

    • 절대경로(Absolute Path)와 상대경로(Relative Path)로 파일이나 디렉터리의 위치를 지정할 수 있다,
  • 절대경로

    • 파일이나 디렉터리의 전체 경로를 제공하는 방식
    • 루트 디렉터리(최상위 디렉터리)에서 시작하여 해당 파일이나 디렉터리까지의 전체 경로 표시
    • 예시:
      • '/home/username/Documents/example.txt'
    • 절대경로는 전체 경로를 나타내기 때문에 파이썬의 실행 위치와 관계없이 어느 위치에서든 동일한 리소스를 가리킨다.
  • 상대경로

    • 현재 작업중인 디렉터리(현재 디렉터리)를 기준으로 파일이나 디렉터리의 위치를 지정한다.
    • 온점(.)은 현재 디렉터리를, 두개의 온점(..)은 상위 디렉터리를 의미한다.
    • 현재 디렉터리를 나타내는 온점과 역슬래시 조합(./)은 생략될 수 있다.
    • 예시:
      • 현재 윈도우에서 작업중인 디렉터리가 'C:\Users\UserName\Documents'라면, 'example.txt''C:\Users\UserName\Documents\example.txt'를 가리킨다.
      • example.txt''./example.txt'는 같은 파일을 가리킨다.
      •  '..\Pictures\image.jpg''C:\Users\UserName\Pictures\image.jpg'를 가리킨다.
      • 상대경로는 파이썬을 실행하는 현재 위치에 따라 가리키는 리소스가 달라질 수 있다.
  • 일반적으로 절대경로는 특정 파일이나 디렉터리의 위치가 변경되지 않아야할 때, 또는 파이썬 프로그램이 특정 위치에 의존하지 않고 어떤 위치에서든 실행될 수 있는 상황에서 사용된다.

  • 상대경로는 현재 위치에 따른 경로만 표시하기 때문에 절대경로에 비해 코드가 더 짧고 간결한 편이다.

  • 임시 디렉터리에서 작업하는 경우처럼 작업 환경이 동적으로 변화하는 경우에는 상대경로가 유용하다.

경로 문자열 처리

  • 절대경로나 상대경로로 파이썬에서 경로를 문자열 형태로 직접 입력할 수 있다.

  • 전달받은 경로를 문자열로 처리하여 문자열 연산을 사용하여 경로를 수정하거나 분석할 수 있다.

  • 주의해야할점:

    • 경로를 문자열로 전달할 때 윈도우에서 경로를 구분하는 백슬래시(\)가 파이썬 문자열에서 이스케이프 문자로 사용되어 오류 발생할 수 있다.
  • 경로를 문자열로 전달할 때는 추가처리가 필요하다.

  • Raw 문자열 사용

    • 문자열 앞에 r을 붙여 raw 문자열로 표현하면, 백슬래시(/)를 이스케이프 문자로 처리❌
path = r'C:\Users\UserName\Documents\example.txt'
  • 백슬래시 두번 사용
    • 각 디렉터리 구분자마다 백슬래시를 두번 사용한다.
path = 'C:\\Users\\UserName\\Documents\\example.txt'
  • 슬래시 사용
    • 윈도우도 파이썬에서는 슬래시(/)를 경로 구분자로 인식하므로 사용 가능하다.
path = 'C:/Users/UserName/Documents/example.txt'

os 모듈

  • os모듈은 운영체제와 상호작용하기 위한 함수들을 제공하는 모듈
  • 폴더(또는 파일)경로를 문자열로 다룬다.
  • os 함수
함수의미예시
getcwd()현재 작업 디렉터리를 반환os.getcwd()
mkdir()지정된 경로에 새로운 디렉터리(폴더) 생성os.mkdir(path)
listdir()입력 경로 내의 모든 파일과 폴더명 리스트 반환os.listdir(path)
chdir()현재 작업 디렉토리 변경os.chdir(path)
exists()지정된 경로가 존재하는지 확인 (True/False 값 반환)os.path.exists(path)
isdir()지정된 경로가 디렉터리인지 확인 (True/False 값 반환)os.path.isdir(path)
isfile()지정된 경로가 파일인지 확인 (True/False 값 반환)os.path.isfile(path)
abspath()지정된 경로의 절대경로를 반환os.path.abspath(path)
join()운영 체제에 맞게 경로를 연결하여 새 경로를 생성os.path.join(path1, path2)
split()경로를 디렉터리와 파일로 분리(튜플로 반환)os.path.split()

pathlib 모듈

  • pathlib모듈은 파일 시스템 경로를 객체 지향적으로 다루기 위해 설계되었음.
  • pathlib모듈의 주요 클래스로 Path클래스가 있다.
from pathlib import Path
 
p = Path('C:/Users/UserName/Documents/example.txt')
  • Path 클래스의 객체(p)를 생성할 때 Path()의 괄호 안에 경로를 입력한다. (경로는 절대경로와 상대경로 모두 가능)
  • 객체를 생성한 후 Path 클래스의 속성과 메서드를 사용할 수 있다.
  • Path클래스의 속성
속성의미예시
parts지정된 경로를 구성하는 요소들을 튜플로 반환p.parts
drive지정된 경로의 드라이브 부분을 반환p.drive
root지정된 경로의 루트 부분을 반환p.root
anchor지정된 경로의 드라이브와 루트 부분을 반환p.anchor
name지정된 경로의 파일 이름을 반환p.name
suffix지정된 경로의 확장자를 반환p.suffix
stem지정된 경로의 파일 이름을 반환(확장자 제외)p.stem
  • Path클래스의 주요 메서드
메서드의미예시
cwd()현재 작업 디렉터리를 반환p.cwd()
home()홈 디렉터리를 반환p.home()
exists()지정된 경로가 존재하는지 확인 (True/False 값 반환)p.exists()
is_dir()지정된 경로가 디렉터리인지 확인 (True/False 값 반환)p.is_dir()
is_file()지정된 경로가 파일인지 확인(True/False 값 반환)p.is_file()
resolve()상대경로를 절대 경로로 변환하여 반환p.resolve()
joinpath()운영 체제에 맞게 경로를 연결하여 새 경로를 생성p.joinpath(path1, path2)
rmdir()빈 디렉터리를 삭제p.rmdir()
mkdir()지정된 경로에 새로운 디렉터리(폴더)를 생성p.mkdir()
unlink()파일 또는 심볼릭 링크를 삭제p.unlink()
touch()지정된 디렉터리에 빈 파일을 생성p.touch()
glob()파일및 폴더 확인p.glob(’*‘)
parent상위 폴더 확인p.parent

예시

1. 현재 폴더(디렉터리) 확인하기
import os
impoty pathlib
 
# 현재 디렉터리 주피터 노트북 파일 또는 파이썬 코드가 포함된 파일을 바라보는 디렉터리
print('os 모듈 =', os.getcwd())
print('pathlib 모듈 =', pathlib.Path.cwd())
 
# 출력값
os 모듈 = C:/Users/UserName/Documents/example.txt
pathlib 모듈 = C:/Users/UserName/Documents/example.txt
2. 파일 또는 폴더 존재 여부
# os
dir_name = 'C:/Users/UserName/Documents'
file_name = 'C:/Users/UserName/Documents/example.txt'
 
print(os.path.exists(dir_name)) ## 폴더가 존재하는가?
 
print(os.path.exists(file_name)) ## Untitled.ipynb 파일이 존재하는가?
 
# 출력값
True
True
path1 = pathlib.Path(dir_name) ## Path 객체로 변환해야한다.
path2 = pathlib.Path(file_name) 
print(pathlib.Path.exists(path1)) ## 폴더가 존재하는가?
print(pathlib.Path.exists(path2)) ## 파일이 존재하는가?
 
# 출력값
True
True
3. 폴더 만들기
  • os.makedirs를 이용하면 폴더를 생성 가능
  • os.makedirs는 경로 중간의 폴더가 없는 경우에도 중간 폴더까지 다 생성해준다.
  • exist_ok=True로 설정하면 폴더가 존재하는 경우에는 에러 발생 ❌(폴더 생성은 ❌)
  • exist_ok=False로 설정하면 기존의 폴더가 있다면 에러(FileExitsError)가 발생
dir_name = './parent_folder/child_folder'
os.makedirs(dir_name, exist_ok=True)
  • pathlib.Path(dir_name).mkdir를 이용하여 폴더 생성 가능
  • parents=True로 설정해야 중간에 있는 폴더까지 생성 가능
dir_name = './parent_folder/child_folder'
wp_dir_name = pathlib.Path(dir_name)
wp_dir_name.mkdir(parents=True, exist_ok=True) ## parents를 True 해야 리커시브하게 폴더 생성할 수 있다.

4. 파일과 폴더 확인

# os
os.listdir('./parent_folder')
 
# 출력값
['child_folder']
  • pathlib에서는 glob메서드를 사용
  • 모든 파일과 폴더를 뜻하는 기호 *를 넣는다.
  • glob이 반환하는 것은 generator이므로 결과를 확인하려면 list로 감싸준다.
  • Path 객체의 리스트가 반환한다.
p = pathlib.Path('./parent_folder').glob('*')
list(p)
 
# 출력값
[WindowsPath('parent_folder/child_folder')]

5. 상위 폴더 확인

# os
cur_dir = os.getcwd() ## 현재 디렉토리
os.path.dirname(cur_dir)
 
# pathlib
wp_cur_dir = pathlib.Path(cur_dir)
wp_cur_dir.parent
 
# 출력값
'C:\\Users\\UserName\\Documents'
WindowsPath('C:/Users/UserName/Documents')
  • Path 객체가 갖고 있는 경로를 문자열로 바꾸고 싶다면 str으로 감싸면 된다.

6. 경로 연결

  • 경로연결
    • 한 폴더와 그 내부에 있는 폴더 또는 파일 경로를 연결하는 것
    • os모듈은 첫번째 인자는 부모 폴더, 두번째 인자는 자식 폴더 또는 파일
    • pathlib모듈은 joinpath에 연결한 자식 폴더 또는 파일을 입력하면 된다.
# 디렉토리 + 디렉토리 또는 디렉토리 + 파일 연결
parent_dir_name = './parent_folder'
child_dir_name = 'child_folder'
print('os :' ,os.path.join(parent_dir_name, child_dir_name))
print('pathlib :', pathlib.Path(parent_dir_name).joinpath(child_dir_name))
 
# 출력값
os : ./parent_folder\child_folder
pathlib : parent_folder\childe_folder

7. 파일명과 확장자 분리

# os
file_path = 'empty2.v.1.txt'
os.path.splitext(file_path)
 
# 출력값
('empty2.v.1','.txt')
  • pathlib에서는 파일명과 확장자를 분리하는 함수는 없지만 stemsuffix를 통해 각각 파일명과 확장자를 얻을 수 있다.
path= pathlib.Path(file_path)
(path.stem, path.suffix)
 
# 출력값
('empty2.v.1','.txt')

Callback 함수

Reference

Callback함수의 의미

블로그의 글을 인용하자면

Quote

A callback function is a function which is:

  • passed as an argument to another function, and,
  • is invoked after some kind of event.

Callback 함수란?

  1. 다른 함수의 인자로써 이용되는 함수
  2. 어떤 이벤트에 의해 호출되어지는 함수

예제 1

# pseudocode
# The callback method
function meaningOfLife() {
    log("The meaning of life is: 42");
}
 
# A method which accepts a callback method as an argument
# takes a function reference to be executed when printANumber completes
function printANumber(int number, function callbackFunction) {
    print("The number you provided is: " + number);
}
 
# Driver method
function event() {
   printANumber(6, meaningOfLife);
}
  • printANumber()의 두 번째 매개변수로 function 타입의 callbackFunction을 인자로 받는다.
  • event() 내부에서 printANumber(6, meaningOfLife);를 하고 있는데, meaningOfLifeprintANumber의 매개변수인 callbackFunction에 전달된다.
  • 따라서, meaningOfLife는 위에서 밝힌 1번 정의(다른 함수의 인자로써 이용되는 함수)에 부합하여 callback 함수라고 할 수 있다.

args, kwargs

Reference

파이썬 *args**kwargs가 뭐예요

args(arguments)

args : (positional) arguments (인자를 이름으로 받지 않고 ”,“로 구분하여 위치로 받는 인자)

args앞에 붙는 *가 여러 개의 인자를 묶어서 하나의 튜플로 묶어주고 이를 args에 할당한다.

def sum_n_ints(*args):
	print(type(args))
	return sum(args)
 
sum_n_ints(1,2,3,4,5,6,7,8,9,10)
 
# 출력값
# <class 'tuple'>
# 55

kwargs(keyword arguments)

kwargs: keyword arguments

kwargs앞에 붙는 **가 여러개의 keyword arguments들을 묶어서 딕셔너리로 묶어주고 이를 kwargs에 할당한다.

def sum_n_ints(**kwargs):
	print(type(kwargs))
	return sum(kwargs.values())
 
sum_n_ints(a=1, b=2, c=3)
 
# 출력값
# {'a':1, 'b':2, 'c':3}
# <class 'dict'>
# 6

*args**kwargs를 같이 써놓은 경우

  • *args는 연속되는 positional arguments를 다 할당한다.
  • **kwargs는 연속되는 keyword arguments를 다 할당한다.
  • *args**kwargs는 순서에 맞춰서 (**kwargs를 먼저 쓰면 안된다) 쓰면 keyword arguments가 나오기 전까지 args가 튜플로 다 저장하고, 뒤 이어오는 keyword arguments는 kwargs가 딕셔너리로 다 저장한다.
def foo(*args, **kwargs):
	print(type(args))
	print(args)
	print(type(kwargs))
	print(kwargs)
 
foo(1,2,3,4,5,a=1,b=2,c=3)
 
# 출력값
# <class 'tuple'>
# (1, 2, 3, 4, 5)
# <class 'dict'>
# {'a': 1, 'b': 2, 'c': 3}

오류 발생 예제

*args, **kwargs 순서를 바꿔쓰면 SyntaxError가 발생한다.

def foo(**kwargs, *args):
	print(args)
	print(kwargs)
 
# 출력값
# {
#	"name": "SyntaxError",
#	"message": "arguments cannot follow var-keyword argument (2236058561.py, line  # 1)",
#	"stack": "  Cell In[46], line 1
#    def foo(**kwargs, *args):
#                      ^
# SyntaxError: arguments cannot follow var-keyword argument
# "
# }

*args는 연속되는 positional arguments를 다 저장한다. 따라서 이 다음에 *args가 올 수 없다.

**kwargs도 마찬가지다.

def foo(*args1, *args2):
	print(args1)
	print(args2)
	
# 출력값
# { "name": "SyntaxError", "message": "* argument may appear only once (1277788646.py, line 1)", "stack": "  Cell In[47], line 1 def foo(*args1, *args2): ^ SyntaxError: * argument may appear only once " }
def foo(**kwargs1, **kwargs2):
    print(kwargs1)
    print(kwargs2)
    
# 출력값
# { "name": "SyntaxError", "message": "arguments cannot follow var-keyword argument (904119874.py, line 1)", "stack": "  Cell In[48], line 1 def foo(**kwargs1, **kwargs2): ^ SyntaxError: arguments cannot follow var-keyword argument " }

Default 값을 가지는 keyword arguments를 **kwargs 뒤에 넣어준다면 **kwargs가 뒤로 연속되는 keyword arguments를 다 저장하므로 에러가 발생한다.

def foo(*args, **kwargs, a=1, b=2):
    return
 
# 출력값
# { "name": "SyntaxError", "message": "arguments cannot follow var-keyword argument (1528138373.py, line 1)", "stack": "  Cell In[49], line 1 def foo(*args, **kwargs, a=1, b=2): ^ SyntaxError: arguments cannot follow var-keyword argument " }
def moo(*args, a=1, b=2, **kwargs ):
    print(args)
    print(a)
    print(b)
    print(kwargs)
 
moo(1,2,3, c='c',d='d',e='e')
 
# 출력값
# (1, 2, 3)
# 1
# 2
# {'c': 'c', 'd': 'd', 'e': 'e'}

*args는 이어지는 positional arguments만 저장하니 a=1전에 끊기고 **kwargsb=2뒤로 이어지는 keyword arguments들을 저장하니 에러발생❌

*args**kwargs 모두 가변인자를 위한 변수다.

function parameter order : 일반 변수, *변수, **변수


동기(Synchronous)와 비동기(Asynchronous)

동기(Synchronous)비동기(Asynchronous)
설명작업을 순차적으로 진행여러 작업을 동시에 진행
장점코드가 단순하고 이해하기 쉽다
디버깅이 쉽다
효율적이다
반응성이 좋다
단점자원 활용이 비효율적이다.
한 작업이 잘못되면 전체 프로그램이 멈출 수 있다
디버깅이 복잡하다

Iterator(이터레이터)

Reference

파이썬 - 기본을 갈고 닦자!

1. Iterable과 Iterator

1.1 Iterable
  • iterable 객체 - 반복 가능한 객체
  • 대표적인 iterable한 타입 : list, dict, set, bytes, tuple, range
1.2 Iterator
  • iterator 객체 - 값을 차례대로 꺼낼 수 있는 객체
  • iterator는 iterable한 객체를 내장함수 또는 iterable객체의 메소드로 객체를 생성 가능하다

2. 예제

  • 파이썬 내장함수 iter()를 사용해 iterator 객체를 만든다.
a = [1, 2, 3]
a_iter = iter(a)
type(a_iter)
 
# 출력값
# <class 'list_iterator'>
  • iterable 객체를 매직메소드 __iter__ 메소드로 만들어 본다.
b = {1, 2, 3}
dir(b)
 
# 출력값
# ['__and__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__iand__', '__init__', '__init_subclass__', '__ior__', '__isub__', '__iter__', '__ixor__', '__le__', '__len__', '__lt__', '__ne__', '__new__', '__or__', '__rand__', '__reduce__', '__reduce_ex__', '__repr__', '__ror__', '__rsub__', '__rxor__', '__setattr__', '__sizeof__', '__str__', '__sub__', '__subclasshook__', '__xor__', 'add', 'clear', 'copy', 'difference', 'difference_update', 'discard', 'intersection', 'intersection_update', 'isdisjoint', 'issubset', 'issuperset', 'pop', 'remove', 'symmetric_difference', 'symmetric_difference_update', 'union', 'update']
 
b_iter = b.__iter__()
type(b_iter)
 
# 출력값
# <class 'set_iterator'>
 
  • iterator가 값을 차례대로 꺼낼 수 있는 객체라는 의미를 확인해본다.
  • next내장함수를 사용할때 마다 첫번쩨, 두번째, 세번째 값이 출력된다.
  • 네번째 실행에서는 StopIteration이라는 예외가 발생한다.
next(a_iter)
# 1
next(a_iter)
# 2
next(a_iter)
# 3
next(a_iter)
 
# Traceback (most recent call last):
#   File "<stdin>", line 1, in <module>
# StopIteration
  • iterator 매직 메소드 __next__를 통해 하나씩 값을 꺼낸다.
b_iter.__next__()
# 1
b_iter.__next__()
# 2
b_iter.__next__()
# 3

Generator(제너레이터)

Reference

파이썬 - 기본을 갈고 닦자!

1. Generator란?

  • iterator를 생성해주는 함수, 함수안에 yield키워드를 사용함
  • 특징
    • iterable한 순서가 지정됨(모든 generator는 iterator)
    • 느슨하게 평가된다.(순서의 다음 값은 필요에 따라 계산됨)
    • 함수의 내부 로컬 변수를 통해 내부 상태가 유지된다.
    • 무한한 순서가 있는 객체를 모델링할 수 있다. (명확한 끝이 없는 데이터 스트림)
    • 자연스러운 스트림 처리를 위 파이프라인으로 구성할 수 있다.

2. Generator 예제

  • yield 키워드를 사용해 generator를 만든다.
  • yield가 호출되면 암시적으로 return이 호출되며, 한번 더 실행되면 실행되었던 yield 다음 코드가 실행된다.
 def test_generator():
     yield 1
     yield 2
     yield 3
 
gen = test_generator()
 
type(gen)
 
# <class 'generator'>
next(gen)
# 1
next(gen)
# 2
next(gen)
# 3
next(gen)
# Traceback (most recent call last):
#   File "<stdin>", line 1, in <module>
# StopIteration
 
  • 생성한 generator는 iterable한 객체가 되며 for문을 사용할 수 있다.
import collections
isinstance(gen, collections.Iterable)
 
# 출력값
# True
for i in test_generator():
     print(i)
 
# 출력값
# 1
# 2
# 3
  • generator를 동시에 두개 생성하면, 서로가 다른 객체이며, 따로 동작한다.
h = test_generator()
i = test_generator()
 
h == i
# False
 
h is i
# False
 
next(h)
# 1
next(i)
# 1
next(h)
# 2
next(i)
# 2
next(i)
# 3
next(h)
# 3
  • yield사이에 추가로 코드를 입력해 동작을 확인한다.
  • yield로 반환하는 중간의 코드들이 실행된다.
def test_generator():
     print('yield 1 전')
     yield 1
     print('yield 1과 2사이')
     yield 2
     print('yield 2와 3사이')
     yield 3
     print('yield 3 후')
 
g = test_generator()
next(g)
# yield 1 전
# 1
next(g)
# yield 1과 2사이
# 2
next(g)
# yield 2와 3사이
# 3
next(g)
# yield 3 후
# Traceback (most recent call last):
#   File "<stdin>", line 1, in <module>
# StopIteration
  • 계속 생성되는 무한한 순서있는 객체를 모델링 할 수 있다.
  • 무한하게 숫자를 리턴할 수 있는 이유는 generator를 실행할때마다 느슨하게 평가되며 내부의 변수가 유지되고 있기 때문이다.
def infinite_generator():
     count = 0
     while True:
             count+=1
             yield count
             
gen = infinite_generator()
next(gen)
# 1
next(gen)
# 2
next(gen)
# 3
next(gen)
# 4
... # 계속
  • list, Set,Dictionary의 표현식의 내부도 사실 generator이다.
type(x*x for x in [2, 4, 6])
# <class 'generator'>

itemgetter

itemgetter는 딕셔너리에서 특정 키의 value를 추출하는 기능을 한다.

from operator import itemgetter
 
prompt = PromptTemplate.from_template("'{input}'을 {language}번역해주세요")
runnable = {
    "input" : itemgetter("input") | RunnableLambda(lambda x: x + "는 맛있어"),
    "language": itemgetter("language")
}
chain = runnable | prompt | model | output_parser
chain.invoke({"input": "오렌지", "language": "영어"})
 
# 출력값
# '"Orange is delicious."'

파이썬 언더스코어(_)에 대하여

출처 및 참고 자료

PEP 8 - Style Guide for Python Code 파이썬 언더스코어(_)에 대하여

Tip

오래전 내용이어서 바뀌었을지 모르니 참고만 하자

파이썬에서 언더스코어(_)는 크게 5가지의 경우에 사용된다.

  • 인터프리터(Interpreter)에서 마지막 값을 저장할 때
  • 값을 무시하고 싶을 때 (흔히 “I don’t care”라고 부른다.)
  • 변수나 함수명에 특별한 의미 또는 기능을 부여하고자 할 때
  • 국제화(Internationalization) / 지역화(Localization) 함수로써 사용할 때
  • 숫자 Literal값의 자릿수 구분을 위한 구분자로써 사용할 때

1. 인터프리터에서 사용되는 경우

파이썬 인터프리터에선 마지막으로 실행된 결과값이 _라는 변수에 저장된다. 이는 표준 CPython 인터프리터에서 먼저 사용되었으며 다른 파이썬 인터프리터 구현체에서도 똑같이 사용할 수 있다.

>>> 10
# 10
>>> _ 
# 10
>>> _ * 3
# 30
>>> _ * 20
# 600

2. 값을 무시하고 싶은 경우

_는 또한 어떤 특정값을 무시하기 위한 용도로 사용되기도 한다. 값이 필요하지 않거나 사용되지 않는 값을 _에 할당하기만 하면 된다.

# 언패킹시 특정값을 무시
x, _, y = (1, 2, 3) # x = 1, y = 3
# 여러개의 값 무시
x, *_, y = (1, 2, 3, 4, 5) # x = 1, y = 5 
# 인덱스 무시
for _ in range(10):
	do_something()
	
# 특정 위치의 값 무시
for _, val in list_of_tuple:
	do_something()

3. 특별한 의미의 네이밍을 하는 경우

파이썬에서 _가 가장 많이 사용되는 곳은 아마 네이밍일 것이다. 파이썬 건벤션 가이드라인인 PEP8에는 다음과 같은 4가지 언더스코어를 활용한 네이밍 컨벤션을 소개하고 있다.

  • _single_leading_underscore
    • 주로 한 모듈 내부에서만 사용하는 private 클래스/함수/변수/메서드를 선언할 때 사용하는 컨벤션이다.
    • 이 컨벤션으로 선언하게 되면 from module import *시 _로 시작하는 것들은 모두 임포트에서 무시된다.
      • 그러나, 파이썬은 진정한 의미의 private을 지원하고 있지는 않기 때문에 private을 완전히 강제할 수는 없다.
    • 즉, 위와 같은 임포트문에서는 무시되지만 직접 가져다 쓰거나 호출을 할 경우엔 사용이 가능하다. 그래서 “weak internal use indicator”라고 부르기도 한다.
_internal_name = 'one_module' # private 변수
_internal_version = '1.0' # private 변수
  • single_trailing_underscore_
    • 파이썬 키워드와의 충돌을 피하기 위해 사용하는 컨벤션이다. 그리 많이 사용하지는 않을 것이다.
Tkinter.Toplevel(master, class_='ClassName') # class와의 충돌을 피함 
list_ = List.objects.get(1) # list와의 충돌을 피함
  • __double_leading_underscores
    • 더블 언더스코어는 클래스 속성명을 맹글링하여 클래스간 속성명의 충돌을 방지하기 위한 용도로 사용된다. (맹글링이란, 컴파일러나 인터프리터가 변수/함수명을 그대로 사용하지 않고 일정한 규칙에 의해 변형시키는 것을 말한다.)
    • 파이썬의 맹글링 규칙은 더블 언더스코어로 지정된 속성명 앞에 _ClassName을 결합하는 방식이다.
    • 즉, ClassName이라는 클래스에서 __method라는 메서드를 선언했다면 이는 _ClassName__method로 맹글링 된다.
class A:
	def _single_method(self):
		pass 
		
	def __double_method(self): # 맹글링을 위한 메서드
		pass
class B(A):
	def __double_method(self): # 맹글링을 위한 메서드
		pass
		
print(dir(A())) # ['_A__double_method', ..., '_single_method']
print(dir(B())) # ['_A__double_method', '_B__double_method', ..., '_single_method']
 
# 서로 같은 이름의 메서드를 가지지만 오버라이드가 되지 않는다.
  • 더블 언더스코어로 지정된 속성명은 위와 같이 맹글링이 되기 때문에 일반적인 속성 접근인 ClassName.__method으로 접근이 불가능하다.

  • 간혹, 이러한 특징으로 더블 언더스코어를 사용해 진짜 private처럼 보이게 하는 경우가 있는데 이는 private을 위한 것이 아니며 private으로의 사용도 권장되지 않는다.

  • __double_leading_and_trailing_underscores__

    • 스페셜 변수나 메서드(매직 메서드라고도 부른다.)에 사용되는 컨벤션
    • __init____len__과 같은 메서드들이 있다.
      • 이런 형태의 메서드들은 어떤 특정한 문법적 기능을 제공하거나 특정한 일을 수행한다.
      • __file__은 현재 파이썬 파일의 위치를 나타내는 스페셜 변수
      • __eq__은 a == b라는 식이 수행될 때 실행되는 스페셜 메서드이다.
      •  __init__의 경우 클래스의 인스턴스가 생성될 때 처음으로 실행되는 메서드
        • 인스턴스의 초기화 작업을 이 메서드의 내용으로 작성할 수 있다.
class A:
	def __init__(self, a): # 스페셜 메서드 __init__에서 초기화 작업을 한다.
		self.a = a
		
	def __custom__(self): # 커스텀 스페셜 메서드. 이런건 거의 쓸 일이 없다.
		pass

4. 국제화(i18n)/지역화(I10n) 함수로 사용되는 경우

이는 어떤 특정한 문법적 규칙이라기보단 말 그대로 컨벤션이다. 즉,_가 국제화/지역화 함수라는 의미는 아니며, i18n/l10n 함수를 _로 바인딩하는 C 컨벤션에서 유래된 컨벤션이다. i18n/l10n 라이브러리인 gettext라는 파이썬 내장 라이브러리 API 문서에서도 이 컨벤션을 사용하고 있으며, i18n과 l10n을 지원하는 파이썬 웹 프레임워크인 Django의 공식 문서에서도 이 컨벤션을 소개하면서 사용하고 있다. 이런게 있다라고 알고만 넘어가자 gettext 공식 문서 참고

import gettext
gettext.bindtextdomain('myapplication', '/path/to/my/language/directory') gettext.textdomain('myapplication') _ = gettext.gettext
# ... 
print(_('This is a translatable string.'))
from django.utils.translation import ugettext as _
from django.http import HttpResponse
def translate_view(request):
	translated = _('This string will be translated.')
	return HttpResponse(translated)

5. 숫자 Literal값의 자릿수 구분을 위한 구분자로써 사용할 때

Python 3.6에 추가된 문법으로 언더스코어로 숫자값을 좀 더 읽기 쉽도록 자릿수를 구분한다.

dec_base = 1_000_000
bin_base = 0b_1111_0000 
hex_base = 0x_1234_abcd
 
print(dec_base) 
# 1000000
print(bin_base)
# 240 
print(hex_base)
# 305441741

코드 리팩토링

리팩토링은 컴퓨터 공학에서 ‘결과의 변경 없이 코드의 구조를 재조정함’을 뜻한다.

  1. 주로 가독성을 높이고 유지보수를 편하게 한다.
  2. 버그를 없애거나 새로운 기능을 추가하는 행위는 아니다.
  3. 사용자가 보는 외부 화면은 그대로 두면서 내부 논리나 구조를 개선하는 유지보수 행위이다.

리팩토링의 잠재적인 목표

  • 소프트웨어의 설계, 구조 및 구현을 개선하는 동시에 소프트웨어의 기능을 보존하는 것이다.
  • 리팩토링은 코드의 가독성을 향상시키고 복잡성을 감소시키는 효과를 가진다.
    • 소스 코드의 유지 보수성을 개선하고 확장성을 개선하기 위해 더 단순하고, 깔끔하거나, 표현력이 뛰어난 내부 아키텍처 또는 객체 모델을 만들 수 있게 한다.
  • 개발자는 더 빠르게 수행되거나 더 적은 메모리를 사용하는 프로그램을 작성해야하는 지속적인 과제에 직면해 있기에 성능 향상이 리팩토링의 또다른 목표가 된다.

정리하자면, 현재 코드의 동작은 그대로 유지하면서 더 이해하기 쉽고, 생각하기 쉽고, 확장하기 쉽게끔 재구성하는 것이다.

Refactoring의 사용시기

  • 삼진 규칙(3번의 중복 / 3번의 같은 행위를 한다면 리팩토링을 진행한다.)
  • 기능을 추가할 때 리팩토링을 한다.
  • 버그를 수정해야 할 때 리팩토링을 한다.
  • 코드 검토(Code Review)를 할 때 리팩토링을 한다.

효율적인 코드 리팩토링 전략

  1. 코드 리뷰와 피드백 수용
    • 다양한 시각과 아이디어를 반영하여 코드의 품질을 향상시킨다.
  2. 코드의 구조 개선
    • 중복 코드를 제거하고 모듈화된 함수와 클래스를 만들어 재사용성을 높이는 등의 작업을 수행
  3. 성능 최적화
    • 불필요한 반복문 제거, 알고리즘 최적화, 캐시 활용 등의 방법을 통해 성능을 향상시킨다.
  4. 테스트와 리팩토링의 연계
    • 테스트 코드를 작성하고 유닛 테스트를 통해 코드의 정확성을 검증하는 것은 코드 리팩토링의 핵심이다.
    • 테스트 주도 개발(TDD)방법론을 적용하여 코드 개선과 테스트 작성을 연계하여 안정적인 코드를 구축한다.

코드 리팩토링 실전 예시

  1. 중복 코드 제거
    • 중복 코드를 함수나 클래스로 추출하여 재사용성을 높이고 코드의 가독성을 향상시킨다.
  2. 변수 및 함수명 명확화
    • 의미 있는 변수명과 함수명을 사용하여 코드의 가독성을 높이고 코드를 이해하기쉽게 만든다.
  3. 불필요한 조건문 단순화
    • 조건문을 단순화하여 가독성을 향상시키고 버그 발생 가능성을 줄인다.
  4. 알고리즘 최적화
    • 시간 복잡도와 공간 복잡도를 고려하여 최적의 알고리즘을 선택하고 사용한다.

코드 리팩토링의 장점과 주의사항

  • 장점
    • 가독성과 이해도의 향상
    • 유지보수성과 생산성을 향상시키는데 도움을 준다.
  • 주의사항
    • 기능의 변경 없이 코드의 개선에 집중해야 한다.
    • 테스트 코드를 작성하여 코드의 정확성을 보장해야한다.
    • 코드의 안정성을 유지해야한다.

딕셔너리 언팩킹

패킹과 언패킹 이해하기 파이썬 패킹, 언패킹, 딕셔너리 언패킹

함수를 정의할 때

  • 파라미터가 *일 때는 튜플을 패킹하여 받는다.
  • **일 때는 딕셔너리를 패킹하여 받는다.

함수를 호출할 때 딕셔너리를 인자로 쓸 경우 정리

*를 사용해 딕셔너리를 파라미터로 전달할 경우

  • *만 사용할 경우 딕셔너리의 key들이 언패킹되어 전달된다.

**를 사용해 딕셔너리를 파라미터로 전달할 경우

  • 딕셔너리의 value들이 파라미터로 언패킹되어 전달된다.

문자열(str)속 표현식(expression)인식하기

문자열(str)속 표현식(expression)인식하기 ast 라이브러리 문자열에 담겨 있는 표현식을 표현하는 방법

  • 예시
    • 변수가 string타입으로 "['사과','바나나']" 이런 식으로 되어있을 때 리스트 타입인 ['사과','바나나']로 반환해주는 방법
import ast
 
list_in_str = '[1, 2, 3, 4, 5]'
list_data = ast.literal_eval(list_in_str)
print(type(list_data)) #list - correct solution
 
list_in_str2 = "{'a': 1, 'b': 2, 'c': 3}"
list_data2 = ast.literal_eval(list_in_str2)
print(type(list_data2)) #dict

Open API? OpenAPI?

Open API

  • 개방된 API를 뜻한다.
  • 누구나 사용될 수 있도록 API의 endpoint가 개방되어 있다면 Open API이다.

OpenAPI

  • RESTful API를 정의된 규칙에 맞게 API spec을 json이나 yaml로 표현하는 방식을 의미한다.
  • RESTful API
    • API를 구성하는 방식 중 대표적인 방식이다.
    • REST(Representational State Transfer) 방식이다.
    • “요청 자체만으로 내가 뭘 원하는지 알게 하자(=요청 자체로 나를 “표현”하자)“는 사상으로 등장했다.
    • 간단히 설명하자면 웹의 장점을 가장 잘살릴 수 있는 가장 간결하고 효율적인 구현 방식