객체지향 프로그래밍의 4가지 특징에 대해 알아본다.
추상화는 공통의 속성이나 유사한 기능을 묶어 이름을 붙여주는 것을 뜻한다. 이를 통해 다른 객체들과 구분되는 핵심적인 특징들에만 집중함으로써, 복잡도를 관리할 수 있도록 한다. 파이썬에서 클래스를 정의하거나 변수를 지정하는 것이 추상화에 해당한다.
추상화를 잘하기 위해서는 다음과 같은 방법이 있다.
docstring 의 경우 help() 함수를 통해 확인할 수 있다.
참고) 문서화 형식
다음과 같은 함수가 있을 때, 해당 함수에 대한 여러가지 docstring 형식이 있다.
def caculate_numbers(first_number,second_number=5):
"""두 숫자에 대한 사칙연산 함수이다
Parameters:
first_number (int): 첫 번째 숫자
second_number (int) : 두 번째 숫자
(기본값 5)
Returns:
int: 연산의 결과값
"""
"""두 숫자에 대한 사칙연산 함수이다
:param first_number: 첫 번째 숫자
:param second_number: 두 번째 숫자
(기본값 5)
:type first_number: int
:type second_number: int
:returns: 연산의 결과값
:rtype: int
"""
""" 숫자에 대한 사칙연산 함수이다
Parameters
----------
first_number: int
첫 번째 숫자
second_number: int
두 번째 숫자 (기본값은 5)
Returns
-------
int
연산의 결과값
"""
파이썬은 동적 타입 언어로써, 변수의 타입이 자동 정의됨 ↔️ Java 같은 정적 타입 언어는 변수를 정의할 때 타입도 함께 지정해야 함.
파이썬 3.5 버전 이상부터는 type hinting 기능을 통해 변수에 타입을 지정할 수 있음
int_var: int = 5
int_float: float = 0.2
# 함수 혹은 메서드의 return 값에 대한 타입 지정(return 값 없는 경우는 None)
def muliply(self, num: float) -> None:
캡슐화는 다음과 같이 정의할 수 있다.
- 객체의 일부 구현 내용에 대한 외부로부터의 직접적인 접근을 차단하는 것
- 객체의 속성과 그것을 사용하는 행동을 하나로 묶는 것
첫 번째, 객체 일부 구현 내용에 대한 외부 접근을 차단하는 방법은 다음과 같다.
class Person:
def __init__(self, name, age, address, wallet):
self.name = name
self.age = age
self.address = address
self.__wallet = wallet # 변수 앞에 __를 붙여서 비공개 속성으로 만듦
class Person:
def __greeting(self): # 메서드도 변수와 같이 __를 붙여서 비공개로 만듦
print('Hello')
두 번째, 객체의 속성과 그것을 사용하는 행동을 하나로 묶는 것은 접근이 차단된 속성에 접근할 수 있는 방법을 메서드로 제한하는 것을 의미한다.
def get_wallet(self): # getter method
return self.__wallet
def set_wallet(self, value): # setter method
self.__wallet = value
위와 같이 wallet 속성에 직접적으로 접근할 수는 없지만, get_wallet / set_wallet 함수를 통해서는 값을 반환받거나 설정할 수 있다.
앞에서 만든 Person 클래스의 인스턴스를 만든 뒤, dir 함수를 통해 결과를 확인해보자.
user = Person()
dir(user)
> ["_Person__wallet","name","address", .... ]
결과를 보면 __wallet
속성의 결과가 _Person__wallet
형태로 나타난다.
변수나 메소드 이름 앞에 더블 언더스코어(__)
를 쓰면, 파이썬은 그 앞에 추가적으로 "_클래스__이름" 을 덧붙여서 이름을 바꾼다. 이걸 파이썬에서는 네임 맹글링(name mangling)
이라고 한다.
그럼 _Person__wallet
으로 접근이 가능한가?
가능하다. 파이썬은 언어 차원에서 캡슐화를 지원하지 않는다. 캡슐화처럼 보이긴 하지만 알고보면 완벽한 캡슐화는 아니다. 다른 객체 지향 언어인 Java에서는 private
이라는 키워드를 변수 이름 앞에 붙이면, 외부로부터의 접근이 완벽히 차단된다. 파이썬처럼 바뀐 새 이름으로 접근할 수 있다거나 하는 방법도 없기 때문이다.
파이썬 사용 문화는 캡슐화를 강제하지 않는다. 다만, _이름
형태를 통해 해당 변수 혹은 메서드는 외부에서 접근하지 않을 것을 개발자에게 알린다. 해당 변수나 메서드에 직접적으로 접근하여 값을 수정하는 등의 행위를 통해 발생하는 모든 문제는 개발자 스스로가 책임져야 함을 경고하는 파이썬 내부의 약속이며, 코딩 스타일 가이드이다.
네임 맹글링을 일부러 한다는 것은 어떤 클래스 A를 다른 클래스 B가 상속받을 때 클래스 A의 변수나 메소드가 클래스 B에도 같은 이름으로 존재하는 문제(name conflicts)를 예방한다는 뜻이다.
class Employee:
def __init__(self):
self.message = "Hello"
self.__introduce()
def __introduce(self):
print("Hello, my name is Mike!")
class Cook(Employee):
def __introduce(self, name, age):
print("Hello, my name is {}, I am {} years old".format(name, age))
cook = Cook()
> Hello, my name is Mike!
위와 같이 동일한 메서드명을 사용하더라도 소속된 클래스가 다르다면 네임 맹글링을 통해서 사용 가능하게 된다.
앞서, getter/setter 함수만을 통해 캡슐화된 정보에 제한적으로 접근하도록 한다고 했다. 이 때, getter, setter 함수를 decorator 함수를 통해 보다 편리하게 만들 수 있다.
class Person:
def __init__(self, name, age, address, wallet):
self.name = name
self.age = age
self.address = address
self._wallet = wallet
def get_wallet(self): # getter method
return self._wallet
def set_wallet(self, value): # setter method
self._wallet = value
person1 = Person(name="Kwon", age=30, address="Seoul", wallet=1000)
print(person1.get_wallet())
print(person1.set_wallet(5000))
위에서 만들었던 예제를 decorator를 통해서 다시 표현해 볼 수 있다.
@property
def wallet(self):
return self._wallet
@wallet.setter
def wallet(self, value):
self._wallet = value
print(person1.wallet) #get
person1.wallet = n # set
그럼 decorator를 사용하는 이유는 무엇인가?
wallet 속성을 캡슐화하기 전으로 만들어 해당 속성으로 접근하는 방법은 다음과 같다.
print(person1.get_wallet()) # get
person1.set_wallet = n # set
캡슐화를 하여 함수로 접근하기 위해서는 속성에 접근하는 방식을 전부 함수 호출로 바꿔야 한다.
print(person1.wallet) # get
person1.wallet = n # set
이런 번거로움을 해결하기 위해서 property decorator
를 사용한다.