82. 파이썬 표준 라이브러리 webbrowser 와 외부 라이브러리

82. 파이썬 표준 라이브러리 webbrowser 와 외부 라이브러리

지난 포스팅에서는 파이썬 표준 라이브러리 urllib 을 이용해 스마트뎁 smartdev.kr 의 홈페이지중 하나의 페이지를 오프라인에 저장해 보는 방법을 알아보았습니다.
이번 포스팅에서는 webbrowser 표준 라이브러리 와 외부 라이브러리에 대해 공부 해 보도록 하겠습니다.

파이썬 표준 라이브러리

webbrowser

webbrowser 모듈은 파이썬 프로그램에서 내 컴퓨터 시스템의 시스템 브라우저를 호출해서 사용하고자 할때 사용하는 모듈입니다.
프로그램 개발 중 이전 했던 포스팅을 참고하려고 할때 smart.dev.kr 사이트를 새로운 웹 브라우저로 열려고 한다면 어떻게 프로그래밍을 해야 할까요?

open_new()

파이썬으로 웹페이지를 새 창으로 열기 위해서는 webbrowser 모듈의 open_new() 함수를 사용하면 됩니다.

Python
# webbrowser_test.py
import webbrowser

webbrowser.open_new('https://smartdev.kr')

위 코드를 아래처럼 실행해 보면 순식간에 인터넷 창이 열리면서 smartdev.kr 로 접속하는 것을 보실 수가 있습니다.

Python
(py_study_31010) D:\Dropbox.My_Job.Study.Python
(py_study_31010) D:\Dropbox\02.My_Job\80.Study\01.Python\00.study_python_3.11.0\01.TEST>python webbrowser_test.py
.study_python_3.11.0.TEST>python webbrowser_test.py

open()

만약에 이미 열린 브라우저로 원하는 사이트를 열어보고 싶다면 open_new() 대신 open() 을 사용하시면 됩니다.

Python
webbrowser.open('https://smartdev.kr')

파이썬 외부 라이브러리

좀전까지 공부한 대로 파이썬 설치시에 기본으로 설치되는 라이브러리를 파이썬 표준 라이브러리 라고 합니다.
이번에는 외부 라이브러리에 대해 공부해 보도록 하겠습니다.
외부 라이브러리의 경우에는 pip 도구를 이용해서 설칠를 해야지 사용이 가능합니다.

pip

pip 은 파이썬 모듈이나 패키지를 쉽게 설치할 수 있도록 도와주는 도구입니다.
pip 으로 파이썬 프로그램을 설치하면 의존성이 있는 모듈이나 패키지를 함께 설치하기 때문에 매우 편리합니다.
예를 들어 B 라는 파이썬 패키지를 설치하려면 A 라는 패키지가 먼저 설치되어야 한다고 가정했을 때
pip 을 사용하면 B 패키지를 설치할 때 A 패키지도 같이 자동으로 알아서 설치해 줍니다.

pip install

PyPl(Python Package Index) 는 파이썬 소프트웨어가 모인 저장 공간입니다.
PyPl에는 십만건 이상의 파이썬 패키지가 등록이 되어 있습니다.
이 패키지들은 누구든지 그냥 내려받아 사용할 수 있게끔 되어 있습니다.
이 곳 PyPl 에서 직접 내려받아서 패키지를 설치 해도 되지만 pip 을 사용한다면 간단하게 설치를 진행 할 수가 있습니다.

Python
pip install AnyPackage

AnyPackage 는 내려받을 수 있는 특정 패키지를 의미합니다.

pip uninstall

pip install 과는 반대로 설치가 되어 있는 패키지를 삭제하고 싶다면 pip uninstall 명령을 실행 하면 됩니다.

Python
pip uninstall AnyPackage

버전 지정 설치

설치하려고 하는 패키지의 버전을 지정해서 설치를 할 수도 있습니다.
아래는 AnyPackage 의 1.04 버전을 설치를 해보는 명령어입니다.

Python
pip uninstall AnyPackage==1.04

위에서 진행 한대로 버전을 생략을 하고 진행을 한다면 최신 버전을 자동으로 설치하게 됩니다.

최신 버전 업그레이드 –upgrade

패키지를 최신 버전으로 업그레이드 하고 싶다면 –upgrade 옵션을 사용하면 됩니다.

Python
pip install --upgrade AnyPackage

설치된 패키지 확인 list

설치된 패키지 목록을 출력 해주는 옵션은 list 입니다.

Python
(py_study_31010) D:\Dropbox.My_Job.Study.Python
(py_study_31010) D:\Dropbox\02.My_Job\80.Study\01.Python\00.study_python_3.11.0\01.TEST>pip list
Package            Version
------------------ ---------
async-generator    1.10
attrs              22.2.0
beautifulsoup4     4.12.2
bs4                0.0.1
certifi            2022.12.7
cffi               1.15.1
charset-normalizer 3.1.0
colorama           0.4.6
et-xmlfile         1.1.0
exceptiongroup     1.1.1
h11                0.14.0
idna               3.4
.... 생략 ....
.study_python_3.11.0.TEST>pip list
Package Version ------------------ --------- async-generator 1.10 attrs 22.2.0 beautifulsoup4 4.12.2 bs4 0.0.1 certifi 2022.12.7 cffi 1.15.1 charset-normalizer 3.1.0 colorama 0.4.6 et-xmlfile 1.1.0 exceptiongroup 1.1.1 h11 0.14.0 idna 3.4 .... 생략 ....

  • 이 포스팅은 ‘위키독스’ 의 ‘점프 투 파이썬‘ 전자책을 구매하여 독학하기 위한 자료로 작성했습니다.
82. 파이썬 webbrowser 표준 라이브러리

81. 파이썬 urllib 표준 라이브러리

81. 파이썬 urllib 표준 라이브러리

지난 포스팅에서 json 파이썬 표준 라이브러리를 통해서 JSON 데이터를 처리하는 방법에 대해 알아 보았습니다.
이번 포스팅에서는 urllib 표준 라이브러리에 대해 공부해 보도록 하겠습니다.

파이썬 표준 라이브러리

urllib

urllib 라이브러리는 URL 을 읽고 분석 할 때 사용되는 모듈입니다.
개발 공부하시는 분들이 많이 알고 싶어하시는 웹 크롤링을 할 때 자주 사용되기도 합니다.
인터넷 브라우저로 스마트뎁(smartdev.kr) 의 특정한 페이지를 읽고 싶다면 아래와 같이 사용하면 됩니다.

https://smartdev.kr/페이지 번호 또는 구분기호 (예 : https://smartdev.kr/80-파이썬-json-표준-라이브러리/)

그럼 위의 웹페이지를 오프라인에서도 읽을 수 있도록 페이지 번호를 입력 받아서 smartdev.kr 의 특정 페이지를 smartdev_페이지 번호 또는 구분기호.html 로 저장하는 함수는 어떻게 만들면 좋을까요?
URL 을 호출을 해서 원하는 리소스를 얻으려면 urllib 모듈을 사용하면 됩니다.

Python
# urllib_test.py
import urllib.request

def get_smartdev(page):
    resource = 'https://smartdev.kr/{}'.format(page)
    with urllib.request.urlopen(resource) as s:
        with open('smartdev_%s.html' % page, 'wb') as f:
            f.write(s.read())
print(get_smartdev('80-파이썬-json-표준-라이브러리'))

위 코드에서 get_smartdev(page) 함수는 스마트뎁(smartdev.kr)의 페이지 번호를 입력 받아서 해당 페이지의 리소스 내용을 파일로 저장을 해주는 함수입니다.
urllib.request.urlopen(resource, context=context)로 s 객체를 생성을 하고 s.read() 함수로 리소스 내용의 전체를 읽어 들여 html 파일로 저장을 할 수 있게 해줍니다.

하지만 위 코드를 실행하게 되면 아래처럼 로그 같은 것들이 쭉 나오면서 마지막에 UnicodeEncodeError 오류가 나오게 됩니다.

Python
(py_study_31010) D:\Dropbox\02.My_Job\80.Study\01.Python\00.study_python_3.11.0\01.TEST>python urllib_test.py
Traceback (most recent call last):
  File "D:\Dropbox\02.My_Job\80.Study\01.Python\00.study_python_3.11.0\01.TEST\test.py", line 10, in <module>
    print(get_smartdev('80-파이썬-json-표준-라이브러리'))
  File "D:\Dropbox\02.My_Job\80.Study\01.Python\00.study_python_3.11.0\01.TEST\test.py", line 6, in get_smartdev
    with urllib.request.urlopen(resource) as s:
  File "C:\Users\donnr\anaconda3\envs\py_study_31010\lib\urllib\request.py", line 216, in urlopen
    return opener.open(url, data, timeout)
  File "C:\Users\donnr\anaconda3\envs\py_study_31010\lib\urllib\request.py", line 519, in open
    response = self._open(req, data)
  File "C:\Users\donnr\anaconda3\envs\py_study_31010\lib\urllib\request.py", line 536, in _open
    result = self._call_chain(self.handle_open, protocol, protocol +
  File "C:\Users\donnr\anaconda3\envs\py_study_31010\lib\urllib\request.py", line 496, in _call_chain
    result = func(*args)
  File "C:\Users\donnr\anaconda3\envs\py_study_31010\lib\urllib\request.py", line 1391, in https_open
    return self.do_open(http.client.HTTPSConnection, req,
  File "C:\Users\donnr\anaconda3\envs\py_study_31010\lib\urllib\request.py", line 1348, in do_open
    h.request(req.get_method(), req.selector, req.data, headers,
  File "C:\Users\donnr\anaconda3\envs\py_study_31010\lib\http\client.py", line 1282, in request
    self._send_request(method, url, body, headers, encode_chunked)
  File "C:\Users\donnr\anaconda3\envs\py_study_31010\lib\http\client.py", line 1293, in _send_request
    self.putrequest(method, url, **skips)
  File "C:\Users\donnr\anaconda3\envs\py_study_31010\lib\http\client.py", line 1131, in putrequest
    self._output(self._encode_request(request))
  File "C:\Users\donnr\anaconda3\envs\py_study_31010\lib\http\client.py", line 1211, in _encode_request
    return request.encode('ascii')
UnicodeEncodeError: 'ascii' codec can't encode characters in position 8-10: ordinal not in range(128)

위 오류는 문자 인코딩과 관련된 오류입니다. ‘ascii’ 코덱은 기본적으로 ASCII 문자만 처리할 수 있으므로, ASCII 범위를 벗어나는 문자를 인코딩하려고 할 때 발생합니다.
URL 에 한글 또는 특수 문자를 포함하는 경우에 발생합니다.
이를 해결하기 위해서는 URL 을 인코딩해야 합니다

Python
import urllib.parse
import urllib.request

def get_smartdev(page):
    encoded_page = urllib.parse.quote(page)
    resource = 'https://smartdev.kr/{}'.format(encoded_page)
    with urllib.request.urlopen(resource) as s:
        with open('smartdev_{}.html'.format(page), 'wb') as f:
            f.write(s.read())

get_smartdev('80-파이썬-json-표준-라이브러리')

위의 코드 에서 처럼 urllib.parse.quote 함수를 사용하여 페이지 이름을 URL 인코딩하여 encoded_page 변수에 저장합니다. 그런 다음 resource 변수에서 이 인코딩된 페이지 이름을 사용하여 URL을 구성합니다.
이렇게 수정하면 URL 인코딩에 문제를 해결하고 해당 오류가 발생하지 않게 됩니다.

아래 처럼 smartdev_80-파이썬-json-표준-라이브러리.html 파일이 잘 생성된 것을 확인 할 수 있습니다.

81. 파이썬 urllib 표준 라이브러리

  • 이 포스팅은 ‘위키독스’ 의 ‘점프 투 파이썬‘ 전자책을 구매하여 독학하기 위한 자료로 작성했습니다.
81. 파이썬 urllib 표준 라이브러리

80. 파이썬 json 표준 라이브러리

80. 파이썬 json 표준 라이브러리

지난 포스팅에서는 파이썬 표준 라이브러리 중 traceback 모듈을 통해 오류가 난 위치와 원인을 찾아보는 방법을 알아 보았습니다.
이번 포스팅에서는 json 표준 라이브러리에 대해 공부해 보도록 하겠습니다.

파이썬 표준 라이브러리

json

json 은 JSON 데이터를 쉽게 처리하게 해주는 모듈입니다.
아래 개인정보를 JSON 형태의 데이터로 만든 mysecret.json 파일이 있습니다.

[파일명 : mysecret.json]

Python
{
    "name": "Python",
    "birth": "0509",
    "age": 15
}

만일 인터넷으로 이 파일을 얻었다고 가정하면 이 파일을 읽어서 파이썬에서 처리할 수 있도록 딕셔너리 자료형으로 만들려면 어떻게 하면 좋을까요?

json.load()

JSON 파일을 읽어서 딕셔너리로 변환을 하기 위해서는 아래 코드처럼 json 모듈을 사용하면 됩니다.

Python
>>> import json
>>> with open('mysecret.json') as f:
...     data = json.load(f)
...
>>> type(data)
<class 'dict'>
>>> data
{'name': 'Python', 'birth': '0509', 'age': 15}
>>>

위 코드 처럼 JSON 파일을 읽어 들일 때에는 json.load(파일 객체)처럼 사용을 하면 됩니다.
이렇게 load() 함수는 읽은 데이터를 딕셔너리 자료형으로 리턴을 하게 됩니다.

json.dump()

반대로 딕셔너리 자료형을 JSON 파일로 생성을 하고자 할 때에는 아래처럼 json.dump(딕셔너리, 파일 객체) 를 사용을 하면 됩니다.

Python
>>> import json
>>> data = {'name': 'Python', 'birth': '0509', 'age': 15}
>>> with open('mysecret.json', 'w') as f:
...     json.dump(data, f)
...
>>>

json.dumps()

이번에는 파이썬 자료형을 JSON 문자열로 만들어 보겠습니다.

Python
>>> import json
>>> d = {"name": "Python", "birth":"0509", "age": 20}
>>> json_data = json.dumps(d)
>>> json_data
'{"name": "Python", "birth": "0509", "age": 20}'
>>>

json_data 가 이상 없이 잘 출력 되는 것을 볼 수 있습니다
그런데 만약 한글이 들어간다면 어떻게 될까요?
아래 코드를 보겠습니다.

Python
>>> import json
>>> d = {"name": "파이썬", "birth":"0509", "age": 20}
>>> json_data = json.dumps(d)
>>> json_data
'{"name": "\\ud30c\\uc774\\uc36c", "birth": "0509", "age": 20}'
>>>

위 json_data 결과에서 보듯이 한글 문자열이 코드의 형태로 표시가 됩니다.
이유는 dump() 나 dumps() 함수는 기본적으로 데이터를 저장할 때 아스키 형태로 저장을 하는데 한글처럼 유니코드 문자열을 아스키 형태로 저장을 하려다 보니 한글 문자열이 깨진 것처럼 보이는 것입니다.
그래도 json.loads()를 이용해서 JSON 문자열을 딕셔너리로 다시 역변환해서 사용하는 데에는 문제가 되지는 않습니다.
아래 코드처럼요

Python
>>> json.loads(json_data)
{'name': '파이썬', 'birth': '0509', 'age': 20}
>>>

ensure_ascii=False

그래도 만약 한글 문자열이 아스키 형태의 문자열로 변경이 되는게 싫다면 이것도 방법은 있습니다.

Python
>>> d = {"name": "파이썬", "birth":"0509", "age": 20}
>>> json_data = json.dumps(d, ensure_ascii=False)
>>> json_data
'{"name": "파이썬", "birth": "0509", "age": 20}'
>>>

위 코드처럼 ensure_ascii=False 옵션을 사용을 하면 됩니다.
이 옵션의 경우 데이터를 저장을 할 때에 아스크 형태로 변환을 하지 않겠다는 옵션입니다.

indent

‘{“name”: “파이썬”, “birth”: “0509”, “age”: 20}’ 이렇게 출력되는 JSON 문자열을 보기 좋게 정렬을 해주는 indent 옵션도 있습니다.

Python
>>> d = {"name": "파이썬", "birth":"0509", "age": 20}
>>> print(json.dumps(d,indent=2, ensure_ascii=False))
{
  "name": "파이썬",
  "birth": "0509",
  "age": 20
}
>>>

딕셔너리 자료형 외에 리스트나 튜플처럼 다른 자료형도 JSON 문자열로 변경할 수가 있습니다.

Python
>>> json.dumps([1 ,2, 3])
'[1, 2, 3]'
>>> json.dumps((4, 5, 6))
'[4, 5, 6]'
>>>

  • 이 포스팅은 ‘위키독스’ 의 ‘점프 투 파이썬‘ 전자책을 구매하여 독학하기 위한 자료로 작성했습니다.
80. 파이썬 json 표준 라이브러리

79. 파이썬 traceback 표준 라이브러리

79. 파이썬 traceback 표준 라이브러리

지난 포스팅에서 파이썬 표준 라이브러리 중 threading 에 대해서 알아보았습니다.
이번 포스팅에서는 파이썬 표준 라이브러리 중 traceback 에 대해서 공부해 보도록 하겠습니다

파이썬 표준 라이브러리

traceback

traceback 은 우리가 프로그래밍을 하고 실행을 했을때 발생하는 오류를 추적하려 할 때 사용되는 모듈 입니다.
간단하게 아래의 예제 코드를 살펴보겠습니다.

Python
# error_test.py
def a():
    return 1 / 0

def b():
    a()

def main():
    try:
        b()
    except:
        print("오류가 발생했습니다.")

main()

위 코드를 실행시켜 보겠습니다.

Python
(py_study_31010) D:\Dropbox\02.My_Job\80.Study\01.Python\00.study_python_3.11.0\01.TEST>python error_test.py
오류가 발생했습니다.

바로 print 문에 의해서 오류가 발생했다는 메시지를 출력 해 줍니다.
main() 함수가 시작 되면서 b() 함수를 호출하는데 b() 함수에서 a() 함수를 호출해서 1 을 0 으로 나누라는 명령에 오류가 발생해서 “오류가 발생했습니다.” 라고 메시지를 출력을 합니다.
실제로 이런 간단한 프로그램인 경우에는 그나마 괜찮을 수도 있지만 복잡한 프로그램이라면 어디에서 오류가 발생했는지 찾기가 힘들어집니다.

그래서 이런 때 이 프로그램에서 어떤 코드에서 오류가 발생했는지 위치와 원인을 정확히 판단할 수 있도록 traceback 모듈을 사용하여 프로그램을 업그레이드 하는 것입니다.
아래 코드 처럼 오류가 발생한 위치에 traceback 모듈을 적용을 해 보겠습니다.

Python
# traceback_test.py
import traceback

def a():
    return 1 / 0

def b():
    a()

def main():
    try:
        b()
    except:
        print("오류가 발생했습니다.")
        print(traceback.format_exc())

main()

이전과 동일한 코드에 print(traceback.format_exe()) 이라는 문장만 추가를 했습니다.
결과는 아래와 같이 나옵니다.

Python
(py_study_31010) D:\Dropbox\02.My_Job\80.Study\01.Python\00.study_python_3.11.0\01.TEST>python traceback_test.py
오류가 발생했습니다.
Traceback (most recent call last):
  File "D:\Dropbox\02.My_Job\80.Study\01.Python\00.study_python_3.11.0\01.TEST\test.py", line 12, in main
    b()
  File "D:\Dropbox\02.My_Job\80.Study\01.Python\00.study_python_3.11.0\01.TEST\test.py", line 8, in b
    a()
  File "D:\Dropbox\02.My_Job\80.Study\01.Python\00.study_python_3.11.0\01.TEST\test.py", line 5, in a
    return 1 / 0
ZeroDivisionError: division by zero

traceback 모듈을 통해 오류 추적을 진행하고 main() 함수에서 b() 함수를 호출하고 b() 함수에서 다시 a() 함수를 호출해서 1 / 0 을 실행하려 했기 때문에 0 으로 나눌 수 없다는 ZeroDivisionError 가 발생했다는 것을 로그를 통해 정확하게 확인을 할 수가 있습니다.

  • 이 포스팅은 ‘위키독스’ 의 ‘점프 투 파이썬‘ 전자책을 구매하여 독학하기 위한 자료로 작성했습니다.
79. 파이썬 traceback 표준 라이브러리

78. 파이썬 threading 표준 라이브러리

78. 파이썬 threading 표준 라이브러리

지난 포스팅에서는 파이썬 표준 라이브러리 중 zipfile 과 tempfile 에 대해 알아보았습니다.
이번 포스팅에서는 threading 표준 라이브러리에 대해 공부해 보도록 하겠습니다.

파이썬 표준 라이브러리

threading

스레드 프로그래밍은 초보 수준의 프로그래머에게는 구현하기가 매우 어려운 기술 중 하나 입니다.
일단 눈으로만 살펴 보고 넘어 가는게 좋겠네요

컴퓨터에서 동작하고 있는 프로그램들을 프로세스(process) 라고 합니다.
일반적으로 1 개의 프로세스는 한 가지의 일을 할 수 있지만 스레드(Thread)를 사용하게 되면 한 프로세스 안에서 2 가지 또는 그 이상의 작업도 한 번에 수행을 할 수가 있게 됩니다.

Python
#  time_test.py
import time

def long_job():    # 6초의 시간이 걸리는 함수
    for i in range(6):
        time.sleep(1)    # 1초간 대기
        print("Working:%s\n" % i)

print("시작합니다")

for i in range(6):    # long_job 을 6회 수행합니다.
    long_job()
    
print("끝났습니다.")

Python
(py_study_31010) D:\Dropbox\02.My_Job\80.Study\01.Python\00.study_python_3.11.0\01.TEST>python time_test.py
시작합니다
Working:0

Working:1

Working:2

Working:3

Working:4

Working:5

Working:0

Working:1

Working:2

Working:3

Working:4

Working:5

Working:0

Working:1

Working:2

Working:3

Working:4

Working:5

Working:0

Working:1

Working:2

Working:3

Working:4

Working:5

Working:0

Working:1

Working:2

Working:3

Working:4

Working:5

Working:0

Working:1

Working:2

Working:3

Working:4

Working:5

끝났습니다.

위 코드에서 long_job 함수는 실행을 하게 되면 6초의 시간이 걸리는 함수입니다.
이 함수를 총 6번 반복해서 실행하는 프로그램입니다.
6초씩 6번을 반복해서 36초라는 시간이 걸리게 됩니다.
그러나 스레드를 이용하게 되면 6초의 시간이 걸리는 long_job 함수를 동시에 실행할 수가 있습니다.
시간이 많이 단축이 되겠죠?
아래는 스레드를 이용한 코드입니다.

Python
#  thread_test.py
import time
import threading # 스레드를 생성하기 위해서는 threading 모듈이 필요합니다.

def long_job():    # 6초의 시간이 걸리는 함수
    for i in range(6):
        time.sleep(1)    # 1초간 대기
        print("Working:%s\n" % i)

print("시작합니다")

threads = []
for i in range(6):    # long_job 을 6회 수행합니다.
    t = threading.Thread(target=long_job) # 스레드를 생성을 합니다.
    threads.append(t)

for t in threads:
    t.start() # 스레드를 실행 합니다.
    
print("끝났습니다.")

Python
(py_study_31010) D:\Dropbox\02.My_Job\80.Study\01.Python\00.study_python_3.11.0\01.TEST>python thread_test.py
시작합니다
끝났습니다.
Working:0

Working:0
Working:0


Working:0

Working:0

Working:0

Working:1

Working:1

Working:1

Working:1

Working:1

Working:1

Working:2
Working:2
Working:2
Working:2


Working:2


Working:2


Working:3

Working:3

Working:3

Working:3

Working:3
Working:3


Working:4
Working:4

Working:4

Working:4

Working:4

Working:4


Working:5

Working:5

Working:5

Working:5

Working:5

Working:5

위 코드 두 개를 모두 실행해 보시면 아시겠지만 36초 정도의 시간이 걸리던 작업이 6초 정도에 빨리 진행이 되는 것을 확인 할 수가 있습니다.
이것은 threading.Thread 를 사용해서 만든 스레드 객체가 동시 작업을 가능하게 해주기 때문입니다.

그러나 ‘시작합니다.’ 와 ‘끝났습니다’가 먼저 출력이 된 후에 스레드의 결과가 출력이 되는 것을 확인 할 수 있습니다.
그리고 정상적인 종료도 아닙니다.
원래의 목적은 ‘시작합니다.’가 먼저 출력이 되고 중간에 스레드의 결과가 출력이 된 후 마지막에 ‘끝났습니다.’ 가 출력이 되는 것입니다.
그래서 아래와 같이 수정을 해보겠습니다.

Python
#  thread_test.py
import time
import threading # 스레드를 생성하기 위해서는 threading 모듈이 필요합니다.

def long_job():    # 6초의 시간이 걸리는 함수
    for i in range(6):
        time.sleep(1)    # 1초간 대기
        print("Working:%s\n" % i)

print("시작합니다")

threads = []
for i in range(6):    # long_job 을 6회 수행합니다.
    t = threading.Thread(target=long_job) # 스레드를 생성을 합니다.
    threads.append(t)

for t in threads:
    t.start() # 스레드를 실행 합니다.
    
for t in threads:
    t.join() # join 으로 스레드가 끝날 때 까지 기다립니다.
    
print("끝났습니다.")

아래 결과 처럼 스레드의 join 함수는 해당하는 스레드가 종료될 때까지 기다리게 해줍니다.
따라서 위 코드처럼 수행을 하게 되면 원하던 결과를 얻을 수가 있습니다.

Python
(py_study_31010) D:\Dropbox\02.My_Job\80.Study\01.Python\00.study_python_3.11.0\01.TEST>python test.py
시작합니다
Working:0
Working:0

Working:0
Working:0


Working:0
Working:0



Working:1
Working:1


Working:1
Working:1

Working:1


Working:1

Working:2
Working:2
Working:2

Working:2


Working:2


Working:2

Working:3
Working:3

Working:3

Working:3

Working:3
Working:3



Working:4
Working:4
Working:4

Working:4

Working:4



Working:4

Working:5
Working:5

Working:5
Working:5
Working:5


Working:5



끝났습니다.

  • 이 포스팅은 ‘위키독스’ 의 ‘점프 투 파이썬‘ 전자책을 구매하여 독학하기 위한 자료로 작성했습니다.
78. 파이썬 threading 표준 라이브러리

77. 파이썬 zipfile tempfile 표준 라이브러리

77. 파이썬 zipfile tempfile 표준 라이브러리

지난 시간에는 파이썬 표준 라이브러리 중 os 에 대해서 알아보았습니다.
이번 포스팅에서는 파이썬 표준 라이브러리 중 zipfile 과 tempfile 에 대해 공부 해 보도록 하겠습니다

파이썬 표준 라이브러리

zipfile

zipfile 은 여러 개의 파일 들을 zip 형식으로 합치거나 이를 해제할 때 사용되는 모듈입니다.
아래와 같이 3개의 파이썬 파일이 있다고 해봅시다

Python
a.py
b.py
c.py

이렇게 3개의 파이썬 파일을 하나로 합쳐서 mypython.zip 이라는 파일로 만들고
이 파일들을 원래의 파이썬 파일 3개로 해제 하는 프로그램을 만들어 보겠습니다.

zipfile.ZipFile()

아래는 zipfile.ZipFile() 함수를 사용한 코드입니다.

Python
#  zip_test.py
import zipfile

# 파일 하나로 합치기
with zipfile.ZipFile('mypython.zip', 'w') as myzip:
    myzip.write('a.py')
    myzip.write('b.py')
    myzip.write('c.py')

# 하나로 합친 파일 다시 해제하기
with zipfile.ZipFile('mypython.zip') as myzip:
    myzip.extractall()

위 코드처럼 ZipFile 객체의 write() 함수로 개별 파일들을 추가하고 extreactall() 함수를 사용하면 모든 파일을 해제 할 수가 있습니다.
합쳐져 있는 파일에서 특정 파일만 해제를 하고 싶다면 아래 코드와 같이 extract() 함수를 사용을 하면 됩니다.

Python
with zipfile.ZipFile('mypython.zip') as myzip:
    myzip.extract('a.py')

만약에 파일을 압축해서 묶고 싶은 경우에는 compression, compresslevel 옵션을 사용 할 수도 있습니다.

Python
with zipfile.ZipFile('mypython.zip', 'w', compression=zipfile.Zip_LZMA, compresslevel=9) as myzip:
    (....생략....)

compression 에는 4가지의 종류가 있습니다.

  • ZIP_STORED : 압축하지 않고 파일을 zip 으로만 묶는 작업을 합니다. 속도가 빠릅니다.
  • ZIP_DEFLATED : 일반적인 ZIP 압축으로서 속도가 빠르고 압축률은 낮습니다. 그리고 호환성이 좋습니다.
  • ZIP_BZIP2 : bzip2 압축으로 압축률이 높고 속도가 느립니다.
  • ZIP_LZMA : lzma 압축으로 압축률이 높고 속도가 느립니다.(7zip 프로그램과 동일한 알고리즘으로 알려져 있습니다.)

compressionlevel 은 압축 수준을 의미하는 숫자입니다.
압축 수준은 1 에서 9 까지 사용을 하며
1 이 속도가 가장 빠르고 압축률이 낮으며
9 가 속도는 가장 느리고 압축률은 높습니다.

tempfile

파일을 임시로 만들어서 사용하는 경우에 tempfile 을 유용하게 사용할 수 있습니다.

tempfile.mkstemp()

tempfile.mkstemp() 는 중복되지 않는 임시 파일의 이름을 랜덤 하게 만들어서 리턴을 해줍니다.

Python
>>> import tempfile
>>> filename = tempfile.mkstemp()
>>> filename
(3, 'C:\\Users\\donnr\\AppData\\Local\\Temp\\tmpmbfgnpr5')
>>>

tempfile.TemporaryFile()

tempfile.TemporaryFile() 은 임시 저장 공간으로 사용할 파일 객체를 리턴을 해 줍니다.
이 파일은 기본적으로 바이너리 쓰기 모드(wb) 의 성격을 가지고 있습니다.
f.close() 가 호출이 되면 자동으로 삭제가 됩니다.

Python
>>> import tempfile
>>> f = tempfile.TemporaryFile()
>>> f.close()

  • 이 포스팅은 ‘위키독스’ 의 ‘점프 투 파이썬‘ 전자책을 구매하여 독학하기 위한 자료로 작성했습니다.
77. 파이썬 zipfile tempfile 표준 라이브러리

75. 파이썬 shutil, glob, pickle 표준 라이브러리

75. 파이썬 shutil, glob, pickle 표준 라이브러리

지난 시간에 파이썬 표준 라이브러리 중 operator.itemgetter 와 operator.attrgetter 에 대해 알아보았습니다.
이번 포스팅에서는 shutil, glob, pickle 라이브러리에 대해 공부해 보도록 하겠습니다.

파이썬 표준 라이브러리

shutil

shutil.copy 파일 백업

shutil 라이브러리는 파일을 복사하거나 이동 할 때 사용하는 모듈입니다.
우리가 작업 중인 파일이 있는데 이 파일을 자동으로 백업하는 기능을 파이썬 프로그램으로 코딩한다고 했을 때 d:\test\aaa.txt 파일을 d:\temp\aaa.txt.bak 라고 복사한다고 해봤을 때 shutil 모듈을 사용해 해결 할 수 있습니다.
단, d:\temp 디렉터리는 이미 만들어져 있고, d:\test\aaa.txt 파일은 만드는 중이라는 조건입니다.
아래 코드는 shutil 모듈을 사용해 aaa.txt 파일을 백업하는 코드 입니다.

Python
#  shutil_copy.py
import shutil

shutil.copy("d:/test/aaa.txt", "d:/temp/aaa.txt.bak")

shutil.move 휴지통으로 삭제

만약에 d:\test\aaa.txt 파일을 복사 방식으로 백업 하는게 아닌 d:\temp\aaa.txt 로 이동을 하게 되면 아래와 같이 shutil.move 코드를 사용하면 됩니다.

Python
#  shutil_move.py
import shutil

shutil.move("d:/test/aaa.txt", "d:/temp/aaa.txt")

glob

프로그래밍을 하다가 보면 파일을 읽고 쓰는 기능이 있는 프로그램을 만들 때도 있습니다.
그 중에서 특정한 디렉터리에 있는 파일들의 이름을 전부 확인하고자 할 때 사용하는 모듈이 바로 glob 입니다.

glob(pathname) 디렉터리에 있는 파일들 리스트 만들기

일단 기본적으로 glob 모듈은 디렉터리 안에 있는 파일들을 읽어서 리턴을 해줍니다.
* 이나 ? 등의 메타 문자를 써서 원하는 파일들만 읽어서 리턴을 할 수도 있습니다.
아래 코드는 d:/test 디렉터리에 있는 파일들 중에서 이름이 mark 로 시작하는 파일들을 모두 찾아서 읽어들이는 코드 입니다.

Python
>>> import glob
>>> glob.glob("d:/test/mark*")
['d:/test\\mark1.py', 'd:/test\\mark2.py', 'd:/test\\mark3.py']
>>>

pickle

pickle 모듈은 객체의 형태를 원형대로 유지하면서 파일에 저장하고 저장한 파일을 불러올 수 있도록 해 주는 모듈입니다.

pickle.dump

아래 코드는 pickle 모듈의 dump 함수를 사용해서 딕셔너리 객체인 data 를 원형 그대로 파일에 저장하게 해주는 코드입니다.

Python
>>> import pickle
>>> f = open("test.txt", 'wb')
>>> data = {1: 'python', 2: 'we need'}
>>> pickle.dump(data, f)
>>> f.close()
>>>

pickle.load

아래 코드는 위에서 pickle.dump 로 저장했던 파일을 pickle.load 함수를 사용해서 원래 있던 딕셔너리 객체(data) 상태 그대로 불러와 보는 코드입니다.

Python
>>> import pickle
>>> f = open("test.txt", 'rb')
>>> data = pickle.load(f)
>>> print(data)
{1: 'python', 2: 'we need'}
>>>

결과 처럼 원형 그대로 가지고 온 걸 확인 할 수 있습니다.
방금 예제는 딕셔너리 객체만을 사용했지만 다른 어떤 자료형이든 저장하고 다시 불러올 수가 있습니다.

  • 이 포스팅은 ‘위키독스’ 의 ‘점프 투 파이썬‘ 전자책을 구매하여 독학하기 위한 자료로 작성했습니다.
75. 파이썬 shutil, glob, pickle 표준 라이브러리

74. 파이썬 표준 라이브러리 operator.itemgetter operator.attrgetter

74. 파이썬 표준 라이브러리 operator.itemgetter operator.attrgetter

지난 포스팅에서는 functools.reduce() 를 사용해서 리스트 요소의 누적값과 최대값, 최소값을 구하는 코드를 알아봤습니다.
이번 포스팅에서는 operator.itemgetter 에 대해 공부해 보도록 하겠습니다.

파이썬 표준 라이브러리

operator.itemgetter

operator.itemgetter 라이브러리는 보통 sorted 같은 함수의 key 매개변수에 적용하여 여러가지 기준으로 정렬을 할 수 있게 도와 주는 모듈입니다.

튜플 정렬

아래 처럼 학생들의 이름과 나이, 성적 같은 정보를 저장한 students 리스트가 있다고 가정해 보겠습니다.

Python
#  students_list.py
students = [
    ("mickle", 25, 'A'),
    ("david", 22, 'C'),
    ("jone", 31, 'C'),
]

students 리스트에는 3개의 튜플이 있고 각 튜플은 이름, 나이, 성적에 해당하는 알파벳 의 순서대로 데이터가 만들어져 있습니다.
이 리스트를 나이 순으로 어떻게 정렬하면 좋을까요?
이 질문은 아래 코드 처럼 sorted() 함수의 key 매개변수에 itemgetter() 를 적용해서 해결을 할 수 가 있습니다.

Python
#  students_tuple.py
from operator import itemgetter

students = [
    ("mickle", 25, 'A'),
    ("david", 22, 'C'),
    ("jone", 31, 'C'),
]

result = sorted(students, key=itemgetter(1))
print(result)

위 코드를 실행을 해보면 아래 처럼 나이 순서대로 정렬이 되어 출력 된 것을 볼 수 있습니다.

Python
(py_study_31010) D:\Dropbox\02.My_Job\80.Study\01.Python\00.study_python_3.11.0\01.TEST>python students_tuple.py
[('david', 22, 'C'), ('mickle', 25, 'A'), ('jone', 31, 'C')]

위 코드에서 itemgetter(1) 은 students 의 아이템인 튜플의 2 번째 요소를 기준으로 정렬을 하겠다는 의미입니다.
만약에 itemgetter(2) 로 사용을 한다면 성적 순 으로 정렬을 하게 됩니다.
1 번째 요소는 이름이고, 2 번째 요소는 나이, 3 번째 요소는 성적을 의미하는 알파벳인 것입니다.

딕셔너리 정렬

그럼 이번에는 위의 students 요소가 리스트 가 아닌 딕셔너리 형태로 있다고 해보겠습니다.

Python
#  students_dict.py
from operator import itemgetter

students = [
    {"name": "mickle", "age": 25, "grade": 'A'},
    {"name": "david", "age": 22, "grade": 'C'},
    {"name": "jone", "age": 31, "grade": 'C'}
]

result = sorted(students, key=itemgetter('age'))
print(result)

위 코드처럼 딕셔너리 형태로 students 의 요소들이 있고 age 를 기준으로 정렬을 시켜 봤습니다.
결과는 아래와 같습니다.

Python
(py_study_31010) D:\Dropbox\02.My_Job\80.Study\01.Python\00.study_python_3.11.0\01.TEST>python students_dict.py
[{'name': 'david', 'age': 22, 'grade': 'C'}, {'name': 'mickle', 'age': 25, 'grade': 'A'}, {'name': 'jone', 'age': 31, 'grade': 'C'}]

위 처럼 나이 순서 대로 잘 정렬이 된 것을 확인 할 수 있습니다.

operator.attrgetter

operator.attrgetter 은 students 리스트 의 요소가 튜플이 아니라 Student 클래스의 객체라고 할 때 사용할 수 있습니다.

Python
#  students_class.py
from operator import attrgetter

class Student:
    def __init__(self, name, age, grade):
        self.name = name
        self.age = age
        self.grade = grade

students = [
    Student('mickle', 25, 'A'),
    Student('david', 22, 'C'),
    Student('jone', 31, 'C'),
]

result = sorted(students, key=attrgetter('age'))
print(result)

위의 코드는 attrgetter(‘age’) 가 Student 클래스의 객체인 age 속성으로 정렬을 하겠다는 의미입니다.
이전에 진행했던 것 처럼 attrgetter(‘grade’) 로 사용을 한다면 성적 순으로 정렬을 한다는 의미입니다.
위 코드를 실행시키면 아래와 같이 나옵니다

Python
(py_study_31010) D:\Dropbox\02.My_Job\80.Study\01.Python\00.study_python_3.11.0\01.TEST>python students_class.py
[<__main__.Student object at 0x000001EF1913B1C0>, <__main__.Student object at 0x000001EF1913B100>, <__main__.Student object at 0x000001EF1913B280>]
>>>

위 처럼 나오는 것은 리스트에 객체의 문자열 표현(representation)이 출력된 것입니다.

각각의 요소는 main.Student object at 0x0000015E409CCC70과 같은 형태로 표시되는데, 이는 해당 객체가 main.Student 클래스의 인스턴스임을 나타냅니다. 0x0000015E409CCC70과 같은 값은 객체가 메모리에서 할당된 주소를 나타냅니다.

이 결과는 클래스의 repr 메서드가 구현되지 않았거나 기본적인 형태로 구현되었기 때문에 발생하는 것입니다. repr 메서드를 클래스에 추가하면 객체의 문자열 표현을 원하는 형태로 커스터마이즈할 수 있습니다.

  • 이 포스팅은 ‘위키독스’ 의 ‘점프 투 파이썬‘ 전자책을 구매하여 독학하기 위한 자료로 작성했습니다.
74. 파이썬 표준 라이브러리 operator.itemgetter operator.attrgetter

73. 파이썬 functools.reduce 표준 라이브러리

73. 파이썬 functools.reduce 표준 라이브러리

지난 시간에는 파이썬 표준 라이브러리 itertools.permutations 와 itertools.combinations 에 대해 알아보았습니다.
이번 포스팅에서는 파이썬 표준 라이브러리 중 functools.reduce 에 대해 공부해 보도록 하겠습니다.

파이썬 표준 라이브러리

functools.reduce

functools.reduce 함수는 functools.reduce(function, iterable) 의 형태로 사용하며 function 을 반복 가능한 객체인 iterable 의 요소에 차례 차례 좌에서 우로 누적해서 적용하며 이 객체를 하나의 값으로 줄여주는 함수 입니다.

아래 코드는 입력 인수인 num 의 요소를 모두 더해서 리턴해 주는 add 함수 입니다.

Python
#  add_num.py
def add(num):
    result = 0
    for i in num:
        result += i
    return result

num = [1, 2, 3, 4, 5]
result = add(num)
print(result)

위의 코드를 실행해 보면 아래와 같은 결과가 나옵니다.

Python
(py_study_31010) D:\Dropbox\02.My_Job\80.Study\01.Python\00.study_python_3.11.0\01.TEST>python add_num.py
15

누적 계산하기

위 코드를 functools.reduce() 함수를 이용해서 동일하게 동작하는 코드를 만들어 보겠습니다.
functools.reduce() 함수를 사용한 코드는 아래의 코드입니다.

Python
#  add_num_functools.py
import functools

num = [1, 2, 3, 4, 5]
result = functools.reduce(lambda x, y: x + y, num)
print(result)

위 코드를 실행해 보면 이전에 작성했던 코드와 마찬가지로 동일하게 15 가 출력 되는 것을 보실 수 있습니다.
이렇게 functools.reduce() 를 사용하게 되면 reduce() 에 선언했던 lambda 함수를 num 요소에 하나씩 차례대로 누적해서 적용해서 ((((1 + 2) + 3) + 4) + 5) 의 형태로 계산을 하게 됩니다.

Python
(py_study_31010) D:\Dropbox\02.My_Job\80.Study\01.Python\00.study_python_3.11.0\01.TEST>python add_num_functools.py
15

결국 앞에서 만들었던 add_num.py 와 동일한 역할을 하게 됩니다.

최대값 구하기

이번에는 functools.reduce() 함수를 이용해 최대값을 구해보도록 하겠습니다.

Python
#  max_num_functools.py
import functools

num = [3, 2, 1, 8, 6, 7]
max_num = functools.reduce(lambda x, y: x if x > y else y, num)
print(max_num)

위 코드에서 처럼 [3, 2, 1, 8, 6, 7] 의 리스트 요소를 차례대로 reduce() 의 lamba 함수로 전달해서 두 값 중 큰 값을 선택하고 이렇게 반복하면서 마지막에는 남은 최대값을 리턴을 하게 됩니다.

Python
(py_study_31010) D:\Dropbox\02.My_Job\80.Study\01.Python\00.study_python_3.11.0\01.TEST>python max_num_functools.py
8

최소값 구하기

Python
#  min_num_functools.py
import functools

num = [3, 2, 1, 8, 6, 7]
max_num = functools.reduce(lambda x, y: x if x < y else y, num)
print(max_num)

위 코드 처럼 부등호의 방향만 바뀌면 리스트 요소들의 최소값을 구할 수도 있습니다.

  • 이 포스팅은 ‘위키독스’ 의 ‘점프 투 파이썬‘ 전자책을 구매하여 독학하기 위한 자료로 작성했습니다.

73. 파이썬 functools.reduce 표준 라이브러리

72. 파이썬 표준 라이브러리 itertools.permutations itertools.combinations

72. 파이썬 표준 라이브러리 itertools.permutations itertools.combinations

지난 포스팅에서는 itertools.zip_longest 이라는 파이썬 표준 라이브러리에 대해 공부해 보았습니다.
이번 포스팅에서는 itertools.permutation 표준 라이브러리에 대해 공부해 보도록 하겠습니다.

파이썬 표준 라이브러리

itertools.permutations

itertools.permutations 함수는 itertools.permutations(iterable, r) 의 형태로 작성되어 사용되는데 반복 가능한 iterable 객체 중에서 r 개를 선택한 순열을 반복 가능한 객체(이터레이터)로 리턴을 해주는 함수 입니다.

1, 2, 3 의 숫자가 적힌 3 장의 카드가 있다고 했을 때 두 개의 카드를 꺼내서 만들 수 있는 2 자리 숫자를 모두 구하려면 어떻게 하는게 좋을까요?
[1, 2, 3] 3장의 카드 중에서 순서에 상관 없이 2 장을 뽑는 경우의 수는 모두 3 가지가 됩니다(조합)

  • 1, 2
  • 2, 3
  • 1, 3

그러나 위에 제시한 문제에서는 2 자리 숫자이므로 위의 3 가지에 순서를 더해 6 가지를 만들 수가 있습니다(순열)

  • 1, 2
  • 2, 1
  • 2, 3
  • 3, 2
  • 1, 3
  • 3, 1

이렇게 만들어지는 순열은 itertools.permutations 함수를 사용해서 간단하게 구할 수가 있습니다.

Python
>>> import itertools
>>> list(itertools.permutations(['1', '2', '3'], 2))
[('1', '2'), ('1', '3'), ('2', '1'), ('2', '3'), ('3', '1'), ('3', '2')]
>>>

위 코드처럼 6 가지의 경우를 만들 수 있고 여기에 아래 코드처럼 추가해서 2 자리 숫자를 만들어 볼 수 있습니다.

Python
>>> for a, b in itertools.permutations(['1', '2', '3'], 2):
...     print(a + b)
...
12
13
21
23
31
32
>>>

itertools.combinations

앞서 진행했던 예제에서 3 장의 카드에서 순서에 상관없이 2 장을 고르는 조합은 아래와 같이 itertools.combinations() 함수를 사용을 하면 됩니다.

Python
>>> import itertools
>>> list(itertools.combinations(['1', '2', '3'], 2))
[('1', '2'), ('1', '3'), ('2', '3')]
>>>

위 코드에서 보는 것 처럼 itertools.combinations 함수는 itertools.combinations(iterable, r) 의 형태로 작성이 되며 반복 가능한 객체 iterable 중에서 r 개를 선택한 조합을 반복 가능한 객체(이터레이터) 로 리턴을 해 주는 함수 입니다.

그럼 1 에서 45 까지의 숫자 중 서로 다른 숫자 6 개를 뽑는 로또 번호 처럼 모든 경우의 수(조합)을 구하고 그 개수를 출력하려면 어떻게 하면 될까요?
아래와 같이 itertools.combinations(range(1, 46), 6) 이라고 하게 되면 로또 번호를 뽑는 것처럼 1에서 45까지의 숫자 중 서로 다른 숫자 6개를 뽑는 경우의 수를 이터레이터로 리턴을 하게 됩니다.
직접 실행해 보게 되면 언제 끝날지 모르는 결과가 쭉 나올 것입니다.

Python
>>> import itertools
>>> lotto = itertools.combinations(range(1, 46), 6)
>>> for number in lotto:
...     print(number)
...
(1, 2, 3, 4, 5, 6)
(1, 2, 3, 4, 5, 7)
(1, 2, 3, 4, 5, 8)
(1, 2, 3, 4, 5, 9)
(1, 2, 3, 4, 5, 10)
...
...
...
...
...

만약 이렇게 순환하여 출력을 하지 않고 개수가 몇 개 인지만 확인하려면 아래와 같이 할 수도 있습니다.

Python
>>> len(list(itertools.combinations(range(1, 46), 6)))
8145060
>>>

위 결과처럼 로또 번호의 경우의 수는 8,145,060 개 입니다.

itertools.combinations_with_replacement

만약의 그냥 로또번호 와는 틀리게 중복이 허용이 된다면 경우의 수가 몇 개가 될까요?
[1, 2, 3, 4, 5, 5] 나 [1, 2, 3, 3, 4, 4] 처럼 같은 숫자가 여러번 나와도 상관이 없다는 의미인데 이럴 경우에는 itertools.combinations_with_replacement() 를 사용하면 됩니다.

Python
>>> len(list(itertools.combinations_with_replacement(range(1, 46), 6)))
15890700
>>>

중복이 허용이 될 경우 에는 경우의 수가 15,890,700 으로 많이 늘어나는 것을 확인 할 수 있습니다.

  • 이 포스팅은 ‘위키독스’ 의 ‘점프 투 파이썬‘ 전자책을 구매하여 독학하기 위한 자료로 작성했습니다.
72. 파이썬 표준 라이브러리 itertools.permutation