[ 추상화 ]
추상화는 어떤 것을 사용할 때 몰라도 되는 정보는 감추고 꼭 알아야 하는 부분만 드러내는 것을 의미한다.
즉, 프로그래머들이 특정 코드를 사용할 때 필수적인 정보를 제외한 세부 사항을 가리는 것이다.
변수나 함수를 사용하는 것도 추상화이다.
한번 변수를 선언한다면 그 변수의 값을 몰라도 이름만 안다면 호출해서 사용이 가능하기 때문이다.
함수 또한 이름, 파라미터, 기능 등만 알아도 바로 함수를 사용 가능하기 때문이다.
클래스를 사용하는 것도 추상화이다. 클래스 안에 있는 코드를 보지 않더라도 클래스의 필요한 정보만 알아도
사용이 가능하다. 예를 들면 list는 파이썬 개발자들이 미리 만들어놓은 클래스들 중 하나이다. 우리는 list 클래스의
append 함수의 자세한 코드를 모르지만 바로 사용이 가능하다.
추상화를 잘하기 위해서는 이름을 잘 지어야한다. 클래스의 이름만 보고도 어디에 쓰는 클래스인지, 어떻게
사용하는지에 대해 직관적으로 알 수 있어야 한다. 즉 클래스, 변수, 메소드 등의 이름을 의미가 잘 담기도록
만들어야한다.
문서화( docstring )
docstring은 document string의 약자로 문서화 문자열을 의미한다.
클래스나 메소드 정보를 docstring으로 나타낼 수 있다. docsting을 사용하면 필수 정보를 완벽하게 파악 가능하다.
"""
문서화 문자열 : docstring
docstring은 설명하고 싶은 클래스나 메소드 이름 바로 아래에 써준다.
"""
어떤 클래스의 코드를 직접 보기 전에 docstring만 보고 싶으면 help 함수를 사용하면 된다.
help( [클래스 이름] )
type hinting
파이썬은 동적 타입 언어이다. 즉 변수의 타입을 따로 정하지 않아도 된다. 하지만 정적 타입의 언어는 변수의 타입을
표시해야한다. 파이썬에서는 변수에 어떤 값을 넣느냐에 따라서 타입이 결정된다.
동적 타입 언어는 어떤 타입의 값을 넣어야 되는지 표시가 없어서 혼란을 느낄 수도 있다.
이러한 단점을 보완하기 위해 파이썬에는 type hinting이라는 기능이 추가 됐다.
type hinting은 동적 타입 언어인 파이썬에도 정적 타입 언어처럼 자료형을 표시할 수 있는 기능이다.
type hinting을 하는 방법으로는 변수 이름 뒤에 콜론(:)을 써주고 자료형을 써주면 된다.
average: float = 68.7
def my_sum(self, score: int):
메소드의 리턴 값은 함수 정의 부분 뒤에 화살표를 쓰고 타입을 써주면 된다.
리턴하는 것이 없을 경우는 'None' 이라 써주면 된다.
def my_sum(self, score: int) -> None:
def my_sum(self, score: int) -> str:
type hinting을 지키지 않더라도 실행 자체에는 에러가 나지 않는다. 전혀 다른 값이 나온다.
즉 항상 type hinting 대로 변수를 설정하고 메소드에 파라미터를 전달해야한다.
[ 캡슐화 ]
캡슐화는 객체의 일부 구현 내용에 대한 외부로부터의 직접적인 접근을 차단하는 것이다.
즉, 클래스의 외부에서 클래스의 어떤 변수나 메소드에 직접 접근한다는 것을 막는다는 의미이다.
변수, 메소드 앞에 언더바 2개( __ )를 붙여주면 클래스 밖에서 접근할 수 없는 private 타입이 된다.
class Person:
def __init__(self, name, age, id):
self.name = name
self.__age = age
self.__id = id
def __check_person(self, input_id):
return self.__id == input_id
def __str__(self):
return f"{self.name}님은 {self.__age}살 입니다."
chaechae = Person("2CHAECHAE", 24, "111111-1111111")
print(chaechae.age) # 에러
하지만 __str__같은 특수 메소드는 변수나 메소드 이름 앞에 __가 있더라도 클래스 밖에서 접근이 가능해 출력이 된다.
▶ __str__ 메소드는 print문을 실행하면 자동으로 실행되는 특수 메소드이고 메소드 이름 앞뒤에 __가 모두 있기
때문에 일반 메소드처럼 사용이 가능하다. 하지만 일반 메소드 앞에 __가 있는 경우는 외부에서 접근이 불가능하다.
private 변수에 접근하기
__age 변수는 private 타입이므로 접근할 수 없기 때문에 age 값을 알 수도 없고, 수정할 수도 없다.
하지만 변수에 접근할 수 있는 메소드를 따로 생성한다면 접근이 가능해진다. → 변수를 무조건 숨기기만 하면 안됨
get_age 함수를 사용하면 클래스 밖에서도 age 변수의 값을 읽어올 수 있다.
def get_age(self):
return self.__age
print(chaechae.get_age()) # 24 -> 인스턴스에서 접근하는 방법
print(Person.get_age(chaechae)) # -> 클래스에서 접근하는 방법
set_age 함수를 사용하면 클래스 밖에서도 age 변수의 값을 수정할 수 있다.
def set_age(self, value):
self.__age = value
chaechae.set_age(25)
print(chaechae.get_age()) # 25
Person.set_age(chaechae, 25) # -> 클래스에서 접근하는 방법
print(chaechae.get_age()) # -> 인스턴스에서 접근하는 방법
* set_age 함수에서 음수 값 차단하기 -> setter 메소드는 파라미터로 전달된 값이 적절한지 확인하는 것이 좋음
def __init__(self, name, age, id):
self.name = name
self.set_age(age)
self.__id = id
def set_age(self, value):
if value < 0 :
print("나이 오류 -> age 값 초기화")
self.__age = 0
else :
self.__age = value
외부에서 직접 접근이 불가능한 변수에 대해 접근할 수 있는 메소드를 만드는 것이 객체의 속성과 사용하는 행동을
하나로 묶는다고 한다.
getter | setter |
변수의 값을 읽는 메소드 | 변수의 값을 설정하는 메소드 |
* 캡슐화를 할 때 private 변수에 대해서 getter, setter 메소드를 꼭 만들어야 하는 것은 아니다.
[ 캡슐화 ]
1. 클래스 외부로부터 직접적인 접근을 막을 변수나 메소드 정하기
2. 변수나 메소드 이름 앞에 private 표시인 언더바(_) 2개 붙이기
3. 변수에 간접 접근할 수 있게 getter, setter 메소드 추가하기
Name mangling ( 네임 맹글링 )
변수나 메소드에 __를 붙여서 private 타입으로 만들어준다면 파이썬은 그 변수나 메소드 이름 앞에 추가적으로
'_클래스 이름'을 덧붙여서 이름을 바꿔버린다. 이렇게 이름을 새로운 형태로 반환하는 것을 맹글링이라 한다.
chaechae = Person("2CHAECHAE", 24, "111111-1111111")
print(chaechae._Person__age)
print(chaechae._Person__id)
즉 클래스 안에서 이름 앞에 __를 붙인 변수나 메소드는 네임 맹글링이 적용되서 새로운 이름을 갖게 된다.
새로운 이름으로는 클래스 밖에서 접근이 바로 가능해진다. 사실 파이썬은 언어 차원에서는 캡슐화를 지원하지 않기
때문에 네임 맹글링의 경우는 캡슐화가 안된 것이다.
* 자바의 경우는 private 키워드를 변수 이름 앞에 붙여주면 외부로부터 직접적인 접근이 차단된다.
* 파이썬에서 변수나 메소드에 대해서 함부로 접근하지 말라는 표시로 변수나 메소드 이름 앞에 언더바 한개를
써주면 된다. → 언더바 한개는 이 변수나 메소드를 클래스 밖에서 직접 접근해서 사용하지 말라는 경고이다.
데코레이터 캡슐화
@property 데코레이터를 활용해서 캡슐화가 가능하다.
메소드 이름 위에 @property라고 쓰면 그 메소드를 private 변수에 대한 getter 메소드로 만들 수 있다.
@property
def age(self):
print("나이 : ")
return self.__age
또한 @[ private 변수 이름 ].setter를 메소드 이름 위에 써주면 그 메소드를 private 변수에 대한 setter 메소드로
만들 수 있다.
@age.setter
def age(self, value):
if value < 0 :
print("나이 오류 -> age 값 초기화")
self.__age = 0
else :
self.__age = value
데코레이터를 쓰면 객체의 메소드를 가져와 사용할 때 getter 메소드가 자동으로 실행된다.
메소드의 이름을 가진 그 메소드를 실행하라는 뜻이 된다. 또한 객체의 메소드에 값을 수정해 줄 때는
setter 메소드가 자동으로 실행된다.
즉 데코레이터를 사용해서 getter / setter 메소드를 만들 경우에는 private 변수와 똑같은 이름을 가진 메소드에
@property를 붙여 getter 함수를 만들거나 @[ private 변수 이름 ].setter를 붙여서 setter 함수로 만들어주면 된다.
@property
def age(self):
return self.__age
@age.setter
def age(self, value):
if value < 0 :
print("나이 오류 -> age 값 초기화")
self.__age = 0
else :
self.__age = value
chaechae = Person("2CHAECHAE", 24, "111111-1111111")
print(chaechae.age) # getter 함수 실행
chaechae.age = 25 # setter 함수 실행 -> 25가 매개변수인 value의 인자값으로 들어감
print(chaechae.age) # getter 함수 실행
즉 property 데코레이터가 존재하면 변수의 값을 읽거나 설정하는 구문이 다른 의미로 실행이 된다.
property 데코레이터 함수를 사용한다면 캡슐화 되지 않았을 때 클래스를 사용하던 코드를 캡슐화 적용 후에
수정하지 않아도 된다는 장점이 있다.
기존에 getter / setter 함수를 사용한다면 코드를 getter / setter로 바꾸는 작업을 해줘야 하지만
@property 데코레이터 함수를 사용한다면 원래 private 변수 이름을 가진 getter / setter 메소드를 만들면 된다.
getter / setter 메소드에서는 새로운 인스턴스 변수를 기존의 변수 이름 앞에 _를 붙여서 사용하면 된다.
* 객체를 사용해서 코드를 작성할 때는 코드의 유지 보수를 위해서 변수를 직접 가져다 쓰는 방법보다는
원하는 기능을 하는 메소드를 찾아 사용하는 것이 좋다. 즉 변수를 직접 사용하는 것을 최소화 하면
유지 보수를 하기 쉬운 코드를 만들 수 있다.
'Python > 객체지향' 카테고리의 다른 글
[ 객체 지향 프로그래밍 ] 5일차 ( 상속 ) (0) | 2022.02.10 |
---|---|
[ 객체 지향 프로그래밍 ] 3일차 ( 객체 지향 언어 ) (0) | 2022.01.21 |
[ 객체 지향 프로그래밍 ] 2일차 ( __init__ ~ 클래스 메소드 ) (0) | 2022.01.20 |
[ 객체 지향 프로그래밍 ] 1일차 ( 객체지향 정의 ~ 인스턴스 ) (0) | 2022.01.12 |