본문 바로가기
프로그래밍/Python

[AI 부트캠프] DAY 14 - 파이썬 8

by HOHHOH 2023. 8. 4.

[오늘의 일지]

녹화 강의 - 멀티스레드, 멀티프로세싱, 일급객체, 클로저, 이터레이터, 제너레이터, 데코레이터

실시간 강의 - 파이썬 복습, 크롤링

[상세 내용]

파이썬 선택 추가 학습

멀티스레드

- 멀티스레드의 정의 : 멀티스레딩은 여러 코어에서 한 번에 여러 개의 스레드를 처리하는 CPU 성능을 활용하는 프로그래밍의 한 유형입니다. 

- 멀티스레드 예시

 

import threading

# 스레드에서 실행할 함수
def work():
    print("[sub] start")
    keyword = input("[sub] 검색어를 입력하세요 >>>")
    print(f"[sub] {keyword}로 검색을 시작합니다...")
    print("[sub] end")

# 메인스레드 실행되는 부분
print("[main] start")

worker = threading.Thread(target=work)
worker.daemon = True # main이 종료될 때 sub가 같이 종료되는 기능
worker.start()

print("[main] 메인 스레드는 자기할일을 합니다..")
print("[main] end")
>>>
[main] start
[sub] start
[main] 메인 스레드는 자기할일을 합니다..
[main] end
[sub] 검색어를 입력하세요 >>>test
[sub] test로 검색을 시작합니다...
[sub] end

 

- 멀티스레드 예시(주식 매수, 매도)

import threading
import time

# 주식 자동매매
# 매수, 매도

# 매수 스레드
def buyer():
    for i in range(5):
        print("[매수] 데이터 요청 중...")
        time.sleep(1) # 1초 기다리고 다음 코드 실행
        print("[매수] 데이터 분석 중...")
        time.sleep(1)
        print("[매수] 매수 구간 진입중 입니다...")
        time.sleep(1)
        print("[매수] 매수를 시작합니다...")
        time.sleep(1)

# 매도 스레드
def saler():
    for i in range(5): 
        print("[매도] 데이터 요청 중...")
        time.sleep(1)
        print("[매도] 데이터 분석 중...")
        time.sleep(1)
        print("[매도] 매도 구간 진입중 입니다...")
        time.sleep(1)
        print("[매도] 매도를 시작합니다...")
        time.sleep(1)

# 메인 스레드
print("[메인] start")
buyer = threading.Thread(target=buyer)
saler = threading.Thread(target=saler)
buyer.start()
saler.start()

buyer.join() # 매수 스레드가 종료될때까지 메인 스레드가 기다림 
saler.join() # 매도 스레드가 종료될때까지 메인 스레드가 기다림 
print("[메인] 장이 종료되었습니다.") 
>>>
[메인] start
[매수] 데이터 요청 중...
[매도] 데이터 요청 중...
[매도] 데이터 분석 중...
[매수] 데이터 분석 중...
[매수] 매수 구간 진입중 입니다...
[매도] 매도 구간 진입중 입니다...
[매수] 매수를 시작합니다...[매도] 매도를 시작합니다...

[매수] 데이터 요청 중...[매도] 데이터 요청 중...

[매도] 데이터 분석 중...[매수] 데이터 분석 중...

[매도] 매도 구간 진입중 입니다...
[매수] 매수 구간 진입중 입니다...
[매도] 매도를 시작합니다...[매수] 매수를 시작합니다...

[매도] 데이터 요청 중...
[매수] 데이터 요청 중...
[매도] 데이터 분석 중...[매수] 데이터 분석 중...

[매수] 매수 구간 진입중 입니다...[매도] 매도 구간 진입중 입니다...

[매도] 매도를 시작합니다...
[매수] 매수를 시작합니다...
[매도] 데이터 요청 중...[매수] 데이터 요청 중...

[매도] 데이터 분석 중...[매수] 데이터 분석 중...

[매수] 매수 구간 진입중 입니다...[매도] 매도 구간 진입중 입니다...

[매수] 매수를 시작합니다...[매도] 매도를 시작합니다...

[매수] 데이터 요청 중...[매도] 데이터 요청 중...

[매수] 데이터 분석 중...
[매도] 데이터 분석 중...
[매도] 매도 구간 진입중 입니다...[매수] 매수 구간 진입중 입니다...

[매도] 매도를 시작합니다...
[매수] 매수를 시작합니다...
[메인] 장이 종료되었습니다.

 

멀티프로세싱

- 멀티프로세싱의 정의 : 멀티 프로세싱 (Multi Processing)이란, 여러 개의 CPU 코어가 동시에 작업을 처리하는 것을 의미합니다.

- 멀티프로세싱 예시

import multiprocessing as mp

# 프로세스에서 실행할 함수
def sub_process(name):
    print("[sub] start")
    print(name)
    cp = mp.current_process()
    print(f"[sub] pid : {cp.pid}")
    print("[sub] end")

# if __name__ == "__main__" 사용하는 이유 
# https://docs.python.org/2/library/multiprocessing.html#multiprocessing-programming
# 메인 프로세스
if __name__ == "__main__":
    print("[main] start")
    p = mp.Process(target=sub_process, args=('test_coding',))
    p.start()
    cp = mp.current_process()
    print(f"[main] pid : {cp.pid}")
    print("[main] end")

>>>
[main] start
[main] pid : 12024
[main] end
[sub] start
test_coding
[sub] pid : 12136
[sub] end

 

- 멀티프로세싱 예시

from multiprocessing import Process
import time

class Subprocess(Process):
    
    def __init__(self, name):
        Process.__init__(self)
        self.name = name

    def run(self):
        print(f"[sub] {self.name} start")
        time.sleep(5)
        print(f"[sub] {self.name} end")

if __name__ == "__main__":
    print("[main] start")
    p = Subprocess(name='test_coding')
    p.start()
    p.join()
    print("[main] end")
>>>
[main] start
[sub] test_coding start
[sub] test_coding end
[main] end

# 추가학습
# 1. 스레드간 데이터 처리 (lock)
# 2. 프로세스간 데이터 전송 (Queue, Pipe)
# 3. 속도 비교
# 4. 운영체제와 메모리

 

 

일급객체

- 일급객체 정의 : 다른 객체들에 일반적으로 적용 가능한 연산을 모두 지원하는 객체를 가리킵니다.

더보기

일급객체의 조건
- 다음과 같은 특징을 만족하는 객체를 말한다. 
1. 데이터처럼 사용이 가능하다. 
2. 매개변수에 넘겨줄 수 있다.
3. 리턴값으로 사용될 수 있다. 
위에 조건에 의해 일반적으로 사용하는 함수가 여기에 포함되기도 합니다.

 

클로저

- 클로저 정의 : 함수가 종료되어도 자원을 사용할 수 있는 함수

# 내부 함수
# 함수 안에 또다른 함수를 정의할 수 있다.

def outer(name):
    def inner():
        print(name, "님 안녕하세요!")
    return inner

func = outer("startcoding")
# func()

# 클로저
# 함수가 종료되어도 자원을 사용할 수 있는 함수

# ** 클로저가 될 조건
# 1) 내부 함수 여야 한다.
# 2) 외부 함수의 변수를 참조해야 한다.
# 3) 외부 함수가 내부 함수를 반환해야 한다.

def greeting(name, age, gender):
    def inner():
        print(name, "님 안녕하세요!")
        print("나이: ", age)
        print("성별: ", gender)
    return inner

closure = greeting('이름', 나이, '성별')
closure()

# print(closure.__closure__[0].cell_contents)

for i in closure.__closure__:
    print(i.cell_contents)

# 전역변수를 사용해서 대체가 가능하다.
# 전역변수 사용을 최소화 하는 것이 좋다 (네이밍문제, 스코프문제)

 

이터레이터

- 이터레이터 정의 : 이터레이터는 순서대로 다음 값을 리턴할 수 있는 객체를 의미합니다. 자체적으로 내장하고 있는 next 메서드를 통해 다음 값을 가져올 수 있는데 list, tuple과 같은 타입의 객체와는 다른 개념입니다.

- 이터레이터 생성 과정

# 이터레이터 생성방법

class Seasons:
    def __init__(self):
        self.season_list = ['spring', 'summer', 'autumn', 'winter']
        self.idx = 0
        self.max_num = 4

    def __iter__(self):
        return self

    def __next__(self):
        if self.idx < self.max_num:
            curr_idx = self.idx
            self.idx += 1
            return self.season_list[curr_idx]
        else:
            raise StopIteration

# for i in Seasons():
#     print(i)

iter_obj = Seasons().__iter__()

print(iter_obj.__next__())
print(iter_obj.__next__())
print(iter_obj.__next__())
print(iter_obj.__next__())
print(iter_obj.__next__()) # 마지막에는 객체가 없기 때문에 에러가 납니다.

 

제너레이터

- 제너레이터의 정의 : 이터레이터에 의존하는 개념으로서 이터레이터를 사용해 자신의 실행을 제어하는 함수로서 범위로 따졌을 때는 제너레이터가 이터레이터에 속한다고 할 수 있습니다.

- 제너레이터 예시

# 제너레이터

# 1. 이터레이터를 만드는 함수

def season_generator(*args):
    for arg in args:
        yield arg

g = season_generator('spring', 'summer', 'autumn', 'winter')

print(g.__next__())
print(g.__next__())
print(g.__next__())
print(g.__next__())
# print(g.__next__())

def func():
    print("첫번째 작업 중...")
    yield 1

    print("두번째 작업 중...")
    yield 2

    print("세번째 작업 중...")
    yield 3

ge = func()
data = ge.__next__()
print(data)
data = ge.__next__()
print(data)
data = ge.__next__()
print(data)

 

데코레이터

- 데코레이터의 정의 : 함수의 앞, 뒤로 부가적인 기능을 넣어주고 싶을 때 사용한다

- 데코레이터 예시

# 데코레이터
# 함수의 앞, 뒤로 부가적인 기능을 넣어주고 싶을 때 사용
# 로깅, 권한확인

# 데코레이터 생성하기
def logger(func):
    def wrapper(arg):
        print("함수 시작")
        func(arg) # 함수 실행
        print("함수 끝")
    return wrapper

@logger
def print_hello(name):
    print("hello", name)

@logger
def print_bye(name):
    print("bye", name)

print_hello('startcoding')
print_bye('fastcampus')

 

실시간 강의 내용 정리

파이썬 복습

- 삼항연산자 : 삼항연산자란 if else 문법을 마치 연산자와 같이 사용하는 것을 말합니다.

- 삼항연산자 사용 방법

# 일반 조건문
if 조건문:
	조건이 참일 때 실행될 코드들
else:
	조건이 거짓일 때 실행될 코드들
    
# 삼항연사자 사용시
(조건이 참일 때 실행될 코드) if 조건문 else (조건이 거짓일 때 실행될 코드)

 

- 리스트 컴프리헨션 : 반복문, 조건문 등의 제어문과 함께 리스트를 한 줄의 코드로 만들 수 있게 해주는 파이썬 문법입니다.

- 리스트 컴프리헨션 예시

# 기본적인 for문을 이용한 리스트 만들기
numbers = []

for i in range(1, 11):
	numbers.append(i)
    
# 리스트 컴프리헨션을 사용하면
numbers = [i for i in range(1, 11)]

# 리스트 컴프리헨션 기본적인 형태
(리스트에 넣고자 하는 변수) for 변수 in 리스트 (혹은 range 등)

# 일반적인 for문에서 if문을 쓰는 상황
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
filterd_numbers = []

for i in numbers:
	if i >= 4:
		filterd_numbers.append(i)

# 리스트 컴프리헨션을 사용하면
filtered_numbers = [number for number in numbers if number >= 4]

# 조건문에서 리스트 컴프리헨션 기본적인 형태
(리스트에 넣고자 하는 변수) for 변수 in 리스트 (혹은 range 등) if (조건문)

 

- lambda 표현식과 map, filter, reduce

- lambda 함수 

# 기본적인 형태
lambda 매개변수 : 식 (리턴값)

# 일반 함수
def plus(x, y):
	return x + y
    
# lambda 를 사용하면
lambda x, y: x + y

# 사용할 때는 이런 방식으로 마지막에 넣어줍니다.
result = (lambda x, y: x + y)(1, 2) # x에 1, y에 2가 들어가고, x+y인 3이 리턴됨
print(result)
>>>3

 

- map 함수 : map함수는 함수와 리스트(반복 가능 객체)를 인자로 받고, 리스트 요소 각각에 해당 함수를 적용시킨 새로운 리스트를 반환해 주는 함수입니다.

# 기본적인 형태
map(함수, 반복가능객체(리스트 등))

# [1,2,3]라는 리스트가 있을 때 각 요소에 10을 더하고 싶은 상황
my_list = [1, 2, 3]
new_list = map(lambda x: x+10, my_list)
# [1, 2, 3] 이라는 리스트 내부 요소 각각마다 x+10 이 수행됨
# new_list = [i+10 for i in my_list] 와 동일
print(list(new_list)) 
>>>
[11, 12, 13]
# map 은 항상 map 객체를 리턴하기 때문에, 리스트로 사용하려면 list() 로 한번 감싸주어야함

 

- filter 함수 : filter함수는 함수와 리스트(반복 가능 객체)를 인자로 받고, 리스트 요소 중에 해당 함수의 return값이 True인 것만을 반환해 주는 함수입니다.

# 기본적인 형태
filter(조건으로 쓰고자 하는 함수, 반복가능객체(리스트 등))

# [1,2,3,4,5]라는 리스트가 있을 때 2만 남기고 싶은 상황
my_list = [1, 2, 3]
new_list = filter(lambda x: x == 2, my_list) # x == 2 이라는 조건을 충족하는 요소만 남음
print(list(new_list)) 
>>>
[2]

 

- reduce 함수 : reduce 함수는 함수와 리스트(반복 가능 객체)를 인자로 받고 각 인자마다 함수를 거쳐서 리턴된 값을 쌓아나가는 함수입니다.

# 기본적인 형태
reduce(함수, 반복가능객체(리스트 등))

# 예제
from functools import reduce

my_list = [1, 2, 3, 4, 5]
result = reduce(lambda x, y: x + y, my_list)
print(result)
>>>
15
# 과정설명
# 1. reduce 함수가 실행되면, 우선 x에는 첫번째 리스트 요소인 1이, y에는 두번째 리스트 요소인 2가 들어갑니다. 그러면 3이 리턴됩니다.
# 2. 그 다음으로, y에는 세번째 리스트 요소인 3이, x에는 앞서 리턴된 값인 3이 들어갑니다. 그러면 6이 리턴됩니다.
# 3. 그 다음으로, y에는 네번째 리스트 요소인 4가, x에는 앞서 리턴된 값인 6이 들어갑니다. 그러면 10이 리턴됩니다.
# 4. 그 다음으로, y에는 다섯번째 리스트 요소인 5가, x에는 앞서 리턴된 값인 10이 들어갑니다. 그러면 15가 리턴됩니다.
# 5. 결과적으로 15가 리턴됩니다.

 

크롤링

- 크롤링의 정의 : 크롤링이란 웹 페이지로부터 데이터를 추출하는 행위를 말합니다.

- 크롤링을 하는 방법 : 우선 크롬 환경에 들어가서 개발자 도구를 활용해서 HTML 문서라는 것을 확인해 봅니다.

더보기
  • 맥 → Command + Option + I
  • 윈도우 → F12

- 개발자 도구를 켜고 아래의 이미지에 왼쪽에 있는 버튼을 누르면 특정 부분의 HTML 문서의 위치를 자세하게 확인할 수 있습니다.

- 특정 부분에 마우스를 가져가면 그 부분의 HTML문서를 확인할 수 있습니다.

 

- 파이썬에서 Request를 사용해서 웹페이지에 요청하는 방식이 있습니다.

- Request 사용 방법

# 패키지 설치 및 모듈 사용
pip install requests
import requests

# get 요청
requests.get(주소)

# 에시
response = requests.get('https://www.naver.com')
print(response.text) # .text 를 붙이면 데이터 부분 출력

# post 요청 보내기
requests.post(주소, 요청 데이터)

 

- HTML과 웹 스크래핑

- 선택자 : HTML 문서의 특정 부분에 이름을 붙인 것을 말하며 그 이름을 가지고 필요한 데이터를 찾을 수 있다.

# 아래 HTML 문서에 id와 class라는 선택자를 추가하면 훨씬 보기도 쉽고, 관리하기 쉬워집니다
<html> 
    <head> 
    </head> 
    <body> 
        <h1> 장바구니
            <p id='clothes' class='name' title='라운드티'> 라운드티
                <span class = 'number'> 25 </span> 
                <span class = 'price'> 29000 </span> 
                <span class = 'menu'> 의류</span> 
                <a href = 'http://www.naver.com'> 바로가기 </a> 
            </p> 
            <p id='watch' class='name' title='시계'> 시계
                <span class = 'number'> 28 </span>
                <span class = 'price'> 32000 </span> 
                <span class = 'menu'> 액세서리 </span> 
                <a href = 'http://www.facebook.com'> 바로가기 </a> 
            </p> 
        </h1> 
    </body> 
</html>

 

- BeautifulSoup 사용

# 패키지 설치 및 모듈 사용
pip install beautifulSoup4
# bs4라는 패키지로부터 BeautifulSoup라는 모듈을 임포트
from bs4 import BeautifulSoup

# HTML 문서를 문자열 html로 저장
html = '''
<html> 
    <head> 
    </head> 
    <body> 
        <h1> 장바구니
            <p id='clothes' class='name' title='라운드티'> 라운드티
                <span class = 'number'> 25 </span> 
                <span class = 'price'> 29000 </span> 
                <span class = 'menu'> 의류</span> 
                <a href = 'http://www.naver.com'> 바로가기 </a> 
            </p> 
            <p id='watch' class='name' title='시계'> 시계
                <span class = 'number'> 28 </span>
                <span class = 'price'> 32000 </span> 
                <span class = 'menu'> 액세서리 </span> 
                <a href = 'http://www.facebook.com'> 바로가기 </a> 
            </p> 
        </h1> 
    </body> 
</html>
'''

# BeautifulSoup 인스턴스 생성. 두번째 매개변수는 분석할 분석기(parser)의 종류.
soup = BeautifulSoup(html, 'html.parser')
# soup.select('태그명') : 태그를 입력으로 사용할 경우
# soup.select('.클래스명') : 클래스를 입력으로 사용할 경우 (`.` 은 클래스를 뜻하는 특수기호입니다.)
# soup.select('#아이디') : ID를 입력으로 사용할 경우 (`#` 은 id를 뜻하는 특수기호입니다.)
# soup.select('상위태그명 하위태그명') : 자손 관계 (어떤 태그 내부에 있는 모든 태그를 자손이라고 함)
# soup.select('상위태그명 > 하위태그명') : 자식 관계 (어떤 태그 내부에 있는 태그 중 바로 한 단계 아래에 있는 태그를 자식이라고 함)

# soup.태그명
#soup.태그명을 사용하시면 해당 태그를 포함하여 그 태그가 끝나는 부분까지의 문장을 가지고 옵니다.
# 단, 해당 태그가 여러개 있다면 첫번째 태그만 가져옵니다.
<a href="http://www.naver.com"> 바로가기 </a>
soup.a

# soup.태그명.get('속성명')
# get('속성명')을 사용하시면 해당 속성의 값을 가져옵니다.
# 만약 a 태그가 href = 'http://www.naver.com' 라는 속성을 가지는 경우,
# get('href')를 하면 'http://www.naver.com'의 값을 가져오게 됩니다.
<a href="http://www.naver.com"> 바로가기 </a>
soup.a.get('href')
>>>
http://www.naver.com

 

[마무리]

 오늘은 녹화 강의부터 실시간 강의까지 새로운 내용을 많이 배웠습니다. 그래서 아직까지는 머릿속에 다 들어오지 못한 느낌을 많이 받은 거 같습니다. 특히 크롤링 파트는 앞으로도 개발자가 되기 위해서 중요하게 사용될 부분인 만큼 실습 위주로 복습을 진행해야겠다고 느꼈습니다. 

 

반응형

댓글