본문 바로가기
🐍Python

[20210729] Python 기본 문법 4 - 클래스, 모듈, 패키지

by 캔 2021. 7. 29.

클래스

클래스는 객체 지향 프로그래밍에서 변수와 메서드를 모아놓은 틀이며 객체의 단위이다. 클래스를 실제로 구현한 것이 인스턴스이다.

# 클래스의 선언은 class 예약어를 사용한다.
class Calculator:
    def __init__(self):
        self.result = 0

    def add(self, num):
        self.result += num
        return self.result


cal1 = Calculator()  # cal1 객체
cal2 = Calculator()  # cal2 객체

print(cal1.add(3))  # 0 + 3
print(cal1.add(4))  # 3 + 4
print(cal2.add(3))  # 0 + 3
print(cal2.add(7))  # 3 + 7


# 사칙연산을 수행하는 FourCal class을 작성한다.
class FourCal:
    def setdata(self, first, second):  # method: class 안에 선언된 함수
        self.first = first  # 객체를 호출할 때 자신에게 전달이 되므로 self를 사용한다. 파이썬의 특징
        self.second = second  # 암묵적으로 self 사용, self 말고 다른 이름도 사용 가능하다.


# 메서드 호출 시에는 self를 반드시 생략해야 한다.
a = FourCal()
a.setdata(4, 2)  # -> def setdata(self, first, second)

a = FourCal()
FourCal.setdata(a, 4, 2)  # 이런 방식의 메서드 호출도 가능하다.


def setdata(self, first, second):
    self.first = first
    self.second = second


# self.first = 4 -> a.first = 4 -> a.first 실행 a 객체, 객체변수 first 생성 후 값 4가 저장된다.
# self.second = 2 -> a.second = 2 -> a.second 실행 a 객체, 객체변수 second 생성 후 값 2가 저장됨.

a = FourCal()
a.setdata(2, 4)
print(a.first)
print(a.second)
b = FourCal()
b.setdata(3, 4)
print(id(a.first))
print(id(b.first))  # a.first와 b.first의 주솟값이 다른 것을 알 수 있다.


# 기능 추가
class FourCal:
    def setdata(self, first, second):  # setdata(first,second)를 먼저 호출.
        self.first = first
        self.second = second

    def add(self):
        result = self.first + self.second
        return result

    def mul(self):
        result = self.first * self.second
        return result

    def sub(self):
        result = self.first - self.second
        return result

    def div(self):
        result = self.first / self.second
        return result


# 사칙연산 클래스 FourCal 생성하기
# a 객체를 만든다.
a = FourCal()
# 숫자 4와 2를 a에 지정해 준다.
a.setdata(4, 2)
# a.add()를 수행하면 4와 2를 합한 결과를 돌려준다.
print(a.add())
# a.mul()을 수행하면 4와 2를 곱한 결과를 돌려준다.
print(a.mul())
# a.sub()를 수행하면 4와 2를 뺀 결과를 돌려준다.
print(a.sub())
# a.div()를 수행하면 4와 2를 나눈 결과를 돌려준다.
print(a.div())

a = FourCal()
b = FourCal()

a.setdata(4, 2)
b.setdata(3, 8)
print("a.first는: %d " % a.first)
print("a.second는: %d" % a.second)
print("a.add()의 값은: %s" % a.add())
print("a.mul()의 값은: %s" % a.mul())
print("a.sub()의 값은: %s" % a.sub())
print("a.div()의 값은: %s" % a.div())
print("-------------------")
print("b.first 는: %d " % b.first)
print("b.second 는: %d" % b.second)
print("b.add()의 값은: %s" % b.add())
print("b.mul()의 값은: %s" % b.mul())
print("b.sub()의 값은: %s" % b.sub())
print("b.div()의 값은: %s" % b.div())


# 생성자 (Constructor)
# 메서드 이름으로 __init__ 을 사용하면 이 메서드는 생성자 된다.
class FourCal:
    def __init__(self, first, second):
        self.first = first
        self.second = second

    def setdata(self, first, second):  # setdata를 다시 사용할 수 있으므로.
        self.first = first
        self.second = second

    def add(self):
        result = self.first + self.second
        return result

    def mul(self):
        result = self.first * self.second
        return result

    def sub(self):
        result = self.first - self.second
        return result

    def div(self):
        result = self.first / self.second
        return result


# a = FourCal() -> 에러 발생, 생성자의 매개변수 first, second 값 누락
a = FourCal(4, 2)
print("a.first 는: %d " % a.first)
print("a.second 는: %d" % a.second)
print("a.add()의 값은: %s" % a.add())
print("a.mul()의 값은: %s" % a.mul())
print("a.sub()의 값은: %s" % a.sub())
print("a.div()의 값은: %s" % a.div())


# 상속(Inheritance)
# class 클래스명(상속할 클래스의 이름)
# FourCal 클래스 기능을 추가: 제곱 연산 기능을 추가 (a²)
# FourCal 클래스에 메서드 하나만 추가하면 된다.
# 상속하는 이유: 기존클래스가 라이브러리 형태로 제공되거나
# 수정이 허용되지 않는 상황이면 상속으로 해결.
class MoreFourCal(FourCal):
    def pow(self):
        result = self.first ** self.second
        return result


a = MoreFourCal(4, 3)
print(a.add())
print(a.mul())
print(a.sub())
print(a.div())
print(a.pow())


# 메서드 오버라이딩
class SafeFourCal(FourCal):
    def div(self):
        if self.second == 0:
            return 0
        else:
            return self.first / self.second


a = SafeFourCal(4, 0)
print(a.div())


# class 변수: 객체변수(정수, 실수, 문자열, 리스트, 튜플, 딕셔너리...), 클래스 변수는 성격이 다르다.
# 클래스 변수는 이 클래스로 만든 모든 객체가 공유한다. 파이썬에서는 객체 변수를 많이 사용한다.
class Family:
    lastname = "김"  # 클래스 변수


print(Family.lastname)
a = Family()
b = Family()
print(a.lastname)
print(b.lastname)
Family.lastname = "박"
print(a.lastname)
print(b.lastname)
print(id(Family.lastname))
print(id(a.lastname))
print(id(b.lastname))

 

모듈

모듈은 함수나 클래스를 모아놓은 파일이다. 다른 파이썬 모듈에서 불러와 사용이 가능하며, 다른 사람이 만들어 놓은 모듈을 사용하거나 자신이 직접 만들어서 사용할 수 있다. 파이썬 파일(.py) 하나하나가 모듈이다.

# mod1.py 파일
def add(a, b):
    return a + b


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


if __name__ == "__main__":
    print(add(1, 4))
    print(sub(4, 2))

# 모듈 불러오기
# import는 현재 디렉터리에 있는 파일이나 파이썬 내장 라이브러리가 저장된 
# 디렉터리에 있는 모듈만 불러올 수 있다.
# 파이썬 내장 라이브러리는 파이썬을 설치할 때 자동으로 설치되는 파이썬 모듈이다.
# 사용할 때는 '모듈.메서드()'와 같이 쓴다.
import mod1 # mod1.py 파일이지만, .py는 생략한다.

print(mod1.add(3, 4))
print(mod1.sub(4, 2))

# mod1.add(), mod1.sub()처럼 쓰지 않고 add(), sub()처럼 사용하고 싶을 때
# from 모듈명 import 모듈 함수
from mod1 import add, sub  # 호출하고 싶은 메서드만 import
from mod1 import *  # 전부 import

print(add(3, 4))
print(sub(4, 2))

"""
if __name__ == "__main__": 의 의미
python mod1.py를 콘솔에서 실행했을 때 5, 2 값이 리턴됨
따라서 import할 경우에도 5, 2값이 리턴되므로
if __name__ == "__main__":을 추가하여 사용한다.
단독으로 사용할 때는 __name__이 __main__와 같아 True가 되어 동작한다.
import로 불려서 사용될 때는 False가 되어 동작하지 않는다.

__name__변수?
파이썬의 __name__변수는 파이썬이 내부적으로 사용하는 특별한 변수명이다.
만약 mod1.py처럼 직접 mod1.py 파일을 실행할 경우
mod1.py의 __name__ 변수에는 __main__ 값이 저장된다.
하지만 파이썬 셸이나 다른 파이썬 모듈에서 mod1을 import할 경우에는
mod1.py 변수에는 mod1.py의 모듈명인 mod1이 저장된다.
"""

# mod2.py 파일
PI = 3.141592


class Math:
    def solv(self, r):
        return PI * (r ** 2)


def add(a, b):
    return a + b


# 파이썬 콘솔창에서
import mod2

print(mod2.PI)

a = mod2.Math()
print(a.solv(2))

print(mod2.add(mod2.PI, 4.4))  # mod2의 add() 함수도 사용 가능

 

패키지

패키지는 여러 모듈을 모아 계층적으로 만든 것이다. "패키지이름.모듈이름"과 같은 형식으로 접근할 수 있다.

# 가상의 game 패키지 예
"""
game/
    __init__.py
    sound/
        __init__.py
        echo.py
        wav.py
    graphic/
        __init__.py
        screen.py
        render.py
    play/
        __init__.py
        run.py
        test.py
"""
# game, sound, graphic, play는 디렉터리이고 확장자가 .py인 파일은 파이썬 모듈이다.
# game 디렉터리가 패키지의 루트 디렉터리(최상위 디렉터리)이고,
# sound, graphic, play는 서브 디렉터리이다.

# 콘솔에서 실행 결과 비교
# 첫 번째 echo 모듈을 import하여 실행
"""
>>> import game.sound.echo
>>> game.sound.echo.echo_test()
echo
"""

# 두 번째 echo 모듈이 있는 디렉터리까지를 from ... import하여 실행
"""
>>> from game.sound import echo
>>> echo.echo_test()
echo
"""

# 세 번째 echo 모듈의 echo_test 함수를 직접 import하여 실행
"""
>>> from game.sound.echo import echo_test
>>> echo_test()
echo
"""

# echo_test() 함수 사용 불가
# import game을 수행하면 game 디렉터리의 __init__.py에 정의된 것만 참조할 수 있다.
"""
>>> import game
>>> game.sound.echo.echo_test()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: module 'game' has no attribute 'sound'
"""

# 도트 연산자(.)를 사용해서 import a.b.c처럼 import할 때
# 가장 마지막 항목 c는 반드시 모듈 또는 패키지여야 한다.
"""
>>> import game.sound.echo.echo_test
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ModuleNotFoundError: No module named 'game.sound.echo.echo_test'; 
'game.sound.echo' is not a package
"""

# __init__.py의 용도
# __init__.py 파일은 해당 디렉터리가 패키지의 일부임을 알려주는 역할
# 만약 game, sound, graphic 등 패키지에 포함된 디랙터리에 __init__.py 파일이 없다면 패키지로
# 인식이 안 된다. *파이썬3.3 버전부터 __init__.py 파일이 없어도 패키지로 인식하지만 하위 버전
# 호환을 위해 __init__.py 파일을 생성하는 것이 안전한 방법
"""
>>> from game.sound import *
>>> echo.echo_test()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'echo' is not defined
"""
# 특정 디렉터리의 모듈을 * 사용하여 import할 때는 다음과 같이 해당 디렉토리의
# __init__.py 파일에 __all__ 변수를 설정하고 import할 수 있는 모듈을 정의해 줘야 한다.
"""
__all__ = ['echo']
"""

# 상대 경로 패키지
# graphic 디렉터리의 render.py모듈이 sound 디렉터리의 echo.py 모듈을 사용.
# render.py
from game.sound.echo import echo_test
from ..sound.echo import echo_test


def render_test():
    print("render")
    echo_test()


"""
relative한 접근자
..: 부모 디렉터리
.: 현재 디렉터리
"""