본문 바로가기
GD's IT Lectures : 기초부터 시리즈/파이썬(Python) 기초부터 ~

[파이썬(PYTHON) : 중급] 모듈과 패키지 고급 활용

by GDNGY 2023. 5. 9.

4. 모듈과 패키지 고급 활용

모듈과 패키지 고급 활용에서는 파이썬 라이브러리를 설치하고 관리하는 방법을 배우며, 유용한 라이브러리를 활용하여 코드를 효율적으로 작성하는 방법을 익힙니다. 또한 자신만의 모듈을 작성하여 배포하는 방법도 알아봅니다.

4.1 모듈 고급 활용

4.1.1 모듈 변수와 모듈 함수

모듈은 파이썬 코드를 논리적으로 구조화하는 데 사용되며, 변수와 함수를 포함할 수 있습니다.

 

4.1.1.1 모듈 변수의 선언과 활용

모듈 변수는 모듈 내부에서 선언되며, 해당 모듈 내부의 모든 함수에서 접근이 가능합니다.

# mymodule.py
module_variable = "I am a module variable"

def print_variable():
    print(module_variable)

 

위의 mymodule.py에서, module_variable이라는 모듈 변수를 선언하고, print_variable 함수 내에서 이를 출력하고 있습니다.

 

4.1.1.2 모듈 함수의 선언과 활용

모듈 함수는 모듈 내부에서 선언되며, 모듈이 임포트 된 후에 호출할 수 있습니다.

# mymodule.py
def print_hello():
    print("Hello, world!")

이 예시에서, print_hello라는 함수를 선언하였습니다. 이 함수는 "Hello, world!"를 출력합니다.

 

4.1.1.3 모듈 변수와 함수의 접근 제어

파이썬에는 직접적인 접근 제어 메커니즘이 없지만, 일반적으로 변수나 함수 이름 앞에 밑줄(_)을 붙여 '비공개'로 표시합니다.

 

4.1.2 모듈의 이름 공간(namespace)

모듈의 이름 공간은 모듈 내부의 모든 변수와 함수를 포함하는 '공간'을 의미합니다.

 

4.1.2.1 이름 공간의 이해

이름 공간은 변수와 함수가 고유한 이름을 가질 수 있도록 하는 메커니즘입니다. 이름 공간은 각 모듈, 클래스, 객체 등에 고유하게 존재합니다.

 

4.1.2.2 이름 공간과 스코프

스코프는 이름 공간에 접근할 수 있는 범위를 의미합니다. 파이썬에서는 로컬 스코프, 전역 스코프, 내장 스코프 등이 있습니다.

 

4.1.2.3 이름 충돌 방지 방법

이름 충돌은 서로 다른 이름 공간에서 동일한 이름을 가진 변수나 함수가 있을 때 발생합니다. 이는 모듈 이름을 앞에 붙여서 해결할 수 있습니다. 예를 들어, module1과 module2라는 두 모듈이 모두 print_hello 함수를 가지고 있다면, 다음과 같이 사용할 수 있습니다.

# main.py
import module1
import module2

module1.print_hello()  # module1의 print_hello 함수 호출
module2.print_hello()  # module2의 print_hello 함수 호출

 

4.1.3 모듈의 상대경로와 절대경로

상대 경로와 절대 경로는 모듈을 임포트할 때 사용되는 경로입니다.

 

4.1.3.1 상대경로와 절대경로의 이해

  • 절대 경로는 모듈이 시스템에서 위치한 전체 경로입니다.
  • 상대 경로는 현재 모듈에 대한 모듈의 위치를 나타냅니다.

 

4.1.3.2 상대경로와 절대경로 활용 예시

# 절대 경로
import mypackage.mymodule

# 상대 경로
from . import mymodule

 

4.1.4 모듈을 이용한 코드의 재사용

모듈은 코드의 재사용성을 높이는데 매우 유용합니다.

 

4.1.4.1 모듈 재사용의 장점

  • 코드 중복을 줄일 수 있습니다.
  • 코드 유지보수를 편리하게 할 수 있습니다.
  • 코드의 가독성을 높일 수 있습니다.

 

4.1.4.2 모듈 재사용 사례

다음 예시는 math 모듈을 임포트하여 다양한 수학 함수를 사용하는 방법을 보여줍니다.

import math

print(math.pi)  # 원주율 출력
print(math.sqrt(16))  # 제곱근 출력

 

4.1.5 모듈의 임포트 방법과 성능 이슈

모듈을 임포트하는 방법에는 여러 가지가 있으며, 각각의 방법은 성능에 다른 영향을 미칩니다.

 

4.1.5.1 다양한 임포트 방식

# 전체 모듈을 임포트
import math

# 특정 함수만 임포트
from math import sqrt

# 모든 함수를 임포트
from math import *

 

4.1.5.2 임포트 성능 이슈와 해결방안

모듈을 임포트할 때, 모듈의 크기가 크거나 필요하지 않은 부분을 임포트하면 성능 이슈가 발생할 수 있습니다. 이러한 문제는 필요한 부분만 임포트하는 방법으로 해결할 수 있습니다. 예를 들어, math 모듈의 sqrt 함수만 필요하다면, from math import sqrt와 같이 sqrt 함수만 임포트 할 수 있습니다. 

# 전체 모듈을 임포트하는 경우
import math
print(math.sqrt(16))

# 필요한 함수만 임포트하는 경우
from math import sqrt
print(sqrt(16))

 

두 번째 방식은 math 모듈의 sqrt 함수만 메모리에 로드하기 때문에 메모리 사용량을 줄일 수 있습니다.

 

4.2 패키지 고급 활용

4.2.1 패키지와 모듈의 차이점

4.2.1.1 패키지와 모듈의 정의

파이썬에서 모듈은 파이썬 코드가 들어 있는 .py 파일을 말합니다. 이 파일 안에는 함수, 클래스, 변수 등이 포함되어 있습니다. 반면에 패키지는 여러 모듈들을 묶어 놓은 것으로, 보통 여러 개의 .py 파일(모듈)과 __init__.py 파일이 있는 디렉토리로 이루어져 있습니다.

 

4.2.1.2 패키지와 모듈의 사용 케이스

패키지와 모듈은 코드의 재사용성을 높이고 관리를 용이하게 합니다. 모듈은 비슷한 기능을 가진 코드를 한 곳에 모아놓는 역할을 하며, 패키지는 이런 모듈들을 구조적으로 관리할 수 있게 돕습니다.

# 모듈 임포트 방법
import my_module

# 패키지 내부의 모듈 임포트 방법
from my_package import my_module

 

4.2.2 패키지 내부의 모듈을 임포트하는 방법

4.2.2.1 패키지 내부 모듈 임포트 방법

패키지 내부의 모듈을 임포트하려면 from 패키지명 import 모듈명 형식을 사용합니다. 또는 import 패키지명.모듈명 형식을 사용할 수도 있습니다.

# 예시
from my_package import my_module
import my_package.my_module

 

4.2.2.2 패키지 내부 모듈 임포트 주의점

패키지 내부의 모듈을 임포트할 때는 절대 경로를 사용하는 것이 좋습니다. 상대 경로를 사용하면 패키지의 구조가 변경될 경우 임포트 문장도 수정해야 하는 문제가 발생할 수 있습니다.

 

4.2.3 init.py 파일과 패키지의 초기화

4.2.3.1 init.py 파일의 역할

__init__.py 파일은 패키지의 초기화를 담당합니다. 이 파일이 있는 디렉토리는 파이썬에게 패키지로 인식되게 하며, 패키지가 임포트 될 때 실행됩니다. 이 파일에는 패키지의 초기화 코드 또는 패키지가 제공해야 하는 모듈을 불러오는 코드를 넣을 수 있습니다.

 

4.2.3.2 패키지 초기화 방법

패키지를 초기화하는 방법은 간단합니다. 패키지 디렉토리에 __init__.py 파일을 생성하면 됩니다. 이 파일이 없으면 파이썬은 해당 디렉토리를 패키지로 인식하지 않습니다. __init__.py 파일 안에는 패키지를 임포트 할 때 실행할 코드를 작성합니다.

# __init__.py

print("이 패키지가 초기화됩니다.")

 

4.2.4 상대경로와 절대경로를 이용한 패키지 내부 모듈 임포트

4.2.4.1 상대경로를 이용한 임포트 방법

패키지 내부에서 다른 모듈을 임포트할 때는 상대경로를 사용할 수 있습니다. 상대경로는 현재 모듈이 있는 위치를 기준으로 계산되며, '.'은 현재 디렉토리, '..'은 상위 디렉토리를 의미합니다.

# my_package 모듈 내부에서 상대경로를 이용한 임포트
from . import my_module

 

4.2.4.2 절대경로를 이용한 임포트 방법

절대경로를 이용한 임포트는 모듈이 위치한 전체 경로를 기준으로 합니다. 절대경로는 패키지의 구조가 변경되더라도 임포트 문장을 수정하지 않아도 되는 이점이 있습니다.

# my_package 모듈 내부에서 절대경로를 이용한 임포트
import my_package.my_module

 

4.2.5 패키지 구조 설계와 코드 구조화 방법

4.2.5.1 패키지 구조의 설계 원칙

패키지를 설계할 때는 관련성이 있는 모듈끼리 묶고, 패키지와 모듈의 이름을 명확하게 지어야 합니다. 그리고 모듈 간의 의존성을 최소화하는 것이 좋습니다.

 

4.2.5.2 코드 구조화를 위한 패키지 활용 방법

패키지를 이용하면 코드를 구조적으로 관리할 수 있습니다. 예를 들어, 웹 애플리케이션을 개발한다면, models, views, controllers 등의 패키지로 코드를 분리할 수 있습니다.

 

4.2.5.3 대형 프로젝트에서의 패키지 활용 사례

대형 프로젝트에서는 패키지를 활용하여 코드를 모듈화하고 구조화하는 것이 일반적입니다. 예를 들어, Django라는 파이썬 웹 프레임워크에서는 프로젝트를 여러 앱으로 분리하고, 각 앱 내부에는 models, views, templates 등의 패키지를 만듭니다. 이런 방식을 사용하면 코드의 재사용성이 높아지고 유지 보수가 용이해집니다.

# Django 프로젝트 구조 예시
my_django_project/
    app1/
        __init__.py
        models.py
        views.py
        templates/
    app2/
        __init__.py
        models.py
        views.py
        templates/

 

이처럼 패키지와 모듈을 활용하면 프로젝트의 구조를 체계적으로 관리할 수 있습니다. 패키지는 관련된 모듈들을 하나로 묶는 역할을 하며, 모듈은 하나의 .py 파일 내에 코드를 정리하는 역할을 합니다. 

 

4.3 모듈과 패키지의 활용 방법

4.3.1 모듈을 이용한 CLI(Command Line Interface) 프로그래밍

4.3.1.1 CLI 프로그래밍의 기본 개념

CLI(Command Line Interface)는 사용자와 컴퓨터 사이의 대화형 인터페이스를 의미합니다. 사용자는 명령어를 입력하여 프로그램을 실행하거나 시스템을 제어하게 됩니다. 파이썬에서는 argparse 모듈을 사용하여 CLI 프로그램을 쉽게 만들 수 있습니다.

# 예제 코드 - argparse 기본 사용법
import argparse

parser = argparse.ArgumentParser(description='CLI 프로그래밍 예제.')
parser.add_argument('--name', type=str, required=True, help='당신의 이름을 입력하세요.')
args = parser.parse_args()

print(f'안녕하세요, {args.name}님!')

 

이 코드를 실행하려면 커맨드 라인에서 다음과 같이 실행하면 됩니다.

python example.py --name "홍길동"

 

4.3.1.2 모듈을 이용한 CLI 프로그래밍 사례

argparse 모듈은 파이썬 표준 라이브러리로, CLI 인터페이스를 쉽게 만들 수 있게 돕습니다. 이 모듈을 이용하여 사용자로부터 입력받은 인자를 처리할 수 있습니다.

# 예제 코드 - argparse를 이용한 CLI 프로그래밍
import argparse

def calculate(args):
    if args.operation == 'add':
        return args.x + args.y
    elif args.operation == 'subtract':
        return args.x - args.y

parser = argparse.ArgumentParser()
parser.add_argument('x', type=int, help='첫 번째 숫자')
parser.add_argument('y', type=int, help='두 번째 숫자')
parser.add_argument('operation', type=str, help='연산자 (add, subtract)')

args = parser.parse_args()
result = calculate(args)

print(result)

 

이 프로그램은 사용자로부터 두 개의 숫자와 연산자를 입력 받아, 해당 연산을 수행한 결과를 출력합니다.

 

4.3.1.3 CLI 프로그래밍 시 주의사항

CLI 프로그래밍 시에는 몇 가지 주의사항이 있습니다.

 

  • 에러 처리: 사용자가 잘못된 입력을 할 경우를 대비하여 적절한 에러 처리가 필요합니다.
  • 도움말 작성: -h 또는 --help 옵션을 통해 도움말을 제공해야 합니다. argparse 모듈은 이를 자동으로 제공합니다.
  • 명확한 인터페이스 설계: 사용자가 이해하기 쉬운 인터페이스를 제공해야 합니다. 복잡한 인터페이스는 사용자가 프로그램을 이해하기 어렵게 만듭니다.

 

4.3.2 모듈과 패키지를 이용한 프로그램 설계 방법

4.3.2.1 모듈과 패키지를 활용한 설계 원칙

모듈과 패키지는 코드의 재사용성을 높이고, 코드의 구조를 체계적으로 관리하는 데 도움을 줍니다. 다음은 모듈과 패키지를 활용한 프로그램 설계 원칙입니다:

  • 단일 책임 원칙(Single Responsibility Principle): 하나의 모듈 또는 패키지는 하나의 기능만 수행해야 합니다.
  • 고립성(Isolation): 모듈이나 패키지는 가능한 독립적으로 동작하도록 설계되어야 합니다. 이를 통해 다른 코드와의 의존성을 최소화하고, 코드의 수정이나 확장을 용이하게 합니다.
# good.py
def add(a, b):
    return a + b

def subtract(a, b):
    return a - b

# bad.py
def calculate(a, b, operation):
    if operation == 'add':
        return a + b
    elif operation == 'subtract':
        return a - b

 

위 예제에서 good.py는 각 함수가 단일 책임 원칙을 따르고 있습니다. 반면에 bad.py는 calculate 함수가 두 가지 책임을 가지고 있습니다.

 

4.3.2.2 설계 패턴 사례

설계 패턴은 특정 문제를 해결하기 위해 재사용 가능한 형태로 코드를 구조화하는 일련의 기법입니다. 파이썬에서는 다양한 설계 패턴이 있습니다. 이 중 팩토리 패턴은 객체 생성 로직을 메서드에 응집시키는 것입니다.

# factory pattern example
class Dog:
    def __init__(self, name):
        self.name = name

    def speak(self):
        return 'Woof!'

class Cat:
    def __init__(self, name):
        self.name = name

    def speak(self):
        return 'Meow!'

def get_pet(pet='dog'):
    pets = dict(dog=Dog('Hope'), cat=Cat('Peace'))
    return pets[pet]

d = get_pet('dog')
print(d.speak())

c = get_pet('cat')
print(c.speak())

 

이 코드는 'dog' 또는 'cat' 문자열을 입력받아 각각 Dog 클래스의 인스턴스 또는 Cat 클래스의 인스턴스를 반환하는 팩토리 함수 get_pet을 정의하고 있습니다. 이런 식으로 팩토리 패턴은 객체 생성 로직을 하나의 함수나 메서드에 집중시킴으로써 코드의 유연성과 재사용성을 높이는 데 도움이 됩니다. 이렇게 객체를 생성하는 로직을 별도의 메서드로 분리함으로써, 객체 생성 방식의 변경이나 추가가 있을 때 해당 메서드만 수정하면 되므로 유지 보수가 용이해집니다.

 

4.3.3 모듈과 패키지를 이용한 코드 리팩토링 방법

4.3.3.1 리팩토링의 필요성

리팩토링은 기존의 코드를 개선하여 가독성을 높이고, 중복을 줄이며, 코드의 구조를 개선하는 작업입니다. 리팩토링은 코드의 기능을 변경하지 않는 방식으로 이루어져야 합니다. 리팩토링이 필요한 이유는 다음과 같습니다:

  • 코드의 가독성 향상
  • 코드의 유지보수성 향상
  • 중복 코드 제거

4.3.3.2 모듈과 패키지를 활용한 리팩토링 방법

모듈과 패키지를 활용하면 코드를 더욱 체계적으로 구성할 수 있습니다. 각 모듈은 하나의 기능을 담당하고, 패키지는 여러 모듈을 묶어 관리합니다.

 

예를 들어, '사용자 인증', '데이터 처리', '결과 출력' 등의 기능이 있는 프로그램이 있다면, 각 기능을 별도의 모듈로 분리하고, 이 모듈들을 하나의 패키지 안에 넣어 관리할 수 있습니다. 이렇게 함으로써 각 기능 간의 의존성을 줄이고, 코드의 가독성과 유지보수성을 향상할 수 있습니다.

# authentication.py
def authenticate(user):
    pass  # 사용자 인증 로직

# data_processing.py
def process_data(data):
    pass  # 데이터 처리 로직

# output.py
def output_result(result):
    pass  # 결과 출력 로직

# main.py
import authentication
import data_processing
import output

def main():
    user = authentication.authenticate('user1')
    data = data_processing.process_data(user)
    output.output_result(data)

if __name__ == "__main__":
    main()

 

위의 코드는 각 기능을 별도의 모듈로 분리하여 코드의 구조를 개선하는 한 예입니다. 이렇게 리팩토링하면 각 모듈이 단일 책임 원칙을 따르게 되고, 코드의 가독성과 유지보수성이 향상됩니다.

 

4.3.3.3 리팩토링 사례

리팩토링은 코드의 가독성과 유지보수성을 향상시키는 중요한 과정입니다. 다음은 간단한 리팩토링 예시입니다.

# Before Refactoring
def calculate_total_price(prices):
    total = 0
    for price in prices:
        total += price
    return total

def apply_discount(total, discount):
    return total - (total * discount / 100)

prices = [100, 200, 300]
discount = 10
total = calculate_total_price(prices)
final_price = apply_discount(total, discount)
print(final_price)

 

이 코드는 물건 가격의 리스트와 할인율을 받아 최종 가격을 계산하는 코드입니다. 이 코드를 더욱 가독성 있게 리팩터링해 보겠습니다.

# After Refactoring
class Order:
    def __init__(self, prices, discount):
        self.prices = prices
        self.discount = discount

    def total(self):
        return sum(self.prices)

    def final_price(self):
        return self.total() - (self.total() * self.discount / 100)

order = Order([100, 200, 300], 10)
print(order.final_price())

 

리팩토링 후의 코드는 Order라는 클래스를 이용해 코드를 조직화했습니다. 이렇게 하면 코드의 가독성이 향상되고, 관련 있는 데이터와 메서드를 함께 그룹화할 수 있습니다.

 

4.3.4 모듈과 패키지를 이용한 유닛 테스트 방법

4.3.4.1 유닛 테스트의 정의와 중요성

유닛 테스트는 소프트웨어의 가장 작은 단위를 테스트하는 방법입니다. 파이썬에서는 unittest 모듈을 이용해 유닛 테스트를 수행할 수 있습니다. 유닛 테스트는 다음과 같은 이유로 중요합니다:

  • 코드의 신뢰성 확보: 코드가 정상적으로 동작하는지 확인하고, 기대하는 결과를 반환하는지 검증합니다.
  • 코드의 유지보수성 향상: 코드를 수정하거나 추가할 때, 테스트를 통해 기존 기능이 올바르게 동작하는지 확인할 수 있습니다.
  • 문서의 역할: 테스트 코드는 해당 코드나 함수가 어떤 기능을 하는지, 어떻게 사용해야 하는지를 보여주는 예제 코드의 역할을 합니다.

4.3.4.2 모듈과 패키지를 이용한 유닛 테스트 기법

unittest 모듈을 사용하면 각 함수나 메서드에 대한 유닛 테스트를 쉽게 작성할 수 있습니다. 각 테스트는 TestCase 클래스의 메서드로 정의되며, assert 메서드를 사용해 예상한 결과와 실제 결과를 비교합니다.

 

다음은 간단한 유닛 테스트의 예시입니다.

# calculator.py
def add(a, b):
    return a + b

# test_calculator.py
import unittest
import calculator

class TestCalculator(unittest.TestCase):
    def test_add(self):
        result = calculator.add(1, 2)
        self.assertEqual(result, 3)

if __name__ == '__main__':
    unittest.main()

 

위의 코드는 add 함수에 대한 유닛 테스트를 정의한 것입니다. TestCalculator 클래스는 unittest.TestCase를 상속받아, test_add 메서드에서 add 함수의 결과를 검증합니다. unittest.main()을 통해 모든 테스트를 실행합니다.

 

4.3.4.3 유닛 테스트 사례

다음은 모듈과 패키지를 이용한 유닛 테스트 사례입니다.

# string_processing.py
def is_palindrome(s):
    return s == s[::-1]

# test_string_processing.py
import unittest
import string_processing

class TestStringProcessing(unittest.TestCase):
    def test_is_palindrome(self):
        self.assertTrue(string_processing.is_palindrome('radar'))
        self.assertFalse(string_processing.is_palindrome('python'))

if __name__ == '__main__':
    unittest.main()

 

위의 코드는 is_palindrome 함수에 대한 유닛 테스트를 정의한 것입니다. assertTrue와 assertFalse 메서드를 사용해 예상한 결과와 실제 결과를 검증합니다.

 

4.3.5 모듈과 패키지를 이용한 빌드, 배포, 패키징 방법

4.3.5.1 파이썬 빌드 시스템의 이해

파이썬의 빌드 시스템은 파이썬 프로젝트를 패키지로 만들어 배포할 수 있게 해주는 도구들을 포함합니다. setuptools, distutils, wheel 등의 패키지가 이에 속합니다.

 

setup.py 파일은 파이썬 패키지의 빌드 설정을 담당하며, 패키지의 이름, 버전, 필요한 의존성 등을 정의합니다.

 

4.3.5.2 모듈과 패키지를 이용한 배포 전략

파이썬 패키지를 배포하는 방법에는 여러 가지가 있지만, 일반적으로 PyPI(Python Package Index)를 통해 배포합니다. PyPI는 파이썬 패키지를 공개적으로 호스팅하는 서비스입니다.

 

twine이라는 도구를 사용하면 패키지를 PyPI에 업로드할 수 있습니다. 먼저, setup.py, README.md, LICENSE 등 필요한 파일을 준비한 후 python setup.py sdist bdist_wheel 명령을 실행하여 소스 배포 및 바이너리 배포를 생성합니다. 그 다음 twine upload dist/* 명령을 실행하여 PyPI에 패키지를 업로드합니다.

# setup.py
from setuptools import setup, find_packages

setup(
    name='your-package-name',
    version='0.1.0',
    packages=find_packages(),
    include_package_data=True,
    install_requires=[
        # package dependencies
    ],
)

 

위의 setup.py 예시에서는 setuptools의 setup 함수를 사용하여 패키지의 기본 정보와 의존성을 정의하였습니다.

 

4.3.5.3 모듈과 패키지를 이용한 패키징 방법

파이썬의 패키징은 코드, 리소스 파일, 실행 가능한 프로그램 등을 하나의 배포 가능한 단위로 묶는 것을 의미합니다. 이를 위해 setuptools를 주로 사용합니다.

 

위에서 언급한 setup.py 파일을 통해 패키지의 정보를 정의하고, python setup.py sdist bdist_wheel 명령을 실행하여 패키지를 생성합니다. 생성된 패키지는 dist 디렉토리에 저장되며, 이를 PyPI에 업로드하거나 사용자에게 직접 배포할 수 있습니다.

 

이렇게 모듈과 패키지를 활용하여 코드를 체계적으로 구성하고, 테스트하며, 배포하는 방법을 익히면 보다 효율적인 프로그래밍이 가능합니다.

 

 

 

2023.05.09 - [프로그래밍/파이썬(Python) 기초부터 ~] - [파이썬(PYTHON) : 중급] 객체 지향 프로그래밍

 

[파이썬(PYTHON) : 중급] 객체 지향 프로그래밍

3. 객체 지향 프로그래밍 객체 지향 프로그래밍에서는 클래스와 인스턴스 개념을 학습하며, 생성자와 소멸자, 상속과 다형성 등 객체 지향의 기본 원리를 이해합니다. 메서드 오버라이딩과 오버

gdngy.tistory.com

 

반응형

댓글