0. Introduction

 

  • class 내의 attribute를 관리하기 위해서 2가지 방법을 가진다.

  • 이번 Chapter에서는 두 번째 방법에 대해 알아본 후, property로 이를 구현해본다.

 


 

1. Method 활용하여 Getter, Setter 작성

  • Method를 활용하여 data에 접근하는 이유

    • access modifier로 구분한 data에 직접 접근하기보다는 class 내의 method를 통해서 접근하는 것이 side effect를 고려했을 때 현명한 방법이다.

    • 그래서 data에 접근하기 위한 method인 Getter (=accessor)Setter(= mutator)에 대해 작성해보겠다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
> class Sample:
>   def __init__(self):
>       self.x = 0
>       self.__y = 0

# Getter
>   def get_y(self):
>       return self.__y

# Setter
>   def set_y(self, value):
>       self.__y = value

> A = Sample()
> A.set_y(2)

> print('Ex > x: {}'.format(b.x))
Ex > x : 0

> print('Ex > y: {}'.format(b.get_y()))
Ex > y : 2

> print(dir(A))
['_SampleB__y', '__class__', ..., 'get_y', 'set_y', 'x']
  • dir()를 통해서 private variable은 naming mangling이 일어난 걸 알 수 있다.
  • method get_yset_y를 확인할 수 있다.

 


2. Property

 

2.1 Property란??

공개적으로 노출된 API를 변경하지 않고도 class 내부의 attribute의 기본 구현을 변경할 수 있는 도구

  • 위 방식의 문제점
    • 위 방식은 접근하려는 변수의 수가 만약 10개라면 각 변수에 대해 method를 작성해야한다.
    • 객체 지향 설계의 캡슐화를 깨뜨린다.
  • 그래서 Python에서는 위 방식보다 간편하고 안전한 도구를 제공하는데, 바로 property 다.
  • property
    • 속성 같이 행동하는 method를 만든다.
    • getter와 setter method를 피하는 pythonic way로, 내부 method를 외부에 노출시키는 것 없이 구현(Implementation)할 수 있다.

 

2.2 Property의 구현 방식 2가지

function으로서 Property( ) 와 decorator로서 @property

 

2.3 @property

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
# circle.py

> class Circle:
>     def __init__(self, radius):
>         self.__radius = radius

>     @property
>     def radius(self):
>         """The radius property."""
>         print("Get radius")
>         return self.__radius

>     @radius.setter
>     def radius(self, value):
>         print("Set radius")
>         self.__radius = value

>     @radius.deleter
>     def radius(self):
>         print("Delete radius")
>         del self.__radius
  • 그러면 위 파일을 import하여 실행해보자.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
> from circle import Circle

> circle = Circle(100)

> circle.radius
Get radius

> print(circle.radius)
Get radius
100

> circle.radius = 50
Set radius

> print(circle.radius)
Get radius
50

> print(dir(circle))
['_Circle__radius', '__class__', ... 'radius']

> del circle.radius
Delete radius

# `del`을 실행한 후, `dir`로 확인하면 `_Circle_radius`가 사라진 걸 확인할 수 있다.
> print(dir(circle))
[ '__class__', ... 'radius']

> circle.radius
AttributeError: 'Circle' object has no attribute '_radius'
  • 또한 property 내의 docstring을 출력할 수 있다.
  • 출력되는 내용은 @property 가 붙은 method 안에 docstring이다.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
> help(circle)
Help on Circle in module circle object:

class Circle(builtins.object)
 |  Circle(radius)
 |
 |  Methods defined here:
 |
 |  __init__(self, radius)
 |      Initialize self.  See help(type(self)) for accurate | 
---------------
 |  Data descriptors defined here:
 |  ......
 |  radius
 |      The radius property
  • docsting으로 property에 대해 달고 싶으면 @property decorator가 붙는 method에 작성해야 한다는 걸 확인했다.

  • 그러면 마지막으로 제약조건을 추가하여 사용해보자.

    • @radius.setter 에 제약조건을 추가한다.
1
2
3
4
5
6
> @radius.setter
> def radius(value):
>   print('Set radius')
>   if value <= 0:
>     raise ValueError('0보다 큰 값을 입력하세요')
>   self.__radius = value
  • 똑같이 import하여 실행해보자.
  • 의도한대로 ValueError가 뜨는 걸 확인할 수 있다.
1
2
3
4
> circle = Circle(100)

> circle.radius = -20
ValueError: 0보다 큰 값을 입력하세요.

 

2.4 Summary

  • @propertygetter method를 decorate 한다.
  • docstring의 위치는 @property 안에 입력해야 한다.
  • setterdeleter method는 getter method name에 .setter 그리고, .deleter 로 추가하여 decorate 된다.

 


Reference