클래스는 파이썬 초보자인 나에게 헷갈리는 개념이다. 
아직 헷갈리지만 최대한 정리해둔다. 

참고로, 파이썬 - 기본을 갈고 닦자!  41번 문서에 클래스가 잘 설명돼 있다. 나중에 제대로 보자. 

클래스(Class)와 객체(Object)를 직관적으로 설명하는 예시는 '클래스는 붕어빵 찍어내는 틀이고, 객체는 이 틀로 찍어낸 붕어빵'이라는 것이다. 쉽게 와닿지만, 어떤 블로그 글에선 이 예시가 잘못됐다고 지적하는 내용도 있어 혼란스럽다. 하지만 일단 이 정도로 이해하고 넘어가겠다. 

위 붕어빵 예시가 보여주듯 클래스는 '설계도'이고 객체는 '설계도로 구현한 대상'이다. 

- 클래스는 객체의 구조와 행동을 정의한다. 
- 클래스는 복잡한 문제를 다루기 쉽도록 만든다.

클래스를 이해하려면
Object, self, __init__ 등 개념을 이해해야 한다.

👉Object : Object is one of the instances of the class which can perform the functionalities which are defined in the class

👉self : self represents the instance of the class. By using the 'self' keyword we can access the attributes and methods of the class.

👉__init__ : __init__ is a reserved method in python classes. It is known as a constructor in object-oriented concepts. This method called when an object is created from the class and it allows the class to initialize the attributes of a class.

아래 내용은 <입문부터 실무까지 한 방에 끝내는 파이썬 프로그래밍_by 조인석> 내용을 정리한 것임을 밝힙니다. 좋은 책이니 파이썬 기초를 공부하는 분에게 추천합니다. 


  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
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
#%% md

# Ch 09. 클래스 

## 09.01 클래스 이해하기 

'''
클래스에서 생성된 객체를 인스턴스라고 부른다. 일반적으로 클래스의 객체를 생성하는 과정을 인스턴스화라고 한다. 
모든 인스턴스에는 타입이 존재한다. 이 타입은 클래스에 의해 정의된다. 

메소드 : 클래스 속에 속해 있는 내장 함수 
'''

#%%

var = '파이썬 객체 지향 이야기'
var.__class__ # var 지역 변수 __class__ 값 확
var.replace('파이썬', 'Python') # var 값 일부 변경

#%% md

### (1) 클래스 정의 및 불러 오기 

#%%

class BookReader:  # 클래스 BookReader 선언
    name = str()   # 문자열 타입 변수 name 선언
    def read_book(self): # 함수 read_book 선언
        print(self.name + ' is reading Book!!')
        

#%% md

클래스명 작성 방식 : 낙타 표기법을 따른다! 

클래스명에서는 언더바를 사용하지 않는다. 
그리고  단어의  음절이 대문자인 단어들의 조합 형태로  있다. 
이런 표기법을 CamelCase (낙타 표기법) 이라고 한다. 
'''

#%%

reader = BookReader() # 인스턴스 생성
type(reader)  # 변수 reader1 타입 확인

#%% md

__ _main_ __ :   
현재 클래스가 생성된 위치를 보여 준다. 파이썬 실행의 최상단 레벨 코드에서 실행이 되었다는 의미로 볼 수 있다.  
이는 곧 이 클래스를 호출할 수 있는 유효범위와도 깊은 관계가 있으며, __main__에 속한 클래스는 전역에서 호출이 가능하다. 
'''

#%%

reader.name = 'Ryu' # 속성 값 세팅
reader.read_book()  # 메소드 호출

#%% md

### (2) 클래스 초기화 함수 __init__() 재정의 

'''
__init__ : __init__() 함수는 새로 생성하는 함수가 아니라 이미 존재하고 있는 초기화 함수다. 
           인스턴스를 만들 때 호출하는 BookReader() 호출 시 불리는 함수라고 보면 된다. 
           이렇게 이미 존재하는 함수를 다시 정의하는 과정을 함수 재정의라고 한다. 
'''

#%% 

class BookReader:
    def __init__(self, name): # 초기화 함수 재정의 
        self.name = name 
    def read_book(self):
        print(self.name + ' is reading Book!!') 

#%%

reader = BookReader('Sooyoun') # 객체 생성
reader.read_book() # 메소드 호출

#%% md


### (3) 클래스 변수와 인스턴스 변수 

'''
클래스 변수 : 클래스에 의해 생성된 모든 객체가 인스턴스화되는 시점에 같은 값을 조회할 때 사용 가능 (country)  
인스턴스 변수 : 객체가 인스턴스화 될 때마다 새로운 값이 할당되며 서로 다른 객체 간에는 값을 공유할 수 없다. (name)   

만약 이해가 잘 안 된다면, 무조건 인스턴스 변수만을 사용하길 권고한다.   
이렇게 작성된 인스턴스 변수 중 값이 절대로 변경되지 않거나, 여러 객체 사이에서 값이 공유돼야 하는 경우에는 클래스 변수로 옮기면 된다.   
이렇게 작성된 소스 코드를 더 보기 좋고 효율적으로 변경하는 작업을 '리팩토링' 이라고 한다.   

소스 코드도 물과 같이 고이면 냄새가 나고 썩기 마련이다. 리팩토링 기법에 관한 서적을 쉽게 접할 수 있으니 관심 있으면 읽어보길.   
'''

#%% 

class BookReader:
    country = 'South Korea' # 클래스 변수 country 선언
    def __init__(self, name): # 초기화 함수 재정의 
        self.name  = name
    def read_book(self): 
        print(self.name + ' is reading Book!!')

reader1 = BookReader('Chris') # 'Christ' 라는 이름을 가진 객체 인스턴스화 
reader2 = BookReader('Anna') # 'Anna' 라는 이름을 가진 객체 인스턴스화 
reader1.country # 클래스 변수 country 값 확인

#%%

class Dog:
    tricks = [] # 클래스 변수 (인스턴스 간 공유 됨) 
    
    def __init__(self, name):
        self.name = name 
    
    def add_trick(self, trick):
        self.tricks.append(trick) # 클래스 변수에 값 추가 
        
fido = Dog('Fido')
buddy = Dog('Buddy')
fido.add_trick('구르기')
buddy.add_trick('두 발로 서기')
buddy.add_trick('뽀뽀하기')
fido.tricks

#%% md

trick  클래스 변수로 선언해 fido의 tricks 변수에 buddy에게 넣은 장난까지 모두 출력됐다.   
이럴  tricks를 인스턴스 변수로 선언해야 한다. 

#%%

class Dog:
    
    def __init__(self, name):
        self.name = name
        self.tricks = [] # 인스턴스 변수 생성
        
    def add_trick(self, trick):
        self.tricks.append(trick)
fido = Dog('Fido')
buddy = Dog('Buddy')
fido.add_trick('구르기')
buddy.add_trick('두 발로 서기')
buddy.add_trick('뽀뽀하기')
fido.tricks, buddy.tricks

#%% md

### (4) 심화/ 데이터 은닉과 이름 장식 

#%% 

class BookReader:
    country = 'South Korea' # 클래스 변수 country 선언
    
dir(BookReader) # dir() 는 파이썬 기본 함수로, 클래스 내부에 들어 있는 객체들을 확인하는 명령문이다.


#%%

BookReader.country = 'USA' # 클래스 변수 값 변경하기
BookReader.country

#%% md

'''
dir(BookReader) 리스트 맨 마지막에 있는 country 변수는 너무 쉽게 접근, 수정할 수 있다.   
이게 무슨 문제일까 생각할 수도 있지만,   

클래스 변수로 쉽게 변경할 수 없는 값을 집어 넣었는데 아무나 이 값을 쉽게 변경할 수 있다면 이 함수에 대한 신뢰도는 떨어지기 마련이다.   

그래서 객체 지향 언어에서는 데이터 은닉 혹은 캡슐화라는 개념을 사용하고 있다. 이런 데이터는 외부에 노출하지 말고 숨기자는 의도이다.   

하지만 파이썬에서는 이를 위한 강력한 도구를 제공하고 있지 않다.   

가령, 자바는 클래스 변수는 밖에서 아예 접근이 불가능하도록 하는 접근 제한자를 가지고 있어 어떤 방식으로든 바로 참조를 할 수 없게 돼 있다.   

대신, 파이썬은 변수명 앞에 __ 가 있는 것에 한하여 이름을 변경해 버리는 Name Mangling 기법을 제공한다.   

'''

#%%

class BookReader:
    __country = 'South Korea' # 클래스 변수 country 선언 (Name Mangling)

dir(BookReader)

#%% md

'''
dir( )로 BookReader 클래스의 내부를 다시 확인해 보니, 리스트 첫 번째 항목에 변형된 변수명을 확인할 수 있다. 
  
변형된 규칙은 _[클래스명]__[변수명]이다.   

이렇게 변형된 변수는 기존 변수명으로는 값을 확인할 수 없게 된다. 
  
다음 소스 코드를 확인해 보자. 속성 에러가 떠서 클래스를 정의할 때 사용했던 변수명으로는 접근이 불가능한 것을 확인할 수 있다.   

BookReader.__country

#%% md

이런 클래스 변수는 어떻게 수정하는 게 좋을까?   
클래스 안에 이 변수를 수정하기 위한 메소드를 선언해 호출하는 방식이 좋다. 

#%%

class BookReader:
    __country = 'South Korea' # 클래스 변수 country 선언 (Name Mangling)
    def update_country(self, country): # country 변경 메소드 선언
        self.__country = country # country 값 변경
    def get_country(self): # country 값 반환 메소드 선언 
        return self.__country # country 값 반환 

br = BookReader() # BookReader 인스턴스 생성
br.get_country()

#%%

br.update_country('USA')
br.get_country()



#%% md

### (5) 객체 지향의 꽃, 상속 (Inheritance) 

객체 지향 언어에서의 상속은 말 그대로 부모 클래스가 자식 클래스에게 무언가를 물려 주는 것이다. 
파이썬에서는 부모 클래스를 베이스 클래스(Base Class)라고 하고, 자식 클래스는 파생 클래스(Derived Class)라고 부른다.   

-Base Class : 부모 클래스. 자식 클래스들에게 물려 줄 속성과 메소드들을 정의한다.   
-Derived Class : 자식 클래스. 부모의 속성, 메소드를 물려 받는다. 

상속은 왜 필요할까?   
중복 코드를 최소화 하고, 클래스 간 계층 관계를 형성해 현실 세계와의 괴리를 줄이기 위해서. 

#%% md

#### - 부모 클래스 Human 선언 

#%%

class Human:
    country = 'South Korea' # 클래스 변수 country 선언
    def __init__(self, name): # 초기화 함수 재정의
        self.name = name # 인스턴스 변수 name 선언 
    def eat_meal(self):
        print(self.name + ' is eating meal!!')
        

#%% md

#### - Human의 자식 클래스인 BookReader 클래스 선언 

#%%

class BookReader(Human): # Human의 자식 클래스인 BookReader 클래스 선언 
    def read_book(self):
        print(self.name + ' is reading Book!!')

class DrumPlayer(Human): # Human의 자식 클래스인 DrumPlayer 클래스 선언
    def play_drum(self):
        print(self.name + ' is playing Drum!!')

br = BookReader('Christ') # BookReader 인스턴스 생성 (부모 init 활용) 
br.country
        

#%%

br.read_book()

#%%

dp = DrumPlayer('Sean') # DrumPlayer 인스턴스 생성 (부모 init 활용) 
dp.country

#%%

dp.play_drum()

#%%

br.eat_meal()

#%% md

#### - 자식 클래스 추가 

#%% 

class BookWriter(Human):
    def write_book(self):
        print(self.name + ' is writing Book!!')

#%% md

이미 만들어진 인스턴스나 클래스의 부모 클래스를 찾는 방법 !!   

이미 만들어진 인스턴스나 클래스의 부모 클래스를 찾는 방법.  
본인이 어떤 클래스의 인스턴스인지를 확인하는 방법은 __class__ 속성 값을 확인하는 것이다.   
부모 클래스인 베이스 클래스 확인을 위해서는 __bases__ 속성 값을 확인하면 된다.   

#%% 

br.__class__ # br 인스턴스의 타입 확인

#%%

BookReader.__class__ # BookReader 클래스의 타입 확인

#%%

BookReader.__bases__ # BookReader의 부모 클래스 확인


#%% 

Human.__bases__

#%% md

우리가 만드는 모든 클래스의 부모 클래스는 object 클래스다. 상속 계층 구조의 가장 높은 곳에 위치한 녀석이다. 다시 한번 파이썬의 모든 것은 객체라는 것이 확인 됐다.   
(object,) 인 이유는 부모 클래스가 한 개가 아닌 여러 개가 될 수도 있다는 것을 암시한다. 


#%% md

#### - 부모 클래스를 두 개 갖는 자식 클래스 

#%% 

class Developer: # Devloper 부모 클래스 선언
    def coding(self):  # coding 메소드 선언
        print(self.name + ' is developer!!')
        
class ProgramBookWriter(Human, Developer):
    def write_book(self):
        print(self.name + ' is writing Book!!')
        
pbw = ProgramBookWriter('Chris') # 인스턴스 생성
pbw.eat_meal() # Human 메소드 호출

#%%

pbw.coding() # Developer 메소드 호출

#%%

pbw.write_book() # ProgramBookWriter 메소드 호출


#%%

ProgramBookWriter.__bases__



#%% md

이처럼 여러 클래스를 하나의 자식 클래스가 상속 받는 것을 다중 상속이라고 한다. 


#%% md

### (6) 심화/ 다형성 (polymorphism) 

#%%

class Developer: # Developer 부모 클래스 선언 
    def __init__(self, name): # 초기화 시 name을 받기 위한 재정의 
        self.name = name
    def coding(self): # coding 메소드 선언
        print(self.name + ' is developer!!')
        
class PythonDeveloper(Developer): # PythonDeveloper 자식 클래스 선언
    def coding(self): # coding 메소드 선언
        print(self.name + ' is Python developer!!')
        
class JavaDeveloper(Developer): # JavaDeveloper 자식 클래스 선언
    def coding(self): 
        print(self.name + ' is Java developer!!')

class CPPDeveloper(Developer): # CPPDeveloper 부모 클래스 선언 
    def coding(self):
        print(self.name + ' is C++ developer!!')

#%%

pd = PythonDeveloper('Sooyoun') # PythonDeveloper 인스턴스 생성
jd = JavaDeveloper('Jason')
cd = CPPDeveloper('Bryan')
pd.coding()

#%%

jd.coding()

#%%

cd.coding()

#%% md


이렇게 부모 클래스와 동일한 이름의 메소드를 그대로 자식 클래스에서 구현해 재정의 하는 것을 다향성의 구현이라고 한다. 


#%%

class CPPDeveloper(Developer): # CPPDeveloper 부모 클래스 선언 
    def coding(self): # coding 메소드 선언 
        super().coding() # 부모 인스턴스의 coding() 함수 호출
        print(self.name + ' is C++ developer!!')
        

#%% md

3번째 줄이 핵심이다. super()를 통해 부모 인스턴스를 확보한 뒤 점 기호(.)를 통해 coding() 함수를 호출하고 있다.

#%%

cd = CPPDeveloper('Bryan') # 자식 클래스 인스턴스 생성
cd.coding() # 자식 클래스 메소드 호출 

#%% md

출력 결과를 보면 부모의 coding() 함수와 본인의 coding() 함수 내 출력문이 출력됐다. 

이런 식의 super() 클래스 호출은 초기화 함수 호출시에도 많이 활용된다. 
가령, __init__() 함수를 재정의하면 부모의 초기화 함수 코드를 사용하지 못하기 때문에 super()를 활용해 사용한다. 
예를 들어, 자식 클래스의 초기화 함수 내 super().__init__()를 호출하면 부모의 초기화 함수를 호출할 수 있다. 


사실 잘 이해 안 된다. 


한 번 코드로 보자. 

1. 새로운 클래스 생성 

새로운 클래스를 만들기 위해서는 class 키워드를 사용한다. 

1
2
class Flight:
    pass

2. 새로운 객체 생성

1
f = Flight()   # 생성자(constructor)


3. 매서드 작성

매서드는 클래스에 들어 있는 함수다.


1
2
3
class Flight:
    def number(self):
        return 'SN060'

파이썬 매서드의 첫 번째 파라미터 이름에 관례적으로 self 를 사용한다. 호출 시 호출한 객체 자신이 전달되기 때문에 self 라고 하는 것이다.


4. 인스턴스 매서드 접근

클래스로부터 만들어진 객체를 그 클래스의 인스턴스(Instance)라고 한다.


1
f.number()

위 코드는 'SN060'를 출력한다.




코드 따라쓰며 연습하기 1)


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
class Rectangle:
    """Find out the cost of a rectangular filed with breadth(b=120), length(I=160)
    It costs x(2000) rupees per 1 square unit"""
    
    def __init__(self, length, breadth, unit_cost = 0):
        self.length = length
        self.breadth = breadth
        self.unit_cost = unit_cost
    
    def get_area(self):
        return self.length * self.breadth
    
    def calculate_cost(self):
        area = self.get_area()
        return area * self.unit_cost 
    
r = Rectangle(160, 120, 2000)
print("Area of Rectangle : %s sq units" % (r.get_area()))
print("Cost of rectangular filed: Rs.%s" % (r.calculate_cost()))

r = Rectangle(160, 120, 2000)

Note
- "r" is the representation of the object outside of the class.
- "self" is the representation of the object inside the class.


output :
Area of Rectangle: 19200 sq units
Cost of rectangular filed: Rs. 38400000



코드 따라쓰며 연습하기 2)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
## 클래스 연습 코드 2
class Knight:

    def __init__(self, health, mana, armor): # 매개변수로 self, health, mana, armor 지정
        self.health = health
        self.mana = mana
        self.armor = armor

    def slash(self): # slash 메서드 생성
        print("베기")

# 인스턴스 생성
x = Knight(health = 542.4, mana = 210.3, armor = 38)

print(x.health, x.mana, x.armor)
x.slash()
코드 출처 : 파이썬 코딩 도장

출력 결과 :

542.4 210.3 38
베기



코드 따라쓰며 연습하게 3)

 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
# 클래스 연습 코드 3
class Customer(object):
    """
    A customer of ABC Bank with a checking account. Customers have the following properties:

    Attributes:
        name : A string representing the customer's name.
        balance : A float tracking the current balance of the customer's account.
    """

    def __init__(self, name, balance=0.0):
        """Return a Customer Object whose name is *name* and starting balance is *balance*."""
        self.name = name
        self.balance = balance

    # class method
    def withdraw(self, amount):
        """Return the balance remaining after withdrawing *amount* collors."""
        if amount > self.balance:
            raise RuntimeError('Amount greater than available balance.')
        self.balance -= amount
        return self.balance

    # class method
    def deposit(self, amount):
        """Return the balance remaining after depositing *amount* dollors."""
        self.balance += amount
        return self.balance

Ryu = Customer('Ryu Han', 1000.0) # 기존 1000원 
Ryu.withdraw(500) # 500원 인출 
Ryu.deposit(700) # 700원 예금
코드 출처 : Jeff Knupp

출력 결과 :
1200.0 ( 1000원 든 통장에서 500원 인출하고 700원 예금하면, 통장에 1200원 남음)



코드 따라쓰며 연습하게 4)


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
import math
class Point(object):
    def __init__(self, x = 0, y = 0):
        self.x = x # 데이터 속성 (attribute)
        self.y = y

    def distance_from_origin(self): # 메서드 속상
        return math.hypot(self.x, self.y) # .hypot( ) 함수는 직각삼각형의 빗변 계산

    def __eq__(self, other):
        return self.x == other.x and self.y == other.y

    def __repr__(self):
        return "point ({0.x!r}, {0.y!r})".format(self)

    def __str__(self):
        return "({0.x!r}, {0.y!r})".format(self)