Python의 클래스와 객체에 대해정리합니다.

1
2
3
# 출처 : 클라우드 스터딩 (https://cloudstudying.kr/lectures/373)
from IPython.display import Image
Image('https://i.imgur.com/gTKon8u.png')

class

  • 실세계의 것을 모델링하여 속성(attribute)와 동작(method)를 갖는 데이터 타입 -> 새로운 타입을 정의 하는 것

  • python에서의 string, int, list, dict.. 모두가 다 클래스로 존재한다.

  • 예를들어 학생이라는 클래스를 만든다면, 학생을 나타내는 속성과 학생이 행하는 행동을 함께 정의 할 수 있다.

  • 따라서, 다루고자 하는 데이터(변수) 와 데이터를 다루는 연산(함수)를 하나로 캡슐화(encapsulation)하여 클래스로 표현

  • 모델링에서 중요시 하는 속성에 따라 클래스의 속성과 행동이 각각 달라짐

List 라는 class를 예시로 들어본다면?

list의 래스

1
2
3
a = [1, 2, 3, 4] # List라는 class
a.append(5) # append라는 method를 사용해서 숫자 5를 추가
print(a)
[1, 2, 3, 4, 5]

object 란?

  • 클래스로 생성되어 구체화된 객체(인스턴스)

  • 파이썬의 모든 것(int, str, list..etc)은 객체(인스턴스)

  • 실제로 class가 인스턴스화 되어 메모리에 상주하는 상태를 의미

  • class가 빵틀이라면, object는 실제로 빵틀로 찍어낸 빵이라고 비유 가능

object 설명이미지

class 선언하기

  • 객체를 생성하기 위해선 객체의 모체가 되는 class를 미리 선언해야 한다.

  • class 키워드를 사용한다.

  • 어떤 정의 없이 class를 실행하고 싶다면 ‘pass’ 를 반드시 사용해야 한다.

  • 정의는 하고 싶은데, 그 행동은 특정하고 싶지 않을때 pass를 사용한다.

1
class Person:
1
2
class Person: # Person이라는 class를 설정
    pass
1
2
3
# 함수 설정에서도 사용 가능
def trial():
    pass
1
2
3
4
5
6
7
8
9
10
11
# Person이라는 클래스에 2개의 객체를 생성
gabriel = Person() 
Sean = Person()

#list에 비유하자면 아래와 같음

a = list()
b = list()

print(type(gabriel), type(Sean))
print(type(a), type(b))
<class '__main__.Person'> <class '__main__.Person'>
<class 'list'> <class 'list'>

method

__init__(self)

  • 생성자, 클래스 인스턴스가 생성될 때 호출된다.

  • self인자는 항상 첫번째에 오며 자기 자신을 가리킨다.

  • 이름이 꼭 self일 필요는 없지만, 관례적으로 self로 사용한다.

  • 생성자에서는 해당 클래스가 다루는 데이터를 정의한다.

    • 이 데이터를 멤버 변수(member variable) 또는 속성(attribute)라고 한다.
1
2
3
def __init__():  같은 형식으로 사용한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 사람이라는 class에 이름, 나이를 속성으로 부여해서 class 선언

class Person:
    def __init__(self):
        print(self, 'is generated')
       
        # 최소 class 생성시 메인 속성 정의
        self.name = 'gabe'
        self.age = 10

        
p1 = Person()
p2 = Person()

print(p1.name, p1.age) # class 선언하며 기본 설정한 name과 age의 설정값이 출력된다.
<__main__.Person object at 0x0000022DCD1D1FA0> is generated
<__main__.Person object at 0x0000022DCD1D19A0> is generated
gabe 10
1
2
3
4
5
# 생성된 class에 속성 설정 변경할 수 있다.
p1.name = 'aaron'
p1.age = 20

print(p1.name, p1.age)
aaron 20

위와 같이 별도의 설정 변경 과정없이 클래스 선언하며 동시에 변경하고 싶다면?

1
2
3
4
5
6
7
8
9
10
11
class Person:
    def __init__(self, name, age):
        print(self, 'is generated')
        self.name = name
        self.age = age
        
p1 = Person('Gabriel', 30)
p2 = Person('Kate', 20)

print(p1.name, p1.age)
print(p2.name, p2.age)
<__main__.Person object at 0x0000022DCD1DF430> is generated
<__main__.Person object at 0x0000022DCD1DF5B0> is generated
Gabriel 30
Kate 20

특정 개체의 속성에 default값을 미리 정할 수도 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
class Person:
    def __init__(self, name, age=10):
        print(self, 'is generated') #class가 생성되어 저장된 위치
        self.name = name
        self.age = age
        
p1 = Person('Gabriel', 30)
p2 = Person('Kate', 20)
p3 = Person('Aaron')

print(p1.name, p1.age)
print(p2.name, p2.age)
print(p3.name, p3.age)
<__main__.Person object at 0x0000022DA3B40FA0> is generated
<__main__.Person object at 0x0000022DA3C2E040> is generated
<__main__.Person object at 0x0000022DCD1EAA90> is generated
Gabriel 30
Kate 20
Aaron 10

Aron을 입력할 경우 age를 지정하지 않았지만, default로 지정한 10이 입력되어 있다.

self

  • 파이썬의 method는 항상 첫번째 인자로 self를 전달

  • self는 현재 해당 메쏘드가 호출되는 객체 자기 자신을 의미한다.

  • C++/C#, Java의 this에 해당

  • 이름이 반드시 self일 필요는 없으나, 위치는 항상 맨 처음의 parameter이며 관례적으로 self로 사용

1
2
3
4
5
6
7
8
9
10
11
12
class Person:
    # Person이라는 클래스에 객체를 생성했을때 입력될 기본 값
    def __init__(self, name, age):
        print('self: ', self)
        self.name = name
        self.age = age
        
a = Person('Aaron', 20)
b = Person('Gabriel', 30)

print(a)
print(b)
self:  <__main__.Person object at 0x0000022DCD1EAAF0>
self:  <__main__.Person object at 0x0000022DCD1EAC40>
<__main__.Person object at 0x0000022DCD1EAAF0>
<__main__.Person object at 0x0000022DCD1EAC40>

mehtod 정의

  • 해당 클래스가 할 수 있는 ‘행동’ 을 의미

  • 멤버함수라고도 하며, 해당 클래스의 object에서만 호출가능하다.

  • 메쏘드는 객체 레벨에서 호출되며, 해당 객체의 속성에 대한 연산을 행한다.

  • {obj}.{method}() 형태로 호출된다.


1
2
3
4
5
a = [1,2,3]

a.append(5) 

와 같이 a라는 list 에 5를 추가하는 append 와 동일

1
2
3
4
5
6
7
class Counter:
    def __init__(self):
        self.num = 0 #Counter라는 클래스 선언하면 Num이라는 데이터를 갖고, 처음 생성되면 0이라는 값을 같는다.

        
test = Counter()
test.num
0
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
# 1. 숫자를 하나 증가
# 2. 숫자를 0으로 초기화

class Counter:
    def __init__(self):
        self.num = 0 #Counter라는 클래스 선언하면 Num이라는 데이터를 갖고, 처음 생성되면 0이라는 값을 같는다.

    # method 추가
    
    # 입력받은 num에 1을 추가하는 method
    def increment(self):
        self.num += 1
    
    # 입력받은 num을 0으로 초기화 하는 method
    def reset(self):
        self.num = 0
    
    # 현재값을 출력하는 method
    def print_current_value(self):
        print('current value is:', self.num)
         
test = Counter() # 0이 생성
test.print_current_value()  # 값은 0이어야 한다.
test.increment() # 0에 +1
test.increment() # 0에 +1
test.increment() # 0에 +1 -> 최종적으로 3
test.print_current_value() # 값은 3이어야 한다.

test.reset() # 3이던 값을 reset하여 0으로 바꾼다.

test.print_current_value() # 초기화 했으므로 0이 나와야 한다.
current value is: 0
current value is: 3
current value is: 0
1
2
3
test2 = Counter() # 다시 생성
test2.increment() # 0 + 1 했으니 1이 된다.
test2.print_current_value() # 1이 출력되어야 한다.
current value is: 1

method type

  • instance method - 객체로 호출 (위에서 실습한 것은 instance method)

    • 메쏘드는 객체 레벨로 호출 되기 때문에, 해당 메쏘드를 호출한 객체에만 영향을 미침
  • class method(static method) - class로 호출

    • 클래스 메쏘드의 경우, 클래스 레벨로 호출되기 때문에, 클래스 멤버 변수만 변경 가능

instance method가 필요없는 경우

  • 내부적으로 데이터를 소유할 필요없이, 전달받은 데이터를 처리해서 바로 내보내기만 하면 되는 경우

  • def위에 @staticmethod 라고 명시해주면, self없이 사용이 가능하다.

1
2
3
4
5
6
7
8
9
10
11
class Math:
    @staticmethod
    def add(a, b):
        return a + b
    
    @staticmethod
    def multiply(a, b):
        return a * b
    
Math.add(10, 20)
Math.multiply(10, 20)
200

Class Inheritance (상속)

  • 기존에 정의해둔 클래스의 기능을 그대로 물려받을 수 있다.

  • 기존 클래스에 기능 일부를 추가하거나, 변경하여 새로운 클래스를 정의한다.

  • 코드를 재사용할 수 있게된다.

  • 상속 받고자 하는 대상인 기존 클래스는 (Parent, Super, Base class 라고 부른다.)

  • 상속 받는 새로운 클래스는(Child, Sub, Derived class 라고 부른다.)

  • 의미적으로 is-a관계를 갖는다

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
class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age
        
    def eat(self, food):
        print('{}은 {}를 먹습니다.'.format(self.name, food))
    
    def sleep(self, minute):
        print('{}은 {}분동안 잡니다.'.format(self.name, minute))
    
    def work(self, minute):
        print('{}은 {}분동안 일합니다.'.format(self.name, minute))

class Student(Person):
    def __init__(self, name, age):
        self.name = name
        self.age = age
    
class Employee(Person):
    def __init__(self, name, age):
        self.name = name
        self.age = age

gabe = Employee('Gabriel', 25)
gabe.eat('Pasta')
gabe.sleep(30)
gabe.work(60)
Gabriel은 Pasta를 먹습니다.
Gabriel은 30분동안 잡니다.
Gabriel은 60분동안 일합니다.

method override

  • 부모 클래스의 method를 재정의(override)

  • 하위 클래스(자식 클래스) 의 인스턴스로 호출시, 재정의된 메소드가 호출됨

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
class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age
        
    def eat(self, food):
        print('{}은 {}를 먹습니다.'.format(self.name, food))
    
    def sleep(self, minute):
        print('{}은 {}분동안 잡니다.'.format(self.name, minute))
    
    def work(self, minute):
        print('{}은 {}분동안 일합니다.'.format(self.name, minute))

# 부모 클래스인 Person을 상속 받아 Student 생성
class Student(Person):
    def __init__(self, name, age):
        self.name = name
        self.age = age
        
    def work(self, minute):
        print('{}은 {}분동안 공부합니다.'.format(self.name, minute))

# 부모 클래스인 Person을 상속 받아 Employee 생성
class Employee(Person):
    def __init__(self, name, age):
        self.name = name
        self.age = age
        
    def work(self, minute):
        print('{}은 {}분동안 업무를 합니다.'.format(self.name, minute))
        
gabe = Employee('Gabriel', 25)
gabe.eat('Pasta')
gabe.sleep(30)
gabe.work(60)
Gabriel은 Pasta를 먹습니다.
Gabriel은 30분동안 잡니다.
Gabriel은 60분동안 업무를 합니다.

super

  • 하위클래스(자식 클래스)에서 부모클래스의 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
class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age
        
    def eat(self, food):
        print('{}은 {}를 먹습니다.'.format(self.name, food))
    
    def sleep(self, minute):
        print('{}은 {}분동안 잡니다.'.format(self.name, minute))
    
    def work(self, minute):
        print('{}은 {}분동안 준비를 합니다.'.format(self.name, minute))

class Student(Person):
    def __init__(self, name, age):
        self.name = name
        self.age = age

# 부모 class인 Person의 work 호출
    def work(self, minute):
        super().work(minute)
        print('{}은 {}분동안 공부합니다.'.format(self.name, minute))
        
class Employee(Person):
    def __init__(self, name, age):
        self.name = name
        self.age = age
        
    def work(self, minute):
        super().work(minute)
        print('{}은 {}분동안 업무를 합니다.'.format(self.name, minute))
        
gabe = Employee('Gabriel', 25)
gabe.eat('BBQ')
gabe.sleep(30)
gabe.work(60)
Gabriel은 BBQ를 먹습니다.
Gabriel은 30분동안 잡니다.
Gabriel은 60분동안 준비를 합니다.
Gabriel은 60분동안 업무를 합니다.

special method

  • __로 시작 __로 끝나는 특수 함수

  • 해당 메쏘드들을 구현하면, 커스텀 객체에 여러가지 파이썬 내장 함수나 연산자를 적용 가능

  • 오버라이딩 가능한 함수 목록은 아래 링크에서 참조

    • https://docs.python.org/3/reference/datamodel.html

    • https://docs.python.org/3/reference/datamodel.html#object.add

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
59
60
61
62
# Point 
# 2차원 좌표평면 각 점(x, y) 
# 연산
# 두점 의 덧셈, 뺄셈 (1, 2) + (3, 4) = (4, 6)
# 한점과 숫자의 곱셈 (1,  2) * 3 = (3, 6)
# 그 점의 길이 (0,0) 부터의 거리
# x, y 값 가져오기
# 출력하기

class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y 
        
    def __add__(self, pt):
        new_x = self.x + pt.x
        new_y = self.y + pt.y
        return Point(new_x, new_y)
    
    def __sub__(self, pt):
        new_x = self.x - pt.x
        new_y = self.y - pt.y
        return Point(new_x, new_y)
    
    def __mul__(self, factor):
        return Point(self.x * factor, self.y * factor)
    
    def __getitem__(self, index):
        if index == 0:
            return self.x
        elif index == 1:
            return self.y
        else:
            return -1
    
    def __len__(self):
        return self.x ** 2 + self.y ** 2
        
    def __str__(self):
        return '({}, {})'.format(self.x, self.y)
        
p1 = Point(3, 4)
p2 = Point(2, 7)

a = 1 + 2
p3 = p1 + p2
p4 = p1 - p2

# p5 = p1.multiply(3)
p5 = p1 * 3

print(len(p1))

# p1[0] -> x
# p1[1] -> y
print(p1[0])
print(p1[1])
print(p1)
print(p2)
print(p3)
print(p4)
print(p5)
25
3
4
(3, 4)
(2, 7)
(5, 11)
(1, -3)
(9, 12)

클래스 복습

  • 복소수(complex number) 클래스를 정의 하기

  • 덧셈, 뺄셈, 곱셈 연산자 method 만들기

  • 길이 (복소수의 크기) mothod 만들기

    • 실수 (real) , 허수(img) 2개로 나눠짐
  • 복소수 출력 ‘1 + 4j’와 같이 표현하게 하기

  • 비교 연산 ==, != method만들기

  • =, <= , <, > 연산 method만들기

  • 절대값 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
import math
class ComplexNumber:
    def __init__(self, real, img):
        self.real = real
        self.img = img
        
    def __add__(self, cn):
        return ComplexNumber(self.real + cn.real, self.img + cn.img)
    
    def __sub__(self, cn):
        return ComplexNumber(self.real - cn.real, self.img - cn.img)
    
    def __mul__(self, x):
        if type(x) == int:
            return ComplexNumber(self.real * x, self.img * x)
        elif type(x) == ComplexNumber:
            return ComplexNumber(self.real * x.real - self.img * x.img, self.real * x.img + self.img * x.real)
#            (a + bj) * (c + dj) = (ac - bd) + (ad + bc)j
        
    def __str__(self):
        if self.img >= 0:
            return '{} + {}j'.format(self.real, self.img)
        else:
            return '{} - {}j'.format(self.real, abs(self.img))
        
    def __eq__(self, cn):
        return self.real == cn.real and self.img == cn.img
    
    def __ne__(self, cn):
        return not (self.real == cn.real and self.img == cn.img)
    
    def __abs__(self):
        return math.sqrt(self.real ** 2 + self.img ** 2)
    
    def __len__(self):
        return self.real ** 2 + self.img ** 2
    
a = ComplexNumber(1, 2)
b = ComplexNumber(3, 2)

abs(a)
len(a)
5

댓글남기기