[SK shieldus Rookies 19기] 인프라 활용을 위한 파이썬 5일차

기록하는짱구·2024년 3월 6일
0

SK Shieldus Rookies 19기

목록 보기
5/43
post-thumbnail

📌 상속 & 메서드 오버라이딩

상속(Inheritance)

재산을 '물려받다, 상속받다' 와 같은 의미
부모가 가지고 있는 데이터나 동작, 함수를 별도의 정의 없이도 그대로 쓸 수 있도록 만드는 것
코드의 재활용성 극대화

상속을 사용하는 이유

① 기존 클래스를 변경하지 않고 기능을 추가하거나 기존 기능을 변경하려고 할 때 사용(기능의 확장)
② 기존 클래스가 라이브러리 형태로 제공되거나 수정이 허용되지 않는 상황에서 사용

💡class 클래스 이름(상속할 클래스 이름)

import time

class Clock:
    def __init__(self) -> None:
        self.hour = 0
        self.miniute = 0
        self.second = 0

    def print(self):
        print(f"{self.hour}:{self.minute}:{self.second}")

    def add_one_hour(self):
        self.hour += 1
        if self.hour >= 24:
            self.hour = 0

    def add_one_minute(self):
        self.minute += 1
        if self.minute >= 60:
            self.minute = 0
            self.add_one_hour()

    def add_one_second(self):
        self.second += 1
        if self.second >= 60:
            self.second = 0
            self.add_one_minute()

class MinuteClock(Clock):   # class 자식 클래스 명(부모 클래스 명)
    pass

c = MinuteClock()

while True:
    c.add_one_minute()      # 부모 클래스의 메소드가 실행
    c.print()               # 자식 클래스는 부모 클래스의 변수와 함수(메서드)를
                              그대로 사용하는 것이 가능

    time.sleep(1)

메서드 오버라이딩(method overriding)

'재정의, 덮어쓰는 것'

부모 클래스(상속한 클래스)에 있는 메서드를 동일한 이름으로 다시 만드는 것
→ 메서드를 오버라이딩하면 부모 클래스의 메서드 대신 오버라이딩한 메서드가 호출됨

import time

class Clock:
    def __init__(self) -> None:
        self.hour = 0
        self.minute = 0
        self.second = 0

    def print(self):
        print(f"{self.hour}:{self.minute}:{self.second}")

    def add_one_hour(self):
        self.hour += 1
        if self.hour >= 24:
            self.hour = 0

    def add_one_minute(self):
        self.minute += 1
        if self.minute >= 60:
            self.minute = 0
            self.add_one_hour()

    def add_one_second(self):
        self.second += 1
        if self.second >= 60:
            self.second = 0
            self.add_one_minute()

class MinuteClock(Clock):   # class 자식 클래스 이름(부모 클래스 이름)

    def print(self):        # 메서드 오버라이딩(overriding)
                            # 부모 클래스의 메서드를 자식 클래스에서 재정의
        print(f"{self.hour}{self.minute}분")

c = MinuteClock()

while True:
    c.add_one_minute()
    c.print()               # 자식 클래스의 메서드가 호출

    time.sleep(1)

📌 패키지

패키지(packages)

모듈의 집합
모듈을 계층적(디렉토리 구조)으로 관리
파이썬 패키지는 디렉토리 + 파이썬 모듈로 구성

패키지의 장점

구성한 모듈들을 묶어주는 기능

① 공동 작업 가능
② 유지 보수 면에서 유리
③ 동일한 모듈명 사용 가능

명령 프롬프트에서 아래 명령 실행

C:\python> mkdir c:\python\game\sound			  → 디렉터리 생성
C:\python> mkdir c:\python\game\graphic
C:\python> mkdir c:\python\game\play

C:\python> echo '' > c:\python\game\__init__.py	  → 빈 파일 생성
C:\python> echo '' > c:\python\game\sound\__init__.py
C:\python> echo '' > c:\python\game\sound\echo.py
C:\python> echo '' > c:\python\game\graphic\__init__.py
C:\python> echo '' > c:\python\game\graphic\render.py

C:\python> tree .\game /F						  → 디렉터리 구조와 파일 확인
폴더 PATH의 목록입니다.
볼륨 일련 번호가 00000016 9027:83B9입니다.
C:\PYTHON\GAME
│  __init__.py
│
├─graphic
│      render.py
│      __init__.py
│
├─play
└─sound
        echo.py
        __init__.py
#c:\python\game\sound\echo.py
def echo_test():
    print("echo")


# c:\python\game\graphic\render.py
def render_test():
    print("render")

__init__.py 파일의 내용 모두 삭제 (모두 3개)

c:\python\main.py

# echo 모듈에 echo_test() 함수를 실행(호출)
import game.sound.echo

game.sound.echo.echo_test()


# echo 모듈에 echo_test() 함수를 실행(호출)
from game.sound.echo import echo_test

echo_test()


# echo 모듈에 echo_test() 함수를 실행(호출)
from game.sound import echo

echo.echo_test()

__init.py__의 용도

__init__.py 파일은 해당 디렉터리가 패키지의 일부임을 알려주는 역할
__init__.py 파일은 패키지와 관련된 설정이나 초기화 코드 포함

💡 <참고>
패키지에 포함된 디렉터리에 __init__.py 파일이 없다면 패키지로 인식되지 않음에 주의(파이썬 3.3 버전부터는 __init__.py 파일이 없어도 패키지로 인식)

c:\python\game__init__.py

# 유즈케이스(사용 예시)
# 패키지와 관련한 설정이나 초기화 코드를 포함할 수 있음

VERSION = 3.5

def print_version_info():
    print(f"Game Version: {VERSION}")

c:\python\main.py

import game

# game 패키지의 __init__.py 파일에 정의된 변수와 함수를 참조
print(game.VERSION)            
game.print_version_info()

c:\python\game__init__.py

# 해당 디렉터리가 패키지의 일부임을 알려주는 역할

# 유즈케이스(사용예)
# 패키지와 관련한 설정이나 초기화 코드를 포함할 수 있음
VERSION = 3.5

def print_version_info():
    print(f"Game Version: {VERSION}")

# 패키지 내의 모듈을 미리 임포트
from .graphic.render import render_test  

c:\python\main.py

import game

# game 패키지의 __init__.py 파일에 정의된 변수와 함수를 참조
print(game.VERSION)            
game.print_version_info()

# from game.graphic import render
# render.render_test()
game.render_test()

c:\python\game__init__.py

# 해당 디렉터리가 패키지의 일부임을 알려주는 역할

# 유즈케이스(사용예)
# 1 패키지와 관련한 설정이나 초기화 코드를 포함할 수 있음
VERSION = 3.5

def print_version_info():
    print(f"Game Version: {VERSION}")

# 2 패키지 내의 모듈을 미리 임포트
from .graphic.render import render_test    


# 3 패키지 초기화 ⇒ 패키지가 처음 호출될때 실행되어야 하는 코드 작성 
    (: 데이터베이스 연결 또는 설정 등)
print("game 패키지 초기화 ...")

c:\python\main.py

import game

print(r'c:/python/main.py')



from game.graphic.render import render_test

print(r'c:/python/main.py')

__all__

특정 디렉터리의 모듈을 모든 것(*)을 사용하여 다짜고짜 import 한다면 Error 발생
→ 오류가 생기지 않게 하려면 해당 디렉토리의 __init__.py 파일에 __all__ 변수를 설정하고 import 할 수 있는 모듈을 정의해야 함

relative한 접근자

💡 <참고>
.. 부모 디렉토리
. 현재 디렉토리

📌 예외 처리

예외 처리
에러가 동작하는 것을 개발자가 통제

에러의 종류

구문 에러(Syntax Error)

컴파일 시 발생하는 문법 상 에러
실행 시점의 값 또는 상태 예외 발생

실행 에러(Runtime Error)

프로그램 실행(run time) 도중에 나는 에러
사용자 입력에 따라 달라짐(실행 불가피)

오류 발생 예시

c:\python\test.py

no = int(input("숫자를 입력하세요."))

result = 100 / no
print(f"100 나누기 {no}의 결과는 {result} 입니다.")


결과

숫자를 입력하세요.0
Traceback (most recent call last):
  File "C:\python\test.py", line 3, in <module>
    result = 100 / no
             ~~~~^~~~
ZeroDivisionError: division by zero		→ 사용자가 0을 입력하면 오류 발생

오류로 인한 비정상 종료를 막는 방법

① 오류가 발생하지 않도록 입력값을 검증 후 사용

Secure Coding 기법(사전 대처)

no = int(input("숫자를 입력하세요."))

if no == 0:
    print("0은 입력할 수 없습니다.")
else:
    result = 100 / no
    print(f"100 나누기 {no}의 결과는 {result} 입니다.")


결과

숫자를 입력하세요.0
0은 입력할 수 없습니다.

② 오류가 발생했을 때 적절한 조치를 수행하는 코드 추가

예외 처리(사후 대처)

no = int(input("숫자를 입력하세요."))

try:
    result = 100 / no
    print(f"100 나누기 {no}의 결과는 {result} 입니다.")
except ZeroDivisionError:
    print("0을 입력했기 때문에 프로그램을 종료합니다.")


결과

숫자를 입력하세요.0
0을 입력했기 때문에 프로그램을 종료합니다.



no = int(input("숫자를 입력하세요."))

try:
    result = 100 / no
    print(f"100 나누기 {no}의 결과는 {result} 입니다.")
except ZeroDivisionError as e:
    print(f"프로그램을 종료합니다. (원인: {e})")


결과

숫자를 입력하세요.0
프로그램을 종료합니다. (원인: division by zero)



no = int(input("숫자를 입력하세요."))

try:
    result = 100 / no
    print(f"100 나누기 {no}의 결과는 {result} 입니다.")

except ZeroDivisionError as e:
    print(f"프로그램을 종료합니다. (원인: {e})")

finally:  # 예외 발생 여부와 관계 없이 무조건 실행되는 코드 기술
    print("자원 해제 코드를 실행")  # 자원 해제와 같은 마무리 코드 기술


결과

숫자를 입력하세요.0
프로그램을 종료합니다. (원인: division by zero)
자원 해제 코드를 실행					→ 예외가 발생한 경우에도 실행

숫자를 입력하세요.10
100 나누기 10의 결과는 10.0 입니다.
자원 해제 코드를 실행					→ 예외가 발생하지 않는 경우에도 실행

오류 예외 처리 기법

① try-except 문

try 블록 수행 중 오류가 발생하면 except 블록 수행
→ try 블록에서 오류가 발생하지 않는다면 except 블록은 수행 X

try :
    ...
except [발생오류 [as 오류변수]] :
    ...

오류 예외 처리에는 세 가지 방법이 더 있음

가장 기본 형태의 except 문

오류의 종류에 상관없이 오류가 발생하면 except 블록 수행

try :
    ...
except :
    ...

발생 오류만 포함한 except 문

오류가 발생했을 때 except 문에 미리 정해 놓은 오류와 동일한 오류일 경우에만 except 블록을 수행

 try :
    ...
except 발생오류 :
    ...

발생 오류와 오류 변수까지 포함한 except 문

두 번째 경우에서 오류의 내용까지 알고 싶을 때 사용

try :
    ...
except 발생오류 as 오류변수 :
    ...

② try-finally 문

finally 문은 예외 발생 여부에 상관없이 항상 수행
리소스를 close 해야 할 때 많이 사용

try :
    ...
(생략)
finally :   # 중간에 오류가 발생하더라도 무조건 실행
    ...

여러 개의 오류 처리

try :
    ...
except 발생오류1 :
   ... 
except 발생오류2 :
   ...

③ try-else 문

try 문 수행 중 오류가 발생하면 except 절 수행
오류가 발생하지 않으면 else 절 수행

try :
    ...
except [발생오류 [as 오류변수]] :
    ...
else :  # 오류가 없을 경우에만 수행
    ...

📌 오류 회피

오류가 발생해도 그냥 통과

try :
     f = open("파일", 'r')
except FileNotFoundError :  # try 문에서 에러가 발생해도
                              pass를 사용하여 오류 회피
    pass

오류를 의도적으로 발생시키는 코드

파이썬은 raise 명령어를 사용해 오류를 강제로 발생시킬 수 있음
→ 자식 클래스에서 메서드를 재정의하도록 강제

NotImplementedError 는 파이썬에 이미 정의되어 있는 오류로, 꼭 작성해야 하는 부분이 구현되지 않았을 경우 일부러 오류를 발생시키기 위해 사용

class Bird:
 def fly(self):
        raise NotImplemented
   
class Eagle(Bird):  # Eagle 클래스는 Bird 클래스 상속
    pass  # Eagle 클래스는 fly 메서드를 오버라이딩하여 구현 X


e = Eagle()
e.fly()	  # 부모 클래스(Bird)의 fly() 메서드가
            실행되어 NotImplemented 오류가 발생

자식 클래스(Eagle)에 fly() 메서드 재정의(override)

class Bird:
    def fly(self):
        raise NotImplemented
   
class Eagle(Bird):
    def fly(self):
        print("very fast")


e = Eagle()
e.fly()

📌 사용자 정의 예외

특수한 경우에만 예외 처리를 하려고 종종 예외를 만들어서 사용

💡 __str__ 메서드
오류 메시지를 출력했을 때 오류 메시지가 보이게 하려면 오류 클래스에 __str__ 메서드 구현
print(e) 처럼 오류 메시지를 print 문으로 출력할 경우에 호출되는 메서드

class MyInputError(Exception):			# 사용자 정의 예외는
                                          Exception 클래스를 상속 받아 정의
    def __init__(self, no) -> None:		# 예외 발생 시 전달받아야 하는
                                          값이 있는 경우 생성자를 활용
        self.no = no					

    def __str__(self) -> str:			# 예외 발생 시 반환할 문자열(메시지)
        return f"{self.no}는 잘못된 입력입니다."

try:
    no = int(input("숫자를 입력하세요."))
    if no == 0:
        raise MyInputError(no)
   
    print(f"10 / {no} = {10 / no}")

except MyInputError as e:
    print(e)

📌 내장 함수

내장(built-in) 함수

print, del, type 등
import가 필요없기 때문에 아무런 설정 없이 사용 가능

abs

숫자를 입력받았을 때 그 숫자의 절댓값 반환

all

all(x) 안의 반복 가능한 데이터 x가 전부 참이면 True, 거짓이 하나라도 있다면 False
만약 all 안의 입력 인수가 빈 값이라면 True를 리턴

📢 여기서 잠깐! 반복 가능한 데이터란?
for 문에서 사용할 수 있는 자료형을 의미
→ 리스트, 튜플, 문자열, 딕셔너리, 집합 등

any

any(x)에서 x의 요소 중 하나라도 참이 있다면 True, 모두 거짓일 때만 False
→ all(x)의 반대로 작동

any의 입력 인수가 빈 값인 경우에 False 리턴

chr

유니코드 숫자 값을 입력받아 그 코드에 해당하는 문자 리턴

📢 여기서 잠깐! 유니코드란?
전 세계의 모든 문자를 컴퓨터에서 일관되게 표현하고 다룰 수 있도록 설계된 산업 표준 코드

dir

객체가 지닌 변수나 함수를 보여주는 함수

>>> dir([1, 2, 3])  # 리스트
['append', 'count', 'extend', 'index', 'insert', 'pop',...]
>>> dir({'1':'a'})  # 딕셔너리
['clear', 'copy', 'get', 'has_key', 'items', 'keys',...]

divmod

divmod(a, b)
2개의 숫자 a, b를 입력받아 a를 b로 나눈 몫과 나머지를 튜플로 리턴

def div_mod(a, b):          # 내장 함수 divmod와 동일한 기능을 구현
    return a // b, a % b

print(div_mod(10, 3))       # 사용자 정의 함수를 호출

print(divmod(10, 3))        # 내장 함수를 호출

enumerate

'열거하다' 라는 뜻

순서가 있는 데이터(리스트, 튜플, 문자열)를 입력으로 받아 인덱스 값을 포함하는 enumerate 객체 리턴

💡 enumerate 함수는 보통 for 문과 함께 사용
for 문처럼 반복되는 구간에서 객체가 현재 어느 위치에 있는지
알려주는 인덱스 값이 필요할 때 enumerate 함수를 사용하면 유용

>>> for i, name in enumerate(['body', 'foo', 'bar']):
...     print(i, name)
...
0 body
1 foo
2 bar

eval(expression)

문자열로 구성된 표현식을 입력으로 받아 해당 문자열을 실행한 결괏값 리턴
→ 매개변수 값을 파이썬 문법으로 해석해서 값 반환

cmd = input("계산할 수식을 입력해 주세요 (예: 2 + 3) ")

print(f"{cmd} = {eval(cmd)}")



# 개발자가 의도한 형태의 입력 
계산할 수식을 입력해 주세요 (: 2 + 3) 3 + 5 * 8 - 10
3 + 5 * 8 - 10 = 33

# 개발자가 의도하지 않은 형태의 입력
계산할 수식을 입력해 주세요 (: 2 + 3) __import__('os').system('dir ')   
 C 드라이브의 볼륨에는 이름이 없습니다.	  # 호스트에서 dir 명령어의 실행 결과가 출력
 볼륨 일련 번호: 9027-83B9

 C:\python 디렉터리

2024-03-06  오전 11:16    <DIR>          .
2024-03-06  오전 11:16    <DIR>          ..
2024-03-05  오후 02:37    <DIR>          .vscode
2024-03-05  오후 05:06               737 calculator.py
2024-03-06  오전 10:06             1,055 click.py
2024-03-05  오후 03:52               757 counter.py
2024-03-06  오전 08:35                42 data
2024-03-06  오전 10:38    <DIR>          game
2024-03-05  오후 03:28             1,476 hangman.py
2024-03-06  오전 11:02                74 main.py
2024-03-05  오후 03:25               107 mod1.py
2024-03-05  오전 09:36               234 new_file.txt
2024-03-05  오후 03:47             1,324 score.py
2024-03-05  오전 10:24                60 score_data.txt
2024-03-06  오후 02:30               101 test.py
2024-03-06  오전 10:26                 5 test.txt
2024-03-05  오후 03:50    <DIR>          __pycache__
              12개 파일               5,972 바이트
               5개 디렉터리  177,886,593,024 바이트 남음
__import__('os').system('dir ') = 0


계산할 수식을 입력해 주세요 (: 2 + 3) __import__('os').system('type test.py')
cmd = input("怨꾩궛???섏떇???낅젰??二쇱꽭??(?? 2 + 3) ")  # test.py 의 내용 출력

print(f"{cmd} = {eval(cmd)}")__import__('os').system('type test.py') = 0

filter

'걸러낸다'는 뜻 그대로 동작하는 함수

반복 가능한 데이터의 요소 순서대로 함수를 호출했을 때 리턴값이 참인 것만 묶어서(걸러 내서) 리턴

filter(함수, 반복 가능한 데이터)
def positive(x):
    return x > 0

print(list(filter(positive, [1, -3, 2, 0, -5, 6])))
      ^ list 함수는 filter 함수의 리턴값을 리스트로 출력하기 위해 사용
      
      
# lmabda 사용
>>> list(filter(lambda x: x > 0, [1, -3, 2, 0, -5, 6]))
[1, 2, 6]

hex

정수를 입력받아 16진수(hexadecimal) 문자열로 변환하여 리턴

id

id(object)는 객체를 입력받아 객체의 고유 주솟값(레퍼런스)을 리턴하는 함수

input

사용자 입력을 문자열로 받는 함수

>>> a = input("Enter: ")
Enter: hi
>>> a
'hi'

int

문자열 형태의 숫자나 소수점이 있는 숫자를 정수로 리턴

isinstance

isinstance(object, class)
첫 번째 인수로 객체, 두 번째 인수로 클래스를 받음
입력으로 받은 객체가 그 클래스의 인스턴스인지 판단하여 참이면 True, 거짓이면 False를 리턴

class Plants:
    pass

class Bird:
    pass

class Eagle(Bird):
    pass

e = Eagle()

print(isinstance(e, Eagle))
print(isinstance(e, Bird))		# 부모 클래스인 경우에도 True를 반환
print(isinstance(e, Plants))

len

입력값의 길이(요소의 전체 개수) 리턴

>>> len("python")
6
>>> len([1,2,3])
3
>>> len((1, 'a'))
2

list

list(iterable)는 반복 가능한 데이터를 입력받아 리스트로 만들어 리턴

>>> list("python")
['p', 'y', 't', 'h', 'o', 'n']
>>> list((1,2,3))
[1, 2, 3]


# list 함수에 리스트를 입력하면 똑같은 리스트를 복사하여 리턴
>>> a = [1, 2, 3]
>>> b = list(a)
>>> b
[1, 2, 3]

map

map(f, iterable)
함수(f)와 반복 가능한 데이터를 입력으로 받음
입력받은 데이터의 각 요소에 함수 f를 적용한 결과를 반환

>>> def two_times(x): 
...     return x*2
...
>>> list(map(two_times, [1, 2, 3, 4]))
    ^ map 함수의 결과를 리스트로 출력하기 위해 list 함수 사용
[2, 4, 6, 8]


#lambda 사용
>>> list(map(lambda a: a*2, [1, 2, 3, 4]))
[2, 4, 6, 8]

max

max(iterable)은 인수로 반복 가능한 데이터를 입력받아 최댓값 리턴

min

max와 반대, 최솟값 리턴

oct

정수를 8진수 문자열로 바꾸어 리턴

open

open(filename, [mode])
‘파일 이름’과 ‘읽기 방법’을 입력받아 파일 객체 리턴
읽기 방법(mode)을 생략할 경우 기본값인 읽기 모드(r)로 파일 객체를 만들어 리턴

>>> f = open("binary_file", "rb")  # rb는 ‘바이너리 읽기 모드’

💡 mode
w
쓰기 모드로 파일 열기
r
읽기 모드로 파일 열기
a
추가 모드로 파일 열기
b
바이너리 모드로 파일 열기

ord

문자의 유니코드 숫자 값 리턴
chr 함수와 반대로 동작

pow

pow(x, y)
x를 y 제곱한 결괏값 리턴

range

range([start,] stop [,step])
range 함수는 입력받은 숫자에 해당하는 범위 값을 반복 가능한 객체로 만들어 리턴
for 문과 함께 자주 사용

# 인수가 하나일 경우
# 시작 숫자를 지정해 주지 않으면 range 함수는 0부터 시작

>>> list(range(5))
[0, 1, 2, 3, 4]



# 인수가 2개일 경우
# 입력으로 주어지는 2개의 인수는 시작 숫자와 끝 숫자를 나타냄, 끝 숫자는 해당 범위에 포함 X

>>> list(range(5, 10))
[5, 6, 7, 8, 9]



# 인수가 3개일 경우
# 세 번째 인수는 숫자 사이의 거리를 뜻함

>>> list(range(1, 10, 2))
[1, 3, 5, 7, 9]
>>> list(range(0, -10, -1))
[0, -1, -2, -3, -4, -5, -6, -7, -8, -9]

round

round(number [,ndigits])
숫자를 입력받아 반올림해 리턴
[,ndigits]는 ndigits가 있을 수도 있고, 없을 수도 있다는 의미

>>> round(4.6)
5

>>> round(5.678, 2)  # 두 번째 인수는 반올림하여 표시하고 싶은
                       소수점의 자릿수(ndigits)를 의미
5.68

sorted

sorted(iterable)는 입력 데이터를 정렬한 후 그 결과를 리스트로 리턴

💡 리스트 자료형에도 sort 함수가 있지만 리스트 자료형의 sort 함수는 리스트 객체 그 자체를 정렬만 할 뿐, 정렬된 결과를 리턴하지는 않음

str

str(object)는 문자열 형태로 객체를 변환하여 리턴

>>> str(1)
'1'

sum

sum(iterable)은 입력 데이터의 합을 리턴

tuple

tuple(iterable)은 반복 가능한 데이터를 튜플로 바꾸어 리턴

type

type(object)는 입력값의 자료형이 무엇인지 알려주는 함수

zip

zip(*iterable)
동일한 개수로 이루어진 데이터들을 묶어서 리턴
두 개의 리스트를 하나로 묶어줌
*iterable은 반복 가능한 데이터를 여러 개 입력할 수 있다는 의미

>>> list(zip([1, 2, 3], [4, 5, 6]))
[(1, 4), (2, 5), (3, 6)]
>>> list(zip([1, 2, 3], [4, 5, 6], [7, 8, 9]))
[(1, 4, 7), (2, 5, 8), (3, 6, 9)]
>>> list(zip("abc", "def"))
[('a', 'd'), ('b', 'e'), ('c', 'f')]

pickle

객체의 형태를 그대로 유지하면서 파일에 저장하고 불러올 수 있게 하는 모듈
딕셔너리, 리스트, 클래스 등의 자료형

📌 역직렬화 취약점

직렬화-역직렬화 과정에서 악의적으로 객체 또는 변수를 추가 작성하여 악성코드를 실행하게끔 만드는 취약점

① MyExeCode 클래스의 인스턴스에 대해 직렬화한 결과를 data 파일에 저장

import os
import pickle

class MyExeCode(object):
    def __reduce__(self):
        return (os.system, ('dir c:\\', ))

def serialize():
    code = pickle.dumps(MyExeCode())
    return code

if __name__ == '__main__':
    code = serialize()
    with open('data', 'wb') as f:
        f.write(code)

② 실행 결과 data 파일(이진파일) 생성

③ data 파일을 읽어서 역직렬화를 수행하는 코드 작성

import pickle

def deserialize(code):
    pickle.loads(code)

if __name__ == '__main__':
    with open('data', 'rb') as f:
        deserialize(f.read())

④ data 파일에 들어있던 dir c:\ 결과가 출력되는 것을 확인

의도하지 않은 동작 제공

⑤ (테스트를 위해) 직렬화한 데이터를 바로 역직렬화 하도록 코드 통합

import os
import pickle

class MyExeCode(object):
    def __reduce__(self):
        return (os.system, ('cat /etc/passwd', ))	# 해당 시스템의 계정 정보를
                                                       담고 있는 파일

def serialize():
    code = pickle.dumps(MyExeCode())
    return code

def deserialize(code):
    pickle.loads(code)

if __name__ == '__main__':
    code = serialize()					# 직렬화 결과를 파일로 저장
    with open('data', 'wb') as f:			
        f.write(code)

    with open('data', 'rb') as f:		 # 파일을 읽어서 역직렬화
        deserialize(f.read())

⑥ 테스트를 위해 리눅스 가상머신을 하나 생성

https://labs.play-with-docker.com/ 가입 후 로그인

⑦ 안전한 역직렬화 코드로 변경

역직렬화한 코드가 실행되는 범위를 제한하는 코드 추가

import os
import pickle
from contextlib import contextmanager

class MyExeCode(object):
    def __reduce__(self):
        return (os.system, ('cat /etc/passwd', ))		# cat /safe_root/etc/passwd 형태로 명령어 실행

def serialize():
    code = pickle.dumps(MyExeCode())
    return code

def deserialize(code):
    with system_jail():
        pickle.loads(code)

@contextmanager
def system_jail():
    os.chroot('/safe_root')				# 시스템의 루트 디렉터리를 변경
    yield
    os.chroot('/')						# 원래 시스템 루트 디렉터리로 재설정

if __name__ == '__main__':
    code = serialize()
    with open('data', 'wb') as f:
        f.write(code)

    with open('data', 'rb') as f:
        deserialize(f.read())

⑧ 변경한 코드 반영 후 기존에 생성한 data 파일을 삭제하고 /safe_root 디렉터리 생성 후 재실행

/safe_root/etc/passwd 파일이 존재하지 않기 때문에 아무런 출력이 존재하지 않음 (직렬화 내용을 저장한 data 파일의 내용은 이전과 동일)

📌 외부 라이브러리

파이썬 설치 시 기본으로 설치되는 파이썬 표준 라이브러리와 달리,
외부 라이브러리는 따로 설치하는 과정이 필요

pip

파이썬 모듈이나 패키지를 쉽게 설치할 수 있도록 도와주는 도구

① pip install

패키지 설치

② pip uninstall

패키지 삭제

③ pip install (패키지 명)==(버전)

특정 버전으로 설치
버전 생략 시, 최신 버전으로 설치

④ pip install --upgrade (패키지 명)

최신 버전으로 업그레이드 하려면 --upgrade 옵션 함께 사용

⑤ pip list

설치된 패키지 목록 출력

Faker 라이브러리

테스트용 가짜 데이터를 생성할 때 사용하는 라이브러리

① Faker 패키지 설치

PS C:\python> pip install Faker
Collecting Faker
  Downloading Faker-24.0.0-py3-none-any.whl.metadata (15 kB)
Collecting python-dateutil>=2.4 (from Faker)
  Downloading python_dateutil-2.9.0.post0-py2.py3-none-any.whl.metadata (8.4 kB)
Collecting six>=1.5 (from python-dateutil>=2.4->Faker)
  Downloading six-1.16.0-py2.py3-none-any.whl.metadata (1.8 kB)
Downloading Faker-24.0.0-py3-none-any.whl (1.8 MB)
   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 1.8/1.8 MB 12.5 MB/s eta 0:00:00
Downloading python_dateutil-2.9.0.post0-py2.py3-none-any.whl (229 kB)
   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 229.9/229.9 kB 7.1 MB/s eta 0:00:00
Downloading six-1.16.0-py2.py3-none-any.whl (11 kB)
Installing collected packages: six, python-dateutil, Faker
Successfully installed Faker-24.0.0 python-dateutil-2.9.0.post0 six-1.16.0

② Faker 사용

from faker import Faker

faker = Faker()

for i in range(10):
    print(faker.name())



Matthew Harper
Shannon Zuniga
Nicholas Garcia
Susan Jones
Juan Deleon
Kendra Willis
Alejandro Butler
Ryan Rodriguez
Kendra Guzman
Steven Smith

③ 지역 설정

한국 가짜 데이터가 필요한 경우

from faker import Faker

faker = Faker('ko-KR')      # 로케일을 한국으로 설정

for i in range(10):
    print(faker.name())



김아름
강성민
이보람
조영자
이상호
김영일
박진우
이진호
김혜진
박경자

④ 테스트 용도의 이름, 주소, 이메일 생성

from faker import Faker

faker = Faker('ko-KR')      # 로케일을 한국으로 설정

for i in range(10):
    print(faker.name(), faker.address(), faker.email())



이민수 세종특별자치시 용산구 양재천가 bagjunseo@example.net
이성진 경기도 홍천군 서초중앙85가 yeonghyi96@example.org
장정남 세종특별자치시 영등포구 양재천가 (현숙이동) gwangsujo@example.com
윤병철 충청북도 춘천시 삼성가 bgim@example.net
이상훈 경상남도 여주시 언주길 (춘자오김읍) miyeongi@example.net
장경자 충청북도 보은군 잠실가 migyeong36@example.com
김영수 경상북도 수원시 팔달구 백제고분로 (지영박마을) hyeonsuggim@example.org
류성민 대전광역시 서초구 역삼가 (지후박리) eson@example.net
박미경 대구광역시 동구 강남대2거리 jeonghyijeong@example.org
이순자 경상북도 부천시 소사구 압구정32(명자김리) ujin26@example.net

⑤ 사용 가능한 대표 항목

📌 데이터베이스 연동 환경 구성

① MySQL / MySQL Workbench 설치

https://myanjini.tistory.com/entry/MySQL-8034-MySQL-Workbench-80-%EC%84%A4%EC%B9%98
MySQL 패스워드를 p@ssw0rd로 설정

② 스키마와 테이블 생성

create schema sampledb default character set utf8;

use sampledb;

create table members (
    member_id 		int(11) 	not null auto_increment 	comment '회원 아이디', 
    member_name	varchar(100)	not null 		   	comment	'회원 이름', 
    member_age		int(3)						comment '회원 나이', 
    member_email	varchar(100)					comment '회원 이메일', 
    primary key(member_id)
);

③ 테스트 데이터 추가

use sampledb;

insert into members (member_name, member_age, member_email)
values ('홍길동', 23, 'hong@test.com');

④ pymysql 라이브러리 설치

PS C:\python> pip install pymysql
Collecting pymysql
  Downloading PyMySQL-1.1.0-py3-none-any.whl.metadata (4.4 kB)
Downloading PyMySQL-1.1.0-py3-none-any.whl (44 kB)
   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 44.8/44.8 kB 1.1 MB/s eta 0:00:00
Installing collected packages: pymysql
Successfully installed pymysql-1.1.0

📌 SQL 유형


데이터 조회 코드 작성

import pymysql


# 데이터베이스에 연결을 생성
try:
    with pymysql.connect(host="localhost", port=3306, user="springboot", passwd="p@ssw0rd", db="sampledb") as conn:
    
        # 조회 쿼리를 작성
        query = "select member_id, member_name, member_age, member_email from members"


        # 커서를 생성해서 조회 쿼리를 실행
        curr = conn.cursor()
        curr.execute(query)


        # 조회 결과를 출력
        for c in curr:
            print(c)

except pymysql.MySQLError as e:
    print(e)

0개의 댓글