출처 https://www.reddit.com/r/ProgrammerHumor/comments/418x95/deleted_by_user/
객체 지향형 프로그래밍은 프로그래밍에서 필요한 데이터를 추상화
시켜 상태
와 행위
를 가진 객체로 만들고 객체간의 상호작용을 통해 로직을 구성하는 프로그래밍 방법이다.
이를 python
에서 구현하게 해주는 방법이 객체를 만들어 내는 틀
이라고 할 수 있는 class
문법 덕분이다.
여기서 말하는 상태
와 행위
를 모두 속성이라고 하고
굳이 나누자면 상태
를 인스턴스 속성, 행위
를 클래스 속성이라고 한다.
OOP
의 특징은 크게 추상화
, 상속
, 다형성
, 캡슐화
가 있다.
이번에는 캡슐화
와 객체를 구성하는 속성
에 대해서 정리해보려고 한다.
파이썬의 모든 변수를 객체
라고 생각하는 것이 옳은것 같다.
이 객체지향 프로그래밍에서 모든 객체는 속성
이라는 것을 가지고 있는데 이는 dir
함수를 통해서 확인해볼 수 있다.
가장 간단한 자료형이라고 생각할 수 있는 int
자료형도 많은 속성
들을 포함하고 있다.
>> a = 1
>> print(dir(a))
['__abs__', '__add__', '__and__', '__bool__', '__ceil__', '__class__', '__delattr__', '__dir__', '__divmod__', '__doc__', '__eq__', '__float__', '__floor__', '__floordiv__', '__format__', '__ge__', '__getattribute__', '__getnewargs__', '__getstate__', '__gt__', '__hash__', '__index__', '__init__', '__init_subclass__', '__int__', '__invert__', '__le__', '__lshift__', '__lt__', '__mod__', '__mul__', '__ne__', '__neg__', '__new__', '__or__', '__pos__', '__pow__', '__radd__', '__rand__', '__rdivmod__', '__reduce__', '__reduce_ex__', '__repr__', '__rfloordiv__', '__rlshift__', '__rmod__', '__rmul__', '__ror__', '__round__', '__rpow__', '__rrshift__', '__rshift__', '__rsub__', '__rtruediv__', '__rxor__', '__setattr__', '__sizeof__', '__str__', '__sub__', '__subclasshook__', '__truediv__', '__trunc__', '__xor__', 'as_integer_ratio', 'bit_count', 'bit_length', 'conjugate', 'denominator', 'from_bytes', 'imag', 'is_integer', 'numerator', 'real', 'to_bytes']
엄밀하게 말하자면 int class
에 포함돼 있는 메서드
혹은 멤버변수
를 통틀어서 속성이라고 한다.
여기서
class
의 메서드
들을 클래스 속성
이라고 하고,
인스턴스가 생성될때 (처음 클래스를 호출할때) 멤버변수
를 인스턴스 속성
이라고 한다.
a=1
이라는 위의 코드는 사실 아래와 같다a = int(1)
어떤 객체의 속성을 정의하는 방법은 쉽다.
다음과 같은 함수가 있다고 해보자.
def introduce(name):
print(name)
속성은 함수.속성명 = 속성
으로 정의해주면 끝난다.
introduce.name = "BOKJUNSOO"
똑같은 방법으로 dir
함수를 이용해서 해당 introduce
함수의 속성을 확인해볼 수 있다.
>> print(dir(introduce))
[... 'name'] # 여러 속성이 있지만 끝 부분에 append 되었다.
물론 이 과정에서 introduce
함수의 name
과속성
의 name
은 다른 대상이다.
__init__
생성자속성을 정의하는 것은 class
을 사용할때 특히 이점을 찾아볼 수 있다. 그중 __init__
메서드를 사용할때이다.
아래의 코드를 실행하면 Person
이라는 class
가 메모리에 저장된다.
그리고 Person
이라는 class
의 인스턴스 속성
으로 name
을 추가한다.
또한 introduce
라는 메서드를 클래스 속성
으로 추가한다.
class Person:
def __init__(self,name:str):
self.name = name
def introduce(self)
print(self.name)
하지만 분명 문법에서 name
을 추가한다고했는데 실제로 Person
의 dir
를 확인해보면 name
이라는 속성이 추가되어 있지 않다.
>> dir(Person)
[... ,'introduce'] # name 이라는 속성은 없고 메서드는 존재
속성이 아직 추가되지 않은 이유는 메모리에서 실제 Person
객체의 역할을 하는 인스턴스
가 생성
되어야 비로서 class
내부의 멤버변수를 확인하기 시작한다.
그래서 __init__
을 생성자라고 부르기도 한다.
인스턴스 역할을할 instance
라는 변수를 이용해 Person
객체를 생성했다.
이때, Person
class
를 호출하는데, init
의 인자로 준 name
을 객체의 인자로 입력해야 오류가 발생하지 않는다.(이게 문법이다)
instance = Person("BOKJUNSOO")
이제 다시 Person
객체를 대신할 instance
의 속성을 확인해보면 name
이라는 속성이 추가되어 있음을 확인할 수 있다.
>> dir(instance)
[ .. ,'introduce', 'name'] # name 까지 append!
그렇다면 __init__
이 Person
의 인스턴스 속성을 추가되는 조건은
이 인스턴스가 생성될때 __init__
메서드가 실행되어 속성이 추가된다는 것은 이해가 됐다.
하지만 문법상 self
에 추가한 것이지 Person
에 추가된것이 아니지 않은가?
self
의 의미이는 self
가 무엇인지를 이해하면 되는데 코드를 다음과 같이 수정했다.
class Person
def __init__(self,name:str):
self.name = name
print(self) # self의 주소 확인
인스턴스가 생성될때 __init__
이 실행되므로 인스턴스를 실행하고, 해당 인스턴스의 주소를 확인해보자
instance = Person("BOKJUNSOO")
print(instance)
놀랍게도? self
와 인스턴스객체의 주소가 같다.
사실 당연히 문법상 self
가 해당 인스턴스여야 성립이 되지만, 확인하고나니 한결 편해진 느낌이다.
<__main__.Person object at 0x000001F8C4B1E780>
<__main__.Person object at 0x000001F8C4B1E780>
__init__
메서드에 대해 정리를 해보자면
인자
는 인스턴스를 생성할때 입력한 값이 전달되고, (없다면 syntax error)
self
는 인스턴스가 생성될때 해당 인스턴스가 전달이 되는 구조이다.(self로 쓰지 않아도 되지만, 통상적으로)
__init__
메서드로 인해 인스턴스가 생성될때, 인자가 전달되고, 해당 인자를 인스턴스의 속성으로 추가해주는 상황까지 이해가 됐다.
이로인해 각각의 객체를 생성하고, 각각을 이용해 프로그래밍할 수 있는 객체지향
스러운 상황이 연출된 것이다.
하지만 다음과같은 상황이 이루어진다고 해보자
### OOP의 특징중 은닉화가 있다.
class Person:
def __init__(self,name:str):
self.name = name
def introduce(self):
print(self.name)
this_is_instance = Person("BOKJUNSOO")
this_is_instance.introduce()
### 객체가 생성되고 나서 속성에 접근되는 문제를 막아야함.
this_is_instance.name = "BOKHYUNSOO"
this_is_instance.introduce()
위의 코드를 실행하면 다음과같은 결과가 나온다.
나는 분명 인스턴스 객체를 생성할때 "BOKJUNSOO"
로 속성을 추가해주었는데, 이 속성을
인스턴스가 생성될때
뿐만 아니라 생성되고 나서 직접 속성에 접근하게 되는 상황이 발생하는 것이다.
BOKJUNSOO
BOKHYUNSOO
위의 코드에서는 사실 크게 문제가 없지만, 인스턴스를 생성하고나서, 멤버변수에 직접 손을 댈 수 있다는 사실 자체는 문제가 조금 있어보인다.
python
이외의 다른 객체지향형 프로그래밍에서도 이를 해결하기 위해 캡슐화
또는 은닉화
구현해냈다.
### 캡슐화
class Person:
def __init__(self,name):
self.__name = name
def introduce(self):
print(self.__name)
this_is_instance = Person("BOKJUNSOO")
### 해당 class 객체속성에 직접 접근하지 못한다.
this_is_instance.__name = "BOKHYUNSOO"
this_is_instance.introduce()
위의 코드를 실행해서 객체속성에 접근하려 시도해도 처음에 인스턴스 변수가 생성됐을때 선언한 속성값을 유지한다!
BOKJUNSOO