심볼 제어 및 하위 모듈 결합

Intermediate

This tutorial is from open-source community. Access the source code

소개

이 랩에서는 Python 패키지 구성과 관련된 중요한 개념을 배우게 됩니다. 먼저, Python 모듈에서 __all__을 사용하여 내보낼 심볼을 제어하는 방법을 배우게 됩니다. 이 기술은 모듈에서 노출되는 내용을 관리하는 데 매우 중요합니다.

둘째, 하위 모듈을 결합하여 더 간단한 임포트를 수행하는 방법과 더 나은 코드 구성을 위해 모듈 분할 기술을 익히게 됩니다. 이러한 실습은 Python 코드의 가독성과 유지 관리성을 향상시킬 것입니다.

이것은 가이드 실험입니다. 학습과 실습을 돕기 위한 단계별 지침을 제공합니다.각 단계를 완료하고 실무 경험을 쌓기 위해 지침을 주의 깊게 따르세요. 과거 데이터에 따르면, 이것은 중급 레벨의 실험이며 완료율은 62%입니다.학습자들로부터 100%의 긍정적인 리뷰율을 받았습니다.

패키지 임포트 복잡성 이해

Python 패키지 작업을 시작하면 모듈을 임포트하는 것이 상당히 복잡하고 장황해질 수 있다는 것을 빠르게 깨닫게 될 것입니다. 이러한 복잡성은 코드를 읽고 쓰기 어렵게 만들 수 있습니다. 이 랩에서는 이 문제에 대해 자세히 살펴보고 임포트 프로세스를 단순화하는 방법을 배우겠습니다.

현재 임포트 구조

먼저, 터미널을 열어보겠습니다. 터미널은 컴퓨터의 운영 체제와 상호 작용할 수 있는 강력한 도구입니다. 터미널이 열리면 프로젝트 디렉토리로 이동해야 합니다. 프로젝트 디렉토리는 Python 프로젝트와 관련된 모든 파일이 저장되는 곳입니다. 이를 위해 "change directory"를 의미하는 cd 명령을 사용합니다.

cd ~/project

이제 프로젝트 디렉토리에 있으므로 structly 패키지의 현재 구조를 살펴보겠습니다. Python 에서 패키지는 관련 모듈을 구성하는 방법입니다. ls -la 명령을 사용하여 숨겨진 파일을 포함하여 structly 패키지 내의 모든 파일과 디렉토리를 나열할 수 있습니다.

ls -la structly

structly 패키지 내에 여러 Python 모듈이 있는 것을 확인할 수 있습니다. 이러한 모듈에는 코드에서 사용할 수 있는 함수와 클래스가 포함되어 있습니다. 그러나 이러한 모듈의 기능을 사용하려면 현재 긴 임포트 문을 사용해야 합니다. 예를 들어:

from structly.structure import Structure
from structly.reader import read_csv_as_instances
from structly.tableformat import create_formatter, print_table

이러한 긴 임포트 경로는 특히 코드에서 여러 번 사용해야 하는 경우 작성하기 번거로울 수 있습니다. 또한 코드를 덜 읽기 쉽게 만들며, 이는 코드를 이해하거나 디버깅하려는 경우 문제가 될 수 있습니다. 이 랩에서는 이러한 임포트를 더 간단하게 만드는 방식으로 패키지를 구성하는 방법을 배우겠습니다.

패키지의 __init__.py 파일의 내용을 먼저 살펴보겠습니다. __init__.py 파일은 Python 패키지에서 특별한 파일입니다. 패키지가 임포트될 때 실행되며, 패키지를 초기화하고 필요한 임포트를 설정하는 데 사용할 수 있습니다.

cat structly/__init__.py

__init__.py 파일이 비어 있거나 코드가 거의 없는 것을 발견할 수 있습니다. 다음 단계에서는 임포트 문을 단순화하기 위해 이 파일을 수정합니다.

목표

이 랩이 끝나면 훨씬 더 간단한 임포트 문을 사용할 수 있게 되는 것이 목표입니다. 앞에서 본 긴 임포트 경로 대신 다음과 같은 문을 사용할 수 있습니다.

from structly import Structure, read_csv_as_instances, create_formatter, print_table

또는 다음과 같이 사용할 수도 있습니다.

from structly import *

이러한 더 간단한 임포트 문을 사용하면 코드가 더 깔끔해지고 작업하기 쉬워집니다. 또한 코드를 작성하고 유지 관리할 때 시간과 노력을 절약할 수 있습니다.

__all__을 사용하여 내보낼 심볼 제어

Python 에서 from module import * 문을 사용할 때 모듈에서 어떤 심볼 (함수, 클래스, 변수) 을 임포트할지 제어하고 싶을 수 있습니다. 이럴 때 __all__ 변수가 유용합니다. from module import * 문은 모듈의 모든 심볼을 현재 네임스페이스로 임포트하는 방법입니다. 그러나 때로는 모든 심볼을 임포트하고 싶지 않을 수 있습니다. 특히 심볼이 많거나 일부 심볼이 모듈 내부에만 사용되도록 의도된 경우입니다. __all__ 변수를 사용하면 이 문을 사용할 때 정확히 어떤 심볼을 임포트해야 하는지 지정할 수 있습니다.

__all__이란 무엇인가?

__all__ 변수는 문자열의 리스트입니다. 이 리스트의 각 문자열은 from module import * 문을 사용할 때 모듈이 내보내는 심볼 (함수, 클래스 또는 변수) 을 나타냅니다. 모듈에 __all__ 변수가 정의되어 있지 않으면 import * 문은 밑줄로 시작하지 않는 모든 심볼을 임포트합니다. 밑줄로 시작하는 심볼은 일반적으로 모듈에 대한 private 또는 internal 로 간주되며 직접 임포트하도록 되어 있지 않습니다.

각 하위 모듈 수정

이제 structly 패키지의 각 하위 모듈에 __all__ 변수를 추가해 보겠습니다. 이렇게 하면 from module import * 문을 사용할 때 각 하위 모듈에서 어떤 심볼이 내보내지는지 제어하는 데 도움이 됩니다.

  1. 먼저, structure.py를 수정해 보겠습니다.
touch ~/project/structly/structure.py

이 명령은 프로젝트의 structly 디렉토리에 structure.py라는 새 파일을 만듭니다. 파일을 만든 후 __all__ 변수를 추가해야 합니다. 파일 맨 위, 임포트 문 바로 뒤에 이 줄을 추가합니다.

__all__ = ['Structure']

이 줄은 누군가 from structure import *를 사용할 때 Structure 심볼만 임포트하도록 Python 에 지시합니다. 파일을 저장하고 편집기를 종료합니다.

  1. 다음으로, reader.py를 수정해 보겠습니다.
touch ~/project/structly/reader.py

이 명령은 structly 디렉토리에 reader.py라는 새 파일을 만듭니다. 이제 파일에서 read_csv_as_로 시작하는 모든 함수를 찾습니다. 이러한 함수가 내보내려는 함수입니다. 그런 다음 이러한 모든 함수 이름을 사용하여 __all__ 리스트를 추가합니다. 다음과 같이 표시됩니다.

__all__ = ['read_csv_as_instances', 'read_csv_as_dicts', 'read_csv_as_columns']

실제 함수 이름은 파일에서 찾은 내용에 따라 다를 수 있습니다. 찾은 모든 read_csv_as_* 함수를 포함해야 합니다. 파일을 저장하고 편집기를 종료합니다.

  1. 이제 tableformat.py를 수정해 보겠습니다.
touch ~/project/structly/tableformat.py

이 명령은 structly 디렉토리에 tableformat.py라는 새 파일을 만듭니다. 파일 맨 위에 이 줄을 추가합니다.

__all__ = ['create_formatter', 'print_table']

이 줄은 누군가 from tableformat import *를 사용할 때 create_formatterprint_table 심볼만 임포트하도록 지정합니다. 파일을 저장하고 편집기를 종료합니다.

__init__.py에서 통합 임포트

이제 각 모듈이 내보내는 내용을 정의했으므로 __init__.py 파일을 업데이트하여 이러한 모든 심볼을 임포트할 수 있습니다. __init__.py 파일은 Python 패키지에서 특별한 파일입니다. 패키지가 임포트될 때 실행되며, 패키지를 초기화하고 하위 모듈에서 심볼을 임포트하는 데 사용할 수 있습니다.

touch ~/project/structly/__init__.py

이 명령은 structly 디렉토리에 새 __init__.py 파일을 만듭니다. 파일의 내용을 다음과 같이 변경합니다.

## structly/__init__.py

from .structure import *
from .reader import *
from .tableformat import *

이러한 줄은 structure, readertableformat 하위 모듈에서 내보낸 모든 심볼을 임포트합니다. 모듈 이름 앞의 점 (.) 은 상대 임포트임을 나타냅니다. 즉, 동일한 패키지 내에서 임포트한다는 의미입니다. 파일을 저장하고 편집기를 종료합니다.

변경 사항 테스트

변경 사항이 제대로 작동하는지 확인하기 위해 간단한 테스트 파일을 만들어 보겠습니다. 이 테스트 파일은 __all__ 변수에 지정한 심볼을 임포트하려고 시도하고 임포트가 성공하면 성공 메시지를 출력합니다.

touch ~/project/test_structly.py

이 명령은 프로젝트 디렉토리에 test_structly.py라는 새 파일을 만듭니다. 파일에 이 내용을 추가합니다.

## A simple test to verify our imports work correctly

from structly import Structure
from structly import read_csv_as_instances
from structly import create_formatter, print_table

print("Successfully imported all required symbols!")

이러한 줄은 Structure 클래스, read_csv_as_instances 함수, create_formatterprint_table 함수를 structly 패키지에서 임포트하려고 시도합니다. 임포트가 성공하면 프로그램은 "Successfully imported all required symbols!" 메시지를 출력합니다. 파일을 저장하고 편집기를 종료합니다. 이제 이 테스트를 실행해 보겠습니다.

cd ~/project
python test_structly.py

cd ~/project 명령은 현재 작업 디렉토리를 프로젝트 디렉토리로 변경합니다. python test_structly.py 명령은 test_structly.py 스크립트를 실행합니다. 모든 것이 제대로 작동하면 화면에 "Successfully imported all required symbols!" 메시지가 표시됩니다.

패키지에서 모든 항목 내보내기

Python 에서 패키지 구성은 코드를 효과적으로 관리하는 데 매우 중요합니다. 이제 패키지 구성을 한 단계 더 발전시키겠습니다. 패키지 수준에서 어떤 심볼을 내보낼지 정의할 것입니다. 심볼을 내보낸다는 것은 특정 함수, 클래스 또는 변수를 코드의 다른 부분이나 패키지를 사용할 수 있는 다른 개발자가 사용할 수 있도록 하는 것을 의미합니다.

패키지에 __all__ 추가

Python 패키지로 작업할 때 누군가 from structly import * 문을 사용할 때 어떤 심볼에 액세스할 수 있는지 제어하고 싶을 수 있습니다. 이럴 때 __all__ 리스트가 유용합니다. 패키지의 __init__.py 파일에 __all__ 리스트를 추가하면 누군가 from structly import * 문을 사용할 때 정확히 어떤 심볼을 사용할 수 있는지 제어할 수 있습니다.

먼저, __init__.py 파일을 만들거나 업데이트해 보겠습니다. 파일이 없으면 touch 명령을 사용하여 파일을 만듭니다.

touch ~/project/structly/__init__.py

이제 __init__.py 파일을 열고 __all__ 리스트를 추가합니다. 이 리스트에는 내보내려는 모든 심볼이 포함되어야 합니다. 심볼은 structure, reader, tableformat 모듈과 같이 출처에 따라 그룹화됩니다.

## structly/__init__.py

from .structure import *
from .reader import *
from .tableformat import *

## Define what symbols are exported when using "from structly import *"
__all__ = ['Structure',  ## from structure
           'read_csv_as_instances', 'read_csv_as_dicts', 'read_csv_as_columns',  ## from reader
           'create_formatter', 'print_table']  ## from tableformat

코드를 추가한 후 파일을 저장하고 편집기를 종료합니다.

import * 이해

from module import * 패턴은 대부분의 Python 코드에서 일반적으로 권장되지 않습니다. 다음과 같은 몇 가지 이유가 있습니다.

  1. 예상치 못한 심볼로 네임스페이스를 오염시킬 수 있습니다. 즉, 현재 네임스페이스에 예상하지 못한 변수나 함수가 있을 수 있으며, 이는 이름 충돌로 이어질 수 있습니다.
  2. 특정 심볼의 출처를 알 수 없게 만듭니다. import *를 사용하면 심볼이 어떤 모듈에서 오는지 알기 어려워 코드를 이해하고 유지 관리하기 어려울 수 있습니다.
  3. 섀도잉 (shadowing) 문제가 발생할 수 있습니다. 섀도잉은 로컬 변수 또는 함수가 다른 모듈의 변수 또는 함수와 동일한 이름을 가질 때 발생하며, 이는 예상치 못한 동작을 유발할 수 있습니다.

그러나 import *를 사용하는 것이 적절한 특정 경우가 있습니다.

  • 전체적으로 사용하도록 설계된 패키지의 경우. 패키지가 단일 단위로 사용되도록 되어 있는 경우 import *를 사용하면 필요한 모든 심볼에 더 쉽게 액세스할 수 있습니다.
  • 패키지가 __all__을 통해 명확한 인터페이스를 정의하는 경우. __all__ 리스트를 사용하면 내보낼 심볼을 제어하여 import *를 더 안전하게 사용할 수 있습니다.
  • Python REPL(Read-Eval-Print Loop) 과 같은 대화형 사용의 경우. 대화형 환경에서는 모든 심볼을 한 번에 임포트하는 것이 편리할 수 있습니다.

Import *로 테스트

모든 심볼을 한 번에 임포트할 수 있는지 확인하기 위해 다른 테스트 파일을 만들어 보겠습니다. touch 명령을 사용하여 파일을 만듭니다.

touch ~/project/test_import_all.py

이제 test_import_all.py 파일을 열고 다음 내용을 추가합니다. 이 코드는 structly 패키지의 모든 심볼을 임포트한 다음 일부 중요한 심볼을 사용할 수 있는지 테스트합니다.

## Test importing everything at once

from structly import *

## Try using the imported symbols
print(f"Structure symbol is available: {Structure is not None}")
print(f"read_csv_as_instances symbol is available: {read_csv_as_instances is not None}")
print(f"create_formatter symbol is available: {create_formatter is not None}")
print(f"print_table symbol is available: {print_table is not None}")

print("All symbols successfully imported!")

파일을 저장하고 편집기를 종료합니다. 이제 테스트를 실행해 보겠습니다. 먼저 cd 명령을 사용하여 프로젝트 디렉토리로 이동한 다음 Python 스크립트를 실행합니다.

cd ~/project
python test_import_all.py

모든 것이 올바르게 설정되어 있으면 모든 심볼이 성공적으로 임포트되었음을 확인할 수 있습니다.

더 나은 코드 구성을 위한 모듈 분할

Python 프로젝트가 커짐에 따라 단일 모듈 파일이 상당히 커지고 여러 개의 관련되지만 별개의 구성 요소를 포함하게 될 수 있습니다. 이런 경우 모듈을 하위 모듈이 있는 패키지로 분할하는 것이 좋습니다. 이 접근 방식은 코드를 더 체계적으로 만들고, 유지 관리가 더 쉬워지며, 확장성이 향상됩니다.

현재 구조 이해

tableformat.py 모듈은 큰 모듈의 좋은 예입니다. 다음과 같이 데이터를 다른 방식으로 형식화하는 여러 형식 지정자 클래스를 포함합니다.

  • TableFormatter (base class): 다른 모든 형식 지정자 클래스의 기본 클래스입니다. 다른 클래스가 상속하고 구현할 기본 구조와 메서드를 정의합니다.
  • TextTableFormatter: 일반 텍스트로 데이터를 형식화하는 클래스입니다.
  • CSVTableFormatter: CSV(Comma-Separated Values) 형식으로 데이터를 형식화하는 클래스입니다.
  • HTMLTableFormatter: HTML(Hypertext Markup Language) 형식으로 데이터를 형식화하는 클래스입니다.

이 모듈을 각 형식 지정자 유형에 대한 별도의 파일이 있는 패키지 구조로 재구성합니다. 이렇게 하면 코드가 더 모듈화되고 관리가 더 쉬워집니다.

1 단계: 캐시 파일 정리

코드를 재구성하기 전에 Python 캐시 파일을 정리하는 것이 좋습니다. 이러한 파일은 코드 실행 속도를 높이기 위해 Python 에서 생성되지만, 코드를 변경할 때 문제가 발생할 수 있습니다.

cd ~/project/structly
rm -rf __pycache__

위 명령에서 cd ~/project/structly는 현재 디렉토리를 프로젝트의 structly 디렉토리로 변경합니다. rm -rf __pycache____pycache__ 디렉토리와 모든 내용을 삭제합니다. -r 옵션은 재귀적 (recursive) 을 의미하며, 이는 __pycache__ 디렉토리 내의 모든 파일과 하위 디렉토리를 삭제한다는 의미입니다. -f 옵션은 강제 (force) 를 의미하며, 확인 없이 파일을 삭제한다는 의미입니다.

2 단계: 새 패키지 구조 만들기

이제 패키지에 대한 새 디렉토리 구조를 만들어 보겠습니다. tableformat이라는 디렉토리와 그 안에 formats라는 하위 디렉토리를 만듭니다.

mkdir -p tableformat/formats

mkdir 명령은 디렉토리를 만드는 데 사용됩니다. -p 옵션은 부모 (parents) 를 의미하며, 필요한 상위 디렉토리가 없으면 모두 생성한다는 의미입니다. 따라서 tableformat 디렉토리가 없으면 먼저 생성된 다음 그 안에 formats 디렉토리가 생성됩니다.

3 단계: 원본 파일 이동 및 이름 바꾸기

다음으로, 원본 tableformat.py 파일을 새 구조로 이동하고 이름을 formatter.py로 바꿉니다.

mv tableformat.py tableformat/formatter.py

mv 명령은 파일을 이동하거나 이름을 바꾸는 데 사용됩니다. 이 경우 tableformat.py 파일을 tableformat 디렉토리로 이동하고 이름을 formatter.py로 바꿉니다.

4 단계: 코드를 별도 파일로 분할

이제 각 형식 지정자에 대한 파일을 만들고 관련 코드를 해당 파일로 이동해야 합니다.

1. 기본 형식 지정자 파일 만들기

touch tableformat/formatter.py

touch 명령은 빈 파일을 만드는 데 사용됩니다. 이 경우 tableformat 디렉토리에 formatter.py라는 파일을 만듭니다.

TableFormatter 기본 클래스와 print_tablecreate_formatter와 같은 일반 유틸리티 함수는 이 파일에 유지합니다. 파일은 다음과 같이 표시됩니다.

## Base TableFormatter class and utility functions

__all__ = ['TableFormatter', 'print_table', 'create_formatter']

class TableFormatter:
    def headings(self, headers):
        '''
        Emit table headings.
        '''
        raise NotImplementedError()

    def row(self, rowdata):
        '''
        Emit a single row of table data.
        '''
        raise NotImplementedError()

def print_table(objects, columns, formatter):
    '''
    Make a nicely formatted table from a list of objects and attribute names.
    '''
    formatter.headings(columns)
    for obj in objects:
        rowdata = [getattr(obj, name) for name in columns]
        formatter.row(rowdata)

def create_formatter(fmt):
    '''
    Create an appropriate formatter given an output format name.
    '''
    if fmt == 'text':
        from .formats.text import TextTableFormatter
        return TextTableFormatter()
    elif fmt == 'csv':
        from .formats.csv import CSVTableFormatter
        return CSVTableFormatter()
    elif fmt == 'html':
        from .formats.html import HTMLTableFormatter
        return HTMLTableFormatter()
    else:
        raise ValueError(f'Unknown format {fmt}')

__all__ 변수는 from module import *를 사용할 때 어떤 심볼을 임포트해야 하는지 지정하는 데 사용됩니다. 이 경우 TableFormatter, print_tablecreate_formatter 심볼만 임포트하도록 지정합니다.

TableFormatter 클래스는 다른 모든 형식 지정자 클래스의 기본 클래스입니다. headingsrow의 두 가지 메서드를 정의하며, 이는 서브클래스에서 구현하도록 되어 있습니다.

print_table 함수는 객체 목록, 열 이름 목록 및 형식 지정자 객체를 가져와 데이터를 형식화된 테이블로 출력하는 유틸리티 함수입니다.

create_formatter 함수는 형식 이름을 인수로 받아 적절한 형식 지정자 객체를 반환하는 팩토리 함수입니다.

이러한 변경을 한 후 파일을 저장하고 종료합니다.

2. 텍스트 형식 지정자 만들기

touch tableformat/formats/text.py

이 파일에 TextTableFormatter 클래스만 추가합니다.

## Text formatter implementation

__all__ = ['TextTableFormatter']

from ..formatter import TableFormatter

class TextTableFormatter(TableFormatter):
    '''
    Emit a table in plain-text format
    '''
    def headings(self, headers):
        print(' '.join('%10s' % h for h in headers))
        print(('-'*10 + ' ')*len(headers))

    def row(self, rowdata):
        print(' '.join('%10s' % d for d in rowdata))

__all__ 변수는 from module import *를 사용할 때 TextTableFormatter 심볼만 임포트하도록 지정합니다.

from ..formatter import TableFormatter 문은 상위 디렉토리의 formatter.py 파일에서 TableFormatter 클래스를 임포트합니다.

TextTableFormatter 클래스는 TableFormatter 클래스를 상속하고 headingsrow 메서드를 구현하여 데이터를 일반 텍스트로 형식화합니다.

이러한 변경을 한 후 파일을 저장하고 종료합니다.

3. CSV 형식 지정자 만들기

touch tableformat/formats/csv.py

이 파일에 CSVTableFormatter 클래스만 추가합니다.

## CSV formatter implementation

__all__ = ['CSVTableFormatter']

from ..formatter import TableFormatter

class CSVTableFormatter(TableFormatter):
    '''
    Output data in CSV format.
    '''
    def headings(self, headers):
        print(','.join(headers))

    def row(self, rowdata):
        print(','.join(str(d) for d in rowdata))

이전 단계와 유사하게 __all__ 변수를 지정하고, TableFormatter 클래스를 임포트하고, headingsrow 메서드를 구현하여 데이터를 CSV 형식으로 형식화합니다.

이러한 변경을 한 후 파일을 저장하고 종료합니다.

4. HTML 형식 지정자 만들기

touch tableformat/formats/html.py

이 파일에 HTMLTableFormatter 클래스만 추가합니다.

## HTML formatter implementation

__all__ = ['HTMLTableFormatter']

from ..formatter import TableFormatter

class HTMLTableFormatter(TableFormatter):
    '''
    Output data in HTML format.
    '''
    def headings(self, headers):
        print('<tr>', end='')
        for h in headers:
            print(f'<th>{h}</th>', end='')
        print('</tr>')

    def row(self, rowdata):
        print('<tr>', end='')
        for d in rowdata:
            print(f'<td>{d}</td>', end='')
        print('</tr>')

다시, __all__ 변수를 지정하고, TableFormatter 클래스를 임포트하고, headingsrow 메서드를 구현하여 데이터를 HTML 형식으로 형식화합니다.

이러한 변경을 한 후 파일을 저장하고 종료합니다.

5 단계: 패키지 초기화 파일 만들기

Python 에서 __init__.py 파일은 디렉토리를 Python 패키지로 표시하는 데 사용됩니다. tableformatformats 디렉토리 모두에 __init__.py 파일을 만들어야 합니다.

1. tableformat 패키지에 대한 파일 만들기

touch tableformat/__init__.py

파일에 이 내용을 추가합니다.

## Re-export the original symbols from tableformat.py
from .formatter import *

이 문은 formatter.py 파일의 모든 심볼을 임포트하고 tableformat 패키지를 임포트할 때 사용할 수 있도록 합니다.

이러한 변경을 한 후 파일을 저장하고 종료합니다.

2. formats 패키지에 대한 파일 만들기

touch tableformat/formats/__init__.py

이 파일은 비워두거나 간단한 docstring 을 추가할 수 있습니다.

'''
Format implementations for different output formats.
'''

docstring 은 formats 패키지가 수행하는 작업에 대한 간략한 설명을 제공합니다.

이러한 변경을 한 후 파일을 저장하고 종료합니다.

6 단계: 새 구조 테스트

변경 사항이 제대로 작동하는지 확인하기 위해 간단한 테스트를 만들어 보겠습니다.

cd ~/project
touch test_tableformat.py

test_tableformat.py 파일에 이 내용을 추가합니다.

## Test the tableformat package restructuring

from structly import *

## Create formatters of each type
text_fmt = create_formatter('text')
csv_fmt = create_formatter('csv')
html_fmt = create_formatter('html')

## Define some test data
class TestData:
    def __init__(self, name, value):
        self.name = name
        self.value = value

## Create a list of test objects
data = [
    TestData('apple', 10),
    TestData('banana', 20),
    TestData('cherry', 30)
]

## Test text formatter
print("\nText Format:")
print_table(data, ['name', 'value'], text_fmt)

## Test CSV formatter
print("\nCSV Format:")
print_table(data, ['name', 'value'], csv_fmt)

## Test HTML formatter
print("\nHTML Format:")
print_table(data, ['name', 'value'], html_fmt)

이 테스트 코드는 structly 패키지에서 필요한 함수와 클래스를 임포트하고, 각 유형의 형식 지정자를 만들고, 일부 테스트 데이터를 정의한 다음, 해당 형식으로 데이터를 출력하여 각 형식 지정자를 테스트합니다.

이러한 변경을 한 후 파일을 저장하고 종료합니다. 이제 테스트를 실행합니다.

python test_tableformat.py

세 가지 다른 방식 (텍스트, CSV 및 HTML) 으로 형식화된 동일한 데이터가 표시되어야 합니다. 예상 출력이 표시되면 코드 재구성이 성공한 것입니다.

요약

이 랩에서는 몇 가지 중요한 Python 패키지 구성 기술을 배웠습니다. 먼저, 모듈에서 내보내는 심볼을 명시적으로 정의하기 위해 __all__ 변수를 사용하는 방법을 익혔습니다. 둘째, 최상위 패키지에서 하위 모듈 심볼을 다시 내보내어 사용자 친화적인 패키지 인터페이스를 만들었습니다.

이러한 기술은 깨끗하고, 유지 관리 가능하며, 사용자 친화적인 Python 패키지를 만드는 데 필수적입니다. 이를 통해 사용자의 보기를 제어하고, 임포트 프로세스를 단순화하며, 프로젝트가 확장됨에 따라 코드를 논리적으로 구성할 수 있습니다.