Python 클래스 기본개념 3-3

2019-05-13

.

Python_studynote(20190513)

[학습자료]

1) https://paphopu.tistory.com/entry/Python-%EB%A7%A4%EA%B0%9C%EB%B3%80%EC%88%98-self-%EC%99%80-cls%EC%9D%98-%EC%B0%A8%EC%9D%B4-%EA%B7%B8%EB%A6%AC%EA%B3%A0-static-method%EC%97%90-%EB%8C%80%ED%95%B4%EC%84%9C

2) http://pythonstudy.xyz/python/article/19-%ED%81%B4%EB%9E%98%EC%8A%A4

3) https://suwoni-codelab.com/python%20%EA%B8%B0%EB%B3%B8/2018/03/11/Python-Basic-class-staticmethod/

[관련자료]

(1) Python 클래스 기본개념 1-3

https://minman2115.github.io/python_DS14

(2) Python 클래스 기본개념 2-3

https://minman2115.github.io/python_DS12

[학습내용]

1. class member

클래스는 method, property(속성), class variable(클래스 변수), instance variable(인스턴스 변수), initializer(초기자), destructor(소멸자) 등의 여러 맴버들을 가질 수 있다.

크게 나누면 데이타를 표현하는 필드와 행위를 표현하는 메서드로 구분할 수 있다.

파이썬에서 이러한 필드와 메서드는 모두 그 객체의 attribute 라고 한다.

2. method

method는 클래스의 행위를 표현하는 것으로 클래스 내의 함수로 볼 수 있다. 파이썬에서 method는 크게 instance method, static method(정적메소드), class method(클래스메소드)가 있다.

가장 흔히 쓰이는 instance method는 인스턴스 변수에 엑세스할 수 있도록 메서드의 첫번째 파라미터에 항상 객체 자신을 의미하는 “self”라는 파라미터를 갖는다.

아래 예시에서 calcArea()가 instance method에 해당된다. instance method는 여러 파라미터를 가질 수 있지만, 첫번째 파라미터는 항상 self 를 갖는다.

class Rectangle:
    count = 0  
    # class variable
 
    # initializer
    def __init__(self, width, height):
        
        # self.[something] : instance variable
        
        self.width = width
        self.height = height
        Rectangle.count += 1
 
    # method
    def calcArea(self):
        area = self.width * self.height
        return area
    
a = Rectangle(5,10)
a.calcArea()
50

3. class variable

클래스 정의에서 메서드 밖에 존재하는 변수를 class variable라 하는데, 이는 해당 클래스를 사용하는 모두에게 공용으로 사용되는 변수이다.

class variable는 클래스 내외부에서 “클래스명.변수명” 으로 엑세스 할 수 있다.

위의 예시에서 count는 class variable로 “Rectangle.count”와 같이 엑세스할 수 있다.

4. instance variable

하나의 클래스로부터 여러 instance variable를 생성해서 사용할 수 있다. 클래스 변수가 하나의 클래스에 하나만 존재하는 반면, instance variable는 각 객체 인스턴스마다 별도로 존재한다.

클래스 정의에서 method 안에서 사용되면서 “self.변수명”처럼 사용되는 변수를 instance variable라 하는데, 이는 각 객체별로 서로 다른 값을 갖는 변수이다.

instance variable는 클래스 내부에서는 self.width 과 같이 “self.” 을 사용하여 엑세스하고, 클래스 밖에서는 “객체변수.인스턴스변수”와 같이 엑세스 한다.

Python은 다른 언어에서 흔히 사용하는 public, protected, private 등의 접근 제한자(Access Modifier)를 갖지 않는다.

Python 클래스는 기본적으로 모든 멤버가 public이라고 할 수 있다.

Python Convention상 내부적으로만 사용하는 변수 혹은 메서드는 그 이름 앞에 하나의 밑줄(_) 을 붙인다.

하지만 이는 코딩 관례에 따른 것일 뿐 실제 밑줄 하나를 사용한 멤버도 public 이므로 필요하면 외부에서 엑세스할 수 있다.

만약 특정 변수명이나 메서드를 private으로 만들어야 한다면 두개의 밑줄(__)을 이름 앞에 붙이면 된다.

def __init__(self, width, height):
    self.width = width
    self.height = height
 
    # private 변수 예시 : __area
    self.__area = width * height
    
# private 메서드 예시
def __internalRun(self):
    pass

5. Initializer

클래스로부터 새 객체를 생성할 때마다 실행되는 특별한 메서드로 init() 이라는 메서드가 있는데, 이를 흔히 클래스 Initializer 라 부른다.

Initializer는 클래스로부터 객체를 만들 때, 인스턴스 변수를 초기화하거나 객체의 초기상태를 만들기 위한 문장들을 실행하는 곳이다.

위의 init() 예제를 보면, width와 height라는 입력 파라미터들을 각각 self.width와 self.height라는 인스턴스변수에 할당하여 객체 내에서 계속 사용할 수 있도록 세팅하고 있다.

6. instance method, static method, class method

1

## instance method 예시
class instance_method:
    
    def __init__(self):
        self.name = "cathy"
        
    def print_name(self):
        print("my name is %s" % (self.name))
        
## instance 선언
name_instance = instance_method()

## print_name이라는 함수를 호출하기 위해서는
## name_instance처럼 객체를 먼저 정의해줘야 한다.
## 이것이 instance method다
name_instance.print_name()

## 이 처럼 특정 method를 호출하기 위해 Instance의 선언을 
## 요구하는 method를 Instance method라고 부른다.
my name is cathy

2

class method는 메서드 앞에 @classmethod 라는 Decorator를 표시하여 해당 메서드가 class method임을 표시한다. class method는 static method와 비슷한데, 객체 인스턴스를 의미하는 self 대신 cls 라는 클래스를 의미하는 파라미터를 전달받는다. static method는 이러한 cls 파라미터를 전달받지 않는다. class method는 이렇게 전달받은 cls 파라미터를 통해 class variable 등을 엑세스할 수 있다.

일반적으로 instance data를 엑세스 할 필요가 없는 경우 class method나 static method를 사용하는데, 이때 보통 class variable를 엑세스할 필요가 있을 때는 class method를, 이를 엑세스할 필요가 없을 때는 static method를 사용한다.

## classmethod 예시
class class_method:
    
    @classmethod
    def print_name(cls):
        print('my name is %s'%(cls.__class__.__name__))
        
## 객체를 따로 선언해줄 필요없이 함수를 호출할 수 있다.
## 이것이 classmethod다.
class_method.print_name()
my name is type

3

class static_method:
    
    @staticmethod
    def print_name(name):
        return "제 이름은 {} 입니다.".format(name)

# 클래스를 통해서 호출가능
print(static_method.print_name('은정'))
제 이름은 은정 입니다.
# 인스턴스를 통해서도 호출이 가능
name = static_method()
print(name.print_name('호동'))
제 이름은 호동 입니다.

7. static method와 class method의 차이

class Language:
    default_language = "English"
    
    def __init__(self):
        self.show = 'i speak ' + self.default_language
    
    @classmethod
    def classmethod_my_language(cls):
        return cls()
    
    @staticmethod
    def staticmethod_my_language():
        return Language()
    
    def print_language(self):
        print(self.show)
    
    
class KoreanLanguage(Language):
    default_language = "Korean" 
  • static method에서는 부모클래스의 클래스속성 값을 가져오지만, class method에서는 cls인자를 활용하여 cls의 클래스 속성을 가져오는 것을 확인할 수 있다.
# class_method
b = KoreanLanguage.classmethod_my_language()
b.print_language()
i speak Korean
# static_method
a = KoreanLanguage.staticmethod_my_language()
a.print_language()
i speak English

8. 그 밖의 method

파이썬에는 Initializer 이외에도 객체가 소멸될 때 (Garbage Collection 될 때) 실행되는 소멸자(del) 메서드, 두 개의 객체를 ( + 기호로) 더하는 add 메서드, 두 개의 객체를 ( - 기호로) 빼는 sub 메서드, 두 개의 객체를 비교하는 cmp 메서드, 문자열로 객체를 표현할 때 사용하는 str 메서드 등 많은 특별한 용도의 메서드들이 있다.

아래 예시는 이 중에 add() 메서드에 대한 예이다.

class Rectangle:
    count = 0  
    # class variable
 
    # initializer
    def __init__(self, width, height):
        
        # self.[something] : instance variable
        
        self.width = width
        self.height = height
        Rectangle.count += 1
        
    def __add__(self, other):
        obj = Rectangle(self.width + other.width, self.height + other.height)
        return obj
 
    # method
    def calcArea(self):
        area = self.width * self.height
        return area
    
r1 = Rectangle(10, 5)
r2 = Rectangle(20, 15)
r3 = r1 + r2  # __add__()가 호출됨
r3.calcArea()
600

9. object(객체)의 생성과 사용

클래스를 사용하기 위해서는 먼저 클래스로부터 Object를 생성해야 한다. 파이썬에서 객체를 생성하기 위해서는 “객체변수명 = 클래스명()”과 같이 클래스명을 함수 호출하는 것처럼 사용하면 된다.

만약 init() 함수가 있고, 그곳에 입력 파라미터들이 지정되어 있다면, “클래스명(입력파라미터들)”과 같이 파라미터를 괄호 안에 전달한다. 이렇게 전달된 파라미터들은 Initializer 에서 사용된다.

아래 예시를 보면, Rectangle 클래스로부터 r 이라는 객체를 생성 하고 있는데, Rectangle(2, 3)와 같이 2개의 파라미터를 전달하고 있다.

이는 Rectangle 초기자에서 각각 width와 height 인스턴스 변수를 초기화하는데 사용된다.

# object 생성
r = Rectangle(2, 3)
 
# method 호출
area = r.calcArea()
print("area = ", area)
 
# instance variable access
r.width = 10
print("width = ", r.width)
 
# instance variable access
print(Rectangle.count)
print(r.count)
area =  6
width =  10
4
4

클래스로부터 생성된 Object로부터 class member들을 호출하거나 엑세스할 수 있다. instance method는 “객체변수.메서드명()”과 같이 호출할 수 있는데, 위의 예시에선 r.calcArea() 이 메서드 호출에 해당된다.

인스턴스 변수는 “객체변수.인스턴스변수” 으로 표현되며, 값을 읽거나 변경하는 일이 가능하다. 위의 예시 r.width = 10 은 인스턴스변수 width 에 새 값을 할당하는 예이다.

파이썬에서 특히 class variable 를 엑세스할 때, “클래스명.클래스변수명” 혹은 “객체명.클래스변수명”을 둘 다 허용하기 때문에 약간의 혼란을 초래할 수 있다.

예를 들어, 위의 예제에서 Rectangle.count 혹은 r.count은 모두 클래스 변수 count를 엑세스하는 경우로서 이 케이스에는 동일한 값을 출력한다.

하지만, 아래 예제와 같이 Rectangle 클래스의 클래스 변수 count를 Rectangle.count로 할당하지 않고 객체 r 로부터 할당하면 혼돈스러운 결과를 초래하게 된다.

r = Rectangle(2, 3)
 
Rectangle.count = 50
r.count = 10   # count instance variable이 새로 생성됨
 
print(r.count, Rectangle.count)
10 50

위의 코드를 보면

파이썬에서 한 객체의 attribute에 값이 할당되면 (예를 들어, r.count = 10), 먼저 해당 객체에 이미 동일한 attribute가 있는지 체크해서 있으면 새 값으로 치환하고, 만약 그 attribute가 없으면 객체에 새로운 attribute를 생성하고 값을 할당한다.

즉, r.count = 10 의 경우 class variable인 count를 사용하는 것이 아니라 새로 그 객체에 추가된 instance variable를 사용하게 되므로 class variable값은 변경되지 않는다.

파이썬에서 한 객체의 attribute를 읽을 경우에는 먼저 그 객체에서 attribute를 찾아보고, 없으면 그 객체의 소속 클래스에서 찾고, 다시 없으며 상위 Base 클래스에서 찾고, 그래도 없으면 에러를 발생시킨다. 따라서, 위 예제에서 클래스 변수값이 출력된 이유는 값을 할당하지 않고 읽기만 했기 때문에, r 객체에 새 instance variable를 생성하지 않게 되었고, 따라서 객체의 attribute가 없어서 클래스의 attribute를 찾았기 때문이다. 이러한 혼돈을 피하기 위해 class variable를 엑세스할 때는 클래스명을 사용하는 것이 좋다.

10. class Inheritance(클래스 상속)과 Polymorphism(다형성)

파이썬은 객체지향 프로그래밍의 Inheritance을 지원하고 있다. 클래스를 상속 받기 위해서는 자식클래스에서 클래스명 뒤에 부모클래스 이름을 괄호와 함께 넣어 주면 된다.

즉, 아래 예시에서 Dog 클래스는 Animal 클래스로부터 파생된 자식클래스이며, Duck 클래스도 역시 Animal 부모클래스부터 파생되고 있다. 참고로 파이썬은 복수의 부모클래스로부터 상속 받을 수 있는 Multiple Inheritance를 지원하고 있다.

class Animal:
    def __init__(self, name):
        self.name = name
    def move(self):
        print("move")
    def speak(self):
        pass
    
class Dog (Animal):
    def speak(self):
        print("bark")
        
class Duck (Animal):
    def speak(self):
        print("quack")
        
# 자식클래스는 부모클래스의 멤버들을 호출하거나 사용할 수 있으며
# 당연하게도 자식클래스 자신의 멤버들을 사용할 수 있다.

dog = Dog("doggy") # 부모클래스의 생성자
n = dog.name # 부모클래스의 인스턴스변수
dog.move()   # 부모클래스의 메서드
dog.speak()  # 파생클래스의 멤버
move
bark

파이썬은 객체지향 프로그래밍의 Polymorphism(다형성)을 또한 지원하고 있다.

아래 예시는 animals 라는 리스트에 Dog 객체와 Duck 객체를 넣고 이들의 speak() 메서드를 호출한 예이다.

코드 실행 결과를 보면 객체의 타입에 따라 서로 다른 speak() 메서드가 호출됨을 알 수 있다.

animals = [Dog('doggy'), Duck('duck')]
 
for a in animals:
    a.speak()
bark
quack