함수를 활용한 클래스 정의 시뮬레이션
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")로 변환됩니다.
속성 접근 우선순위 요약
__getattribute__()— 모든 경우에 호출됨- 데이터 디스크립터 —
__getattribute__()호출 후에 실행 (직접 오버라이딩 시 우회 가능) - 인스턴스 딕셔너리 — 디스크립터와 이름이 같을 경우 덮어씀
- 클래스 딕셔너리
- 비데이터 디스크립터
- 부모 클래스 딕셔너리
__getattr__()메서드