본문 바로가기

😀 기초/파이썬(Python)

2장 파이썬 기초문법(2.11 ~ 2.12)

2.11 파이썬에서 딕셔너리 자료형 다루기

 

1. 자료의 개수
- 리스트 자료형과 마찬가지로 딕셔너리 자료형의 원소의 개수도 len 명령으로 구할 수 있다.

 

x = {'a': 10, "b": 20}

len(x)

# 결과 : 2

2. 자료의 갱신, 추가, 삭제
- 딕셔너리 자료에서 특정한 키 값을 다른 값으로 바꾸려면 인덱싱 결과에 바꾸고자 하는 값을 대입한다.

x['a']

#결과 : 10

x['a'] = 30
# 결과 : {'a': 30, 'b': 20}

# 새로운 키와와 이에 대응하는 값을 추가
x['c'] = 40
print(x)
# 결과 :
{'a': 30, 'b': 20, 'c': 40}

# 특정한 키와 value 삭제는 del 명령
del x["b"]
x

# 결과 : 
{'a': 30, 'c': 40}

3. 키 확인

- 딕셔너리 자료에 특정한 키가 있는지 알기 위해선 in 명령을 사용
- 있다면 True 없다면 False

"a" in x
True

"d" in x
False

4. 딕셔너리 자료형의 반복

- 딕셔너리 자료형도 for 반복문을 사용할 수 있음
- ***딕셔너리 자료형은 내부적으로 자료의 순서를 보장하지 않으므로*** 특정한 순서로 반복될 것을 기대하면 안된다는 주의!
- for 반복문에서 딕셔너리 자료형을 사용하는 방법은 다음과 같이 세가지 있음.
- 1. 키만 반복하는 경우
- 2. 값만 반복하는 경우.
- 3. 키와 값 쌍을 반복하는 경우

 

5. 딕셔너리 자료형의 키 반복
- 리스트와 마찬가지로 for 반복문의 in 다음에 딕셔너리 자료형 변수를 넣으면 된다.

print(x)

for k in x:
    print(k)
    
# 결과 : 
{'a': 30, 'c': 40}
a
c

# key 메서드는 딕셔너리 자료의 키 목록을
# 리스트로 반환하므로 반복문에 이를 사용할 수도 있다.

x.keys()
# 결과 : 
dict_keys(['a', 'c'])

for k in x.keys():
    print(k)
    
# 결과:
a
c

6. 딕셔너리 자료형의 값 반복

- 딕셔너리 자료의 키를 반복하려면 
- 딕셔너리 자료의 값 목록을 리스트로 반환하는 values 메서드를 사용한다.

x.values()
# 결과:
dict_values([30, 40])

for v in x.values():
    print(v)
    
# 결과 : 
30
40

7. 딕셔너리 자료형의 키와 값의 쌍 반복
- 딕셔너리 자료의 키와 값을 쌍으로 하여 반복하려면 items 메서드 사용
- 이때 for다음의 카운터 변수를 다음과 같이 두 개 지정해야한다.
- 첫번째 카운터 변수에는 ***키*** 값, 두 번째 카운터 변수에는 ***값(value)***

for k, v in x.items():
    print("key [%s] => value [%d]" % (k, v))

#결과 : 
key [a] => value [30]
key [c] => value [40]

# 연습문제
# 아래의 자료를 for문을 사용하여 코드를 출력해라. 
data = {
    "철수": 98,
    "영희": 80,
    "순이": 100,
    "돌이": 70,
}

sum = 0
for k, v in data.items():
    
    sum = sum + v
    ave = sum / len(data)
    print("{} {} {}".format(k, "         ", v))
    
print("=" * 20)
print(ave)

#결과 : 
철수           98
영희           80
순이           100
돌이           70
====================
87.0

2.11 파이썬 객체지향 프로그래밍

 

1. 객체
- 프로그램이 어떤 작업을 수행하기 위해서는 (1)데이터와 (2) 데이터를 조작하는 행위, 두 가지 요소가 필요하다.
- 일반적으로 데이터는 변수에 넣어서 사용하고
- 데이터를 조작하는 일은 함수로 구성해서 쉽게 실행할 수 있도록 만들어 놓는다.

- 객체(object, instance)는 서로 연관된 데이터와 그 데이터를 조작하기 위한 함수를 하나의 집합에 모아놓은 것을 말한다.
- 이때 집합의 원소가 되는 변수나 함수는 멤버(Member) 또는 속성(attribue)이라고 한다.
- 특히 객체의 속성인 함수는 메서드(method)라고 부른다.

- 예를 들어 사격형의 면적을 구하는 프로그램을 만든다 가정하면 필요한 변수와 함수는 다음과 같다
- 1. 가로 길이와 세로 길이라는 두 개의 데이터를 넣을 변수
- 2. 두 길이를 곱해서 면접을 구하는 함수.

- 객체지향을 사용하지 않고 파이썬으로 구현하면 아래와 같다.

h = 10
v = 20

def area(h, v):
    return h * v

a = area(h, v)
print(a)

# 결과 : 200

- 위 프로그램에서 사각형의 가로 길이를 나타내는 변수 h, 사각형의 세로 길이를 나타내는 변수 v, 그리고 사각형의 면적을 계산하는 함수 area는 제각기 떨어져있다.
- 하지만 객체지향 프로그래밍에서는 이 세가지를 하나의 객체(object)로 묶을 수 이다.

- 다음은 이 프로그램을 객체지향 방식으로 다시 구현한 것.

class Rectangle(object):
    
    def __init__(self, h, v):
        self.h = h
        self.v = v
        
    def area(self):
        return self.h * self.v

# 이 부분은 클래스(class) 이라고 한다.
# 실제로 길이 변수들을 저장하고
# 면적을 계산하는 코드는 다음과 같다

 

r = Rectangle(10, 20)
a = r.area()
print(a)

# 결과 : 200

- 위 프로그램에서 r이 바로 ***객체***이다.
- 객체 r은 사격형의 가로 길이와 세로 길이를 나타내는 변수 h와 v 그리고 면적을 계산하는 함수 area()가 합쳐져서 만들어 진것이다.
- 객체 r에 포함된 이 변수들과 함수, 즉 속성을 꺼내려면 객체 이름 뒤에 점(.)을 붙인 다음 속성 이름을 입력하면 된다.

 

2. 클래스
- 객체지향 프로그래밍에서 객체를 만들려면 바로 만들지 못하고 항상 클래스(class)라는 것을 만든 후에 그 클래스를 이용하여 객체를 만들어야 한다.

- 위 예제에서 Rectangle은 ***클래스***이고 r은 Rectangle 클래스로 만들어진 ***객체***이다
- 즉 정해진 속성, 여기에는 가로 길이h와 세로 길이라는 속성을 가지도록 사격형 클래스를 한 번 만들어 놓으면 이속성을 가지는 실제 사각형은 얼마든지 만들 수 있다.

- 예를 들어 위에 만들어 놓은 Rectangle 클래스로 다음과 같이 5개의 사격형을 만들 수 있음
 

a = Rectangle(1, 1) #가로 1, 세로 1인 사각형
b = Rectangle(2, 1) #가로 2, 세로 1인 사각형
c = Rectangle(3, 1) #가로 3, 세로 1인 사각형
d = Rectangle(1, 4) #가로 1, 세로 4인 사각형
e = Rectangle(1, 5) #가로 1, 세로 5인 사각형

# 면적 계산은 다음과 같다.
print(a.area())
print(b.area())
print(c.area())
print(d.area())
print(e.area())

# 결과:
1
2
3
4
5

3. 생성자

- 파이썬에서 클래스를 정의하는 문법은다음과 같다.

class 클래스이름(object):

    def __init__(self, 속성값1, 속성값2, 속성값3):
        self.속성이름1 = 속성값1
        self.속성이름2 = 속성값2
        self.속성이름3 = 속성값3

- 이때 속성값 인수는 필요하지 않다면 없어도 된다.
- 여기서 class 블럭 안에 정의된 __init__란 함수는 생성자(constructor)라고 하며 클래스 정의에서 가장 중요한 함수이다.
- 객체를 생성할 때는 클래스이름을 함수처럼 호출해야 하는데, 이때 실제로는 __init__로 정의된 생성자 함수가 호출된다.
- 생성자 함수 내부에서는 생성자를 호출할 때 넣은 입력 변수, 즉 인자의 값을 속성값으로 저장한다.

#### 연습문제
---
삼각형의 넓이를 계산하기 위한 클래스를 만든다. 이 클래스는 다음과 같은 속성을 가진다.

밑변의 길이 b 와 높이 h

삼각형의 넓이를 계산하는 메서드 area

============================================

# 👍
class Triangle():
    def __init__(self, b, h):
        self.b = b
        self.h = h
    def area(self):
        return self.b * self.h
        
    
r = Triangle(5, 4)
a = r.area()
print(a)
# 정답 : 20


==========================================

#### 연습문제
---
사각 기둥의 부피를 계산하기 위한 클래스를 만든다. 이 클래스는 다음과 같은 속성을 가진다.

밑면의 가로 길이 a, 밑면의 세로 길이 b, 높이 h

부피를 계산하는 메서드 volume

겉넓이를 계산하는 메서드 surface

# 👍
class SquarePillar():
    def __init__(self, a, b, h):
        self.a = a
        self.b = b
        self.h = h
    def volume(self):
        return 2 * (self.a * self.b + self.b * self.h + self.h * self.a)
    def surface(self):
        return self.a * self.b * self.h
    
a = SquarePillar(2, 3, 5)
print(a.volume())
print(a.surface())

4. 게임 캐릭터와 객체

- 이번에는 컴퓨터 게임의 캐리터를 만드는 예저를 통해 클래스 상속(Inheritance)의 개념을 공부한다.
- 게임에 사용되는 플레이어의 캐릭터는 객체지향 프로그램을 통해 만든다고 생각해보자.
- 캐릭터의 능력치, 경험치 등의 숫자는 캐릭터마다 다르게 관리되어야 하므로 객체의 속성이 될 수 있다.
- 또한 모든 캐릭터 조작에 공통적으로 필요한 이동, 공격 등의 조작은 메서드로 구현할 수 있을 것이다.

- * 플레이어의 캐릭터
- * 속성 : 캐릭터의 능력치, 경험치 등
- * 메서드 : 캐릭터를 움직이는 방법, 이동, 공격 등

- 이를 기반으로 캐릭터를 만들어내는 Character라는 클래스를 생성
- 이 클래스로 만든 캐릭터는 1,000이라는 life 속성값을 가지고 생성되며
- 게임상에서 공격받을 때는 attacked라는 메서드가 호출되어 
- life 속성값을 10만큼 감소시키고 공격받았음을 표시한다.

class Character(object):
    def __init__(self):
        self.life = 1000
        
    def attacked(self):
        self.life -= 10
        print("공격받음! 생명력=", self.life)
        
a = Character()
b = Character()
c = Character()

a.life, b.life, c.life

# 결과 : 
(1000, 1000, 1000)

# 공격 받은 캐릭터는 생명력 삼소
a.attacked()

# 결과 : 공격받음! 생명력= 990

b.attacked()
b.attacked()
b.attacked()
b.attacked()
b.attacked()
b.attacked()

#결과 : 
공격받음! 생명력= 950
공격받음! 생명력= 940
공격받음! 생명력= 930
공격받음! 생명력= 920
공격받음! 생명력= 910
공격받음! 생명력= 900

a.life, b.life, c.life
(990, 900, 1000)

5. 클래스 상속
- 이제 클래스 상속이라는 개념을 생각한다.
- 앞에 만든 클래스는 모든 캐릭터에 공통적인 life 속성만 가지고 있음
- 하지만 전사, 법사 등 다양한 직업을 가진 캐릭터가 서로 다른 초기 속성을 가져야함
- 각자 직업의 클래스 생성할 수 있지만, 상속을 하면 코드 재사용을 할 수 있다.
- 이때 상속 받는 클래스는 자식 클래스(child class), 상속의 대상이 되는 클래스를 부모 클래스(parent class) 라고함.

- 부모 클래스에서 상속을 통해 만든 자식 클래스 문법은 아래와 같다

class 자식클래스이름(부모클래스이름):
    
    def __init__(self, 속성값1, 속성값2):
        super(자식클래스이름, self).__init__()
        자식 클래스의 초기화 코드

- 사실 우리가 지금까지 쓰던 클래스의 정의를 살펴보면 object라는 부모 클래스에서 상속받는 것

- 😢이 코드에서super(자식클래스이름, self).__init()__ 부분은 부모 클래스의 초기화 생성자를 호출하는 부분이다.
- 예를 들면 Warrior 라는 클래스에서 부모 클래스인 Character 클래스의 생성자를 호출하면 life라는 속성값을 초기화하므로 자식 클래스에서는 이 속성값을 초기화해줄 필요가 없다.

class Character(object):
    
    def __init__(self):
        self.life = 1000
        self.strength = 10
        self.intelligence = 10
        
    def attacked(self):
        self.life -= 10
        print("공격받음! 생명력=", self.life)
        
    def attack(self):
        print("공격!")
class Warrior(Character):
    
    def __init__(self):
        super(Warrior, self).__init__()
        self.strength = 15
        self.intelligence = 5
        
    def attack(self):
        print("육탄 공격!")
class Wizard(Character):
    
    def __init__(self):
        super(Wizard, self).__init__()
        self.strength = 5
        self.intelligence = 15
        
    def attack(self):
        print("마법 공격!")
a = Character()
b = Warrior()
c = Wizard()

a.attack()
공격!

b.attack()
육탄 공격!

c.attack()
마법 공격!

a.attacked()
공격받음! 생명력= 990

b.attacked()
공격받음! 생명력= 990

c.attacked()
공격받음! 생명력= 990

6. 메서드 오버라이딩

- 메서드 오버라이딩(Method Overriding)이란, 여러 클래스에 걸쳐서 같은 이름의 메서드를 만드는 것.
- 예를 들어 부모, 존사, 마법사 캐릭터 클래스에 공통으로 attack 이라는 메서드가 있지만
- 각각 하는 일이 다를 때는 다음처럼 같은 이름의 메서드를 클래스별로 구현하면 된다.
- 이렇게 되면 부모 클래스에서 만든 메서드 정의를 자식 클래스에서 변경해서 사용한다.

class Character(object):
    
    def __init__(self):
        self.life = 1000
        self.strengh = 10
        self.intelligence = 10
        
    def attacked(self):
        self.life -= 10
        print("공격받음! 생명력 =", self.life)
        
    def attack(self):
        print("공격!")
class Warrior(Character):
    def __init__(self):
        super(Warrior, self).__init__()
        self.life = 1500
        self.strengh = 15
        self.intelligence = 5
            
    def attack(self):
        print("물리 공격!")
class Wizard(Character):
    
    def __init__(self):
        super(Wizard, self).__init__()
        self.life = 500
        self.strength = 5
        self.intelligence = 15
        
    def attack(self):
        print("마법 공격!")
a = Character()
b = Warrior()
c = Wizard()

a.attack()
공격!
b.attack()
물리 공격!
c.attack()
마법 공격!

a.attacked()
공격받음! 생명력 = 990
b.attacked()
공격받음! 생명력 = 1490
c.attacked()
공격받음! 생명력 = 490

7. 😢특수 메서드
- 파이썬에서는 특수 메서드(Speical Methods)라는 것이 존재함.
- 메서드 이름 앞과 뒤에 두 개의 밑줄이 붙어있는 메서드
- 이 메서드들은 특수한 용도에 사용하는 것


- 예를 틀어 파이썬 셸에서 변수 이름을 입력하고 키(주피터 노트북에서는 +)를 누르면 변수의 값이 호출되는데, 사실 이것은 해당 변수가 가지는 __prpr__이라는 메서드가 호출되는 것.
- repr은 representation의 약자.
- 또 변수를 str이라는 함수에 넣으면 변수를 문자열로 변환해주는데, 이것도 사실은 __str__이라는 메서드가 호출되는 것.

# 예를 들어
# 다음과 같이 복소수에 대한 클래스인 complex 클래스를 만든다. 
# r이라는 속성에 실수부를, i이라는 속성에 허수부를 넣는다.

class Complex(object):
    
    def __init__(self, realpart, imagepart):
        self.r = realpart
        self.i = imagepart

- __repr__ 메서드를 정의하지 않으면 object 클래스가 가진 기본 __repr__메서드를 사용한다.
- 이 함수는 클래스 이름과 변수가 위치하고 있는 메모리 주소를 <>안에 써서 반환한다.
- 기본 __str__ 메서드도 마찬가지이다.

c = Complex(1, 2)
c

<__main__.Complex at 0x2543876cd60>

str(c)

'<__main__.Complex object at 0x000002543876CD60>'
# 이번에는 __repr__ 메서드와 __str__ 
# 메서드를 다음과 같이 새로 정의하여 오버라이딩한다.

class Complex2(Complex):
    
    def __repr__(self):
        return "Complex: real = %f image = %f" % (self.r, self.i)
    
    def __str__(self):
        return "[for str]" + self.__repr__()
c2 = Complex2(1, 1)
c2

Complex: real = 1.000000 image = 1.000000

str(c2)

'[for str]Complex: real = 1.000000 image = 1.000000'
# __getitem__ 스페셜 메서드를 정의하면
# 마치 리스트나 사전처럼 [] 기호를 사용한 인덱싱을 할 수 있다.

class Complex3(Complex2):
    
    def __getitem__(self, key):
        if key == "r":
            return self.r
        if key == "i":
            return self.i
            
c3 = Complex3(1, 2)
c3

Complex: real = 1.000000 image = 2.000000

c3["i"]
2