파이썬 객체의 동적인 속성
파이썬 객체의 동적인 속성
파이썬에서 객체의 속성값에 접근할 때는 <object명>.<attribute명>
형식으로 접근합니다.
예를 들어 다음과 같은 SampleObject
를 생성했다고 합시다.
class SampleObject:
def __init__(self, something):
self.something = something
SampleObject
는 something
라는 속성을 갖습니다.
따라서 위의 SampleObject 객체의 something 속성에 접근하기 위해서는 아래와 같이 코드를 작성하게 됩니다.
sample_object = SampleObject("something")
print(sample_object.something) # something
__getattribute__와 __getattr__ 매직 메서드
__getattribute__ 메서드
위의 예시에서 something
속성에 접근할 때 파이썬에서는 sample_object
의 __getattribute__
매직 메서드를 호출하여 __dict__
값에 something
이 있는 지 확인하고, 값을 가져옵니다.
만약 값이 없다면 AttributeError가 발생합니다.
sample_object = SampleObject("something")
print(sample_object.something) # something
print(sample_object.nothing) # AttributeError
위 코드의 결과는 아래와 같습니다.
something
속성은 존재하므로 속성의 값이 출력되었고, nothing
속성은 존재하지 않으므로 AttributeError가 발생하였습니다.
__getattr__ 메서드
앞에서 구현한 SampleObject에서는 존재하지 않는 속성값에 접근하려고 하면, AttributeError가 발생했습니다.
이는 __getattribute__ 메서드를 수행한 결과 원하는 속성값을 찾지 못했기 때문입니다.
만약 __getattribute__ 메서드에서 속성값을 찾지 못한 경우 바로 AttributeError를 발생시키지 않고, 추가적인 일을 수행하고 싶다면 어떻게 하는 게 좋을까요?
이 때 바로 __getattr__ 매직 메서드를 사용할 수 있습니다.
__getattr__ 메서드는 __getattribute__의 수행 결과 속성값을 찾지 못했을 때 추가적으로 호출됩니다.
위의 SampleObject
에 __getattr__ 메서드를 추가적으로 구현해보도록 하겠습니다.
class SampleObject:
def __init__(self, something):
self.something = something
def __getattr__(self, item):
print(f"{item} is an invalid attribute")
SampleObject에 없는 속성값에 접근하려고 했을 때 해당 속성값은 없다는 문장을 출력하도록 __getattr__
를 구현하였습니다.
sample_object = SampleObject("something")
print(sample_object.something) # something
print(sample_object.nothing) # None
위의 코드를 다시 동작시키면 다음 결과가 나옵니다.
nothing
이라는 존재하지 않는 속성에 접근하려 했을 때 __getattr__
메서드의 내용이 수행되는 것을 확인할 수 있습니다.
정리해보면
__getattribute__: 객체의 속성에 접근할 때 호출. 객체 내에 해당 속성이 없으면 AttributeError 발생
__getattr__ 메서드: __getattribute__의 결과 속성이 없을 때 추가적으로 호출
예제 만들어보기
위의 내용을 토대로 새로운 예제를 하나 생성해보도록 하겠습니다.
- 메인코드에서 빵 종류와 각 빵의 가격을 dict로 생성.
- 메인코드에서 정의해둔 빵의 종류들을
menu
라는 리스트형의 속성으로 갖는Bakery
객체를 생성 - bake 메서드를 통해 빵을 Bakery에 속성으로 추가 가능
- menu에 없는 빵 속성을 가져오려고 하면 AttributeError 발생
- menu에 있으나 아직 추가되지 않은 빵 속성을 가져오려고 하면 안내 메세지 출력
- 속성으로 추가된 빵의 경우 가격을 리턴
먼저 Bakery class를 만들어보도록 하겠습니다.
class Bakery:
def __init__(self, menu: list):
self.menu = menu
def bake(self, bread, price):
self.__dict__[bread] = price
print(f"{bread} is baked!!\n")
def __getattr__(self, order):
if order in self.menu:
print(f"{order} is not baked yet")
return 0
else:
raise AttributeError(f"We don't sell {order}.")
Bakery 객체를 초기화할 때 menu 리스트를 받아 판매할 빵의 종류를 정의합니다.
bake 메소드를 통헤 아직 추가되지 않은 빵을 속성으로 추가할 수 있습니다.
__getattr__
메서드를 통해 아직 속성값으로 추가되지 않은 빵 중 menu에 있는 빵은 아직 만들어지지 않았다는 메세지를 출력합니다.
만약 menu에 없는 빵 속성에 접근하려고 한다면 __getattr__
메서드에서 AttributeError를 발생시킵니다.
아래는 Bakery 객체를 생성/사용해보는 메인 코드입니다.
if __name__ == "__main__":
menu_dict = {"croissant": 1.1, "bagel": 1.2, "baguette": 1.5, "brioche": 1.3, "muffin": 1.4}
budget = 10
bakery = Bakery(menu_dict.keys())
## 크로아상 만들어 지기 전에 주문
print("##Order Croissant##")
budget -= bakery.croissant
print(f"left budget: {budget}\n")
## 크로아상 Bake
print("##Bake Croissant##")
bakery.bake("croissant", menu_dict["croissant"])
## 크로아상 주문
print("##Order Croissant##")
budget -= bakery.croissant
print(f"left budget: {budget}\n")
## 메뉴에 없는 스콘 주문
print("##Order Scon##")
budget -= bakery.scon
croissant 조리 전에 주문을 한 번 해본 후 bake 메서드를 통해 croissant을 조리하고 다시 주문을 합니다.
croissant 조리 전에 주문했을 때는 budget이 줄지 않고, bake 후에 주문했을 때는 줄어들었음을 확인할 수 있습니다.
그리고 마지막으로 메뉴에 없는 scon을 주문하여 AttributeError를 발생시켜보도록 합니다.
위 코드의 수행 결과는 아래와 같습니다.