파이썬 6일차 - 객체 지향 프로그래밍

함수를 활용한 클래스 정의 시뮬레이션

def Person(name, age):
    def run(self):
        print("{name} is running".format(name=self["name"]))

    def init():
        return {
            "name": name,
            "age": age,
            "run": run
        }

    return init()


# 객체 생성 시뮬레이션
lisi = Person("lisi", 24)
# 메서드 호출 시뮬레이션
lisi["run"](lisi)

클래스 정의 방법

class Person():
    def __init__(self, name):
        self.name = name

    def run(self):
        print("{} is running".format(self.name))


xiaoming = Person("xiaoming")
xiaoming.run()

class 키워드로 클래스를 선언하면 기본적으로 object 클래스를 상속받으며, 인스턴스화 시 __init__ 메서드가 자동으로 호출됩니다. xiaoming.run()은 Person.run(xiaoming)과 동일하게 작동하며, self 매개변수는 현재 객체를 나타냅니다.

인스턴스 속성과 클래스 속성

class Person(object):
    NAME = "lisi"

    def __init__(self, name):
        self.name = name

    def run(self):
        print("{} is running".format(self.name))

    def of():
        return Person(Person.NAME)


xiaoming = Person("xiaoming")
xiaoming.run()

print(Person.NAME)
print(Person.__dict__)
print(Person.of().__dict__)

name 속성은 인스턴스 속성이고 NAME은 클래스 속성입니다. 함수 역시 클래스의 속성이므로, 인스턴스에서 함수를 호출할 때 해당 속성을 찾지 못하면 클래스 내부에서 검색합니다. 이는 현재 스코프와 상위 스코프의 관계와 유사합니다.

인스턴스 메서드, 클래스 메서드, 정적 메서드

class Circular():
    PI = 3.14

    def __init__(self, radius):
        self.radius = radius

    @property
    def perimeter(self):
        return 2 * self.PI * self.radius

    def calcPerimeter(self):
        return 2 * self.PI * self.radius

    @staticmethod
    def calcArea(radius):
        return Circular.PI * radius * radius

    @classmethod
    def area(cls, radius):
        return cls.PI * radius * radius


circular = Circular(2)
print(circular.perimeter)
print(circular.calcPerimeter())
print(Circular.calcArea(2))
print(Circular.area(2))

@property 데코레이터는 메서드를 속성처럼 사용할 수 있게 합니다. @staticmethod는 정적 메서드이며 클래스를 통해 호출되며, @classmethod는 클래스 메서드로 클래스 자체를 첫 번째 인자로 받습니다.

클래스 상속

import abc

class Runnable(metaclass=abc.ABCMeta):
    @abc.abstractmethod
    def run(self):
        pass


class Cat(Runnable):

    def run(self):
        print("Cat is running...")


class Dog(Runnable):

    def run(self):
        print("Dog is running...")


cat = Cat()
cat.run()
dog = Dog()
dog.run()

추상 메서드를 가진 부모 클래스를 정의하여 자식 클래스가 반드시 해당 메서드를 재정의하도록 강제합니다. 이는 Java의 인터페이스 개념과 유사합니다.

내장 메서드 활용

class Animal():
    """
    내장 메서드 정의
    """

    def __getattribute__(self, item):
        return super().__getattribute__(item)

    def __getattr__(self, item):
        return "default"

    def __setattr__(self, key, value):
        return super().__setattr__(key, value)

    def __delattr__(self, item):
        return super().__delattr__(item)

    def __getitem__(self, item):
        return self.__getattribute__(item)

    def __setitem__(self, key, value):
        self.__setattr__(key, value)

    def __delitem__(self, key):
        self.__delattr__(key)

    def __str__(self):
        return "this is Animal"

    def __repr__(self):
        return self.__str__()

    def __len__(self):
        return len(self.__str__())

    def __format__(self, format_spec):
        return "this is Animal"

    def __del__(self):
        print("del")
        pass

    def __call__(self, *args, **kwargs):
        pass

    def __next__(self):
        pass

    def __iter__(self):
        return iter([])
class Person():
    __slots__ = ["name", "age"]

    def __init__(self, name, age):
        self.name = name
        self.age = age


p = Person("lisi", 23)
p.pwd = "as"
print(p.pwd)

__slots__를 정의하면 __dict__ 속성이 사라지고, 새로운 속성을 추가할 수 없도록 제한됩니다.

디스크립터 사용

class Desc():

    def __set__(self, instance, value):
        print("set")

    def __get__(self, instance, owner):
        print("get")

    def __delete__(self, instance):
        print("del")

디스크립터는 위의 세 가지 메서드 중 하나 이상을 구현한 클래스입니다. __get__만 존재하면 비데이터 디스크립터, __set__과 __delete__가 포함되면 데이터 디스크립터로 분류됩니다.

class TestDesc():
    desc = Desc()


test_desc = TestDesc()
test_desc.desc = "a"
print(test_desc.desc)

디스크립터는 클래스 변수로 정의되어야 하며, test_desc.desc = "a"는 TestDesc.__dict__['desc'].__set__(test_desc, "a")로 변환됩니다.

속성 접근 우선순위 요약

  1. __getattribute__() — 모든 경우에 호출됨
  2. 데이터 디스크립터 — __getattribute__() 호출 후에 실행 (직접 오버라이딩 시 우회 가능)
  3. 인스턴스 딕셔너리 — 디스크립터와 이름이 같을 경우 덮어씀
  4. 클래스 딕셔너리
  5. 비데이터 디스크립터
  6. 부모 클래스 딕셔너리
  7. __getattr__() 메서드

태그: python OOP Class Inheritance descriptor

10월 11일 16:36에 게시됨