[혼공학습단 9기] 혼공파 6주차

Klaus·2023년 2월 19일
0
post-thumbnail

6주간의 혼공학습단 일정이 마무리되었다.
혼자 공부하는 데이터 분석도 같이 하려고 했는데 욕심이었던 것 같다😅
그래도 혼공파를 끝낸 것이 너무 뿌듯하다!
내가 시작해놓고 제대로 마무리하지 않은 경우가 많았기 때문에 이번에도 자신이 없었지만 마지막까지 올 수 있었다.
내가 공부한 부분을 봐주시는 분들이 있다고 생각하니 늦어도 제출해야 한다라는 의무감이 생겨서 혼자 할 때보다 더 집중하게 되고 책의 마지막 장을 넘길 수 있게 된 것 같다.
매번 댓글로 응원해주시고 동기 부여해주신 혼공족장님과 제 게시물을 읽어주시고 함께 혼공단에 참여한 모든 분께 감사드립니다!
정말 감사합니다. 모두 건강하세요!!🙇‍♀️


6주차 진도(2/13~2/19) 진도 : Chapter 07(07-2)~08

✅ 기본 미션

p.342 [직접 해보는 손코딩: BeautifulSoup 스크레이핑 실행하기] 
예제 실행 후 결과 화면 캡처하기

✅ 선택 미션

혼공 용어 노트에 나만의 언어로 객체, 클래스, 인스턴스, 생성자, 메소드 
정리하고 공유하기




(07-2) 외부 모듈

📖 외부 모듈
파이썬이 기본적으로 제공해 주는 것이 아니라, 다른 사람들이 만들어 배포하는 모듈.


모듈 설치

pip install 모듈 이름 	# 명령 프롬프트 창에서 실행
  • ⌨️ Beautiful Soup 설치
    (본인 : 가상 환경에서 실습을 진행)
conda install beautifulsoup4 	# 가상환경에 설치
  • 🖥️ 설치 확인
pip list


모듈 찾기

  1. 책에서 추천하는 모듈 사용.
  2. 새로운 모듈 접하고 싶을 때 → 파이썬 커뮤니티 가입.
  3. 개발하면서 모듈 필요할 때 → 구글에 'Python + 원하는 모듈' 검색

BeautifulSoup 모듈

  • 유명한 파이썬의 웹 페이지 분석 모듈.
  • 구글에 'Python Beautiful Soup'으로 검색해서 Beautiful Soup 모듈 공식 홈페이지 접속 가능.
BeautifulSoup()	# 사용 방법 → 단순한 함수 X, 클래스의 생성자 O

⌨️ BeautifulSoup 모듈 이용 : 날씨 가져오기

from urllib import request
from bs4 import BeautifulSoup

# urlopen() 함수로 기상청 전국 날씨 읽기
target = request.urlopen("http://www.kma.go.kr/weather/forecast/mid-term-rss3.jsp?stnId=108")

# BeautifulSoup 사용해 웹 페이지 분석
soup = BeautifulSoup(target, "html.parser")

# location 태그 찾기
for location in soup.select("location"):
    # 내부 city, wf, tmn, tmx 태그 찾아 출력
    print("도시:", location.select_one("city").string)
    print("날씨:", location.select_one("wf").string)
    print("최저 기온:", location.select_one("tmn").string)
    print("최고 기온:", location.select_one("tmx").string)
    print()

🖥️ 결과


Flask 모듈

  • 파이썬으로 웹 개발할 대 Django(장고) / Flask(플라스크) 등의 모듈 사용.
    - Django : 매우 다양한 기능 제공하는 웹 개발 프레임워크.
    - Flask : 작은 기능만 제공하는 웹 개발 프레임워크.
  • ⌨️ 설치 방법
pip install flask
  • 🖥️ 설치 확인
  • 사용 방법
# 윈도우 사용자
set FLASK_APP=파일 이름.py
flask run

# 맥, 리눅스 사용자
export FLASK_APP=파일 이름.py
flask run

⌨️ Flask 모듈 사용

from flask import Flask
app = Flask(__name__)

@app.route("/")	# @app.route() : 데코레이터 

def hello():
    return "<h1>Hello World!</h1>"  

🖥️ 결과

  • 웹 브라우저와 통신할 수 있는 간단한 웹 서버 생성.

    📖 Flask 모듈 : @app.route(경로)
    - '<경로>에 들어갈 때 실행할 함수' 지정하는 형태로 사용.
    - 함수에서 리턴하는 문자열 기반 HTML 파일을 웹 브라우저에 제공.


BeautifulSoup 스크레이핑

  • Flask 모듈 : <경로>에 들어갈 때마다 함수 실행
  • BeautifulSoup 스크레이핑 실행 코드 만드는 법
    - 이전 코드의 hello() 함수에 넣음.
    - 문자열 리턴하게 만듦.

⌨️ BeautifulSoup 스크레이핑 실행

# 모듈 읽어들임
from flask import Flask
from urllib import request
from bs4 import BeautifulSoup

# 웹 서버 생성
app = Flask(__name__)
@app.route("/")

def hello():
    # urlopen() 함수로 기상청 전국 날씨 읽음
    target = request.urlopen("http://www.kma.go.kr/weather/forecast/mid-term-rss3.jsp?stnId=108")

    # BeautifulSoup 사용해 웹 페이지 분석
    soup = BeautifulSoup(target, "html.parser")

    # location 태그 찾기
    output =""
    for location in soup.select("location"):
        # 내부 city, wf, tmn, tmx 태그 찾아 출력
        output += "<h3>{}</h3>".format(location.select_one("city").string)
        output += "날씨: {}<br/>".format(location.select_one("wf").string)
        output += "최저/최고 기온: {}/{}"\
            .format(\
            location.select_one("tmn").string,\
            location.select_one("tmx").string)
        output += "<hr/>"
    return output

🖥️ 결과


라이브러리와 프레임워크

구분설명
라이브러리(library)정상적인 제어를 하는 모듈
프레임워크(framework)제어 역전 발생하는 모듈

📖 라이브러리 (library)

  • 역전되지 정상적인 제어.
  • 개발자가 모듈 기능 호출하는 형태의 모듈.
    - 개발자가 모듈의 함수 호출 = 일반적 제어 흐름
  • math 모듈
# 모듈 읽어들임
from math import sin, cos, tan, floor, ceil
# sin 구함
print("sin(1):", sin(1))

📖 프레임워크 (framework)

  • 제어 역전(IoC, Inverse of Control) : 제어가 역전되어 있음.
  • 모듈이 개발자의 코드를 실행하는 형태의 모듈.
    - 모듈이 개발자의 함수 호출 = 제어 역전됨
  • Flask 모듈
from flask import Flask
app = Flask(__name__)
# 웹 서버 생성
@app.route("/")
def hello():
	return "<h1>Hello World!</h1>"

데코레이터 (decorator)

  • @로 시작하는 구문.
  • 만드는 방법 따른 종류
    - 함수 데코레이터
    - 클래스 데코레이터

📖 함수 데코레이터

  • 함수에 사용되는 데코레이터.
  • 대상 함수의 앞뒤에 꾸밀 부가적 내용 또는 반복 내용을 데코레이터로 정의해 손쉽게 사용.

⌨️ 함수 데코레이터

# 함수 데코레이터 생성
def test(function):
    def wrapper():
        print("인사가 시작되었습니다.")
        function()
        print("인사가 종료되었습니다.")
    return wrapper

# 데코레이터 붙여 함수 만듦
@test

def hello():
    print("hello")

# 함수 호출
hello()

🖥️ 결과

  • test() 함수 : wrapper() 함수 리턴 → hello에 함수 들어가 hello() 형태로 호출.

왜 데코레이터 사용해서 복잡하게 작성할까?

  • 데코레이터 사용 → functools라는 모듈 이용 가능
  • 함수 데코레이터 사용 → 매개변수 등 전달 가능
    ➡️ 반복 구문 많을 때 소스 가독성 ⬆️ & 매우 유용 사용 가능.
# 함수로 데코레이터 생성
def test(function):
    @wraps(function)
    def wrapper(*arg, **kwargs):
        print("인사가 시작되었습니다.")
        function(*arg, **kwargs)
        print("인사가 종료되었습니다.")
        return wrapper

📖 functools

  • 고차함수(high order functions) 위해 설계된 라이브러리.
  • 호출 가능한 기존 객체, 함수 사용다른 함수 반환 & 활용 가능.
    기존 파이썬 함수 재사용해 원래 함수처럼 작동하는 새 부분 객체 반환



(07-3) 모듈 만들기

모듈 만드는 방법

  • 파이썬 파일 만든다.
    - 모듈 내부에 변수, 함수 등 넣어줌.
  • 외부에서 읽어 들인다.

__name__="__main__"

  • __name__
    - 변수
    - 엔트리 포인트(entry point) / 메인(main) : 프로그램의 진입점

  • 모듈의 __name__
    - 엔트리 포인트 아니지만 엔트리 포인트 파일 내에서 import되어 모듈 내 코드 실행됨
    - 모듈 내에서 __name__ 출력 → 모듈 이름 나타남.

  • __name__ 활용
    - 엔트리 포인트 파일 내부 : __name__ = "__main__"
    → 현재 파일이 모듈로 실행되는지, 엔트리 포인트로 실행되는지 확인 가능

# 현재 파일이 모듈 실행 / 엔트리 포인트 실행 인지 확인해줌
if __name__ == "__main__":
	print ..	# 엔트리 포인트일 때만 실행


패키지

📖 패키지 (package)

  • 모듈 모여서 구조 이룬 것.
  • 모듈(module) : import로 가져오는 모든 것.
  • pip(Python Package Index) : 패키지 관리 시스템(Package Management System)

__init__.py 파일

  • 패키지 읽을 때 어떤 처리 수행해야 하거나 패키지 내부 모듈들 한번에 가져오고 싶을 때 생성.
  • 패키지 폴더 내부에 생성 → 패키지 읽을 때 가장 먼저 실행.
  • 패키지 관련 초기화 처리 등 가능.
  • __all__이란 리스트 만듦.
from <패키지 이름> import* # 이 리스트에 지정된 모듈들 전부 읽어짐.
  • 파이썬 3.3 이후 버전 : __init__.py 없어도 패키지 작동 가능.


텍스트 데이터(text data)

  • 쉽게 읽을 수 있는 형태의 데이터.
  • 텍스트 에디터 있으면 쉽게 편집 가능.
  • 컴퓨터 : 이진 숫자(binary)로 모든 처리 수행.
  • 인코딩: 숫자와 알파벳 대응하는 방법
    - ASCII, UTF-8, UTF-16, EUC-KR, Shift-JIS 등

바이너리 데이터(binary data)

  • 텍스트 에디터로 열었을 때 의미 이해할 수 없는 데이터.
  • 이미지, 동영상
  • 인코딩 방식 : 바이너리 데이터 읽어서 이미지로 보여주기 위한 변환 방식.
    - JPEG, PNG, GIF 등
비교텍스트 데이터바이너리 데이터
구분 방법텍스트 에디터로 읽기 O텍스트 에디터로 읽기 X
장점사람이 쉽게 읽기 O, 텍스트 에디터로 쉽게 편집 O용량 작음
단점용량 큼사람이 쉽게 읽을 수 X, 텍스트 에디터 편집 X

인코딩(encoding)

  • 인코딩 방식 기반 A라는 형식 → B라는 형식으로 변환하는 것.

디코딩(decoding)

  • 인코딩된 데이터 반대로 돌리는 것.

인터넷 이미지 저장 방법

  • 웹에서 가져온 데이터 → 바이너리 데이터로 저장.
  • 파일 열 때 "wb"로 적으면 바이너리 형식으로 읽고 써줌.



(08-1) 클래스의 기본


객체 지향 프로그래밍 언어 (Object Oriented Programming)

  • 객체를 우선 생각해 프로그래밍한다.
  • 모든 프로그래밍 언어 : 클래스 기반
    - 클래스(class) 기반으로 객체(object) 만듦.
    - 그 객체를 우선으로 생각해 프로그래밍.
  • 핵심 : 객체와 관련된 코드를 분리할 수 있게 하는 것.

📖 객체(object)

  • 여러 가지 속성 가질 수 있는 대상.
  • 추상화(abstraction) : 프로그램에서 필요한 요소만으로 객체 표현하는 것.

⌨️ 딕셔너리로 객체 만들기

# 학생 리스트 선언
students = [
    {"name": "윤인성", "korean": 87, "math": 98, "english": 88, "science": 95},
    {"name": "연하진", "korean": 92, "math": 98, "english": 96, "science": 98},
    {"name": "구지연", "korean": 76, "math": 96, "english": 94, "science": 90},
    {"name": "나선주", "korean": 98, "math": 92, "english": 96, "science": 92},
    {"name": "윤아린", "korean": 95, "math": 98, "english": 98, "science": 98},
    {"name": "윤명원", "korean": 64, "math": 88, "english": 92, "science": 92},    
]
# 학생 한 명씩 반복
print("이름", "총점", "평균", sep="\t")

for student in students:
    # 점수의 총합, 평균 구함
    score_sum = student["korean"] + student["math"] + student["english"] + student["science"]
    score_average = score_sum / 4

    # 출력
    print(student["name"], score_sum, score_average, sep="\t")

🖥️ 결과


⌨️ 위의 코드를 객체 만드는 함수 이용해 만들기

# 딕셔너리 리턴하는 함수 선언
def create_student(name, korean, math, english, science):
    return{
        "name": name,
        "korean": korean,
        "math": math,
        "english": english,
        "science": science
    }

# 학생 리스트 선언
students = [
create_student("윤인성", 87, 98, 88, 95),
create_student("연하진", 92, 98, 96, 98),
create_student("구지연", 76, 96, 94, 90),
create_student("나선주", 98, 92, 96, 92),
create_student("윤아린", 95, 98, 98, 98),
create_student("윤명원", 64, 88, 92, 92)               
]

# 학생 한 명씩 반복
print("이름", "총점", "평균", sep="\t")
for student in students:
    # 점수의 총합, 평균 구함

     score_sum = student["korean"] + student["math"] + student["english"] + student["science"]
     score_average = score_sum / 4

     # 출력
     print(student["name"], score_sum, score_average, sep="\t")

🖥️ 결과


⌨️ 위의 코드에서 학생을 매개변수로 받는 형태의 함수로 만들기

# 딕셔너리 리턴하는 함수 선언
def create_student(name, korean, math, english, science):
    return{
        "name": name,
        "korean": korean,
        "math": math,
        "english": english,
        "science": science
    }

# 힉셍 처리하는 함수 선언
def student_get_sum(student):
    return student["korean"] + student["math"] + student["english"] + student["science"]

def student_get_average(student):
    return student_get_sum(student) / 4

def student_to_string(student):
    return "{}\t{}\t{}".format(student["name"], student_get_sum(student), student_get_average(student))

# 학생 리스트 선언
students = [
create_student("윤인성", 87, 98, 88, 95),
create_student("연하진", 92, 98, 96, 98),
create_student("구지연", 76, 96, 94, 90),
create_student("나선주", 98, 92, 96, 92),
create_student("윤아린", 95, 98, 98, 98),
create_student("윤명원", 64, 88, 92, 92)               
]

# 학생 한 명씩 반복
print("이름", "총점", "평균", sep="\t")
for student in students:
    # 출력
    print(student_to_string(student))

🖥️ 결과



❗️ 클래스(class)

  • 효율적으로 객체 생성하기 위해 만든 구문.
  • 클래스 이름과 같은 함수(생성자) 사용해 객체 만듦.
  • 클래스 사용 = 작정하고 속성 & 기능 가진 객체 만들겠다는 의미
  • 생성 방법
class 클래스 이름:
	클래스 내용

📖 인스턴스(instance)

  • 클래스 기반으로 만들어진 객체.
인스턴스 이름/변수 이름 = 클래스 이름() # 생성자 함수


생성자(constructor)

  • 클래스 이름과 같은 함수.
  • 클래스 내에 __init__ 함수 만들면 객체 생성 시 처리할 내용 작성 가능.
  • 클래스 내부 함수
    - 첫 번째 매개변수 : 반드시 self 입력❗️
    • self : '자기 자신' 나타내는 딕셔너리
    • self가 가진 속성 & 기능에 접근 시 self.<식별자> 형태
class 클래스 이름:
	def __init__(self, 추가적인 매개변수):
    	pass

⌨️ 위의 코드에서 생성자 이용해 함수 구현

# 클래스 선언
class Student:
    def __init__(self, name, korean, math, english, science):
        self.name = name
        self.korean = korean
        self.math = math
        self.english = english
        self.science = science

# 학생 리스트 선언
students = [
    Student("윤인성", 87, 98, 88, 95),
    Student("연하진", 92, 98, 96, 98),
    Student("구지연", 76, 96, 94, 90),
    Student("나선주", 98, 92, 96, 92),
    Student("윤아린", 95, 98, 98, 98),
    Student("윤명원", 64, 88, 92, 92)               
]

# Student 인스턴스의 속성에 접근하는 방법
print(students[0].name)
print(students[0].korean)
print(students[0].math)
print(students[0].english)
print(students[0].science)

🖥️ 결과



메소드(method)

  • 클래스가 가지고 있는 함수.
  • 클래스 내부에 메소드 만드는 방법
    - 생성자 선언 방법과 동일.
    - 첫 번째 매개변수로 반드시 self 넣기❗️
class 클래스 이름:
	def 메소드 이름(self, 추가적인 매개변수):
    	pass
  • C#, Java 등
    - 클래스 함수 = '메소드'
  • 파이썬 :
    - '멤버 함수(member function)', 인스턴스 함수(instance function)' 더 많이 사용.

⌨️ 위의 함수에서 함수(메소드) 선언하기

# 클래스 선언
class Student:
    def __init__(self, name, korean, math, english, science):
        self.name = name
        self.korean = korean
        self.math = math
        self.english = english
        self.science = science
    
    def get_sum(self):
        return self.korean + self.math + self.english + self.science
    
    def get_average(self):
        return self.get_sum() / 4
    
    def to_string(self):
        return "{}\t{}\t{}".format(\
            self.name,\
                self.get_sum(),\
                    self.get_average())
    
# 학생 리스트 선언
students = [
    Student("윤인성", 87, 98, 88, 95),
    Student("연하진", 92, 98, 96, 98),
    Student("구지연", 76, 96, 94, 90),
    Student("나선주", 98, 92, 96, 92),
    Student("윤아린", 95, 98, 98, 98),
    Student("윤명원", 64, 88, 92, 92)               
]

# 학생 한 명씩 반복
print("이름", "총점", "평균", sep="\t")
for student in students:
    # 출력
    print(student.to_string())

🖥️ 결과




(08-2) 클래스의 추가적인 구문

인스턴스의 클래스 확인

📖 isinstance() 함수

  • 객체(인스턴스)가 어떤 클래스로부터 만들어졌는지 확인 가능.
  • 클래스 간 상속 관계까지 확인.
  • 하나의 리스트로 여러 종류 데이터 처리 가능.
  • 첫 번째 매개변수 : 객체(인스턴스), 두 번째 매개변수 : 클래스
isinstance(인스턴스, 클래스)

⌨️ 클래스 확인 예제

# 클래스 선언
class Student:
    def __init__(self):
        pass

# 학생 선언
student = Student()

# 인스턴스 확인 방법 1
print("isinstance(student, Student):",isinstance(student, Student))

# 인스턴스 확인 방법 2
type(student) == Student	# 상속 사용 관계는 확인하지 않음

🖥️ 결과



특수한 이름의 메소드

📖 특수 메소드

  • 파이썬이 클래스 사용 시, 제공해주는 보조 기능.
  • 특수 상황에 자동 호출됨.
  • 형태
__<이름>__()

종류

  • __str__() 함수 : 객체를 문자열로 변환.
  • 크기 비교 함수
    • eq(equal) : 같다
    • ne(not equal) : 다르다
    • gt(greater than) : 크다
    • ge(greater than or equal) : 크거나 같다
    • lt(less than) : 작다
    • le(less than of equal) : 작거나 같다

❗️ 원하는 함수 이름을 사용하면 안되는 이유

  • 프로그래밍할 때는 항상 다른 사람과의 협업 관점에서 함수를 사용해야 하기
    때문!


클래스 변수와 메소드

  • 클래스 : 인스턴스처럼 속성(변수), 기능(함수) 가질 수 있음.

클래스 변수

  • 만드는 방법 : class 구문 바로 아래 단계에 변수 선언.
  • 클래스 변수 만들기
class 클래스 이름:
	클래스 변수 =
  • 클래스 변수에 접근하기
클래스 이름.변수 이름

클래스 함수

  • 만드는 방법
    • 일반 함수 만들기와 비슷.
    • 첫 번째 매개변수 : 클래스 자체. cls란 이름의 변수로 선언.
  • '클래스가 가진 기능'이라고 명시적으로 나타냄.
  • 클래스 함수 만들기
class 클래스 이름:
	@classmethod
    def 클래스 함수(cls, 매개변수):
    	pass
  • 클래스 함수 호출
클래스 이름.함수 이름(매개변수)


가비지 컬렉터

📖 가비지 컬렉터

  • 더 사용할 가능성 없는 데이터를 메모리에서 제거.
    • 변수에 저장되지 않는 경우.
    • 함수 등에서 나오면서 변수 활용할 수 없는 경우.
  • 스왑(swap)
    • 메모리가 부족할 때 컴퓨터가 하드디스크를 메모리처럼 사용해 올리는 것.
    • 하드디스크 : 메모리보다 매우 느려 스왑 처리 속도 매우 느림.
  • 알아서 잘 작동.


프라이빗 변수와 게터/세터

📖 프라이빗 변수

  • 변수 마음대로 사용하는 것 방지.
  • 언더바 2개 붙이기만 하면 외부에서 사용할 수 없는 변수가 됨.
인스턴스 변수 이름 → '__<변수 이름>' 형태로 선언.
  • 게더(getter), 세터(setter)
    • 프라이빗 변수 값 추출 또는 변경 목적.
    • 간접적 속성 접근 가능하게 해주는 함수.
    • 데코레이터 사용해 쉽게 만들기 가능.
      # 데코레이터 추가
      @ property
      # 변수 이름과 같은 함수 정의.
      @ <변수 이름>.setter	


상속 (inheritance)

📖 상속

  • 다른 이가 만들어 놓은 기본 형태에 원하는 것만 교체하는 것.
    - 부모(parent) : 기반이 되는 것 → 자식에게 자신의 기반 물려줌.
    - 자식(child) : 기반으로 생성한 것.
  • 다중 상속
    - 다른 이가 만들어 놓은 형태들을 조립해 원하는 것 만드는 것.
    - 거의 사용 X
  • 클래스 기반 객체 지향 언어들이 지원하는 기능.

⌨️ 상속

# 부모 클래스 선언
class Parent:
    def __init__(self):
        self.value = "테스트"
        print("Parent 클래스의 __init()__ 메소드가 호출되었습니다.")
    def test(self):
        print("Parent 클래스의 test() 메소드입니다.")

# 자식 클래스 선언
class Child(Parent):
    def __init__(self):
        Parent.__init__(self)
        print("Child 클래스의 __init()__ 메소드가 호출되었습니다.")

# 자식 클래스의 인스턴스를 생성하고 부모 메소드 호출
child = Child()
child.test()
print(child.value)

🖥️ 결과


상속 예시) 예외 클래스 만들기

  • 상속 : 기존 클래스 기반으로 조금 수정하여 원하는 클래스 만들 때 사용.

⌨️ 사용자 정의 예외 클래스

class CustomException(Exception):
    def __init__(self):
        Exception.__init__(self)
raise CustomException

🖥️ 결과


상속 예시) 재정의(오버라이드)

📖 재정의/오버라이드(override)

  • 부모에 정의되어 있는 함수를 자식에서 다시 정의하는 것.
  • 코드 실행하면 출력 약간 변화.

⌨️ 자식 클래스로써 부모 함수 재정의

class CustomException(Exception):
    def __init__(self):
        Exception.__init__(self)
        print("### 내가 만든 오류가 생성되었어요! ###")
    def __str__(self):
        return "오류가 발생했어요"

raise CustomException

🖥️ 결과


상속 예시) 자식 클래스에 새로운 함수 정의

  • 자식 클래스에서 기존에 있던 함수/변수 이외의 것 완전히 새로 정의하는 것 가능.

⌨️ 자식 클래스로써 새로운 함수 정의

class CustomException(Exception):
    def __init__(self, message, value):
        Exception.__init__(self)
        self.message = message
        self.value = value
    
    def __str__(self):
        return self.message
    
    def print(self):
        print("### 오류 정보 ###")
        print("메세지:", self.message)
        print("값:", self.value)

# 예외 발생
try:
    raise CustomException("딱히 이유 없음", 777)
except CustomException as e:
    e.print()

🖥️ 결과

0개의 댓글