ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Python] Class & Underscore( _ , __ )
    💫 Computer Science/Python & AI Framework 2020. 12. 18. 16:54

     

    이번 시간은 파이썬 '클래스'와 '언더스코어(_)'에 대한 개념을 짚고 넘어가보려고 합니다.

     

     

     


     

     

    파이썬의 클래스

    사실 C언어처럼 파이썬은 굳이 클래스가 없어도 프로그램을 충분히 만들 수 있다고 합니다. 

     

     

    • 왜 사용할까?
    # order.py
    
    customer1 = 0
    
    def order(price):
      global customer1
      customer1 += price
      return customer1
    
    
    print(order(3000))
    print(order(5000))
    # 결과
    
    3000
    8000

    고객의 누적 주문금액을 반환하는 order()라는 함수가 있다고 해봅시다. 먼저 고객이 1명일때는 이렇게 함수를 1개만 구현하면 되겠죠. 근데 고객이 3명이라면?,,

     

    # order.py
    
    customer1 = 0
    customer2 = 0
    customer3 = 0
    
    
    def order1(price):
      global customer1
      customer1 += price
      return customer1
    
    def order2(price):
      global customer2
      customer2 += price
      return customer2
    
    def order3(price):
      global customer3
      customer3 += price
      return customer3
    
    
    print('customer1의 총 주문금액 : ', order1(3000))
    print('customer1의 총 주문금액 : ', order1(4000))
    print()
    print('customer2의 총 주문금액 : ', order2(1000))
    print('customer2의 총 주문금액 : ', order2(2000))
    print()
    print('customer3의 총 주문금액 : ', order3(3000))
    print('customer3의 총 주문금액 : ', order3(8000))
    # 결과
    customer1의 총 주문금액 :  3000
    customer1의 총 주문금액 :  7000
    
    customer2의 총 주문금액 :  1000
    customer2의 총 주문금액 :  3000
    
    customer3의 총 주문금액 :  3000
    customer3의 총 주문금액 :  11000
    

    똑같은 일을 하는 함수를 3개 만들게 되었습니다. 이를 클래스를 통해 1번만 만들어줄 수 있어요.

     

     

     

    # order.py
    
    class Order:
        def __init__(self):
            self.customer = 0
    
        def order(self, price):
            self.customer += price
            return self.customer
    
    customer1 = Order()
    customer2 = Order()
    
    print(customer1.order(14000))
    print(customer1.order(5000))
    print()
    print(customer2.order(1000))
    print(customer2.order(4000))
    # 결과
    
    14000
    19000
    
    1000
    5000

     

    customer1, customer2 처럼 원하는 만큼 객체를 만들고 객체마다 총 합인 customer 변수 값이 독립적으로 유지 됩니다. 여기서 customer1, customer2는 클래스를 통해 생성한 객체이고 Oder()클래스의 인스턴스..! 라고 합니다.

     

     

     

    • self

    클래스의 생성자나 클래스의 함수의 파라미터로 'self'라는 매개변수를 넣는데 self에는 객체가 생성자와 함수에 자동으로 전달되록 하는 역할을 합니다. 파이썬의 독특한 특징 중 하나. 파이썬의 메서드의 첫 번재 매개변수 이름을 관례적으로 'self'로 사용하는 것이고 객체를 호출한 자신이 전달되기 때문에 'self'라는 이름을 붙였다고 합니다. self말고 다른 이름을 사용해도 상관 없긴 합니다.

     

     

    • 생성자

    객체에 초기값을 설정해야 할 때 사용합니다. 

    # order.py
    
    class Order:
        def setData(self, customer):
            self.customer = customer
    
        def order(self, price):
            self.customer += price
            return self.customer
    
    customer1 = Order()
    
    customer1.setData(0)
    print(customer1.order(14000))
    print(customer1.order(5000))

    생성자를 사용하지 않고 setData()와 같이 초깃값을 정해주는 함수를 구현할 수도 있지만, 그렇게 되면 setData()를 호출한 후에만 order() 함수를 사용할 수 있게됩니다.

     

     

    class Order:
        def __init__(self):
            self.customer = 0
    
        def order(self, price):
            self.customer += price
            return self.customer
    
    customer1 = Order()
    
    print(customer1.order(14000))
    print(customer1.order(5000))

    생성자는 __init__메서드를 통해 만들 수 있고 위 예제는 생성자에 별다른 매개변수가 없지만,

     

    class Order:
        def __init__(self, name):
            self.customer = 0
            self.name = name
    
        def order(self, price):
            self.customer += price
            return self.customer
    
    customer1 = Order('길동이')
    
    print(customer1.order(14000))
    print(customer1.order(5000))

    이렇게 매개변수가 있는 경우에는 객체를 생성할 때 꼭 매개변수 값을 전달해주어야 합니다.

    customer1 = Order()처럼 매개변수를 입력하지 않으면 오류가 납니다.

     

     

     

    • 클래스의 상속

    상속은 말 그대로 어떤 클래스에 다른 클래스의 기능을 물려받을 수 있도록 할 수 있는 기능입니다. 

    class 클래스이름(상속할 클래스 이름)

    class Order:
        def __init__(self, name):
            self.customer = 0
            self.name = name
    
        def order(self, price):
            self.customer += price
            return self.customer
    
    class extraOrder(Order):
        pass

    이렇게 되면 Order()의 기능을 extraOrder()라는 클래스가 물려받게 되는 것 입니다. 그리고 Order의 기능을 모두 사용할 수 있게 됩니다.

     

    class Order:
        def __init__(self, name):
            self.customer = 0
            self.name = name
    
        def order(self, price):
            self.customer += price
            return self.customer
    
    class extraOrder(Order):
        pass
    
    extraCustomer = extraOrder('영희')
    
    print(extraCustomer.order(1000))

    extraOrder 생성자에 order라는 함수를 추가하지 않았지만 자동으로 사용할 수 있게 되었고 생성할 때도 마찬가지로 name이라는 매개변수를 넣어주지 않으면 에러가 납니다.

     

    상속은 기존 클래스를 변경하지 않고 기능을 추가하거나 기존 기능을 변경하려고 할 때 사용합니다. 사실 기존 클래스를 수정하면 되지만 수정이 허용되지 않는 라이브러리 형태라든가 하는 상황이라면 상속을 이용해야 합니다.

     

     

     

    • 메서드 오버라이딩
    class Order:
        def __init__(self, name):
            self.customer = 0
            self.name = name
    
        def order(self, price):
            self.customer += price
            return self.customer
    
    class extraOrder(Order):
        def order(self, price):
            self.customer += price
            return str(self.customer) + '원'
    
    extraCustomer = extraOrder('영희')
    
    print(extraCustomer.order(1000))

    상속한 클래스(부모 클래스)에 있는 메서드를 동일 이름으로 다시 만드는 것을 '메소드 오버라이딩(Method Overriding)이라고 한다. 

     

     

     

    • 클래스 변수
    class Family:
        lastname = '김'
    
    print(Family.lastname)
    print()
    
    a = Family()
    b = Family()
    
    print(a.lastname)
    print(b.lastname)
    print()
    
    Family.lastname = '박'
    
    print(a.lastname)
    print(b.lastname)
    # 결과
    
    김
    
    김
    김
    
    박
    박

    생성자(__init__)로 만든 변수는 '객체 변수' 이고 객체 변수는 다른 객체들에 영향을 받지 않고 독립적인 값을 유지합니다. '클래스 변수'는 클래스 내에 선언한 변수를 말하며 사용은 클래스이름.클래스변수 로 합니다. 클래스 변수는 객체 변수와 특징이 다른데 위 예시 처럼 객체가 여러개 있어도 클래스 변수의 값은 서로 공유합니다. 클래스 변수보다는 객체 변수를 훨씬 많이 사용하기 때문에 생성자와 객체 변수를 잘 익혀놓읍시다.

     

     


     

    파이썬의 언더스코어 ( _ , __ )

     

    파이썬 유저라면 for _ in range()나 __init__ 문법을 자주 사용할 것입니다. 여기서 언더스코어는 어떤 의미를 가지고있을까요? 크게 다섯가지 경우가 있다고 합니다.

     

     

     

    CASE 1 : 언더스코어( _ )가 독립적으로 사용되는 경우

    -> 인터프리터에서 마지막 실행결과 값을 가지는 변수로 사용된다.

     

    파이썬을 인터프리터(프롬프트)에서 사용할 경우 가장 마지막에 실행된 결과의 값을 가진 변수로 '_'가 사용됩니다.

     

     


     

    CASE 2: 변수로 사용될 경우 

    -> 변수 값을 굳이 사용할 필요가 없을때 사용한다.

     

    for _ in range(5): # 단순히 5번 반복할 목적으로
        print('hello world')
    
    for _ in range(3): # _를 i로 바꿔서 보면 우리에게 익숙한 표현식일 것이다.
        print(_)
    
    def values():
        return (1,2,3,4) # 튜플을 반환
    
    a, b, _, _ = values() # 반환된 튜플 값중 2개만 필요할 경우
    print(a, b)

     

    # 결과
    
    hello world
    hello world
    hello world
    hello world
    hello world
    0
    1
    2
    1 2

     

    변수를 문자로 할당해주어도 되지만 굳이 사용하지 않을 경우에 _로 치환하여 줍니다. 변수명이 낭비될 필요가 없기 때문이죠.

     


     

    CASE 3 : __이름__

    -> 내장된 특수한 함수와 변수를 나타낸다.

     

    class vector:
      def __init__(self, x,y,z):
        self.x = x
        self.y = y
        self.z = z
    
      def __add__(self, other):
        x_ = self.x + other.x
        y_ = self.y + other.y
        z_ = self.z + other.z
    
        return vector(x_,y_,z_)
    
      def show(self):
        print(f"x:{self.x}, y:{self.y}, z:{self.z}")
    
    
    v1 = vector(1,2,3)
    v2 = vector(4,5,6)
    v3 = v1 + v2
    v3.show()
    # 결과
    
    x:5, y:7, z:9

     

    __init__은 클래스의 생성자 함수이며, __add__ 함수는 연산자 오버로드용으로 사용됐다.

     

     


     

    CASE 4 : 네이밍

    -> 가장 많이 사용하는 경우고 변수 이름 뒤에 _가 붙는 경우, 함수 이름 왼쪽 _를 붙이는 경우, 변수 왼쪽에 __를 붙이는 경우, 그리고 맹글링할 때 사용한다. 

     

     

    • 변수_
    class Order:
      def __int__(self):
        self.coffee = 'Americano'
        self.price = 3000
    
    def printClassName(class_):
      print(class_)
    
    order = Order()
    printClassName(order.__class__)
    # 결과
    
    <class '__main__.Order'>

    예약어를 변수명으로 사용할 수 없을 때 사용한다. 위 예제도 className 이런식으로 지으면 되기때문에 굳이 사용하지는 않는 것 같다.

     

     

     

    • _함수
    # func.py
    
    def publicFunc():
      print('this is public function')
    
    def _privateFunc():
      print('this is private function')
    # test.py
    
    from func import *
    
    publicFunc()
    _privateFunc()
    # 결과
    
    this is public function
    Traceback (most recent call last):
      File "c:\Projects\test\test.py", line 4, in <module>
        _privateFunc()
    NameError: name '_privateFunc' is not defined

    함수 이름 왼쪽에 _를 붙이면 다른 파일을 임포트 할 때 _를 붙인 함수는 가져오지 않는다..!

    보안성이 약한 private 의미로 사용하는 경우이다. 파이썬은 진정한 private를 지원하지 않기 때문에 임포트문에서는 누락이 가능하지만 직접 가져다쓰거나 호출을 할 때는 사용이 가능하다.

     

    # test.py
    
    import func
    
    func.publicFunc()
    func._privateFunc()
    # 결과
    
    this is public function
    this is private function

    모듈명을 가져올 수 있어버린다. 그래서 약한 private라고 하는 것이다.

     

     

    • 변수 왼쪽에 __(더블스코어)를 붙인경우 -> Mangling
    # order.py
    
    class Order:
      def __init__(self):
        self.coffee = 'Americano'
        self.__price = 3000
    
      def printInfo(self):
        print(f"coffee : {self.coffee}")
        print(f"price : {self.__price}")
    # test.py
    
    import order
    
    order1 = order.Order()
    print(order1.coffee)
    print(order1.__price)
    # test.py 결과
    
    Americano
    Traceback (most recent call last):
      File "c:\Projects\test\test.py", line 5, in <module>
        print(order1.__price)
    AttributeError: 'Order' object has no attribute '__price'

    언더스코어를 두개(__)붙인 변수는 선언된 클래스 안에서만 해당 이름으로 사용 가능하다. 위 예제에서는 order.py안에서만 __price 변수가 __price이름으로 사용이 가능하고, 외부 모듈인 test.py 안에서는 __price이름으로 사용이 불가능하다.

     

    test.py
    
    import order
    
    order1 = order.Order()
    
    print(order1._Order__price)
    order1.printInfo()
    # 결과
    
    3000
    coffee : Americano
    price : 3000
    

    외부에서 order.py의 __price 변수에 접근하려면 _클래스명__변수명 형식으로 변경해서 사용할 수 있다. 이러한 작업을 '맹글링(Mangling)'이라고 한다.

     

     

     

     

     

     

    References

     

    Python Naming

     

    댓글

Designed by Tistory.