ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 91 - (파이썬) 패키지
    study with Q - 파이썬 2024. 9. 22. 20:27

    #패키지

    : 모듈의 규모가 커졌을 때, 그 모듈을 나누는 방법

     

    ㄱ) 모듈 : 관심사를 기반으로 함수와 변수를 나누는 것

    → 프로그래밍 언어에 따라서 관심사를 묶는 방법이 매우 다양. 그 중 파이썬은 파일들을 폴더를 활용해서 묶는다.

     

    방법1) 폴더 내부의 있는 모듈을 읽어들이는 방법

     

    [1] school이라는 이름의 폴더를 만든다

     

    [2] 내부에 StudentList를 배치해 볼 studentlist.py라는 파일과 클래스를 배치할 student.py라는 파일을 만든다. 
    (일반적으로 클래스의 규모가 커지게 되면 클래스 각각을 파일로 만든다.)

     

    [3] student.py의 student 클래스 내부에는 수학 성적 입력 / 합계를 입력을 받는 코드를 작성한다.

    <student.py>

    class Student:
      def __init__(self, 수학):
          self.수학 = 수학
      def sum(self):
        return self.수학

     

    [4] studentlist.py에는 studentlist라는 클래스에 다음의 코드를 구성한다.

     - init 내부에서 self.list를 초기화 한 뒤에 append 함수를 구현해주고, 학생이 전달되면 self.list에 학생이 append되도록 구성한다.

    - 출력을 하면 for 반복문을 활용해서 list 내부에 있는 학생들을 반복을 돌리고 student 내부에 있는 sum을 호출해서 출력하도록 구성한다.

    <studentlist.py>

    class StudentList(list):
      def __init__(self): #외부로부터 캡슐화 해서 숨기기
        self.__list = []
      def append(self, 학생):
          self.__list.append(학생) #self.__리스트로 수정
      def print(self):
          for student in self.__list:
              print(student.sum())

     

    [5] main.py에서 읽어 들이기

    - from import 구문 등을 사용해서 읽어들이고 StudentList에 append를 해서 Student 클래스에 인스턴스를 추가한다.

    - print를 넣고 코드를 실행하면 모듈을 읽어서 작동하는 것을 볼 수 있다.

    <main.py>

    from school.student import Student #student.py 내부에는 Student 밖에 없어서 import Student 대신 import *을 써도 동일하게 작동한다
    from school.studentlist import StudentList
    
    sl = StudentList()
    sl.append(Student(100))
    sl.append(Student(75))
    sl.append(Student(80))
    sl.print()
    >>>
    100
    75
    80

     

     

    방법2) 폴더 자체를 모듈로 읽어들이는 방법

    - 위의 예시에서 school.student또는 school.studentlist로 각각의 파일에 접근하는 것이 아니라 import school이라고 해서 폴더를 읽어 들이는 방법

    🧐 import school일 때 무슨 내용을 읽어 들이는지 어떻게 알지?

     폴더를 import했을 때 읽어지는 파일은 폴더 내부에 있는 __init__ .py 파일이다!

     

    <__init__ .py>

    a = "내가 모듈이 될 상인가"
    def b():
      print("school의 모듈임다.")

     

    <main.py>

    import school
    print(school.a)
    school.b()
    >>>
    내가 모듈이 될 상인가
    school의 모듈임다.

     

    두 가지 방법을 결합 뾰로롱

    - 내부에서 from student import Student라고 해서 student라는 이름의 식별자를 만들면 파일 내부에서 접근할 수 있지 않나?

    → 놉! student라는 모듈과 studentlist라는 모듈을 찾을 수 없다는 오류가 발생한다.

     

    <__init__ .py>

    from student import Student
    from studentlist import StudentList
    >>>
    Traceback (most recent call last):
      File "/home/runner/byeonsuwa-boghabdaeibyeonsanja/main.py", line 1, in <module>
        from student import Student
    ModuleNotFoundError: No module named 'student'

     

    ✔️ 패키지 내부에 있는(다른 폴더에 있는) 위치에서 해당 폴더에 있는 모듈을 읽어 들이기 위해서는 모듈이 상대적인 경로에 있다는 것을 나타내야 한다. 

    🧐 어떻게요...?

    모듈 이름앞에 점 하나 찍어주슈

    <__init__ .py>

    from .student import Student
    from .studentlist import StudentList

    이렇게 모듈 이름 앞에 점을 찍게 되면 현재 __init__.py이 있는 위치 기준으로 모듈을 찾게 된다!

     

    <main.py>

    import school
    
    school.Student
    school.StudentList

     코드를 실행하면 정상적으로 읽어 들이는 것을 알 수 있다.

    요러한 형태로 패키지를 구성하게 되면 Student 파일과 StudentList 파일을 한 번에 읽을 수 있다.

     

     

    ✋ 앞에 school.을 계속 입력하는 것이 구찮습니다

    사실 나도 그래요.

    import *을 활용해서 패키지 내부에 있는 모든 것을 읽으면 어떨까염...?

    <main.py>

    from school import *
    
    school.Student
    school.StudentList

    클래스 캡슐화에서 나온 것처럼 너무 많은 권한을 부여해서 위험한 상황이 발생할 수 있다!

     

    그래서

    <__init__ .py>에서 __all__ = []라는 변수를 만들어서 패키지에서 무엇을 읽을 것인지 선택적으로 지정할 수 있다.

    <__init__ .py>

    __all__ = ["Student", "StudentList"] #나는 Student와 StudentList만 읽겠어요
    from .student import Student
    from .studentlist import StudentList
    
    a = "내가 모듈이 될 상인가"
    def b():
      print("school의 모듈임다.")

    <main.py>

    from school import *
    
    print(Student)
    print(a)
    >>>
    <class 'school.student.Student'>
    Traceback (most recent call last):
      File "/home/runner/byeonsuwa-boghabdaeibyeonsanja/main.py", line 4, in <module>
        print(a)
    NameError: name 'a' is not defined

    Student 클래스는 정상적으로 출력하지만 a는 없다고 나온다!

     

    #__all__ = []로 서브 모듈 만들기

    <__init__ .py>

    __all__ = [
      "student", "studentList"
    ]

    <main.py>

    from school import *
    
    student.Student
    studentlist.StudentList

     

    <__init__ .py>에 student와 studentList라는 모듈 이름을 지정하게 되면 <main.py>에서 파일들을 서브 모듈로 읽어 들이게 되어서 student.Student, studentlist.StudentList 등으로 모듈 내부에 있는 클래서를 읽어 들일 수 있다.

     


    이런 것을 기억해서 다른 사람들이 만들 패키지를 분석할 수 있어야 한다!

    강사님의 말)

    더보기

    (1) 이후에 패키지를 읽어 들일 때 
    "이걸 왜 이렇게 읽어들이는가?"를 대충 이해하실 수 있는 정도

    예) 다음이 폴더 내부의 파일을 읽어들이는 것이라는걸 떠올릴 수 있으면 됩니다.
    import matplotlib.pyplot as plt
    import matplotlib.ticker as ticker
    (2) 나중에 패키지 분석해보면서 __all__을 봤을 때,
    "선택적 공개 또는 서브 모듈 공개를 하고 있겠구나"하고만
    떠올릴 수 있으시면 됩니다.

    예) 예를 들어 tensorflow 폴더 내부의 __init__.py 파일에
    keras라는 식별자를 만드는 과정이 없는데
    import tf
    tf.keras
    라는 코드를 사용하고 있는 코드가 나왔을 때
    "아.... 잘 모르겠지만 __all__로 읽어들이고 있겠구나...?" 정도 떠올리실 수 있으면 됩니다.

    PS. 실제로 보면 `__all__ = [s for s in dir() if s in _exported_dunders or not s.startswith('_')]`과 같은 코드로 현재 폴더의 내용을 패키지로 제공합니다(

Designed by Tistory.