1. Python underscore 활용

  • Interpreter 내에서의 마지막 값
1
2
3
4
5
> 2 + 3
5

> _ + 5
10
  • 특정값을 무시하는 용도
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
> x, _, y = (1, 2, 3)


# Unpacking에 사용
> a, *_, b = (1, 2, 3, 4, 5)

> print(x, y, a, b)
1 3 1 5

# Error가 뜨지 않는다.
> for _ in range(10):
>   pass

# 각 index에 대응하는 value만 출력
> for _, val in enumerate(range(10)):
>   print(val, end = '')
0123456789
  • magic method 사용 시, double underscore를 사용한다.

  • Access modifier 접근 지정자로 사용된다.

    • 이 접근 지정자 private에서 Naming Mangling 이 발생된다.

 


2. Access modifier

접근 지정자라 하며, 정보 은익(Information Hiding)을 위해 사용된다. 즉, class의 attribute, method에 대해 접근을 제어할 수 있는 기능이다.

  • Public -> Protected -> Private 단계로 접근할 수 있는 대상이 좁아진다.

  • underscore를 통해 각 대상의 범위를 지정한다.

    • name: public
    • _name: protected
    • __name: private
  • Python에서는 약속된 규약에 따라 자유도와 책임감을 가지고 코딩하는 것을 장려한다.

  • 그러면 Public, Protected, Private에 대해 각각 알아보자.

 

2.1 Public

public으로 선언된 attribute, method는 어떤 클래스라도 접근 가능하며, Python에서 모든 attribute, method는 기본적으로 public 이다.

  • 클래스 외부에서 attribute, method가 접근 가능하기 때문에 사용 가능하다.
  • underscore는 없다.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
> class kim:
# constructor
>     def __init__(self, name, age):
>         self.kimname = name
>         self.kimage = age

>     # public member function
>     def display_age(self):

>         # accessing public data member
>         print("Age: ", self.kimage)

# creating instance of the class
> inst = kim('king', 30)

# accessing public data member
> print("Name: ", inst.kimname)
> inst.display_age()

Name:  king
Age:  30
  • public이어도 다음과 같이 접근하여 직접 수정하는 건 권장하지 않는다.
  • 수정하고 출력하는 별도의 method를 사용하는 걸 권장한다.
1
2
3
4
# 권장하지 않는 방법
> inst.kimname = 'Wang'
> print("Name: ", inst.kimname)
Name:  Wang

 

2.2 Protected

정의한 해당 class 또는 해당 class를 상속 받은 클래스에서만 접근이 가능하다는 의미지만, 실제로는 public 처럼 접근 가능하다.

  • 상속받은 자식 class에서 사용하자는 의미다.
  • 하나의 underscore를 name 앞에 붙여서 표시만 한다.
    • 즉, 실제로 제약되지는 않고, 일종의 경고 표시로 사용 된다.
    • 단지 경고 표시일 지라도 접근하여 수정하지 않는 걸 권고한다.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
> class Sample:
>   def __init__(self):
>       self.x = 0
>       self._y = 0

> a = Sample()

# 출력 가능하다.
# 이렇게 직접 출력하는 걸 권장하지 않는다.
> print('y = ', a._y)
y = 0
  • 상속받은 클래스에서 사용하기
  • 아래 code처럼 출력 method를 통해서 접근하는 걸 권장한다.
 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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
## super class
> class Student:

    # protected data members
>     _name = None
>     _roll = None
>     _branch = None

>     # constructor
>     def __init__(self, name, roll, branch):
>         self._name = name
>         self._roll = roll
>         self._branch = branch

>     # protected member function
>     def _displayRollAndBranch(self):

>         # accessing protected data memebers
>         print("Roll: ", self._roll)
>         print("branch: ", self._branch)


## Subclass
> class Geek(Student):

>     # constructor
>     def __init__(self, name, roll, branch):
>         Student.__init__(self, name, roll, branch)

>     # public function
>     def displayDetails(self):

>         # accessing protected data of super class
>         print("Name: ", self._name)

>         # accessing protected function of super class
>         self._displayRollAndBranch()

> inst = Geek("Wang",130205, "Information Technology")

> inst.displayDetails()

Name:  Wang
Roll:  130205
branch:  Information Technology

 

2.3 Private: Naming Mangling

  • 정의한 해당 class에서만 직접 접근이 가능하다.
  • 만약 상속받은 클래스에서 접근하고자 한다면 상위 클래스에서의 private 변수에 접근할 수 있는 method를 만들어야 가능하다.
  • 두 개의 underscore를 name 앞에 붙여서 사용하여 지정한다.
  • OOP의 캡슐화를 의미한다.

  • python에서는 double underscore를 name 앞에 붙이면 해당 이름으로 접근이 허용되지 않는다.

  • 왜냐하면 Naming Mangling이 일어나기 때문이다.

  • double underscore를 붙이면 해당 이름이 _<해당 class name>_name 으로 mangling이 일어난다.

    • __name -> _<해당 class name>_name
 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
31
32
33
34
## 정의한 class에서만 접근이 가능하다.

> class Student:

    # private members
>     __name = None
>     __roll = None
>     __branch = None

>     # constructor
>     def __init__(self, name, roll, branch):
>         self.__name = name
>         self.__roll = roll
>         self.__branch = branch

>     # private function
>     def __displayRollAndBranch(self):

>         # accessing private memebers
>         print("Roll: ", self.__roll)
>         print("branch: ", self.__branch)

>     # public function
>     def accessPrivateFunction(self):
>       # accessing private function
>       self.__displayRollAndBranch()

> inst = Student("Wang",130205, "Information Technology")

> inst.acessPrivateFunction()

Name:  Wang
Roll:  130205
branch:  Information Technology
  • 만약 Protected chapter에서 다뤘던 code에서 protected data로 지정한 _name = None 을 private로 바꿔서 상속된 class를 통해서 실행한다면 어떻게 될까???
    • _name = None -> __name = None
 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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
## Super class
> class Student:

>     # private data
>     __name = None

>    # protected data members
>     _roll = None
>     _branch = None

>     # constructor
>     def __init__(self, name, roll, branch):

>         # private data
>         self.__name = name

>         # protected data
>         self._roll = roll
>         self._branch = branch

>     def _displayRollAndBranch(self):

>         # accessing protected data memebers
>         print("Roll: ", self._roll)
>         print("branch: ", self._branch)


## Subclass
> class Geek(Student):

>     # constructor
>     def __init__(self, name, roll, branch):
>         Student.__init__(self, name, roll, branch)

>     # public function
>     def displayDetails(self):

        # accessing protected data of super class
>         self._displayRollAndBranch()

>         # accessing private data of super class
>         print("Name: ", self.__name)


> inst = Geek("Wang",130205, "Information Technology")

# super class의 private 변수에 직접 접근을 불허한다. 
> inst.displayDetails()

AttributeError: 'Geek' object has no attribute '_Geek__name'
  • 위의 경우처럼 Error가 뜬다.
  • private data는 상속된 class에서 출력할 수 없다는 걸 확인했다.

 

Naming Mangling 확인하기

  • 그러면 Naming Mangling을 확인해보자.
  • 위의 AttributeError 에서도 확인할 수 있듯이 _Geek__name으로 private data인 __name이 변경된 걸 확인했다.
1
2
3
4
5
# 위 코드에서 실행했다.
> print(dir(inst))

# 결과는 다음과 같다.
['_Student__name', '__class__', ... '_branch', '_displayRollAndBranch', '_roll', 'displayDetails']
  • 맨 처음 private data를 선언된 class의 이름이 붙여져서 _Student__name으로 naming Mangling 된 걸 확인했다.
  • mangling의 의미처럼 기존의 설정한 name은 훼손되어 사용할 수 없다.

 

다른 언어와의 차이점

  • 타 클래스의 private attribute, method에 접근하지 않는 것이 원칙이지만, 파이썬은 사실 접근이 가능하다.

  • 하지만, 원칙에 맞게 Python 오픈 소스 프로젝트들은 이를 준수하고 있다.

 

2.4 Summary

  • 그러면 마지막으로 3가지 access modifier에 대해 코드로 정리한 후, 마무리하겠다.
 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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
## Super class
> class Super:

>     # public
>     var1 = None

>     # protected data
>     _var2 = None

>     # private data
>     __var3 = None

>     # Constructor
>     def __init__(self, var1, var2, var3):
>         self.var1 = var1
>         self._var2 = var2
>         self.__var3 = var3

>     # public function
>     def displayPublicMembers(self):

>         print("Public data: ", self.var1)

>     # protected function
>     def _displayProtectedMembers(self):

>         print("Protected data: ", self._var2)

>     # private function
>     def __displayPrivateMembers(self):

>         print("private data: ", self.__var3)

>     # public function
>     def accessPrivateMembers(self):
>         # accessing private member function
>         self.__displayPrivateMembers()

## derived class
> class Sub(Super):

    # constructor
>     def __init__(self, var1, var2, var3):
>             Super.__init__(self, var1, var2, var3)

>     # public fucntion
>     def accessProtectedMembers(self):
>         self._displayProtectedMembers()

> inst = Sub("Wang",4, "!")
> inst.displayPublicMembers()
> inst.accessProtectedMembers()
> inst.accessPrivateMembers()


Public data:  Wang
Protected data:  4
private data:  !

 


Reference