오늘은 몸살이 났는지 컨디션이 좋지 못한 하루지만
조금씩이라도 강의를 들어가면서 내가 할 수 있는 것들을 최선을 다해 보자!
CLASS 심화
_init_ 함수
# class에 __init__메소드를 사용할 경우 인스턴스 생성 시 해당 메소드가 실행된다.
class CookieFrame():
def __init__(self, name):
print(f"생성 된 과자의 이름은 {name} 입니다!")
self.name = name
cookie1 = CookieFrame("cookie1") # 생성 된 과자의 이름은 cookie1 입니다!
cookie2 = CookieFrame("cookie2") # 생성 된 과자의 이름은 cookie2 입니다!
이 문장을 이해하기 위해서는 클래스와 인스턴스 그리고 메소드를 이해하고 있어야 한다.
클래스와 인스턴스
파이썬에서 클래스에 정의된 데이터나 함수를 사용하기 위해서는 인스턴스를 생성할 필요가 있다.
인스턴스란 클래스를 실체화 한 것이다.
하나의 클래스에 여러가지의 인스턴스를 만드는 것도 가능하다.
위와 같이 cookie1과 2가 인스턴스가 된다. 한마디로 클래스의 인스턴스가 각 쿠기에 할당이 되었다고 보면 된다.
인스턴스를 생성하였으므로 클래스 내부에 있는 함수를 호출할 수 있게 된다고 보면 된다.
위를 보면 self에 들어가는 것들이 CookieFrame() 괄호 안에 들어가는 것과 동일한 변수이다.
클래스와 메소드
클래스 내에 있는 함수를 메소드라고 부른다. 메소드 또한 한 클래스에 여러 개의 메소드를 만드는 것이 가능하다.
__init__의 이해
__init__에 대하여 정리를 하자면
- 컨스트럭 터라고 불리는 초기화를 위한 함수(메소드)
- 인스턴스화를 실시할 때 반드시 처음에 호출되는 특수한 함수
- 오브젝트 생성(인스턴스를 생성)과 관련하여 데이터의 초기를 실시하는 함
__init__()에 첫 번째 인수는 반드시 self를 지정해야 한다. self에는 인스턴스 자체가 전달이 되어 있다.
class MyStatus:
def __init__(self,age,name,height,weight):
self.age = age
self.name = name
self.height = height
self.weight = weight
def print_name(self):
print(self.name)
def print_age(self):
print(self.age)
def print_height(self):
print(self.height)
def print_weight(self):
print(self.weight)
a = MyStatus(34,"yamada",170,78)
출처: https://engineer-mole.tistory.com/190 [매일 꾸준히, 더 깊이:티스토리]
한 사이트의 작성자 분이 작성해 주신 글을 보면 그 예를 잘 이해할 수 있다.
MyStatus라는 클래스를 호출하고 거기에 처음으로 __init__함수를 불러 기본이 되는 self를 넣어주고 그 뒤에부터는
각각 age = 34, name = yamada, height = 170, weight = 78로 각 데이터에 값을 넣어준다.
그리고 클래스 안에 있는 함수 내에서 변수를 만들어 줄 때는 self. 변수명이 붙어서 나온다.
이걸 보면 한쪽에서만 클래스를 호출하는 것이 아니라 여러 군데에서도 호출을 할 때 값이 섞이거나 하는 것이 아니라 각자 호출 할 때 이름표를 부여받아 그 안에서 헷갈리지 않도록 하는 것과 같다고 생각한다.
CLASS 상속
상속이란?
💡 python의 class 상속이란, 클래스를 생성할 때 다른 클래스에 선언된 변수, 메소드 등의 기능들을 가져와 사용할 수 있도록 해주는 기능입니다. 동일한 코드를 여러 클래스에서 조금씩 수정해 사용하거나 모듈에 내장되어 있는 클래스를 변경할 때 주로 사용됩니다. 이때 상속 해주는 클래스를 부모(parents) 혹은 슈퍼(super) 클래스라 부르며, 상속받는 클래스를 자식(child) 혹은 서브(sub) 클래스라고 부릅니다.
다른 클래스를 가져와서 사용할 수 있도록 하는 것이며 어디서 가져오나에 따라서 위아래가 나뉜다고 보면 될 것 같다.
Monster라는 클래스를 먼저 만들었는데 거기에 각 속성가지는 몬스터를 만들고 싶을 때
공통되는 몬스터의 체력설정 함수랑 대미지를 받는 함수형식은 Monster의 형식과 같아서 전부 하나하나 적어주기
난잡한 상황에서는 클래스상속을 이용하면 Monster에 있는 함수를 가져와서 사용을 할 수 있다.
그렇기에 속성을 가지고 있는 몬스터를 만들고 싶을 때에는 Monster클래스를 가져오고 속성효과만 추가를 해주면
내용이 깔끔하게 정리가 된다는 것이다.
먼저 FireMonster클래스를 호출하면서 hp라는 변수에 100이라는 값을 넣어주겠다고 하면
def __init__(self, hp):
self.hp = hp
Monster클래스에 hp를 가져와서 클래스 내 self.hp변수에 100이라는 값을 넣어줄 수 있게 된다.
그 self.hp라는 변수 값을 fire_monster라는 인스턴스 안에 넣어준다.
그리고 FireMonster함수를 호출하는 동시에 클래스 안에서는 self.attribute = "fire"라는
속성값이 자동으로 입력이 되게 된다.
그러면 이제 fire_monster 인스턴스 안에는 self.hp와 self.attribute라는 값을 가지고 있는 상태이다.
여기서 attack(20)이라는 클래스 메서드 함수를 호출 했으므로 메소드 함수의 내용을 확인해보자
def attack(self, damage):
self.hp -= damage
20이라는 숫자를 fire_monster의 고유의 태그를 붙여서 damage를 만들어 주고었다.
그리고 기존에 있던 self.hp의 값에 damage의 값을 빼주는 메소드가 발생한다고 보면 된다.
그러면 기존의 100의 값에서 20을 뺀 80이 self.hp에 들어가게 될 것이고 이를 확인하기 위해서
fire_monster.status_check()를 호출하면
def status_check(self):
print(f"fire monster's hp : {self.hp}")
기존 Monster클래스에도 def status_check(self): print(f"monster's hp : {self.hp}")라는 메서드가 있었지만
class FireMonster(Monster): 에서 그 메서드를 수정하여 내용을 변경시켜주었기 때문에
FireMonster클래스를 수행할 때에는 FireMonster안에 변경을 시켜준 메소드 함수가 사용이 된다는 것을 알 수 있습니다.
상속받은 클래스의 특정 코드를 변경해 사용하기
class Calc:
def _print_zero_division_error(self):
print("can't be division by zero")
def plus(self, num1, num2):
...
def minus(self, num1, num2):
...
def divide(self, num1, num2):
if num2 == 0:
self._print_zero_division_error()
return False
...
def multiply(self, num1, num2):
...
calc = Calc()
calc.divide(5, 0)
# result print
"""
can't be division by zero
"""
누군가가 이런 클래스를 만들었을 때 "can't be division by zero" 이 문구를 영어에서 한글로 바꾸고 싶다면
클래스 내부로 들어가서 바꾸어도 작동은 하지만 새로운 업데이트가 되거나 다른 함수 내에서 이걸 사용하게 될 때 오류가
발생을 할 수 있습니다 그런 오류를 방지하기 위해서 사용할 수 있는 것이 상속입니다.
class CustomCalc(Calc):
def _print_zero_division_error(self):
print("0으로는 나눌 수 없습니다.")
calc = CustomCalc()
calc.divide(5, 0)
"""
0으로는 나눌 수 없습니다.
"""
위와 같은 CustomCalc 클래스를 만들어주어서 기존 Calc안에 있는
def _print_zero_division_error(self): 의 메소드 내용을
print("0으로는 나눌 수 없습니다.")으로 변경해 주면
Calc클래스를 건드리지 않아도 내가 원하는 값을 변경해 줄 수 있습니다.
그때 잊지 말아야 하는 것이
calc = CustomCalc()
calc.divide(5, 0)
인스턴스를 선언할 때 클래스명도 바꾸어 주어야 한다는 것을 잊어서는 안 됩니다.
class 객체(object) 다루기
객체란?
💡 이전 강의에서 클래스를 과자 틀, 클래스를 사용해 만들어진 과자를 인스턴스(instance)라고 설명드렸습니다.
객체와 인스턴스는 동일한 용어이며, CookieFrame 클래스를 사용해 생성된 cookie를 “객체” 혹은 “CookieFrame의
인스턴스”라고 표현합니다.
앞전에 사용했던 list생성과 그 리스트를 정렬하는 sort() 함수호출이 이런 클래서 호출과 비슷하다고 보면 됩니다.
sample_list = [1, 2, 3, 4]
sample_dict = {"key": "value"}
print(type(sample_list)) # <class 'list'>
print(type(sample_dict)) # <class 'dict'>
sample_list.sort() # list 클래스의 sort 메소드 사용
datetime 모듈에서 객체 활용해 보기
from datetime import datetime
now = datetime.now() # 현재 시간이 담긴 datetime의 객체(object) 생성
print(type(now)) # <class 'datetime.datetime'>
datetime이라는 함수에 어떤 내용이 포함이 되어 있는지 확인하기 위해서 ctrl + 클릭 을 하면 그 위치로 찾아갈 수
있으며 기존에 사용하던 방식 말고 다른 명령어가 있는지 찾아볼 수 있게 된다.
정규표현식(regex)
정규표현식이란?
💡 정규표현식은 regular expression의 약자인 regex라고도 하며 문자열이 특정 패턴과 일치하는지 판단하는 형식 언어입니다. 예를 들어 사용자가 입력한 이메일이 유효한 이메일인지, 유효한 핸드폰 번호를 입력했는지, 대문자로 시작하고 숫자로 끝나는 패턴의 단어가 몇 번 반복되는지 등 다양한 패턴을 지정하고 검증할 수 있습니다.
정규표현식 예제
💡 이메일 형식을 검증할 때 정규표현식을 사용하지 않은 코드와 정규표현식을 사용한 코드를 비교해 보겠습니다.
유효한 이메일인지 판단하는 최소한의 패턴은 아래와 같습니다.
- 숫자, 알파벳 대/소문자, 일부 특수문자( - _ . )를 조합한 문자로 시작합니다.
- 문자열 중간에는 @가 반드시 1개 포함되어 있어야 합니다.
- @ 이후에는 숫자, 알파벳 대/소문자, 일부 특수문자( - _ . )를 조합한 문자가 들어갑니다.
- 3번 문자 이후에는. 이 한 개 이상 포함되어 있어야 합니다.
- 마지막. 이후에는 2 ~ 4글자의 숫자, 알파벳 대/소문자, 일부 특수문자( - _ )를 조합한 문자 포함되어 있어야 합니다.
만약 정규표현식 없이 위 조건들을 만족하기 위해서는 아래와 같이 코드를 작성해야 합니다.
- 정규표현식 없이 이메일 검증하기
from pprint import pprint
alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
number = "0123456789"
special_char = "-_."
def verify_email(email):
# 이메일에 @가 한개 포함되어 있는지 확인
if email.count("@") != 1:
return False
# @를 기준으로 사용자명과 도메인을 분리
username, domain = email.split("@")
# username이 1자 이상인지 확인
if len(username) < 1:
return False
# 도메인에 한개 이상의 .이 포함되어 있는지 확인
if domain.count(".") < 1:
return False
# username에 알파벳, 숫자, 허용된 특수문자 이외의 문자가 포함되어 있는지 확인
if not all([x in alphabet + number + special_char for x in username]):
return False
# domain에 알파벳, 숫자, 허용된 특수문자 이외의 문자가 포함되어 있는지 확인
if not all([x in alphabet + number + special_char for x in domain]):
return False
# 마지막 .을 기준으로 도메인을 분리
_, last_level_domain = domain.rsplit(".", 1)
# 마지막 레벨의 도메인의 길이가 2~4글자인지 확인
if not 2 <= len(last_level_domain) <= 4:
return False
# 모든 검증이 완료되면 True를 리턴
return True
test_case = [
"apple", # False
"sparta@regex", # False
"$parta@regex.com", # False
"sparta@re&ex.com", # False
"sparta@regex.com", # True
"sparta@regex.co.kr", # True
"sparta@regex.c", # False
"sparta@regex.cooom", # False
"@regex.com", # False
]
result = [{x: verify_email(x)} for x in test_case]
pprint(result)
하지만 정규표현식을 사용할 경우 코드를 매우 간소화시킬 수 있습니다.
- 정규표현식을 사용해 이메일 검증하기
from pprint import pprint
import re
# rstring : backslash(\)를 문자 그대로 표현
# ^[\w\.-]+@([\w-]+\.)+[\w-]{2,4}$ : 이메일 검증을 위한 정규표현식 코드
email_regex = re.compile(r"^[\w\.-]+@([\w-]+\.)+[\w-]{2,4}$")
def verify_email(email):
return bool(email_regex.fullmatch(email))
test_case = [
"apple", # False
"sparta@regex", # False
"$parta@regex.com", # False
"sparta@re&ex.com", # False
"spar_-ta@regex.com", # True
"sparta@regex.co.kr", # True
"sparta@regex.c", # False
"sparta@regex.cooom", # False
"@regex.com", # False
]
result = [{x: verify_email(x)} for x in test_case]
pprint(result)
엄청 단순하게 되었지만
email_regex = re.compile(r"^[\w\.-]+@([\w-]+\.)+[\w-]{2,4}$")
이걸 누가 알고 쓰냐 이 말입니다.
그런 당신을 위해서 준비된 사이트가 있습니다.
이런 조건들을 쉽게 풀이해서 알려주는 사이트가 있으므로 여기서 만들어서 붙여 넣기를 해주면 된다.
확인뿐만 아니라 사람들이 만들어 둔 커뮤티니에서 가져오는 것 또한 가능하다.
glob을 활용한 파일 및 디렉터리 목록 확인
파이썬으로 내 폴더에 무엇이 있는지 명령어를 통해서 확인이 가능하다
만약에 코딩 중에 어디 파일에 무엇을 가져와야 할 때 사용이 될 것으로 예상이 된다.
from pprint import pprint
import glob
# ./는 현재 python 파일이 위치한 경로를 의미합니다.
# *은 "모든 문자가 일치한다" 라는 의미를 가지고 있습니다.
# ./venv/*은 venv 폴더 내 모든 파일들을 의미합니다.
path = glob.glob("./venv/*")
pprint(path)
# result output
"""
['./venv\\Include', './venv\\Lib', './venv\\pyvenv.cfg', './venv\\Scripts']
"""
# **은 해당 경로 하위 모든 파일을 의미하며, recursive 플래그와 같이 사용합니다.
# recursive를 True로 설정하면 디렉토리 내부의 파일들을 재귀적으로 탐색합니다.
path = glob.glob("./venv/**", recursive=True)
pprint(path)
# result output
"""
['./venv\\',
'./venv\\Include',
'./venv\\Lib',
'./venv\\Lib\\site-packages',
'./venv\\Lib\\site-packages\\autopep8-2.0.1.dist-info',
...
]
"""
# *.py와 같이 작성 시 특정 확장자를 가진 파일들만 볼 수 있습니다.
# ./venv/**/*.py는 venv 하위 모든 폴더들을 재귀적으로 탐색하며 .py 확장자 파일을 탐색합니다.
path = glob.glob("./venv/**/*.py", recursive=True)
pprint(path)
# result output
"""
['./venv\\Lib\\site-packages\\autopep8.py',
'./venv\\Lib\\site-packages\\pycodestyle.py',
'./venv\\Lib\\site-packages\\pip\\__init__.py',
'./venv\\Lib\\site-packages\\pip\\__main__.py',
'./venv\\Lib\\site-packages\\pip\\__pip-runner__.py',
...
]
"""
그때 필요한 함수가 glob이며 경로를 표시를
./venv/*에서./는 파이썬 파일이 있는 위치이며 그 폴더 중에
venv폴더 안에 있는 모든 것들을 선택했다는 말이다.
path = glob.glob("./venv/*")
pprint(path)
결과로는
['./venv\\Include', './venv\\Lib', './venv\\pyvenv.cfg', './venv\\Scripts']
위와 같이 출력이 된다.
내용을 확인해 보면 venv\\는 venv폴더 안에 라는 뜻이며
venv폴더 안에는 각각 Include, Lib, Scripts라는 폴더와
pyvenv.cfg라는 파일이 있다.
여기서 venv안에 있는 3개의 폴더 내부까지 확인을 하고 싶다면
recursive를 활용하면 된다. 우리말로는 재귀에 해당하는 말이다.
path = glob.glob("./venv/**", recursive=True)
pprint(path)
recursive를 사용하기 위해서는 glob함수호출 안에 두 번째 인자로 recursive=Ture를 넣어주어야 하며
경로위치마지막에 **을 두 개를 꼭 넣어주어야 경로에 있는 모든 하위 폴더 내부까지 반복해서 출력이 가능하다.
만약에 그 두 개가 적용이 안되어 있다면??
위에 보다시피./venv/*과 같은 값이 나오게 된다는 것을 확인하였습니다.
만약에 나는 폴더를 꺼내서 찾아야 하는데 내가 원하는 확장자는
py파일만 필요로 하다면 필터링을 해서 검색을 하는 것도 가능하다.
path = glob.glob("./venv/**/*. py", recursive=True)
pprint(path)
위와 같이 **이후로 /*. py라는 것이 추가가 되어 있는 것을 확인할 수 있는데.
이 말인즉슨 모든 파일을 꺼내어 보겠다 = /**
하지만 그중에서 제목은 상관없이 확장자가 파이썬인 것만 = /*. py
이렇게 확인을 할 수 있다.
응용 방법으로 확장자 상관없이 파일의 이름만 중요한 경우에는 반대로
/파일명.*
으로 적어주면 파일명에 맞는 파일의 위치가 나오게 된다.
open을 활용한 파일 다루기
이제 파일의 경로를 알아내는 법을 알았으니 파일을 여는 방법도 알아보자.
# python에서는 파일을 편집하거나 생성할 때, with open 문법을 사용할 수 있습니다.
# "file.txt" : 파이썬에서 사용할 파일을 지정합니다.
# "w" : 파일을 쓰기 모드로 엽니다. 만약 파일이 없다면 새로 생성합니다.
# r(읽기), a, 등 다양한 mode가 존재하며, 더 많은 mode의 종류는 여기서 확인할 수 있습니다.
# encoding : 파일의 encoding 형식을 지정합니다.
# case 1. open 함수를 사용해 파일 열기
f = open("file.txt", "w", encoding="utf-8")
f.write("파이썬 파일 쓰기 테스트!\n")
# open 함수를 실행하면 python 스크립트가 끝날때 까지 파일이 열려있게 됩니다.
# 때문에 파일에 대한 작업이 끝나면 close()를 사용해 파일을 닫아줘야 합니다.
f.close()
f = open("file.txt", "w", encoding="utf-8") 여기를 보면
open함수를 이용 파일을 여는데 파일의 명은 file.txt이며
"w"는 쓰다의 약자로 쓰기 모드로 파일을 여는 것입니다.
마지막에 encoding="utf-8" 같은 경우는 파일의 글을 저장할 때 글자를 저장하는 타입을 정하는 것인데
저장하는 타입과 나중에 읽어 들이는 타입이 다르면 글자가 깨지는 현상이 발생을 하므로
인코딩명을 잘 지정을 해주어야 하며 대부분은 utf-8을 사용한다고 합니다.
f.write("파이썬 파일 쓰기 테스트!\n")
write함수를 이용하여 내가 오픈한 파일의 내용에 적어줄 내용을 기입할 수 있습니다.
f.close() 모든 내용을 적고 난다음에는 우리가 파일을 닫아주듯이
닫아주어야 하며 닫은 후에는 write를 해도 오류만 나오게 됩니다.
그러면 결과를 확인해 보려고 파이썬 실행을 눌러주면
우리가 print문을 입력을 하지 않았기 때문에 터미널창에는 아무것도 입력이 되어 있지 않은 것을 확인할 수 있습니다.
하지만 탐색이창을 보면 보지 못했던 flie.txt가 생성이 되어 있는 것을 확인할 수 있는데
아까 우리가 입력해 준 값이 파일 내용으로 들어가 있게 되었습니다.
w대신에 r, a, 등 다양한 mode가 존재하며
r은 읽기 read
a는 추가 append
로 이세가지를 가장 많이 사용한다고 합니다.
w는 파일의 내용이 있던 없던 지우고 처음부터 다시 쓰기 때문에 기존의 내용이 사라진다고 생각해야 합니다.
a는 추가이기 때문에 기존의 내용을 살리고 추가된다는 것을 알아두어야 합니다.
f = open("file.txt", "a", encoding="utf-8")
f.write("파이썬 파일 쓰기 테스트!\n")
이렇게 a로 바꾸어서 실행을 하면
기존내용 밑으로 계속 생성이 되는 것을 알 수 있습니다.
그 이외에 다른 모드들은 아래의 링크에서 찾을 수 있습니다.
https://docs.python.org/ko/3/library/functions.html?highlight=open#open
with open
open을 with와 같이 사용하면 with 구문이 끝날 때 자동으로 파일이 close 됩니다.
때문에 with를 사용할 때는 별도로 close 해주지 않아도 됩니다.
with open("file.txt", "a", encoding="utf-8") as a:
오픈한 파일이 as뒤에 있는 w라는 변수에 들어가게 됩니다.
a.write("파이썬 내용 추가 테스트!")
이렇게 입력해 주면 새로운 글을 추가하게 되는 것이며
f = open("file.txt", "w", encoding="utf-8")
f.write("파이썬 파일 쓰기 테스트!\n")
기존 앞에 사용했던 문법과 동일한 것으로 알 수 있습니다.
read
이렇게 우리가 적었던 내용을 터미널에 적어줄 수 있습니다
여기서 추가로 print(r.readlines())의 readlines을 readline으로 단수형을 쓰게 되면
한 줄씩 나타내게 하는 것도 가능합니다.
그냥 print(r.readline())만 사용하게 된다면 맨 첫 줄인
파이썬 파일 쓰기 테스트! 만 출력이 되고 나머지는 출력이 되지 않기 때문에 반복문을 이용해서
한 줄을 출력하고 다음줄로 이동하여 계속 출력을 할 수 있게 하는 것입니다.
그리고 더 이상 출력을 할 것이 없다면 break문을 이용하여 반복문을 빠저 나와 주면 됩니다.
itertools
- itertools란?
💡 itertools는 효율적인 루핑을 위한 이터레이터를 만드는 함수입니다.
특정 패턴이 무한하게 반복되는 배열을 만들거나 배열의 값을 일괄적으로 계산하는 등의 작업을 할 수 있으며,
이번 강의에서는 조합형 이터레이터에 대해서 다룰 예정입니다.
더 많은 이터레이터의 기능들이 궁금하시다면
[여기](https://docs.python.org/ko/3/library/itertools.html)를 참고해 주세요
반복문예시
for i, v in enumerate(product(sample1, sample2), 1):
enumerate는 결과물을 표처럼 보여주기 위해서 쓰는 것입니다
만약에 없다면? -> 오류가 뜹니다 아마도 반복문 때문인 거 같은데.
product(sample1, sample2) 데카르트 곱을 구하는 것
숫자로 넣어도 행렬이 만들어지네요.
다음은 순열을 구하는 법입니다
간단하게 말해서 경우의 수를 구하는 법이라고 생각을 하면 좋을 것 같은데.
A, B, C의 리스트에서 줄을 지을 때 나올 수 있는 경우의 수를 구하면
이렇게 나올 수 있다는 것을 볼 수 있습니다.
permutations(sample, 3)을 보면 sample 리스트를 넣어주고 사용할 원소의 개수 3개가 있다고 말해주면
그것들이 만들 수 있는 순서의 예를 하나씩 for반복문에 들어가서 만들어 주는 것이라고 볼 수 있습니다.
끝의 숫자를 1 또는 2로 넣어주면
이렇게 경우의 수가 나오는 것을 볼 수 있습니다.
다음으로는 순열이 아닌 조합으로 만들어 줄 수 있는 것인데
이건 우리가 수학으로 배우는 C와 P의 차이 정도로 이해하면 될 것 같다.
영단어 그대로 p는 순서가 중요하기 때문에 같은 인자가 있어도 순서가 다르면 다른 값으로 하지만
c의 경우는 순서에 상관없이 값이 동일하게 들어가 있으면 같은 값으로 치는 것으로 이야기하면 될 것 같다.
for i in combinations(sample, 2)
이와 같이 마지막 숫자로 사용한 원소의 개수를 정하여 줄 수 있다.
1이라면 a, b, c 총 3개
3이라면 (a, b, c)로 하나가 나올 것입니다.
또 다른 하나는 같은 조합인 콤비네이션이지만 한 번 사용한 원소를 다시 사용할 수 있다고 가정을 하고
만들었을 때 나올 수 있는 경우의 수를 지어줄 수 있다.
for i in combinations_with_replacement(sample, 3):
위와 같이 with_replacement 다시 한번 사용하여라는 것을 볼 수 있다.
그렇게 나온 값이
('A', 'A', 'A')
('A', 'A', 'B')
('A', 'A', 'C')
('A', 'B', 'B')
('A', 'B', 'C')
('A', 'C', 'C')
('B', 'B', 'B')
('B', 'B', 'C')
('B', 'C', 'C')
('C', 'C', 'C')
이렇게 나올 수 있다.
이것 또한 순서가 상관이 없기 때문에 안에 들어있는 원소의 개수로 경우를 나누어준다.
request
requests란?
requests 파이썬에서 http 통신을 가능하게 해주는 모듈로,
beautifulsoup과 함께 웹 크롤링을 하거나 api 통신이 필요할 때 사용됩니다.
requests 요청에는 크게 네 가지 종류의 method가 존재합니다.
이번 강의에서는 가장 많이 사용되는 GET 요청과 POST 요청을 다룰 예정입니다.
- GET : 데이터 정보 요청
- POST : 데이터 생성 요청
- PUT : 데이터 수정 요청
- DELETE : 데이터 삭제 요청
requests를 요청하면 서버에서는 응답(response)을 내려주며,
응답은 내용(content)과 상태 코드(status code)를 받아오게 됩니다.
content는 서버에서 사용자에게 주는 응답 본문이며,
status는 서버가 어떤 상태인지를 표현해 줍니다.
requests 모듈로 request 보내보기
requests 모듈을 사용하기 위해서는 먼저 pip install requests 명령어를 사용해 패키지를 설치해야 합니다.
이번 강의에서 http 통신 요청을 보내게 될 https://jsonplaceholder.typicode.com/라는 사이트는
다양한 http 요청을 간편하게 테스트할 수 있는 사이트입니다.
r = requests.get(f"{url} users/1")
request로 요청을 하고 {url} users/1에 해당하는 내용 값을 get으로 받아들이겠다고 선언
pprint({ "contents": r.text, "status_code": r.status_code})
받아온 값 중에서 r.text는 contents에 넣어주고 r.status_code는 status_code에 넣어준다.
생긴 모양은 딕셔너리 같이 생겼지만 강의에서는 json모양이라고 한다.
post
post는 request와는 반대로 내가 넣어주는 것인데 필요한 내용으로는
넣어줄 데이터를 양식에 맞추어 작성하고
# 데이터 생성에 사용될 값 지정
data = {
"name": "sparta",
"email": "sparta@test.com",
"phone": "010-0000-0000",
}
# 사용자를 생성하기 위해 users 경로에 data를 담아 post 요청
r = requests.post(f"{url}users", data=data)
pprint({
"contents": r.text,
"status_code": r.status_code,
})
request와 다른 점은 request는 가져오기만 하면 되기 때문에 양식이 필요가 없었는데
post는 json양식에 있는 데이터에 맞추어 데이터를 작성하여 주고
r = requests.post(f"{url} users", data=data)
data라는 변수에 작성한 데이터의 값을 넣어주게 된다
json 다루기
아까부터 json모양이라는 말을 했는데
그 json이 무엇이냐?
💡 json은 javascript Object Notation의 약자로 데이터를 저장하거나 데이터 통신을 할 때 주로 사용됩니다.
json을 파일로 다룰 때는. json 확장자를 사용합니다.
json의 형태는 key: value 쌍으로 이루어져 있으며, 파이썬의 dictionary 형태와 매우 유사합니다.
이러한 특성으로 인해 파이썬에서는 json 데이터를 dictionary 데이터로 변경하고,
반대로 dictionary 데이터를 json으로 변경할 수 있습니다.
# json 모듈을 사용하기 위해 import 합니다.
import json
import requests
# 해당 사이트는 요청에 대한 응답을 json 형태의 문자열로 내려줍니다.
url = "https://jsonplaceholder.typicode.com/"
r = requests.get(f"{url}users/1")
print(type(r.text)) # <class 'str'>
# 문자열 형태의 json을 dictionary 자료형으로 변경합니다.
response_content = json.loads(r.text)
print(type(response_content)) # <class 'dict'>
# dictionary 자료형이기 때문에 key를 사용해 value를 확인할 수 있습니다.
print(f"사용자 이름은 {response_content['name']} 입니다.")
# result output
"""
사용자 이름은 Leanne Graham 입니다.
"""
request로 받아온 json데이터를 읽기 위해서는 json을 import 해야 합니다.
왜냐하면 우리가 보기에는 받아온 데이터는 딕셔너리의 형태인 것 같지만
그 문자의 형태를 보면 그냥 모양만 비슷한 글자의 형태인 것을 알 수 있습니
그래서 딕셔너리로 사용을 하고 싶다면?
아까 import 했던 json을 사용해 주면
response_content = json.loads(r.text) 이 부분으로
우리가 원하는 딕셔너리 형태로 변환을 시켜줍니다.
pprint로 내용을 확인해 보면
딕셔너리형태로 잘 들어와 있는 것을 알 수 있습니다.
이런 걸 모든 주소에서 할 수 있냐
그건 또 아닌 것이 주소가 json데이터를 가지고 있어야 하므로
아무런 주소를 가져오면 없는 형태라는 오류가 뜨기도 합니다.
csv 다루기
csv 파일이란?
💡 csv는 comma-separated values의 약자로 텍스트에 쉼표( , )를 사용해 필드를 구분하며. csv 확장자를 사용합니다.
쉼표를 사용해 데이터를 구분한다는 특성 덕분에 텍스트 편집기를 사용해 간단한 csv 파일을 만드는 것도 가능합니다.
단순한 텍스트 파일이다.
그렇기에 읽고 쓰는 속도가 빠르다.
이렇게 vscode에서도 읽을 수 있다.
내용을 잘 보면 ,를 기준으로 전부 색이 나누어져 있는 것을 확인할 수 있다.
이것은 엑셀 파일로도 읽을 수 있다.
csv_file = open(csv_path, "r", encoding="utf-8")
파이썬 open과 마찬가지로 open함수를 쓰고 r모드로 읽어주
vscode에서도 실행을 하면 이렇게 자료가 들어간다.
먼저 csv파일을 리스트 형식으로 출력하면
['email', 'birthyear', 'name', 'Location']
['laura@example.com', '1996', 'Laura Grey', 'Manchester']
['craig@example.com', '1989', 'Craig Johnson', 'London']
['mary@example.com', '1997', 'Mary Jenkins', 'London']
['jamie@example.com', '1987', 'Jamie Smith', 'Manchester']
['jhon@example.com', '1998', 'John', 'Manchester']
이렇게 한 줄로 나오게 된다.
하지만 우리가 이 데이터를 사용할 때에는 딕셔너리 형태를 이용하는 것이 더욱 유용하기에
읽어 들이는 방법은 동일 하지만 저장하는 방법을 딕셔너리로 저장하게
csv_data = csv.DictReader(csv_file)을 입력해 주면 데이터 값을 딕셔너리의 형태로 저장을 할 수 있습니다.
csv파일 쓰기
읽기를 했다면 반대로 우리 쪽에서 쓸 줄도 알아야 하는 법
csv_file = open(csv_path, "a", encoding="utf-8", newline='')
open 함수에 a로 추가하는 모드로 들어가고 newline=''을 입력해 준다
아마도 새로운 내용을 입력할 때에 다른 줄에 입력을 해주기 위해서 적어주는 것 같다.
만약 없앤다면??
오히려 밑에 공백이 하나 더 생겨 버렸다는 것을 확인할 수 있다.
csv_writer = csv.writer(csv_file)
먼저 cvs데이터를 받아오고 거기에 내가 적어 넣을 변수를 설정해 준다.
이제 내가 원하는 데이터를 넣어보자
csv_writer.writerow(["lee@sparta.com", '1989', "lee", "Seoul"])
내가 넣고자 하는 데이터를 순서에 맞게 작성을 해주고
짜잔 내가 적은 데이터가 알맞게 입력이 되었습니다.
데코레이터(decorator)
데코레이터란?
💡 데코레이터란 이름 그대로 파이썬의 함수를 장식해 주는 역할을 합니다.
데코레이터는 선언되는 함수 위에 골뱅이를 사용해 @decorator 형태로 작성하며 해당 함수가 실행될 때 데코레이터에서 선언된 코드가 같이 실행됩니다.
# 데코레이터는 호출 할 함수를 인자로 받도록 선언합니다.
def decorator(func):
# 호출 할 함수를 감싸는 wrapper 함수를 선언합니다.
def wrapper():
# func.__name__에는 데코레이터를 호출 한 함수의 이름이 들어갑니다.
print(f"{func.__name__} 함수에서 데코레이터 호출")
func()
print(f"{func.__name__} 함수에서 데코레이터 끝")
# wrapper 함수를 리턴합니다.
return wrapper
@decorator
def decorator_func():
print("decorator_func 함수 호출")
decorator_func()
# result output
"""
decorator_func 함수에서 데코레이터 호출
decorator_func 함수 호출
decorator_func 함수에서 데코레이터 끝
"""
함수를 호출을 할 때에 특정 상황에 같이 함수가 작동을 하는 것을 말하는데.
그림을 보면 함수호출은 decorator_func()를 했지만 위에 있는 def decorator(func)까지 같이 작동을 한 것을 볼 수 있다.
@decorator 아마도 이것이 발동 조건인 것 같다.
이제 예제를 보면
# 특정 함수의 실행 시간 구하기
import time
import random
def time_checker(func):
def wrapper():
# 함수가 실행될 때 시간을 저장합니다.
start_time = time.time()
# 함수를 실행합니다.
func()
# 함수가 종료된 후 시간에 실행될 때 시간을 빼 실행 시간을 구합니다.
executed_time = time.time() - start_time
# 실행 시간을 소수점 5자리까지만 출력합니다.
print(f"{func.__name__} 함수의 실행시간 : {executed_time:.05f}s")
return wrapper
@time_checker
def main():
# 함수의 실행 시간을 테스트하기 위해 0.1초 ~ 1초간 sleep 합니다.
time.sleep(random.randint(1, 10) / 10)
for i in range(10):
main()
# result output
"""
main 함수의 실행시간 : 0.80095s
main 함수의 실행시간 : 0.90009s
main 함수의 실행시간 : 1.00027s
main 함수의 실행시간 : 0.20020s
main 함수의 실행시간 : 0.90011s
main 함수의 실행시간 : 0.60041s
main 함수의 실행시간 : 0.30027s
main 함수의 실행시간 : 0.40024s
main 함수의 실행시간 : 0.10026s
main 함수의 실행시간 : 0.50032s
"""
아까라 똑같이
@time_checker를 달아서 같이 실행이 되도록 하는데
def wrapper():
# 함수가 실행될 때 시간을 저장합니다.
start_time = time.time()
# 함수를 실행합니다.
func() <-- 이 부분이 우리가 넣어준 함수가 실행이 되는 부분입니다.
# 함수가 종료된 후 시간에 실행될 때 시간을 빼 실행 시간을 구합니다.
executed_time = time.time() - start_time
# 실행 시간을 소수점 5자리까지만 출력합니다.
print(f"{func.__name__} 함수의 실행시간 : {executed_time:. 05f} s")
그래서 생각을 해보면 데코레이션을 하면 저기 func() 위치가 우리가 넣은 함수가 작동을 하는 곳이고
양옆으로 시간 체크가 작동을 하는 것을 볼 수 있습니다.
이런 식으로도 사용 가능하다.
# 입력받은 인자에 2를 곱해주기
def double_number(func):
def wrapper(a, b):
# 함수에서 받은 인자에 2를 곱해줍니다.
double_a = a * 2
double_b = b * 2
return func(double_a, double_b)
return wrapper
@double_number
def double_number_add(a, b):
return a + b
def add(a, b):
return a + b
print(double_number_add(5, 10))
print(add(5, 10))
# result output
"""
30
15
"""
print(double_number_add(5, 10)) 함수를 호출할 때에는
@double_number 함수도 같이 호출이 되는데 내용을 보면
double_number_add(5, 10)가 더해주기 전에 먼저 a와 b을 두 배로 만들어 줄다음 다시 함수에 넣어주어라가 되어서
이 함수를 실행할 때에는 이미 a의 값은 10이고 b의 값은 20이 되어있다고 보면 된다.
'AI 코딩 교육 TIL' 카테고리의 다른 글
2024-02-23 AI 코딩 TIL (0) | 2024.02.23 |
---|---|
2024-02-22 AI 코딩 TIL (0) | 2024.02.22 |
2024-02-20 AI 코딩 TI (0) | 2024.02.20 |
2024-02-19 AI 코딩 TIL (0) | 2024.02.19 |
2024-02-08 AI 코딩 TIL (0) | 2024.02.16 |