-
[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
'💫 Computer Science > Python & AI Framework' 카테고리의 다른 글
[Python] Effective Python 2 - Pythonic (1) 2021.03.01 [Python] Effective Python 1 - call by assignment (0) 2021.02.20 [Python] list, tuple, dictionary, iterator(map, filter) (0) 2020.09.06 [Flask] Heroku에 Flask 배포 (0) 2020.08.11 [Numpy] 딥러닝을 위한 Numpy3 - 함수 정리 (1) 2020.03.16