# 상속과 오버라이딩

 

1. 상속

 

상속은 사전적 정의에 따르면 부모나 친족적 관계에 있는 사람의 유산을 물려 받는 제도입니다. 클래스에서의 상속은 부모의 유산을 자식이 물려 받듯이 부모 클래스의 멤버와 메소드를 자식 클래스가 물려받을 수 있습니다. 이런 경우는 흔히 있습니다. 예를 들어 라이브러리를 제작할 때 특정 자료구조를 만들어서 모든 클래스가 이 자료구조를 이용하게 하고 싶다면 클래스의 상속을 이용하게 하면 됩니다.  추가적으로 파이썬에서 기본 제공되는 자료형 또한 클래스이기 때문에 자신만의 독특한 자료형을 만들 수 도 있겠죠.

 

상속을 이해하고자 할 때 가장 쉬운방법은 기존의 클래스를 이용하되, 내가 원하는 멤버와 메소드를 추가하는 것으로 이해 하는 것입니다.

 

클래스의 상속

먼저 총이라는 간단한 클래스가 있다고 가정해 보겠습니다. 클래스라는 것은 유형, 무형으로 존재하는 어떤 것을 코드로 나타내는 것입니다.  그러면 총(Rifle)이라는 객체는 파괴력, 탄창, 현재 남은 총알 등의 속성을 가지고 있을 테고, 기능으로는 장전이나 발사가 있을 수 있습니다.

 

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 Rifle:
    def __init__(self,Mag, atk):
        self.__Mag = Mag
        self.__leftMag = Mag
        self.__atk = atk
    
    @property
    def Mag(self):
         return self.__Mag
    
    @property
    def leftMag(self):
        return self.__leftMag
    
    @leftMag.setter
    def leftMag(self, num):
        self.__leftMag = num
    
    @property
    def atk(self):
        return self.__atk
    
    @atk.setter
    def atk(self,num):
        self.__atk = num
        
    def Reload(self):
        self.__leftMag = self.__Mag
        
    def Shoot(self):
        if self.__leftMag:
            self.__leftMag -= 1
        else:
            print("장전하세요")
cs

 

이 클래스는 Rifle입니다. 제가 앞서 언급한 대로 생성자에 탄창, 남은 총알, 파괴력등의 요소를 만들었습니다. 또한 상속을위해 각 클래스멤버의 property와 setter를 작성하여 직접 멤버변수에는 접근하지 못하지만, 메소드를 통하여 가능하게 구현하였습니다. 탄창(Mag)은 처음 인스턴스 생성 이후 오염을 방지하기위해 setter에서 제외했습니다. Rifle클래스의 메소드로는 총이 실제로 가지고있는 기능인 발사(Shoot), 장전(Reload) 등을 포함 시켰습니다. 같은 클래스 안에서는 property나 setter를 이용하여 멤버에 접근할 필요가 없기때문에 직접접근하는 방식으로 구현하였습니다.(장전과 발사에 있는 self.__leftMag)

 

만약 총이라는 클래스를 상속받아 저격총이라는 클래스를 만들고자 하면 총이 가지고 있는 속성은 그대로 유지할 것입니다. 하지만 총이라는 클래스에는 망원경이라는 요소가 없기 때문에 속성을 하나 추가하고 망원경 조준이라는 기능을 추가해야겠죠.  그런 의미에서 멤버 변수에 Zoom을 추가하였고 메소드로는 increaseAtk을 넣어 Zoom과 파괴력을 곱해 다시 파괴력에 저장하는 코드를 작성하였습니다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class ScopeRifle(Rifle):
    def __init__(self,Mag,atk):
        super().__init__(Mag,atk)
        self.__Zoom = 1
    
    @property
    def Zoom(self):
        return self.__Zoom
    
    @Zoom.setter
    def Zoom(self,num):
        self.__Zoom *= num
    
    def increaseAtk(self):
        self.atk *= self.__Zoom
cs

 

__init__을 보면 super().__init__이라는 구문이 있습니다. 이는 부모클래스의 생성자 때문에 필요한 구문입니다. 생성자 규칙에 따라 부모클래스는 탄창과 파괴력이라는 멤버를 필요로 합니다. 이 멤버를 채워주지 않으면 에러가 발생합니다. 

아래의 코드를 실행시키고 각각의 메소드를 테스트 해보겠습니다.

 

1
2
rifle = Rifle(5,10)
sniper = ScopeRifle(5,10)
cs

 

 

Rifle 클래스에 정의되어 있는 내용은 잘 동작하는 것을 확인하였습니다.  rifle을 상속받은 sniper도 잘 동작하는 것을 확인 할 수 있습니다. 

이번에는 sniper클래스의 고유 기능을 테스트 해보도록 하겠습니다.

 

 

제가 의도한 대로 잘 동작하고 있습니다.  atk는 부모클래스의 멤버이면서 property지만 setter를 같이 작성했기 때문에 sniper에서 접근하고 같을 변경시킬 수 있었습니다. public 하게 구현하면 사실 이런 고생은 안해도 됩니다. 하지만 객체지향 프로그래밍에서는 getter setter를 제외하고는 클래스 멤버에대한 외부 클래스의 접근을 금하고 있기때문에 property 설정을 꼭 하셔야 합니다.

 

 


 

 

2. 오버라이딩

 

총과 저격총 사이에는 명확하지 않지만 가끔씩  상속받은 클래스가 부모의 기능을 똑같이 가지고 있지만 구현 방법이 다를 때가 존재합니다. 이런 경우에는 같은 이름의 메소드를 작성하고 다른 기능을 수행하게 해야합니다. 예를 들어 산탄총의 경우를 생각해보겠습니다. 일반 총은 발사당 1개의 총알이 소비되지만 산탄총은 3발이 나간다고 칩시다. 산탄총도Shoot이라는 메소드가 당연히 필요하지만 부모 클래스의 Shoot과 다르게 동작해야 되겠죠? 파이썬에서는 부모 클래스의 메소드와 같은 이름의 메소드를 자식 클래스에 작성하여 부모 클래스의 메소드를 덮어버리는 방법이 있습니다. 이를 우리는 오버라이딩이라고 합니다.

 

1
2
3
4
5
6
7
class ShotGun(Rifle):
    def __init__(self,Mag,atk):
        super().__init__(Mag,atk)
    
    def Shoot(self):
        if self.leftMag:
            self.leftMag -= 3
cs

 

위의 코드는 Rifle클래스를 상속받아 만든 ShotGun클래스입니다.  ShotGun은 Rifle과 모든게 같지만 발사당 3개의 총알이 소비된다고 했었죠? 그 내용을 Shoot이라는 메소드를 정의하고 그 안에 구현하면 됩니다.

부모 클래스에 Shoot이라는 메소드가 있다고 하더라도 오버라이딩을 통해 이를 덮어 쓸 수 있습니다. 실제로 이 코드가 잘 작동하는지 확인 해보겠습니다.

 

 

코드가 정상적으로 작동하는 것을 확인 할 수 있습니다. 상속을 받을 때는 이런 식으로 기능을 고려해서 메소드를 다시 설계 해야할 필요가 있는 경우도 있습니다.

# 클래스

클래스란(Class),속성이나 기능을 공유하는 유사한 성질의 객체들을 하나로 그룹화한 것입니다. 클래스 내부에는 해당 클래스의 객체를 위한 클래스 멤버(Class Member)와 기능(method)의 구현 등 세부사항을 기술합니다.
클래스에 관하여 설명하기 전에 인스턴스(instance)에 관하여 알아보도록 하겠습니다.

 

 


 

 

1. 인스턴스

 

인스턴스란 클래스에 의해 만들어진 객체를 칭하는 말입니다. 예를 들어 클래스를 붕어빵 틀이라고 한다면, 인스턴스는 붕어빵이 되는 것입니다. 즉 클래스는 인스턴스를 만드는 하나의 틀입니다. 앞으로 클래스를 이용하여 인스턴스를 계속적으로 만들어 낼 수 있습니다.

캔디 클래스와 그 인스터스 캔디

 


 

 

2. 클래스 선언방법

 

클래스는 class 명령어 뒤에 클래스 이름을기술하여 선언합니다. 클래스 구문 하위에 인덴트(tab)을 넣어 데이터 구조나 함수등을 명시하면 됩니다. 예시를 보겠습니다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Person:
    # class Person의 멤버변수
    name = "홍길동"
    number = "01077499954"
    age = "20"
 
    # class Person의 메소드
    def info(self):
        print("제 이름은 " + self.name + "입니다.")
        print("제 번호는 " + self.number + "입니다.")
        print("제 나이는 " + self.age + "세 입니다.")
 
if __name__ == "__main__":# main namespace를 의미합니다.
    customer = Person()#Person의 객체 customer 생성
    customer.info()#info 함수 호출
 
#결과값
제 이름은 홍길동입니다.
제 번호는 01077499954입니다.
제 나이는 20세 입니다.
cs

위의 예시처럼 클래스를 구성하시면 됩니다. 클래스내의 name space에 사용할 변수를 만들어 주시면 되고 함수를 추가하여 사용하시면 됩니다. 그런데 함수에 뜬금 없는 self란 친구가 들어왔습니다. self는 현재 인스턴스 객체를 가리키는 기능을 하는 친구입니다. 쉽게 말하자면 사전적 의미대로 자기자신을 이야기 하 는 것입니다. 만약 customer라는 인스턴스를 만들었다면 self는 customer가 되는겁니다.(클래스내에 함수가 선언 될 때에는 첫번째 인자로 무조건 self가 와야합니다. 객체 사용유무와 상관없음)

 

객체에 클래스를 할당하고 싶으면 위 코드처럼 객체를 하나 만들어 클래스를 대입하면 됩니다.(customer = Person())

클래스에 있는 데이터에 접근하고자 할 때는 "."을 사용하면 됩니다. customer의 이름을 변경하고 싶을때에는 customer.name으로 접근해서 변경하면 되는겁니다. 함수의 이용도 마찬가지입니다. info 함수를 사용하고 싶을 때에는 customer.info로 사용할 수 있습니다. 하지만 이러한 접근방식은 OOP(Object Oriented Programming)의 캡슐화의 근간을 훼손하는 행위로서 getter나 setter를 이용하는것이 현명합니다.

 

파이썬에서는 모든 클래스가 public으로 작성되기 때문에 getter메소드나 setter 메소드가 없습니다. 대신 파이썬에서는 사용자가 속성에직접 접근하는 것을 막기 위해getter또는setter메서드 대신에프로퍼티(property)를 사용합니다.

 

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
class Person:
    # class Person의 멤버변수
    # __(double underscore)
    # __는 클래스외부에서 클래스 멤버에 접근하지 못하게 하기위함
    __name = "홍길동"
    __number = "01077499954"
    __age = "20"
 
    @property
    def name(self):
        return self.__name
 
    @name.setter
    def name(self, newName):
        self.__name = newName
 
    # class Person의 메소드
    def info(self):
        print("제 이름은 " + self.__name + "입니다.")
        print("제 번호는 " + self.__number + "입니다.")
        print("제 나이는 " + self.__age + "세 입니다.")
 
if __name__ == "__main__":# main namespace를 의미합니다.
    customer = Person()#Person의 객체 customer 생성
    customer.info()#info 함수 호출
    print(customer.name)
    customer.name="이태일"
    print(customer.name)
 
 # 결과 값
제 이름은 홍길동입니다.
제 번호는 01077499954입니다.
제 나이는 20세 입니다.
홍길동
이태일
cs

 

먼저 private하게 작성할 멤버변수의 이름앞에 더블 언더스코어(__, double underscore)를 붙여줍니다.그리고 아까와는 다르게 name 메소드위에 '**@'가 붙은 것을 볼 수 있습니다. 이를데코레이터(decorator)라고 하는데, 말그대로 뭔가를꾸며** 준다는 것을 의미합니다.

같은 name 이라는이름을 갖는 메소드이지만 데코레이터에 의해 서로 다른 역할을 합니다.클래스를 완성하고 __main__에서 테스트를 해보았습니다. 결과가 잘 나온 것을 볼 수 있죠? 그런데 여기에는 주의할 점이 있습니다.name은 customer 클래스의 메소드인데도 불구하고 호출 시 멤버변수처럼 접근합니다. 원래대로라면 customer.name()이 될텐데 customer.name 형식으로 호출하고 있는걸 볼 수 있죠? 코드의 생김새는 객체의 멤버에 직접 접근하는듯이 사용하지만**실제로는 메소드 호출을 통해 변수에 접근하게 됩니다.**

 


 

3. Initializing

 

자 클래스의 기본을 배웠으니 다음 예제로 넘어가겠습니다. Monster라는 클래스를 만들건데요 Monster는 체력,공격력,방어력을 가지는 클래스입니다. 공격이 가능하며 공격을 받았을 경우에 체력이 상대방의 공격력-자신의 방어력 만큼 감소한다고 칩시다. 이를 구현한 코드는 아래와 같습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Monster:
    def __init__(self,hp,atk,dfn):
        self.hp = hp
        self.atk = atk
        self.dfn = dfn
 
    def attack(self,target):
        target.ReduceHP(self.atk)
 
    def ReduceHP(self,atk) :
        self.hp = self.hp - (atk-self.dfn)
 
if __name__ == "__main__":
    m1 = Monster(100,10,3)
    m2 = Monster(100,10,3)
    m1.attack(m2)
    print(m2.hp)
 
#결과값
93
cs

__init__이라는 메소드에 주목해주세요. __init__은 파이썬에서 기본적으로 제공하는 클래스 생성자입니다. 아까 전에는 클래스 멤버변수를 만들고 그 값을 클래스에 직접넣어 주는 형식으로 구현했는데요, 생성자를 쓰면 클래스의 인스턴스가 생성될 때마다 다른 멤버값을 갖도록 구현 할 수 있습니다.

Monster 클래스는 제가 상상속으로 생각해 낸 괴물에 대해 코드로 구현한 것입니다. 먼저 게임에서의 몬스터는 체력과 공격력 그리고 방어력을 갖고 있죠? 이를 각각 클래스 멤버로 할당하고 각 몬스터 마다 값을 다르게 하기 위하여 생성자를 이용하였습니다.

또한 공격 기능 , 체력 감소 기능을 attack과 ReduceHP라는 메소드를 통해 구현해 보았습니다. 이 메소드를 이용하면 상대 몬스터에게 공격을 가하거나, 공격을 받았을 시에 체력이 감소되는 모습을 확인 할 수 있습니다.

그리고 객체 2개를 만들어 공격하게끔 해 보았습니다. 그 후 피 공격 객체의 체력 값을 확인해 보았더니 상대방의 공격력-자신의 방어력 만큼의 체력이 감소하였습니다. 잘 동작하는것 같네요.

# 함수

 

1. 함수의 정의

 

파이썬에서의 함수에 대해 알아보기 전에, 어디선가 함수란 말을 들은 기억이 있지 않나요? 학교 수학시간에 나온 함수는, 우리가 배울 프로그래밍 언어의 함수와 비슷합니다. x의 값에 따라서 y의 값이 정해지듯이 프로그래밍 언어의 함수 역시도 값을 함수에 집어넣으면, 함수는 결과값을 되돌려줍니다. 즉 함수란, 특정한 기능을 수행하는 코드의 집합입니다.

프로그래밍 언어에서의 함수란 이름이 주어진 특정 순서로 실행되는 문장의 연속입니다. 호출된 순간부터 정해진 로직을 실행하죠. 따라서 우리는 적용 할 각 로직에 대해 코드를 반복해서 작성할 필요가 없습니다. 이를 코드 재사용 가능성이라고합니다. 함수는 가독성을 높여주고 코드를 재사용하여 프로그래밍 작성에 좋은 효율을 가져다 줍니다.

 

파이썬 함수 

 


 

2. 사용자 정의 함수

 

파이썬에서 사용자 정의 함수를 만드는 방법은 다음과 같습니다..

def add(num1,num2):
	reslut = num1+num2
    return result

print(add(1,2))

# 결과 값
3

먼저 함수 정의를 위해서는 def라는 키워드를 사용합니다.. 그 다음에 함수를 호출하기 위한 이름을 적고 함수에서 계산을 위해 전달할 인자 값을 넣습니다.. 함수의 이름은 변수명과 같이 함수의 기능을 간단하게 설명하는 이름이면 좋습니다. return은 반환 한다는 의미를 지니고 있습니다. 그래서 return을 만나면 함수는 종료되고 return값을 함수를 호출한 곳으로 전달합니다. 위의 코드에서는 return된 result의 값이 print문의 인자로써 사용되는 것입니다.

 

만약 위 코드에서 return문이 존재하지 않는다면 어떻게 될까요? return을 지우고 함수를 실행시켜 보면 알 수 있듯이 아무일도 일어 나지 않습니다. 함수안에서 정의하는 변수는 지역변수로서 메인 name space에 아무런 영향을 미치지 못합니다. 즉 함수안에서 정의 되었다가, 함수가 끝날 때 메모리에서 해제되어 사라지는 변수입니다. 고로 함수안에서 연산한 결과를 메인함수에서 사용하고 싶을 때에는 반드시 return 해당 값을 해줘야 합니다.

 

def add(num1,num2):
    print(num1+num2)

add(1,2)

#결과값
3

 

위 코드는 return 문 없이 정의한 함수입니다. 이렇게 리턴문이 없는 함수를 우리는 프로시져라고 부릅니다. 이러한 프로시져들은 명령문을 순차적으로 실행하여 마지막 명령문에 도달하였을 때 자동적으로 종료됩니다. 인자값을 받아 함수안에서만 처리하는 것이 목적이라면 위의 소스코드처럼 굳이 return을 해주지 않아도 상관없습니다. 굳이 return하는 데이터 없이 정의하며, 그런 경우에만 특수하게 사용하시면 됩니다.

 


3. 변수의 유효범위

 

함수의 정의방법에서 잠깐 언급했듯이 프로그래밍 언어의 변수에는 종류가 있습니다. 각각의 함수는 별도의 name space를 갖고 있기 때문에 함수 안에서 변수를 선언하게 되면 그 변수는 그 함수의 name space에 생성됩니다.

파이썬에서 name space 규칙은 총 3가지가 있으며 각각을 지역(local), 전역(global), 내장(Built-in)이라고 합니다. 지역변수는 특정 name space에 생성되는 변수이고, 전역변수는 어떠한 name space에서도 접근할 수 있는 변수이며, 내장변수는 파이썬자체에 내장되어있는 변수입니다. 함수안에서 선언되는 변수는 전부 지역변수로 볼 수 있고 함수 바깥 즉, 전역 name space에서 선언된 변수는 전역변수라고 볼 수 있습니다. 다음 예제를 통하여 변수의 유효범위를 알아보도록 하겠습니다.

 

num = 10 # 전역변수 num선언

def printNum():
    print(num) # 전역변수 출력

printNum()

#결과값
10

 

전역변수 num을 선언하고 10으로 초기화 한 후, printNum이라는 함수를 만들어 함수 내부에서 num을 출력하게 끔 해보았습니다. printNum을 실행하였을때 10라는 값이 정확하게 나오는 것을보아 위 설명 그대로 전역변수는 말그대로 전역에서 사용할 수 있습니다. 전역에서 사용할 수 있고 특정 구문(global)이 아니면 변경불가한 특징으로 인하여 상수를 저장할 때 사용합니다.
예를 들어 원주율을 구하는 스크립트가 있을 때 원의 반지름은 유동적일 수 있지만 파이값은 고정적이어야 합니다. 이런 경우에 전역변수 pi를 선언하여 3.14로 초기화 하면 변경 가능성을 미연에 방지할 수 있습니다.

 

num = 10 # 전역변수 num선언
def variable(num):
    num = 20 # 지역변수 num선언

valriable(num)
print(num)

#결과값
10

 

전역 변수 num을 선언하여 값을 10으로 초기화 합니다. 그 후 함수 variable을 정의하여 그 안에 인자값을 20으로 변경해주는 코드를 작성합니다. 그리고 variable을 실행하고 num을 출력시켜보니 값이 그대로 10인것을 확인 할 수 있습니다. 지역변수는 그 변수가 정의된 함수 안에서만 읽을 수 있습니다.
그런데 스크립트 작성자의 의도는 전역변수 num의 값을 수정하는 것이었을지 모릅니다. 이런경우에

만약 전역변수의 값을 변경하고 싶을때에는 global문을 사용하면 손쉽게 변경할 수 있습니다. global 문을 사용했다면 값이 진짜 변경 되는지 한번 확인해 봅시다.

 

num = 10

def variable():
    global num # global문의 사용
    num = num * 100

variable()
print(num)

#결과값
1000

 

저희의 예상 대로 global문을 사용했을 때에는 값이 바뀌었습니다. global 문은 변수가 이미 전역공간에 선언되어 있을경우에 지역공간에서 global <변수이름>의 구조로 사용합니다. 꽤나 편리한 기능인 것 같지만 전역변수를 너무 많이 사용하게되면 스크립트가 복잡해지므로 최소한의 사용만을 지향합시다.

# Comprehension

 


 

1. list comprehension

 

파이썬에서 for문은 사용할 수 있는 범위가 굉장히 넓습니다. 예를 들어 리스트를 하나 만들고 그 안에 값을 동적으로 추가해야 하는 상황이라면 아래와 같은 방법을 이용할 수 있겠죠.

myList = []

for i in range(10):
	myList.append(i) # [0,1,2,3,4,5,6,7,8,9]

 

만약에 리스트에 값을 삽입하는 규칙이 있다면, 리스트를 선언하면서 값을 삽입 할 수 있으면 편하지 않을까요? 파이썬에서는 이를 Comprehension이라고 하고 리스트나 딕셔너리에 적용하는 것이 가능합니다.

 

myList = [i for i in range(10)] # [0,1,2,3,4,5,6,7,8,9]

 

위의 코드가 바로 list comprehension 입니다. 리스트를 선언함과 동시에 리스트 안에 for 문을 넣어 요소를 저장하는 방법인데요 for 문의 i가 늘어남과 동시에 i의 값이 저장되는 모습입니다. 즉 가장 앞에 명시한 변수에 저장된 값이 반복문이 실행되면서 리스트의 요소로써 저장되는 것입니다.

 

만약 list comprehension에 for문만 사용이 가능하다고 한다면 의미가 없는 구문일 수 있습니다. 역시 파이썬 제작자는 이를 간파하고 if else등의 제어문도 사용할 수 있게 만들었습니다.

 

#if
myList = [i for i in range(10) if i%2==0] # [0,2,4,6,8]

#if else
myList = [i if i%2==0 else i-1 for i in range(10)] # [0,0,2,2,4,4,6,6,8,]

#nested loop
myList = [j for i in range(3) for j in range(3)] # [0,1,2,0,1,2,0,1,2]

위 의 코드를 보면 if 와 if - else를 쓸 때의 문법의 차이가 있다는 것을 느끼셨을 텐데요, if 는 for문 뒤에오는 반면 if-else는 for 문 전에 옵니다. 그리고 if문은 조건문에 부합하는 요소만 저장하는 반면에 if-else는 조건문에 부합하지 않는 요소도 값을 변형시켜서 저장할 수 있습니다.(i-1)

 

list comprehension은 편리한 기능이지만, 이를 무분별하게 사용하는 것은 코드의 가독성을 해칩니다. 따라서 조건문이 다중이거나 nested된 반복문이라면 list comprehension을 지양하는 것이 좋습니다.

 


 

2. dictionary comprehension

 

사실 dictionary는 list 만큼 comprehension이 유용하지 않습니다. 하지만 길이가 같은 리스트 2개를 각각 Key Value로 삼아 딕셔너리로 형변환 하고자 한다면 유용하게 사용할 수 있습니다.

 

아래의 코드를 보시면 for문을 이용하여 길이가 같은 리스트 2개를 딕셔너리로 만드는 방법이 기재 되어있습니다. 기존의 코드는 리스트의 크기만큼 for문을 실행시켜서 딕셔너리에 각각의 Key, Value로 저장하는 방법인데요, 저는 개인적으로 Dictionary comprehension이 코드의 길이도 짧고 특정상황에서는 훨씬 가독성이 좋다고 생각합니다.

myList_1 = [1,2,3]
myList_2 = ['a','b','c']

# original code
newDict = dict()
for i in range(len(myList_1)):
    newdict[myList_1[i]] = myList_2[i]

# dictionary comprehension
newDict = {myList_1[i]: myList_2[i] for i in range(len(myList_1))}

 

Dictionary comprehension은 dictionary의 Key-Value를 반전시키는데도 사용할 수 있습니다. dictionary class의 메소드로 제공되고 있는 items()는 dictionary의 모든 key,value쌍을 tuple로 상속된 객체를 리턴합니다. 해당 객체는 iteration 클래스이기 때문에 for문의 iterable 객체로 사용할 수 있습니다. 그렇기 때문에 다음과 같은 방식을 이용하면 dictionary  Key-Value를 반전 시킬 수 있습니다.

 

# original code
myDict = {1: 'a', 2: 'b', 3: 'c'}
newDict = dict()
for key,value in myDict.items():
    newDict[value] = key

# dictionary comprehension
myDict = {1: 'a', 2: 'b', 3: 'c'}
newDict = {value : key for key,value in myDict.items()}

 

 


 

3. zip함수를 이용한 자료형 변환

 

파이썬에서 기본으로 제공하고 있는 zip함수는 리스트를 딕셔너리로 변경해주거나 딕셔너리의 Key-Value를 반전시키는데 가장 효율적인 함수입니다. 

# List to dictionary
myList_1 = [1,2,3]
myList_2 = ['a','b','c']
newDict = dict(zip(myList_1,myList_2))

# Key-Value reverse
myDict = {1: 'a', 2: 'b', 3: 'c'}
reverse_dictionary = dict(zip(myDict.values(),myDict.keys()))

위 코드는 zip함수를 이용하여 list 2개를 dictionary로 변환시키는 코드입니다. Dictionary comprehension이나 일반적인 for문으로 변환 시키는것보다 코드의 길이도 짧고 가독성도 훌륭합니다. 

또한 마지막 라인을 보시면 zip함수를 이용하여 dictionary를 반전시켰습니다. dictionary class의 메소드인 values()와 keys()를 이용하여 모든 value와 key를 추출하고 zip함수의 첫 번째, 두 번째 인자로 넣습니다.

여기까지만 하면 zip 클래스의 인스턴스가 되게 됩니다. 이 객체는 Iterable하지만 dictionary라고 할 수 없죠, 그래서 전체 구문을 dict로 감싸 형변환을 마무리합니다.

 

 

'Dev > python' 카테고리의 다른 글

[python] 클래스(class)와 인스턴스  (1) 2019.04.07
[python] 함수  (0) 2019.04.04
[python] 제어문(if, for, while) - 3  (0) 2019.03.23
[python] 시퀀스 자료형과 이터러블  (1) 2019.03.23
[python] 제어문(if, for, while) - 2  (0) 2019.03.23

# 파이썬 제어문



1. for문의 기본적인 사용법


for문은 while문과 같이 특정작업을 반복해야 할 때 사용합니다. while문과의 차이점이라면 특정 조건식이 참인 동안 동작하는 것이 아니라 in 키워드 뒤에오는 자료 구조에 대하여 원소단위로 순환하며 순환이 끝날때 까지 반복합니다.

즉 in 뒤에 오는 객체는 반드시 iterable 해야 합니다. 


1
2
for each in 시퀀스자료형 or iterable한 객체:
    print(each)
cs


for 키워드 다음에 오는 each는 for문이 iterable 객체에 접근하여 원소를 하나씩 순환하는 동안에 원소 하나하나가 저장되기 위한 변수입니다. 이름은 아무렇게나 정하셔도 무방합니다. 아래 그림을 보고 다시한번 이해해 보도록 하겠습니다.


for문이 자료구조의 요소 각각에 접근하면서 그 요소들은 임시적으로 each에 저장되고 다음 스텝에서 다른 값으로 대체 됩니다. 


요약하자면 for문은 이터러블 객체를 인자로 사용하는 것이 가능하고 해당 객체의 요소 각각에 접근하는 방식으로 동작합니다. 

그러면 간단하게 숫자를 하나씩 늘려가면서 반복할 수 있는 fot문은 없을까요? 답은 간단합니다. 숫자요소로 이루어진 iterable 객체를 만들어서 인자로 사용하면됩니다. 게다가 파이썬에서는 이 방식을 지원하고 있습니다.

range라는 내장함수는 숫자로 이루어진 이터러블 객체를 만들어냅니다. 


1
2
3
range(10# [0,1,2,3,4,5,6,7,8,9]
range(1,10# [1,2,3,4,5,6,7,8,9]
range(0,10,2# [0,2,4,6,8]
cs


range는 3개의 int형 데이터를 인자로 받습니다. 첫 번째 인자는 시작점인데 ~이상 으로 해석할 수 있고 두 번째는 종료점입니다. ~미만 으로 해석 가능합니다. 마지막 인자는 step으로 작동합니다. 만약 첫번째 인자가 빈칸이라면 인터프리터는 이를 0부터라고 해석합니다.


이를 for 문에 적용하면 아래와 같이 사용할 수 있습니다.


1
2
for i in range(10):
    print(i)
cs


# 파이썬 제어문



!!시퀀스 자료형은 추후에 배울 이터레이터, 제너레이터등을 위하여 필수적으로 알아야 하는 지식입니다!!


1. 시퀀스 자료형


시퀀스 자료형은 원소들을 가지고 있는 데이터 구조이며 멤버쉽 테스트를 지원하는 자료형입니다. 이는 메모리에 상주하는 데이터 구조로써, 보통 모든 원소 값을 메모리에 저장합니다. 파이썬에서 지원하는 시퀀스 자료형은 아래와 같습니다.


- list

- dictionary

- set

- tuple

- string


특정한 자료구조가 원소를 포함하고 있는지 판단 가능하면 시퀀스자료형이라고 생각하시면 됩니다. set, list, dictionary, tuple 등은 간단히 생각해도 원소를 담고 있는데 string은 이해가 쉽지 않죠?

C언어에서 string을 생각해보면 간단합니다. C 언어에서는 char* string = "my name is Rekt77"과 같은 형태로 문자열을 선언하고 각각 문자에대해 인덱스로 접근하는것이 가능합니다.

파이썬에서도 마찬가지 입니다. string은 연속적인 문자의 집합입니다. 고로 인덱스로 접근하는 것이 가능하고 각각의 문자로 분리가능하기 때문에 한 자료구조안에 원소를 담고 있다고 볼 수 있습니다.


1
2
3
4
5
assert 1 in [1,2,3# list
assert 1 in (1,2,3# tuple
assert 1 in {1,2,3# set
assert 1 in {1:"a",2:"b",3:"c"#dictionary
assert "1" in "123" # string
cs


위의 코드는 시퀀스 자료형의 멤버쉽 테스트입니다. assert 문은 시퀀스 자료형의 멤버쉽 테스트를 위해 쓰이는 명령어입니다. 조건이 맞지 않을 경우에는 Assertion Error를 출력하여 자료구조 테스트로 자주 사용됩니다.



2. 이터러블(iterable)


이터러블하다의 의미는 사전적 정의로 '개별 원소를 반복적으로 셀 수 있는' 이라는 뜻을 가지고 있습니다. 이는 파이썬에서 상당히 중요한 의미를 지니는데, 반복문의 사용시 자료구조 내에 포함되어 있는 원소 하나하나에 접근할 수 있는 객체를 뜻합니다. 앞서 말씀드린 시퀀스 자료형들은 전부 이터러블한 객체입니다. 즉 인덱스나 키를 이용하여 접근할 수 있습니다. 

앞으로 for문 같은 반복문에서 자주 보게될 형태이므로 반드시 기억해 놓으셔야 합니다.


# 파이썬 제어문



제어문이란 프로그래밍 언어에서 실행순서를 변경시키거나 또는 조건에 따라 실행해야 할 명령문을 제어하는데에 사용되는 구문입니다. 일반적으로 프로그램은 위에서 아래로 순차적으로 실행되지만, 어떤 조건에 대해서만 실행해야 하는 경우나 특정 부분을 반복하여 실행해야 하는 경우 등이 있습니다.

python에서는 if, for, while문을 사용하여 흐름을 제어할 수 있습니다.



1. while 문


특정작업을 반복하여 실행해야 할 경우 반복문을 사용하면 간단히 해결할 수 있습니다. while문은 반복문의 일종으로 조건문이 참일 경우 반복을 계속합니다. 


1
2
3
while 조건:
    수행할 작업
    ...
cs


while이라는 키워드를 먼저 적고 그 뒤에 조건을 적어주면 됩니다. 앞선  if문 강의에서 배웠던 비교, 논리연산자가 쓰일 수 있습니다.


2. while 문 사용예제


glass라는 변수의 값을 0으로 초기화 하고 while문의 조건으로 glass가 10보다 작을 경우를 설정합니다. 이렇게 하면 while문의 조건이 참이니 동작을 시작하겠죠? 하지만 glass 의 값을 while문 안에서 늘려주지 않는다면 while은 무한히 실행될 것입니다. 고로 glass의 값을 +=을 통해 1씩 늘려주기로 하겠습니다. 또 while 문안에 print문을 넣어 한잔 마셨습니다. 를 출력해 보도록 하겠습니다. 그리고 glass의 값이 10이 되었을 때 취했으니 집에 돌아가요. 라는 문구를 출력해보도록 하겠습니다.



1
2
3
4
5
6
7
8
glass = 0
#10번 반복
while glass < 10:
    glass+=1
    print("한잔 마셨습니다.")
    if glass == 10:
        print("취했으니 집에 돌아가요.")
    
cs




3. while문 이스케이프(escape)


while문은 조건문이 참인 동안 계속해서 하위 구문을 반복적으로 실행합니다. 하지만 중간에 while문을 빠져나가고 싶을 경우에는 break문을 넣어 즉시 while문을 빠져나갈 수 있습니다.


1
2
3
4
5
6
7
8
9
number = 77
counter = 2
while counter < number:
    if number%counter == 0:
        print("소수가 아닙니다.")
        break
    counter += 1
    if counter == number
        print("소수 입니다.")
cs


'Dev > python' 카테고리의 다른 글

[python] 제어문(if, for, while) - 3  (0) 2019.03.23
[python] 시퀀스 자료형과 이터러블  (1) 2019.03.23
[python] 제어문(if, for, while) - 1  (0) 2019.02.15
[python] 튜플과 Immutable  (0) 2019.02.14
[python] 딕셔너리(dictionary)  (0) 2019.02.13

# 파이썬 제어문



제어문이란 프로그래밍 언어에서 실행순서를 변경시키거나 또는 조건에 따라 실행해야 할 명령문을 제어하는데에 사용되는 구문입니다. 일반적으로 프로그램은 위에서 아래로 순차적으로 실행되지만, 어떤 조건에 대해서만 실행해야 하는 경우나 특정 부분을 반복하여 실행해야 하는 경우 등이 있습니다.

python에서는 if, for, while문을 사용하여 흐름을 제어할 수 있습니다.



1. if문


if문은 일반적으로 프로그램의 흐름을 조건에 따라 다르게 해야하는 경우 사용합니다. 예를 들어 홀, 짝을 구분해야하는 프로그램에서는 if 조건에서 2로 나눈 나머지 연산의 결과가 0이냐 1이냐에 따라 다른 흐름을 갖도록 작성해야 겠죠?


if문을 선언하는 방법은 아래와 같습니다. if뒤에 조건을 넣어주고 그 값이 참이면 동작하고 참이 아니면 동작하지 않습니다.

여러가지 조건을 넣고 싶은 경우에는 elif 명령어를 씁니다. elif는 기존 if가 참이아니라 동작하지 않았을 경우 다른 대안을 제시하는데에 사용합니다. 물론 if가 참이 아니라고해서 elif가 무조건 동작하는 것은 아닙니다. 반드시 '조건이 참일 때'라는 전제조건을 수반합니다.


if나 elif의 조건에 부합하지 않았을 경우 실행시킬 루틴을 작성하고 싶을 때에는 else 명령어를 사용하시면됩니다. if나 elif가 참이 아닐경우 무조건 else가 실행됩니다.


ps.elif가 없어도 else사용가능


 

1
2
3
4
5
6
7
8
9
#elif를 추가하면 다중 조건 사용가능
#else는 위 조건에 부합하지 않는 모든 조건(여집합)
 
if 조건1:
    ...
elif 조건2:
    ...
else :
    ...
cs


하위 구문을 작성할 때 파이썬만의 독특한점이 있는데요 바로 indent라는 것입니다. indent는 들여쓰기라는 뜻입니다. 즉 하위 구문을 들여쓰기의 정도로 구분하겠다는 소리죠. 파이썬에서 공식적으로 indent로 인정받는 것은 space 4번과 tab입니다. tab이 에디터 상에서 space 4번과 동치일 경우에만 tab이 indent로 인정받는데 대부분의 에디터가 그러합니다. 하지만 간혹 space 8번으로 인식하는 경우가 있으므로 주의하셔서 사용하시면 됩니다.



2. 비교 연산자


비교 연산자라는 것은 값을 비교하여 참, 거짓을 판단해야 할 경우 사용합니다. if 문에서는 조건이 참인지 거짓인지의 여부가 대단히 중요하기 때문에 비교연산자를 상당히 많이 사용하게됩니다.

그럼 연산자의 종류에 관하여 알아볼까요?


 연산자

 설명

a < b

 a가 b보다 작으면 true 

a > b

a가 b보다 크면 true

a == b

a와 b가 같으면 true

a != b

a와 b가 같지 않으면 true

a >= b

a가 b보다 크거나 같으면 true

a <= b

a가 b보다 작거나 같으면 true


꽤 많은 연산자의 종류가 있습니다. 상황에 따라서 바꿔가며 사용하시면 됩니다.

추가적으로 if문은 0이 아닌 숫자나 비어있지 않은 자료형에 관하여 true로 인식합니다. 즉 1도 true고 'a'도 true입니다. 하지만 빈 리스트나 빈 튜플같이 비어있는 자료형은 false로 인식합니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
if 'a':
    print("1_True")
 
if 1:
    print("2_True")
 
if []:
    print("3_True")
 
if '':
    print("4_Ture")
 
# 결과 값
# 1_True
# 2_True
cs



3. 논리연산자


논리연산자는 조건식이나 값을 논리적으로 판단하여 참과 거짓을 판단하는데 사용됩니다. 일반적으로 if문에서는 조건을 다중으로 설정하고 싶을 경우에 사용합니다.


다른 언어에서 && 나 ||와 같은 연산자가 파이썬에서는 명령어 형태로 제공되고 있으니 명령어를 꼭 기억하셔야 합니다.


그리고의 조건을 만드는 연산자는 and입니다. 다른 언어에서의 &&과 똑같습니다. or 명령어는 다른언어에서의 ||와 같고 ~이거나라는 조건식을 만드는데에 사용됩니다.


마지막으로 조건을 부정하는 연산자는 !연산자입니다. 파이썬에서는 not이라는 명령어로 제공됩니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
= 2
= 3
 
if a%2==0 and b%2==0:
    print("1_True")
 
if a > 3 or b < 5:
    print("2_True")
 
if not a==b:
    print("3_True")
 
# 결과 값
# 2_True
# 3_True
cs


4번 라인을 보시면 a는 짝수이기 때문에 첫번째 조건에서 true를 반환합니다. 하지만 두번째 조건이 맞지않아 if 절이 실행되지 않았습니다. and 연산자가 두개의 조건이 전부 참일 때만 ture를 반환하기 때문이죠.

반대로 7번 라인에서는 첫번째 조건이 false고 두번째 조건이 true인데 true로 인식이 되었습니다. or 연산자 때문인데요 둘중 하나만 참이면 true를 반환합니다.

마지막으로 not 연산자는 조건을 반전시킨다고 했죠 ? a==b는 누가 봐도 false입니다. 하지만 앞에 not을 붙여서 true로 인식이 됩니다.



'Dev > python' 카테고리의 다른 글

[python] 시퀀스 자료형과 이터러블  (1) 2019.03.23
[python] 제어문(if, for, while) - 2  (0) 2019.03.23
[python] 튜플과 Immutable  (0) 2019.02.14
[python] 딕셔너리(dictionary)  (0) 2019.02.13
[python] 리스트 함수  (0) 2019.02.11

# 파이썬 튜플



튜플은 몇 가지 특성을 빼면 리스트와 거의 동일하다고 볼 수 있는 자료구조입니다.

인덱싱, 슬라이싱이 가능하고 튜플끼리의 연산 또한 가능하죠.


차이점이 있다면, 리스트는 '[]'을 이용하여 선언하는 반면 튜플은 '()'을 이용해서 감싸줍니다.


1
2
3
4
tup1 = () # 빈 튜플 선언
tup2 = tuple() # 빈 튜플 선언
tup3 = (1,2,3# tup3 == (1,2,3)
tup4 = 1,2,3 # tup4 == (1,2,3)
cs


1. Immutable


튜플은 한번 정의되는 순간 그 요소를 변경하거나, 삭제하는것이 불가능해집니다.

이러한 특성을 영어로는 Immutable하다고 이야기합니다. Immutable한 자료형은 데이터의 오염이 적기 때문에 데이터 분석에서 원시데이터를 저장할 때 자주 쓰입니다.


그렇다면, 한번 테스트를 안해볼 수 없겠죠?


1
2
myTup = ('python',['anaconda','spyder'])
myTup[1= ['man']
cs


이 코드를 실행시키면 인터프리터는 에러를 출력할 것 입니다.


tuple 객체는 새로운 요소의 할당을 지원하지 않는다면서 TypeError가 출력되었습니다. 그러면 진짜로 tuple에서는 값을 전혀 못바꾸는게 확정이네요??  결론부터 말하자면 그렇지 않습니다.


우리는 리스트에서 배웠던 Shallow Copy를 떠올려보셔야 합니다. Shallow Copy는 레퍼런스를 그대로 가져오는 반면 Deep Copy는 값만 복사해 오기 때문에 실질적으로 같은 객체가 아닙니다. 즉 아래와 같은 식이 성립됩니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
origin = ('python', ['anaconda''spyder'])
clone = ('python', ['anaconda''spyder'])
 
# 값이 같기 때문에 True
origin == clone
 
# 같은 객체가 아니기 때문에 False
origin is clone
 
# Shallow Copy
clone = origin
 
# 같은 값, 같은 객체이기 때문에 True
origin == clone
origin is clone
cs


origin과 clone을 같은 값으로 설정하면 == 에서는 true를 보이지만 is 연산에서는 false를 보입니다. 둘의 레퍼런스 id가 다르기 때문입니다. 하지만 clone에 origin을 대입하면 레퍼런스를 그대로 가져오기 때문에 ==, is 둘 다에서 true를 보이죠? 이 방법을 이용하면 튜플안의 값을 변경할 수 있습니다.

파이썬에서 Immutable이라는것은 어디까지나 레퍼런스가 변경되지 않는다는 의미로만 동작합니다. 즉, 레퍼런스가 변하지 않으면 값이 변해도 상관이 없다는 의미겠죠?


그러면 바로 테스트 해보도록 하겠습니다.


1
2
3
4
5
6
myTup = ('python',['anaconda','spyder'])
myTup[1].append("pig")
print(myTup)
 
# 결과 값
# ('python', ['anaconda', 'spyder', 'pig'])
cs


myTup의 1번째 인덱스에 있는 리스트의 내용이 바뀐것을 확인할 수 있습니다. append는 레퍼런스가 변경되지 않는 대표적인 함수이기 때문에 레퍼런스를 유지 하면서 값을 바꿀 수 있었습니다.


하지만 위에서 처럼 myTup[1]에 = 연산자를 통해 값을 대입하려는 순간 레퍼런스가 변경되기 때문에 TypeError가 발생한 것입니다. 

'Dev > python' 카테고리의 다른 글

[python] 제어문(if, for, while) - 2  (0) 2019.03.23
[python] 제어문(if, for, while) - 1  (0) 2019.02.15
[python] 딕셔너리(dictionary)  (0) 2019.02.13
[python] 리스트 함수  (0) 2019.02.11
[python] 리스트와 Shallow Copy  (0) 2019.02.10

# 파이썬 딕셔너리



딕셔너리란 대응되는 데이터를 Key:Value 형식으로 저장할 수 있는 자료구조입니다. 타 언어에서는 연관배열이나 해시맵이라고 하죠?


딕셔너리는 리스트처럼 순차적으로 요소에 접근하는 시퀀스 자료형이 아닙니다. 반드시 Key를 통하여 Value값을 얻습니다. 예를 들어 language라는 KeyPython이라는 Value가 저장되어있을 경우 language를 통해 Python이라는 값을 얻을 수 있습니다.



1. 딕셔너리 선언


딕셔너리의 각 요소는 Key:Value 형태로 이루어져 있고 쉼표(",")로 데이터를 구분 짓습니다. 그리고 Key와 Value가 같은 자료형일 필요는 없습니다. Key가 문자열이고 Value가 숫자나 리스트나 딕셔너리여도 상관이 전혀 없습니다.

하지만 만약 중복되는 키를 입력시에는 데이터가 무시 되기 때문에 필수적으로 중복검사를 실시해야 합니다.


1
2
3
dic ={} # 빈 딕셔너리 선언
dic = dict() # 빈 딕셔너리 선언
dic = {"Key":"Value","number":1234#여러데이터 저장 가능
cs



2. 딕셔너리 값 추가 및 수정


딕셔너리의 값을 수정하기 위해서는 먼저 요소에 키를 통하여 접근할 필요가 있습니다. 만약 수정하려는 값의 key가 2일 경우에는 2로 접근하시면 됩니다.

값을 추가하고 싶은 경우에도 마찬가지입니다. 딕셔너리 이름이 dic일 경우에 dic["newKey"] 처럼 새로운 키를 설정해주고 값을 대입하면 됩니다. newKey라는 키는 제가 임의로 만든것이며 어떤 값이여도 상관없습니다. 


1
2
3
4
5
6
dic = {1:"a"}
dic[2= "b"
# dic == {1:"a",2:"b"}
 
dic[2= "c"
# dic == {1:"a",2:"c"}
cs



3. 딕셔너리 데이터 접근


리스트에서는 요소 값에 접근하기 위하여 인덱싱을 사용하였지만 딕셔너리는 인덱싱이아니라 Key이름을 기반으로 데이터에 접근합니다.


1
2
3
dic = {"apple":500,"banana":1000}
#dic["apple"] == 500
#dic["banana"] == 1000
cs



# 딕셔너리 내장함수



1. keys


딕셔너리 모든 Key를 객체로 리턴합니다. key만을 추출할 때 사용합니다.


1
2
3
4
dic = {"apple":500"banana":1000}
dic.keys()
 
#dict_keys(['apple', 'banana'])
cs



2. values


딕셔너리 모든 Value를 객체로 리턴합니다. Value만을 추출할 때 사용합니다.


1
2
3
4
dic = {"apple":500"banana":1000}
dic.values()
 
# dict_values([500, 1000])
cs


3. items


items 함수는 딕셔너리의 모든 데이터를 객체로 리턴합니다.


1
2
3
4
dic = {"apple":500"banana":1000}
dic.items()
 
# dict_items([('apple', 500), ('banana', 1000)])
cs


4. get


get함수는 키를 알고 있을 데 value를 추출할 때 사용합니다.


1
2
3
4
dic = {"apple":500"banana":1000}
dic.get("apple")
 
# 500
cs



'Dev > python' 카테고리의 다른 글

[python] 제어문(if, for, while) - 1  (0) 2019.02.15
[python] 튜플과 Immutable  (0) 2019.02.14
[python] 리스트 함수  (0) 2019.02.11
[python] 리스트와 Shallow Copy  (0) 2019.02.10
[python] 문자열 함수  (0) 2019.02.09

+ Recent posts