목차보기

모듈7_Module

외부에서의 호출

지금까지 하나의 문서(.py)에 함수와 로직을 넣는 예제를 보았습니다. 새로운 프로젝트를 진행 할 때, 이전에 사용했던 함수를 파일단위로 불러올 수 있습니다.

예를들어, 아래와 같이 작성된 fibo.py파일이 있습니다.

def fib(n):  # write Fibonacci series up to n
    a, b = 0, 1
    while a < n:
        print(a, end=" ")
        a, b = b, a + b
    print()

다음과 같이 수행합니다.

7_Module > 01_Import.py
import fibo  # 모듈을 불러옵니다. 모듈의 이름은 fibo입니다.

fibo.fib(1000)  # fibo모듈의 fib함수를 수행합니다.
# 0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 

이 외에도, 불러오는 방법이 존재합니다.

7_Module > 01-1_From.py 7_Module > 01-2_Import_all.py 7_Module > 01-3_Import_as.py 7_Module > 01-4_From_as.py
import fibo  # 모듈을 불러옵니다.

from fibo import fib, fib2 # fibo 모듈은 호출되지 않습니다.
fib(500)

from fibo import * # fibo모듈의 모든 함수를 로딩합니다. 추천하지 않습니다.

import fibo as fib #모듈의 이름을 fibo에서 fib으로 변경할 수 있습니다.
fib.fib(500)

from fibo import fib as fibonacci # fibo 모듈을 호출하지 않고, fib함수만 fibonacci로 변경하여 호출합니다.
fibonacci(500)

내장변수 (직접 선언 하지 않아도, 내부적으로 이미 선언된 변수)

내장변수 __name__을 이용하면 현재 모듈이 직접 실행되었는지, 외부에서 호출되었는지 알 수 있습니다. 직접 실행되는 경우 내장변수 __name__이 "__main__"이 됩니다.

7_Module > fibo.py
if __name__ == "__main__":
    print("이 파일에서 실행되었습니다.")
내장 변수
분류 변수명 설명
모듈 __name__ 모듈의 이름(if 현재 모듈=실행 모듈: "__main__")
__file__ 파일의 경로 + 파일명
__doc__ 모듈, 클래스, 함수의 "문서화 문자열(Docstring)"
__package__ 패키지 이름(최상위 모듈: None)
__annotations__ 변수나 함수의 타입 힌트(annotations)
__builtins__ 내장 함수와 객체를 포함한 모듈(재정의 영향을 받지 않음)
__loader__ 모듈읠 업로드한 로더 객체 정보
__spec__ 모듈의 import 관련 메타 데이터를 포함하는 객체
__cached__ 컴파일된 캐시 파일(.pyc)의 경로(없다면 "None")
__debug__ 현재 디버그 모드인지
객체 __dict__ 속성이 저장된 딕셔너리
__class__ 객체 클래스
__module__ 클래스나 함수가 정의된 모듈 이름
__slots__ 클래스가 사용할 속성을 제한(메모리 관리 용)
7_Module > 01-5a_Builtin variables.py
"""
    이 문서는 내장 변수(built-in variables)을 설명하기 위한 파일
"""
# 1. 실행된 파일 감별
print(__name__)  # "__main__" (직접 실행 시)

# 2. 파일의 위치 감별
print(__file__)  # 실행 파일의 "디렉토리 + 파일명"

# 3. 문서 설명서 조회(있다면...)
print(__doc__)  # "이 문서는... "

# 4. 현재 모듈
print(myPackage.__package__)  # "None": 최상위 모듈

# 5. 변수나 함수의 타입힌트
def func1(name: str, age: int) -> str:
    pass

print(func1.__annotations__)

# 6. 내장함수 abs와 동일한 이름의 함수를 재정의
def abs(num):
    return num + 10

print(abs(-4), "/", built-ins(-4): __builtins__.abs(-4))  # -4 + 10 / |-4|

# 7. 업로드한 로더 객체 정보
print(__loader__)  # <_frozen_importlib_external.SourceFileLoader object at...

# 8. 모듈의 메타 데이타
print(myPackage.__spec__)  # ModuleSpec(name='__main__', ...)

# 9. 캐시 정보를 조회
print(myPackage.__cached__)

# 10. 현재 디버그 모드인지
print(__debug__) # 디버그모드 = true

참고 영역(호출 영역)

모듈을 참고 할 때, 디렉토리(저장)의 위치를 신경써 주어야 합니다. 다만, 기본적으로 참고하는 루트가 있습니다.

  1. sys.builtin_module_names를 검색한다.
  2. 현재 디렉터리를 검색한다.
  3. PYTHONPATH (환경변수, 유저와 프로그램이 등록한 검색할 디렉터리 목록)
  4. installation-dependent default (site-packages directory)

builtin모듈과 PYTHONPATH는 다음과 같이 알 수 있습니다.

7_Module > 01-7_Sys.path.py
# built in 모듈
print(f"Mudule Names: {sys.builtin_module_names}")

# python path
for path in sys.path:
    print(path)

내장 함수

python이 기본 제공하는 함수에 대하여 https://docs.python.org/3/library/functions.html 참조합니다.

dir함수 모듈이 가진 항목(name)을 반환합니다.

7_Module > 02_Dir.py
# 내장 변수(예: __name__) 제외한 호출(import)된 모듈 조회
[item for item in dir() if not item.startswith("_")]

# 모듈을 호출하지 않고도 사용할 수 있는 함수 목록 조회
import builtins
[item for item in dir(builtins) if not item.startswith("_")]

# random모듈에 속한 모든 항목으로 리스트 작성
[item for item in dir(random) if not item.startswith("_")]

모듈을 호출하지 않고도 사용할 수 있는 내장 함수 목록은 다음과 같습니다.

ArithmeticError, AssertionError, AttributeError, BaseException, aseExceptionGroup, BlockingIOError, BrokenPipeError, BufferError, BytesWarning, ChildProcessError, ConnectionAbortedError, ConnectionError, ConnectionRefusedError, ConnectionResetError, DeprecationWarning, EOFError, Ellipsis, EncodingWarning, EnvironmentError, Exception, ExceptionGroup, False, FileExistsError, FileNotFoundError, FloatingPointError, FutureWarning, GeneratorExit, IOError, ImportError, ImportWarning, IndentationError, IndexError, InterruptedError, IsADirectoryError, KeyError, KeyboardInterrupt, LookupError, MemoryError, ModuleNotFoundError, NameError, None, NotADirectoryError, NotImplemented, NotImplementedError, OSError, OverflowError, PendingDeprecationWarning, PermissionError, ProcessLookupError, RecursionError, ReferenceError, ResourceWarning, RuntimeError, RuntimeWarning, StopAsyncIteration, StopIteration, SyntaxError, SyntaxWarning, SystemError, SystemExit, TabError, TimeoutError, True, TypeError, UnboundLocalError, UnicodeDecodeError, UnicodeEncodeError, UnicodeError, UnicodeTranslateError, UnicodeWarning, UserWarning, ValueError, Warning, WindowsError, ZeroDivisionError, abs, aiter, all, anext, any, ascii, bin, bool, breakpoint, bytearray, bytes, callable, chr, classmethod, compile, complex, copyright, credits, delattr, dict, dir, divmod, enumerate, eval, exec, exit, filter, float, format, frozenset, getattr, globals, hasattr, hash, help, hex, id, input, int, isinstance, issubclass, iter, len, license, list, locals, map, max, memoryview, min, next, object, oct, open, ord, pow, print, property, quit, range, repr, reversed, round, set, setattr, slice, sorted, staticmethod, str, sum, super, tuple, type, vars, zip

내장 함수 예제

math

7_Module > 03_Math.py
abs(-5) # 절대값
pow(4, 2) # 4^2
max(5, 12) # 최대값
min(5, 12) # 최소값
round(3.14)  # 반올림

# math모듈을 import해야 하는 함수
from math import *

floor(4.99)  # 내림
ceil(3.14)  # 올림
sqrt(2)  # 제곱근
      

random 난수발생

7_Module > 04_Random.py
from random import *

random()  # 난수 생성(0 ~ 1사이의 실수 생성)

int(random() * 10)  # 0 ~ 9까지 정수

randrange(1, 10)  # 1 <= x < 10
randrange(1, 10, 2)  # 1 <= x < 10, 홀수만

randint(1, 10)  # 1 <= x <= 10
        

시스템 날짜

7_Module > 05_date.py 7_Module > 05-1_day of week.py 7_Module > 05-2_between.py 7_Module > 05-3_number_of_week.py 7_Module > 05-4_time zone.py
import datetime

# 현재 시간 가져오기
current_time = datetime.datetime.now() # 예: 2024-11-28 18:31:30.848214
current_date = datetime.datetime.now().date() # 날짜만
current_time = datetime.datetime.now().time() # 시간만

# 현재 요일 가져오기
current_day = datetime.datetime.now().strftime('%A')
day_number = datetime.datetime.now().strftime('%w') # 요일을 숫자로 (e.g., Sunday = 0, Saturday = 6)

# 두 날짜간의 차이를 구함
past_date = datetime.date(2000, 1, 1)
today_date = datetime.date.today() # 오늘
difference_from_today = today_date - past_date

# 국제표준(ISO 8601 week number)에 의한 몇 번째 주인지 확인
iso_year, iso_week, iso_weekday = current_date.isocalendar()

# 협정 세계시(UTC, Universal Time Coordinated)
# 예) UTC offset: UTC-9:00
time_zone_offset = time.altzone if time.daylight else time.timezone
offset_hours = time_zone_offset // 3600
offset_minutes = (time_zone_offset % 3600) // 60
print(f"UTC offset: UTC{offset_hours:02d}:{offset_minutes:02d}")

# 시스템 타임존
# 예) "대한민국 일광 절약 시간"
time_zone_name = time.tzname[0] if time.daylight else time.tzname[1]

파일

7_Module > 06_File.py
f = open("test.txt", "a+", encoding="utf8")

f.write("안녕하세요\n") # 파일 마지막에 추가 작성됨

f.seek(0) # 읽기 커서를 처음으로 이동시킴
print(f.read())

f.close() # 중요: 시스템 자원을 반드시 반환해야 함

더 안전하게..

with를 이용해서 코딩하면 f.close()를 이용해서 리소스 반환을 하지 않아도 되어서 편리하고, 코드 누락이나 에러로 인한 비정상 이탈로 메모리 누수를 걱정하지 않아도 됩니다.

7_Module > 06-1_With.py
with open("test.txt", "a+", encoding="utf8") as f:
    f.write("안녕하세요.\n")
    f.seek(0)
    print(f.read())

json 파일

json파일은 콜렉션(자료구조)를 text형식으로 저장하기 위한 방식(및 파일)로, 파싱(parsing, 구조나 의미를 파악하는 과정)의 효율을 극대화 할 수 있습니다.

7_Module > 06-4_Json.py
with open(file_path, "w+", encoding="utf-8") as f:
    json.dump(dir, f)

    f.seek(0)
    data = f.read()

json파일의 예시

{
  "dir_name": "example_directory",
  "files": [
    {
      "file_name": "file1.txt",
      "size": 1024
    },
    {
      "file_name": "file2.txt",
      "size": 2048
    },
    {
      "file_name": "file3.txt",
      "size": 512
    }
  ]
}

직렬화

직렬화(Serialization)란 Python 객체를 바이트 스트림으로 변환하여 저장하거나 전송 가능하도록 만드는 과정입니다.

7_Module > 06-5_Pickle.py
import pickle

profile_file = open("profile.pickle", "wb")
profile = {"이름": "이방원", "나이": 30, "취미": ["축구", "골프", "코딩"]}
pickle.dump(profile, profile_file)
profile_file.close()

예외8_Exception

예외처리란, 프로그램의 의도하지 않은 동작에 대하여, 해결방법을 제시하거나 오류 내용을 보고하도록 처리하는 기법입니다.
오류에 대한 처리를 하지 않으면, 오류가 발생하는 순간 프로그램을 종료합니다.

오류의 종류

8_Exception > 8_Exception.py
a = 1 / 0  # ZeroDivisionError: 0 으로 나누려 할 때

# 이 후 실행되지 않음
a = b + 1 # NameError: 정의되지 않은 이름이라 처리 할 수 없을 때

a = "2" + 2 # TypeError: 변수의 타입으로 인해 의도가 모호할 때 "22" vs 4

오류 처리

오류가 예상되는 부분은 try로 작성하고, 오류가 발생했을 때 처리될 부분을 except로 작성합니다.

8_Exception > 02_try.py
try:
    a = 1 / 0  # "0으로 나눔 오류"발생
except ZeroDivisionError:
    print("에러: 0으로 나눌 수 없습니다.")
        

try구문 안에 있는 함수 내부에서 발생하는 오류도 잡아낼 수 있습니다.

8_Exception > 02-1_indirectly work.py
def this_fails():
    x = 1 / 0

try:
    this_fails()
except ZeroDivisionError as err:
    print("에러 내용:", err)
      

모든 에러가 처리된 후에 수행할 내용은 finally에 기재합니다(오류의 발생 여부와는 상관 없습니다).

8_Exception > 03_Finally.py
try:
    raise KeyboardInterrupt
except KeyboardInterrupt:
    print("Caught a KeyboardInterrupt!")
finally:
    print("Goodbye, world!")

고의적 발생

raise키워드를 이용하여, 고의로 예외를 발생 시킬 수 있습니다. 고의적 발생은 '선제적 대응'이 가능하며, '오류에 대한 추가 정보'를 수집할 수 있습니다.

8_Exception > 04_Raise.py
try:
    raise Exception("spam", "eggs")
except Exception as inst:
    print(inst)
    

예외 연쇄

연쇄적 발생을 통해, 수집된 정보를 외부에서 더 상세하게 체크 할 수 있습니다.

8_Exception > 04-1_Raise chaining.py
def func():
    raise ConnectionError

try:
    func()
except ConnectionError as exc:
    raise RuntimeError("Failed to open database") from exc

사용자 정의 예외

예외처리 구조를 빌려와 사용자 정의 에러를 선언하고 사용할 수 있습니다.

8_Exception > 05-User Exception.py
class AppError(Exception): # 기존 예외를 커스터마이징
    def __init__(self, message, code=500):
        super().__init__(message)
        self.code = code

class DatabaseError(AppError): # ..를 다시 커스터마이징한 사용자 에러
    def __init__(self, message, query=None):
        super().__init__(message, code=500)
        self.query = query

try:
    raise DatabaseError("DB 연결 실패", query="SELECT * FROM users") # 사용자 에러 발생
except DatabaseError as e:
    print(f"에러: {e}, 코드: {e.code}, 쿼리: {e.query}") # 처리

예외 그룹

병령구조의 프로그램등에서 예외 발생시 바로 예외를 수행하기 보다, 우선 수행한 후, 한 번에 예외를 처리해야한다고 할 때, group으로 처리 할 수 있습니다.

8_Exception > 06-ExceptionGroup.py
def f():
  raise ExceptionGroup(
      "group1",
      [
          OSError(1),
          SystemError(2),
          ExceptionGroup(
              "group2",
              [
                  OSError(3),
                  RecursionError(4)
              ]
          )
      ]
  )

try:
    f()
except* OSError as e:
    print("There were OSErrors")
except* SystemError as e:
    print("There were SystemErrors")

출력 결과

There were OSErrors
There were SystemErrors
  + Exception Group Traceback (most recent call last):
  |   File "", line 2, in 
  |     f()
  |     ~^^
  |   File "", line 2, in f
  |     raise ExceptionGroup(
  |     ...<12 lines>...
  |     )
  | ExceptionGroup: group1 (1 sub-exception)
  +-+---------------- 1 ----------------
    | ExceptionGroup: group2 (1 sub-exception)
    +-+---------------- 1 ----------------
      | RecursionError: 4
      +------------------------------------

add note

에러가 발생한 후에 정보를 추가 할 수 있습니다.

8_Exception > 07_Add note.py
try:
    raise TypeError("bad type")
except Exception as e:
    e.add_note("Add some information") # 정보 추가 1
    e.add_note("Add some more information") # 정보 추가 2
    raise
8_Exception > 07-1_Add note2.py
def f():
    raise OSError("operation failed")

excs = []
for i in range(3):
    try:
        f()
    except Exception as e:
        e.add_note(f"Happened in Iteration {i+1}")
        excs.append(e) # 추가할 내용을 list로 작성

raise ExceptionGroup("We have some problems", excs)
이전 | 모듈 & 에러처리 | 다음