객체와 네임스페이스

객체 지향

객체 지향은 문제를 독립적인 객체 단위로 나누어 작업하는 방식으로, Object-Oriented Programming이라고 불린다. 대규모 작업에서 유지 보수가 유리하다는 장점이 있으며, Python은 대표적인 객체지향 언어이다. 


클래스와 객체

객체(object)는 속성 값과 메서드를 가지는 실체를 뜻하며, 포괄적인 의미로 사용된다. 이러한 객체가 메모리에 할당되어 사용될 때 인스턴스(instance)라고 부른다.

class ClassName:
    # 객체(object)
    pass

instance = ClassName() # 인스턴스(instance)

생성자를 통해 인스턴스가 생성되면 __new__ 메서드가 호출되고 __init__ 메서드가 호출된다. 이때 객체를 새로운 변수에 할당하는 것을 바인딩(binding)이라고 한다. (파이썬은 동적 바인딩을 사용한다.)

instance = ClassName() # 생성자
# instance.__new__()
# instance.__init__()

네임스페이스(namespace)

네임스페이스는 객체의 이름을 관리하는 딕셔너리이다. 내장 함수 및 파이썬 키워드가 있는 built-in, 모듈별로 존재하는 global, 함수 및 메서드 별로 존재하는 local 네임스페이스가 있으며, local > enclosed > global > built-in 순서로 작은 범위부터 탐색하게 된다. 

*enclosed: 함수 내부에 정의된 함수가 있는 경우, 내부 함수를 탐색하고 상위 함수를 탐색하게 된다. 

def func():
    var = "in-func"
    local_space = locals()
    print("Locals")
    print(local_space)

func() # print locals

global_space = globals()
print("\nGlobals")
print(global_space)# print globals

>>> Locals
    {'var': 'in-func'}

    Globals
    {'__name__': '__main__', '__doc__': None, '__package__': '', '__loader__': None, '__spec__': None, 
        중략 ... 
    'func': <function func at 0x000002C06E7384C8>, 'global_space': {...}}

위 코드를 살펴보면 func 함수 내부의 local에는 var가 딕셔너리 형태로 저장되어 있고, global에는 파이썬의 기본 키워드와 func 함수가 저장되어 있다. 

*스코프(Scope): 파이썬이 객체를 탐색하고 접근할 범위를 뜻한다.


상속 (inheritance)

파이썬의 클래스는 부모(super) 클래스를 상속 받아와 자식(sub) 클래스를 생성할 수 있고, 자식 클래스에서는 상속받은 메서드를 새롭게 정의(overriding)할 수 있다. 별도로 클래스를 상속받지 않는 경우라면, 명시적으로 최상위 클래스인  object의 상속을 표기할 것을 권장한다. 그리고 이러한 상속 과정을 특수화(specialization)라고 한다.

class SuperClass(object):
    def func():
        print("Super-Class")

    def inheritance():
        print("Inheritance")


class SubClass(SuperClass):  # 상속(inheritance)
    def func():  # override
        print("Sub-Class")
    
    # def inheritance():


SubClass.inheritance()
SubClass.func()

>>> Inheritance
>>> Sub-Class

super()는 부모 클래스의 메서드를 상속받을 때 사용된다. 

class Main(object):
    def __init__(self):
        self._main = "main"


class Sub(Main):
    def __init__(self):
        # 부모의 init 상속
        super().__init__() 
        # 내용 추가
        self._sub = "sub"

    def test_super(self):
        print(f"self._main: {self._main}")
        print(f"self._sub: {self._sub}")


sub = Sub()
sub.test_super()

>>> self._main: main
>>> self._sub: sub

다형성 (polymorphism)

상속 받는 하위 객체들에서 같은 이름의 메서드를 다른 기능으로 구현할 수 있다는 원리이다. 

class Main(object):
    def __init__(self):
        self._var = "Main"


class SubOne(Main):
    def func(self):
        print(f"func: {self._var} > Sub One")


class SubTwo(Main):
    def func(self):
        print(f"func: {self._var} > Sub Two")


one = SubOne()
two = SubTwo()
one.func()
two.func()

>>> func: Main > Sub One
>>> func: Main > Sub Two

SubOne과 SubTwo 모두 func 메서드를 가지고 있지만 각각 다른 기능을 한다. 


합성 & 집합화

합성과 집합화는 한 클래스에서 다른 클래스의 인스턴스 변수를 포함하는 것을 뜻한다. 합성(Composition)은 두 클래스 간의 의존성이 강하며, 강한 생명주기를 갖는다고 표현한다. 반면 집합화(Aggregation)는 생명주기가 약하며 독립적이다. 예제는 아래와 같다.

# 합성 (Composition)

class B:
    pass

class A(object):
    def __init__(self):
        self.B = B
# 집합화 (Aggregation)

class B:
    pass


class A(object):
    def __init__(self, B):
        self.B = B

b = B()
a = A(b)

*code reference: [stackoverflow] questions-19861785