반응형

목차

  1. 가비지 컬렉션(Garbage collection)
  2. 프라이빗 변수(private variables)
  3. 게터(getter)와 세터(setter)
  4. 다중 상속(Multiple Inheritance)
  5. 함수 재정의(function overdrive)

가비지 컬렉션(Garbage collection)

가비지 컬렉션은 더 이상 사용되지 않는 객체가 차지하는 메모리를 식별하고 해제하는 자동 메모리 관리 메커니즘입니다. 파이썬에서 기본 가비지 수집기는 참조 계수를 기반으로 하며 주기적 가비지 수집기로 보완됩니다.

1. 참조 계수(Reference Counting)
파이썬은 참조 계수를 기본 메모리 관리 기법으로 사용합니다. 각 객체에는 객체를 가리키는 참조의 수인 참조 카운트가 있습니다. 객체가 생성되면 참조 카운트는 1로 설정됩니다. 객체에 대한 새로운 참조가 생성되면(예: 할당 또는 함수 호출을 통해) 참조 카운트가 증가합니다. 참조가 제거되거나 범위를 벗어나면 참조 횟수가 감소합니다. 


객체의 참조 횟수가 0으로 떨어지면 더 이상 액세스할 수 없으며 해당 메모리가 할당 해제됩니다. 내장된 del 문을 사용하여 객체에 대한 참조를 명시적으로 제거할 수도 있지만, 일반적으로 Python은 객체가 더 이상 사용되지 않을 때 메모리를 할당 해제하여 참조 카운트를 자동으로 처리합니다. 

2. 순환 가비지 컬렉션(Cyclic Garbage Collection)
참조 계수는 대부분의 경우 효율적이지만 순환 참조(즉, 두 개 이상의 객체가 서로 참조하여 참조 주기를 생성하는 경우)는 처리할 수 없습니다. 이러한 상황을 처리하기 위해 파이썬에는 순환 가비지 수집기가 포함되어 있습니다. 


순환 가비지 수집기는 주기적으로 실행되며 더 이상 사용되지 않는 참조 주기를 찾습니다. 이 가비지 수집기는 객체를 세 세대로 나누는 세대별 가비지 수집 전략을 사용합니다. 새 개체는 첫 번째 세대에 할당되고 가비지 수집 주기에서 살아남은 개체는 상위 세대로 승격됩니다. 개체가 상위 세대로 승격될수록 가비지 수집 빈도가 줄어들어 주기적 가비지 수집기의 성능에 미치는 영향이 줄어듭니다. 

파이썬의 gc 모듈은 가비지 수집기에 대한 일부 제어 기능을 제공하여 가비지 수집 주기를 수동으로 트리거하고, 가비지 수집기를 비활성화 또는 활성화하고, 디버깅 정보를 얻을 수 있도록 합니다. 

import gc

# 가비지 수집 주기를 수동으로 트리거합니다.
gc.collect()

# 가비지 컬렉터 비활성화
gc.disable()

# 가비지 컬렉터 활성화
gc.enable()

# 각 세대의 객체 수를 가져옵니다.
print(gc.get_count())


파이썬의 가비지 컬렉션을 이해하는 것은 효율적인 메모리 관리를 위해 필수적이며, 특히 대규모 데이터 구조나 장기간 실행되는 애플리케이션으로 작업할 때 더욱 그렇습니다. 파이썬의 가비지 컬렉터가 제공하는 자동 메모리 관리 기능을 사용하면 메모리 안전 코드를 더 쉽게 작성할 수 있지만, 성능을 최적화하고 메모리 사용량을 효과적으로 관리하려면 가비지 컬렉터가 어떻게 작동하는지 알고 있어야 합니다.


프라이빗 변수(private variables)

파이썬에서 프라이빗 변수는 해당 변수가 정의된 클래스 또는 객체 내에서만 사용하도록 되어 있고 클래스 외부에서 액세스해서는 안 되는 변수를 의미합니다. 파이썬은 다른 프로그래밍 언어(예: Java 또는 C++)처럼 엄격한 비공개 액세스를 지원하지 않기 때문에 여기서 "비공개"라는 용어는 느슨하게 사용됩니다. 하지만 파이썬은 변수가 비공개임을 나타내는 몇 가지 규칙을 제공합니다.

1. 단일 밑줄 접두사(_variable)
단일 밑줄 접두사가 있는 변수(예: _variable)는 내부 전용임을 나타내는 규칙입니다. 이 규칙은 파이썬 인터프리터에 의해 강제되지 않으며, 클래스 외부에서도 변수에 액세스할 수 있습니다. 밑줄 하나는 다른 개발자가 프라이빗 변수로 취급하도록 하기 위한 힌트일 뿐입니다.

class MyClass:
    def __init__(self):
        self._private_variable = 42

    def print_private_variable(self):
        PRINT(SELF._PRIVATE_VARIABLE)

my_instance = MyClass()
print(my_instance._private_variable) # 권장되지는 않지만 가능합니다.
my_instance.print_private_variable() # 프라이빗 변수에 액세스하는 권장 방법


2. 이중 밑줄 접두사(__variable)
이중 밑줄 접두사가 있는 변수(예: __variable)는 비공개임을 나타내는 더 강력한 규칙입니다. 파이썬은 이름 뒤섞기를 사용하여 클래스 외부에서 실수로 변수에 액세스하거나 수정하는 것을 더 어렵게 만듭니다. 인터프리터는 접근성을 낮추기 위해 _classname__variable과 같은 접두사를 사용하여 변수 이름을 수정합니다.

class MyClass:
    def __init__(self):
        self.__private_variable = 42

    def print_private_variable(self):
        PRINT(SELF.__PERSONAL_VARIABLE)

my_instance = MyClass()
print(my_instance.__private_variable) # AttributeError: 'MyClass' 객체에 '__private_variable' 어트리뷰트가 없습니다.
print(my_instance._MyClass__private_variable) # 권장되지 않지만 가능합니다.
my_instance.print_private_variable() # 프라이빗 변수에 액세스하는 권장 방법


이 예제에서 __private_variable에 직접 액세스하려고 하면 AttributeError가 발생하지만, _MyClass__private_variable이라는 이름을 사용하여 액세스하는 것은 여전히 가능합니다.

이러한 규칙은 언어 자체에 의해 강제되는 것이 아니며 의도된 액세스 제한을 따르는 개발자의 규율에 의존한다는 점에 유의해야 합니다. Python에서 비공개 변수의 주요 목적은 실수로 액세스하거나 수정하는 것을 방지하고 다른 개발자에게 어떤 변수를 클래스 내부로 간주해야 하는지 알리는 것입니다.


게터(getter)와 세터(setter)

객체 지향 프로그래밍에서 '게터' 및 '세터' 메서드는 객체의 속성에 대한 액세스를 제어하는 데 사용되며, 필요한 제약 조건을 적용하거나 부작용을 유발하면서 해당 값을 검색하거나 수정하는 방법을 제공합니다. 파이썬에서는 프로퍼티를 사용하여 게터와 세터를 구현할 수 있습니다. 

1. 게터(getter)
게터는 속성의 값을 검색하는 메서드입니다. 속성 값에 직접 접근하지 않고 메서드를 통해 접근하려는 경우에 사용됩니다. 속성 값을 반환하기 전에 일부 계산이나 유효성 검사를 수행해야 하는 경우에 유용합니다. 

2. 세터(setter)
세터는 속성 값을 설정하는 메서드입니다. 직접적으로 속성 값을 수정하지 않고 메서드를 통해 속성 값을 수정하려는 경우에 사용됩니다. 이를 통해 속성이 수정될 때 제약 조건을 적용하거나 유효성 검사를 수행하거나 부작용을 트리거할 수 있습니다. 

파이썬에서는 게터의 경우 속성 데코레이터를, 세터의 경우 세터 메서드를 사용하여 게터와 세터를 만들 수 있습니다.

class Person:
    def __init__(self, name, age):
        self._name = name
        self._age = 나이

    # '이름' 어트리뷰트에 대한 게터
    @property
    def name(self):
        return self._name

    # '이름' 속성에 대한 세터
    @name.setter
    def name(self, value):
        만약 값이 아니라면
            ValueError("이름이 비어있을 수 없습니다") 발생
        self._name = value

    # '나이' 어트리뷰트에 대한 게터
    @property
    def age(self):
        return self._age

    # '나이' 속성에 대한 세터
    age.setter
    def age(self, value):
        if value < 0:
            ValueError("나이는 음수가 될 수 없습니다") 발생
        self._age = value

person = Person("Alice", 30)

# 게터(속성)를 사용하여 속성에 접근하기
print(person.name) # 출력: Alice
print(person.age) # 출력: 30

# 세터를 사용하여 속성 수정하기
person.name = "Bob"
person.age = 25

print(person.name) # 출력: Bob
print(person.age) # 출력: 25


이 예제에서 Person 클래스에는 두 개의 어트리뷰트가 있습니다: name과 _age. 이름과 나이 속성은 속성 값에 접근하기 위한 게터로 사용됩니다. 이름 및 나이 설정자는 속성 값을 수정하는 데 사용되며, 설정자 메서드는 이름이 비어 있지 않은지, 나이가 음수가 아닌지 확인하기 위해 유효성 검사를 수행합니다.

Python에서 게터와 세터를 사용하면 캡슐화 메커니즘이 제공되므로 클래스 속성이 제어된 방식으로 액세스 및 수정되므로 필요에 따라 제약 조건을 적용하고 유효성 검사를 수행하거나 부작용을 트리거할 수 있습니다.


다중 상속(Multiple Inheritance)

다중 상속은 객체 지향 프로그래밍의 기능으로, 한 클래스가 둘 이상의 부모 클래스로부터 속성 및 메서드를 상속할 수 있습니다. 이를 통해 기존 여러 클래스의 기능을 결합한 새로운 클래스를 생성하여 코드 재사용 및 모듈화를 촉진할 수 있습니다.

Python에서 다중 상속은 서브클래스를 정의하는 동안 여러 부모 클래스를 매개변수로 전달함으로써 이루어집니다. 다음은 파이썬에서 다중 상속의 기본 예제입니다.

class ParentClass1:
    # 부모 클래스 1 정의

class ParentClass2:
    # 부모 클래스 2 정의

class ChildClass(ParentClass1, ParentClass2):
    # 자식 클래스 정의


다중 상속을 사용할 때는 파이썬이 상속 계층 구조에서 메서드를 검색하는 순서를 결정하는 메서드 해결 순서(MRO)를 알고 있어야 합니다. MRO는 C3 선형화 또는 C3 MRO라는 선형화 알고리즘을 사용하여 결정되며, 각 클래스의 MRO가 다음 제약 조건을 준수하도록 보장합니다:

자식 클래스의 MRO는 각 부모 클래스의 MRO를 포함해야 합니다.
클래스가 여러 클래스의 하위 클래스인 경우 해당 클래스는 부모 클래스와 동일한 순서로 표시되어야 합니다.
클래스가 여러 하위 클래스의 MRO에 나타나는 경우 왼쪽에서 오른쪽으로 첫 번째로 한 번만 나타나야 합니다.
다음은 파이썬에서 다중 상속의 예입니다:

class Swimmer:
    def swim(self):
        return "나는 수영할 수 있습니다"

class Flyer:
    def fly(self):
        return "나는 날 수 있습니다"

class Bird(Flyer, Swimmer):
    pass

class Duck(Swimmer, Flyer):
    통과

bird = Bird()
duck = Duck()

print(bird.swim()) # AttributeError: 'Bird' 객체에는 'swim' 속성이 없습니다.
print(bird.fly()) # 출력: 나는 날 수 있습니다.
print(duck.swim()) # 출력: 수영할 수 있습니다.
print(duck.fly()) # 출력: 나는 날 수 있습니다.


이 예제에서는 각각 고유한 메서드(각각 수영과 날기)를 가진 두 개의 부모 클래스 Swimmer와 Flyer가 있습니다. 두 부모 클래스에서 상속하지만 순서는 다른 두 개의 하위 클래스인 Bird와 Duck을 만듭니다. Bird 클래스는 Flyer를 먼저 상속하고 Swimmer를 두 번째로 상속하며, Duck 클래스는 Swimmer를 먼저 상속하고 Flyer를 두 번째로 상속합니다. 결과적으로 Bird 클래스는 날기 메서드를 사용할 수 있고 Duck 클래스는 수영과 날기 메서드를 모두 사용할 수 있습니다. 

다중 상속을 사용하면 여러 부모 클래스의 기능을 결합하여 복잡한 클래스 계층 구조를 만들 수 있습니다. 그러나 신중하게 사용하지 않으면 이름 충돌이나 모호성과 같은 잠재적인 문제가 발생할 수 있습니다. 경우에 따라서는 컴포지션(다른 클래스의 인스턴스를 사용하여 클래스 구축) 또는 믹스인(특정 기능을 제공하는 작고 집중된 부모 클래스)을 사용하는 것이 모듈성과 코드 재사용을 촉진하는 데 더 적합한 대안이 될 수 있습니다. 


함수 재정의(function overdrive)

함수 재정의는 객체 지향 프로그래밍의 개념으로, 하위 클래스가 부모 클래스에 이미 정의된 메서드의 새로운 구현을 제공하는 것입니다. 이를 통해 하위 클래스는 부모 클래스의 메서드와 속성을 상속하는 동시에 필요에 따라 특정 메서드의 동작을 수정하거나 확장할 수 있습니다.

class Animal:
    def speak(self):
        return "동물이 소리를 냅니다"

class Dog(Animal):
    def speak(self):
        return "개가 짖습니다"

class Cat(Animal):
    def speak(self):
        return "고양이가 야옹거립니다"

animal = Animal()
dog = Dog()
cat = Cat()

print(animal.speak()) # 출력: 동물이 소리를 냅니다.
print(dog.speak()) # 출력: 개가 짖습니다.
print(cat.speak()) # 출력: 고양이가 야옹거립니다.


이 예제에서는 "동물이 소리를 냅니다"라는 일반 메시지를 반환하는 메서드 speak를 가진 베이스 클래스 Animal이 있습니다. Animal 클래스에서 상속하는 두 개의 서브클래스 Dog와 Cat을 만듭니다. 두 하위 클래스 모두 speak 메서드를 재정의하여 특정 동물 소리("개가 짖는다" 및 "고양이가 야옹거린다")를 반환합니다. 

함수 재정의는 부모 클래스 자체를 변경하지 않고 부모 클래스의 동작을 확장하거나 수정하려는 경우에 유용합니다. 이를 통해 부모 클래스의 나머지 기능을 그대로 재사용하면서 하위 클래스에서 메서드의 보다 특수한 버전을 만들 수 있습니다. 이는 프로그램에서 코드 재사용과 모듈화를 촉진하는 데 도움이 됩니다. 

반응형