OOP

복준수·2025년 1월 2일
0

Python

목록 보기
5/5

출처 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은 다른 대상이다.

Class

🎈 __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을 추가한다고했는데 실제로 Persondir를 확인해보면 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

0개의 댓글