코딩, 개발에 대한 기록 저장소
레이블이 Python인 게시물을 표시합니다. 모든 게시물 표시
레이블이 Python인 게시물을 표시합니다. 모든 게시물 표시

python apscheduler 의 job 관리(Job Store)

Job Store

apscheduler에서 Job Store는 스케줄러가 관리하는 작업(job)에 대한 모든 정보를 저장하고 유지하는 역할을 합니다. 여기에는 다음과 같은 정보가 포함됩니다.

  • 실행할 함수 또는 콜러블 객체: 어떤 작업을 실행해야 하는지에 대한 정보
  • 트리거 (Trigger): 작업이 언제, 얼마나 자주 실행되어야 하는지에 대한 스케줄링 규칙
  • 작업의 상태: 작업이 일시 중지되었는지, 다음 실행 시간은 언제인지 등의 상태 정보
  • 작업의 설정: 작업 ID, 실행 시 동시 실행 허용 여부 등 추가적인 설정 정보

기본적으로 apscheduler는 MemoryJobStore라는 메모리 기반의 Job Store를 사용합니다. 이 저장소는 애플리케이션이 실행되는 동안에만 작업 정보를 유지합니다.

  • 장점: 설정이 간단하고 빠릅니다. 별도의 데이터베이스 설치나 설정이 필요하지 않습니다.
  • 단점: 애플리케이션이 종료되면 모든 작업 정보가 사라집니다. 따라서 재시작 후에는 이전에 예약된 작업들이 유지되지 않습니다.

영구 저장소의 필요성

애플리케이션이 재시작되거나 여러 인스턴스(예: 웹 애플리케이션의 여러 서버)에서 스케줄러를 공유해야 하는 경우에는 메모리 기반의 저장소만으로는 충분하지 않습니다. 이때 영구 저장소를 사용하여 작업 정보를 데이터베이스와 같은 외부 저장소에 저장해야 합니다. 영구 저장소를 사용하면 다음과 같은 이점을 얻을 수 있습니다.

  • 애플리케이션 재시작 후에도 작업 유지: 애플리케이션이 종료되었다가 다시 시작되더라도 이전에 예약된 작업들이 그대로 유지됩니다.
  • 스케줄러 상태 공유: 여러 프로세스 또는 서버에서 동일한 데이터베이스를 공유하여 스케줄러 상태를 동기화하고, 작업을 중복 실행하는 것을 방지할 수 있습니다. (이를 위해서는 잠금(locking) 설정이 필요할 수 있습니다.)

주요 영구 저장소

apscheduler는 다양한 데이터베이스 백엔드를 지원하는 여러 영구 저장소를 제공합니다.

SQLAlchemyJobStore

SQLAlchemy ORM을 사용하여 다양한 SQL 데이터베이스 (SQLite, PostgreSQL, MySQL, Oracle 등)에 작업 정보를 저장합니다. 가장 널리 사용되고 강력한 옵션 중 하나입니다.

from apscheduler.jobstores.sqlalchemy import SQLAlchemyJobStore

jobstores = {
    'default': SQLAlchemyJobStore(url='sqlite:///jobs.sqlite') # SQLite 데이터베이스 사용 예시
}

scheduler = BackgroundScheduler(jobstores=jobstores)

MongoDBJobStore

MongoDB NoSQL 데이터베이스에 작업 정보를 저장합니다.

from apscheduler.jobstores.mongodb import MongoDBJobStore
from pymongo import MongoClient

client = MongoClient('mongodb://localhost:27017/')
jobstores = {
    'default': MongoDBJobStore(database='apscheduler', client=client)
}

scheduler = BackgroundScheduler(jobstores=jobstores)

RedisJobStore

Redis 인-메모리 데이터 구조 저장소에 작업 정보를 저장합니다. 고성능 스케줄링에 적합할 수 있습니다.

from apscheduler.jobstores.redis import RedisJobStore
import redis

redis_client = redis.Redis(host='localhost', port=6379, db=0)
jobstores = {
    'default': RedisJobStore(client=redis_client)
}

scheduler = BackgroundScheduler(jobstores=jobstores)

저장소를 선택 고려사항

  • 간단한 테스트 또는 애플리케이션 수명이 짧은 경우: MemoryJobStore로 충분할 수 있음
  • 애플리케이션 재시작 후에도 작업을 유지해야 하는 경우: SQLAlchemyJobStore, MongoDBJobStore, RedisJobStore 중 하나를 선택
  • SQL 데이터베이스를 이미 사용하고 있거나 관계형 데이터 모델이 적합한 경우: SQLAlchemyJobStore
  • NoSQL 데이터베이스를 선호하거나 확장성이 중요한 경우: MongoDBJobStore
  • 높은 성능과 빠른 읽기/쓰기가 필요한 경우: RedisJobStore. 다만, Redis는 기본적으로 휘발성 데이터베이스이므로 영구 저장을 위한 별도의 설정을 고려해야 함


맥에서 업로드한 한글 파일을 linux 파일에서 못 읽는 이유

다음의 "한"과 "한"은 시각적으로는 동일해 보이지만, 내부적으로는 다른 Unicode 인코딩을 사용하여 표현됨.

"한" (U+D55C):

이는 단일 유니코드 문자로, "한"의 조합을 하나의 문자로 결합한 것
유니코드 값: U+D55C
UTF-8 인코딩: E D5 9C

"한" (U+1112 U+1161 U+11AB):

이는 세 개의 개별 유니코드 문자로 구성된 조합형 문자
ᄒ (초성): U+1112
ᅡ (중성): U+1161
ᆫ (종성): U+11AB
UTF-8 인코딩: 각각 E1 84 92, E1 85 A1, E1 86 AB로 인코딩됨

문제 해결

이 차이는 Unicode 정규화(Normalization)를 통해 해결할 수 있음.

  • NFC (Normalization Form C): 조합형 문자들을 가능한 한 결합하여 하나의 문자로 만듦
  • NFD (Normalization Form D): 조합형 문자들을 가능한 한 분해하여 각각의 구성 요소로 나눔

아래 예제 코드 참고

import unicodedata

# 조합형 문자
composed = '한'
# 분해된 문자
decomposed = '한'

# NFC 정규화
composed_nfc = unicodedata.normalize('NFC', decomposed)
# NFD 정규화
decomposed_nfd = unicodedata.normalize('NFD', composed)

print(f"Composed: {composed}, NFC: {composed_nfc}")
print(f"Decomposed: {decomposed}, NFD: {decomposed_nfd}")

# 출력 결과
# Composed: 한, NFC: 한
# Decomposed: 한, NFD: 한


파이썬(python) URL 쿼리스트링 분리하기

Python에서 URL의 쿼리스트링을 분리하기 위해서는 urllib.parse 모듈을 사용함

URL에서 특정 쿼리스트링을 뽑아내는 법

from urllib.parse import urlparse, parse_qs

url = "https://example.com/path?name=John&age=30"

# urlparse 함수를 사용하여 URL의 구성 요소를 추출
parsed_url = urlparse(url)

# parse_qs 함수를 사용하여 쿼리스트링을 분리
query_params = parse_qs(parsed_url.query)

print(query_params)

결과

{'name': ['John'], 'age': ['30']}

URL에서 특정 쿼리스트링을 삭제하는 방법

URL의 쿼리스트링 중에서 특정 파라미터를 삭제하려면 파싱한 후에 딕셔너리 형태로 저장된 쿼리스트링을 삭제하고 다시 URL로 조립해야 함

from urllib.parse import urlparse, urlencode

url = "https://example.com/path?name=John&age=30"

# urlparse 함수를 사용하여 URL의 구성 요소를 추출
parsed_url = urlparse(url)

# 딕셔너리 형태로 저장된 쿼리스트링을 가져옵니다.
query_params = dict(parse_qsl(parsed_url.query))

# 삭제할 파라미터 이름을 지정
param_to_delete = 'age'

# 딕셔너리에서 삭제할 파라미터를 제거
query_params.pop(param_to_delete, None)

# 수정된 쿼리스트링을 URL로 조립
updated_url = parsed_url._replace(query=urlencode(query_params)).geturl()

print(updated_url)

결과

https://example.com/path?name=John


파이썬(Python) 데이터프레임에서 생략없이 모든 행, 모든 열 보기

파이썬(Python) 데이터프레임에서 생략없이 모든 행, 모든 열 보기

크기가 큰 데이터프레임(DataFrame)을 출력하면 ... 으로 생략되어 나오기 때문에 가끔 데이터 확인이 불가능할 때가 있음
이럴때 변경 가능한 pandas 옵션 정리

행 관련 설정

# 모든 행 출력
pd.set_option('display.max_rows', None)

# 행 100개 출력
pd.set_option('display.max_rows', 100)

열 관련 설정

# 모든 열 출력
pd.set_option('display.max_columns', None)

# 열 100개 출력
pd.set_option('display.max_columns', 100)

# 표시할 가로의 길이
pd.set_option('display.width', 1000)

옵션 확인

# pandas 모든 옵션 보기
pd.describe_option()    


Anaconda 사용하여 32bit python 설치하는 방법

Anaconda 사용하여 32bit python 설치하는 방법

32bit Anaconda를 설치

  1. Anaconda 3.7 32bit 버전 설치
  2. Anaconda Navigator 실행하여, Environment에서 Conda environment 'project-dev32' 추가
  3. project-dev32 환경 추가되면 마우스 좌클릭으로 터미널 열기

64bit Anaconda가 설치된 경우

  1. Anaconda prompt 를 관리자 모드로 실행 후 아래 명령어를 통해 32bit python 가상 환경 생성
    $ set CONDA_FORCE_32BIT=1
    $ conda create -n project-dev32 python=3.7.3 anaconda
    
  2. anaconda prompt 에서 아래 명령어를 통해 32bit python 환경으로 변경하고 버전 확인
    $ conda activate project-dev32
    $ python을 실행시켜 버전을 확인함
    


파이썬(Python) list 모든 요소 삭제

파이썬(Python)에서 리스트의 모든 요소를 제거하는 방법

  • clear()로 모든 요소 제거
  • del을 이용하여 모든 요소 제거

clear()로 모든 요소 제거

list = ['aaa', 'bbb', 'ccc', 'ddd']
list.clear()

print(list)
print(len(list))

결과

[]
0

del을 이용하여 모든 요소 제거

list = ['aaa', 'bbb', 'ccc', 'ddd']

del list[:]
# 주의: 아래와 같이 하면 객체를 메모리에서 제거하므로 에러 발생
# del list 

print(list)
print(len(list))

결과

[]
0


파이썬(Python)리스트 반복문

파이썬(Python) 리스트 반복문

for 이용

list = ["a", "b", "c", "d"]

for item in list:
    print(item)

결과

a
b
c
d

enumerate 이용(index 출력)

list = ["a", "b", "c", "d"]

for item in enumerate(list):
    print(f"index={item[0]}, item={item[1]}")

또는

list = ["a", "b", "c", "d"]

for index, item in enumerate(list):
    print(f"index={index}, item={item}")

결과

index=0, item=a
index=1, item=b
index=2, item=c
index=3, item=d


파이썬(Python) 문자열 자르기

단순

a = "I love you"
b = a[2] + a[3] + a[4] + a[5]

print(b)

결과

love

> 이렇게 사용하지는 않음

슬라이싱

a = "I love you"
b = a[2:6]

print(b)

결과

love
  • a[시작 번호:끝 번호]를 지정할 때 끝 번호에 해당하는 문자는 포함되지 않습니다.
  • a[시작 번호:끝 번호]에서 시작 번호를 생략하면 처음부터라는 의미이고, 끝 번호를 생략하면 끝까지를 의미합니다.


파이썬(Python) 소수점 자리수 처리 - format

파이썬(Python) 소수점 자리수 처리 - format

소수점 자리수 처리

아래 예제는 소수점 둘째 자리까지만 표시합니다.

test = 12345.123456
a = "{0:0.2f}".format(test)
print(a)

>> 12345.12

정수 자리수 처리

test = 12345.123456
a = "{0:10.2f}".format(test)
print(a)

>> 12345.12



파이썬(Python) 대문자 소문자 변환

파이썬(Python) 대문자 소문자 변환

소문자를 대문자로 변환

a = "love"
a = a.upper()
print(a)

> 결과

LOVE

대문자를 소문자로 변환

a = "LOVE"
a = a.lower()
print(a)

> 결과

love


파이썬(Python) 문자열 공백 삭제

파이썬(Python) 문자열 공백 삭제, 제거, 지우기

왼쪽 공백 삭제

a = "   love   "
a = a.lstrip()

오른쪽 공백 삭제

a = "   love   "
a = a.rstrip()

양쪽 공백 삭제

a = "   love   "
a = a.strip()


파이썬(Python) 문자열 단어 나누기

파이썬(Python) 문자열 단어 나누기

a = "I love you"
b = a.split()

print(type(b))
print(b)

결과:

<class 'list'>
['I', 'love', 'you']
split 함수에 아무 값도 넣지 않으면 공백을 기준으로 처리

아래는 쉼표(,)를 기준으로 문자열을 나누는 예:

a = "I,love,you"
b = a.split(',')

print(type(b))
print(b)

결과:

<class 'list'>
['I', 'love', 'you']


파이썬(Python) 텍스트 파일 라인별 읽는 방법

파이썬(Python) 텍스트 파일 라인별 읽는 방법

읽을 파일이 다음과 같은 경우:

data.txt

a
b
c
d

readlines()

path = "data.txt"

with open(path) as f:
    lines = f.readlines()

print(lines)

결과:

['a\n', 'b\n', 'c\n', 'd']

splitlines()

path = "data.txt"

with open(path) as f:
    lines = f.read().splitlines()

print(lines)

결과:

['a', 'b', 'c', 'd']


파이썬(Python)3 나누기(division) 몫과 나머지

파이썬(Python)3 나누기(division) 몫과 나머지

나누기 몫

파이썬3와 파이썬2의 나누기 결과는 다름

python3

>>> 1000 / 3
333.3333333333333

>>> 1000 // 3
333

python2

>>> 1000 / 3
333

나누기 나머지

>>> 1000 % 3
1

몫과 나머지를 한번에 처리

>>> divmod(1000, 3)
(333, 1)


아나콘다(anaconda) 가상환경 관련 명령어

아나콘다(anaconda) 가상환경 관련 명령어

가상환경 생성

conda create -n 가상환경이름 python=버전
예) conda create -n pyqt-env python=3.7

다른 가상환경으로 전환(활성화 / 비활성화)

conda activate 가상환경이름
conda deactivate

가상환경 리스트 확인

conda env list
conda info --envs

가상환경 삭제

conda remove -n 가상환경이름 --all

패키지 리스트 저장, 설치

pip freeze > requirements.txt
pip install -r requirements.txt


파이썬(Python) IP가 여러개 인 경우 자신의 IP 확인 방법

파이썬(Python)에서 NIC가 여러 개일 경우 자신의 IP 확인 방법

  • 기본적으로 gethostbyname()를 사용하면 IP를 찾을 수 있으나, NIC가 여러 개인 경우(예: VirtualBox 같은 가상환경 운영 시) 제대로 찾지 못하는 문제가 있습니다.
  • 이때는 실제 연결을 시도하여 사용하는 IP를 직접 찾도록 처리하면 됩니다.
import socket

def get_ip_address():
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    s.connect(("8.8.8.8", 80))
    return s.getsockname()[0]


파이썬 문자열 처리 - format 함수

파이썬 문자열 처리 - format 함수

파이썬에서 문자열 대입 처리는 보통 format 함수를 사용합니다.
아래는 format 함수 사용하는 세 가지 방법을 소개합니다.

① 가장 간단한 방법

who = "you"
action = "love"
a = "I {} {}".format(action, who)
print(a)

② 인덱스를 사용하는 방법

who = "you"
action = "love"
a = "I {0} {1}".format(action, who)
print(a)

③ 이름을 지정해서 사용하는 방법

who = "you"
action = "love"
a = "I {action} {who}".format(action="love", who="you")
print(a)

모두 결과는 동일하게 I love you 입니다. 😊



파이썬(Python) 숫자 표현 포멧 - 숫자를 표현하는 다양한 방법

파이썬(Python) 숫자 표현 포멧 - 숫자를 다양하게 표현하는 방법

파이썬은 print 함수를 사용해 숫자 형식을 다양하게 표현할 수 있습니다.

print 함수의 기본 표현 포멧

# 3자리수 콤마
print("{0:,}".format(12345))

# +/- 표시
print("{0:+}".format(12345))
print("{0:+}".format(-12345))

# 소수점 특정 자리수까지만 표시
print("{0:.2f}".format(3.14159))

# 오른쪽 정렬(10자리 공간)
print("{0: >10}".format(123))

# 왼쪽 정렬(10자리 공간, 빈 칸은 _으로 채움)
print("{0:_<10}".format(123))

결과:

12,345
+12345
-12345
3.14
       123
123_______

포멧 조합으로 다양한 숫자 표현

# +/- 표시, 오른쪽 정렬(10자리 공간)
print("{0: >+10}".format(123))
print("{0: >+10}".format(-123))

# 3자리수 콤마, +/-부호, 오른쪽 정렬(10자리 공간)
print("{0:>+10,}".format(12345))

# 3자리수 콤마, +/-부호, 왼쪽 정렬(10자리 공간, 빈 칸은 _으로 채움)
print("{0:_<+10,}".format(12345))

결과:

      +123
      -123
   +12,345
+12,345___

숫자 앞에 0 채우기

n1 = 1
n2 = 12
n3 = 123

print(str(n1).zfill(5))
print(str(n2).zfill(5))
print(str(n3).zfill(5))

결과:

00001
00012
00123


파이썬(Python) 데이터 타입(type) 확인 및 비교

파이썬(Python) 데이터 타입(type) 확인 및 비교

데이터 타입 확인

a = ['a', 'b']
print(type(a))

b = 123
print(type(b))

c = 3.14
print(type(c))

d = 'love'
print(type(d))

결과:

<class 'list'>
<class 'int'>
<class 'float'>
<class 'str'>

type()으로 비교

a = ['a', 'b']
b = 123
c = 3.14
d = 'love'

if type(a) is list:
    print("a is list")

if type(b) is int:
    print("b is int")

if type(c) is float:
    print("c is float")

if type(d) is str:
    print("d is str")

결과:

a is list
b is int
c is float
d is str

isinstance()으로 비교

a = ['a', 'b']
b = 123
c = 3.14
d = 'love'

if isinstance(a, list):
    print("a is list")

if isinstance(b, int):
    print("b is int")

if isinstance(c, float):
    print("c is float")

if isinstance(d, str):
    print("d is str")

결과:

a is list
b is int
c is float
d is str