함수뿐만 아니라 다양한 변수를 포함한 큰 프로그램을 하나의 묶음으로 정의하는 것이 "클래스(class)"이다. 클래스의 기본적인 사용법부터 설명한다.


함수와 클래스

함수는 하나의 처리를 하나로 통합한 것이지만, 이런 함수가 많이 늘어나면, 점차적으로 어느 것이 무슨 역할를 하는지 의미를 알기 힘들어 진다. 예를 들어, 수백개의 함수가 나열되어 있으면, 그것을 전부 이해해 나가는 것은 힘들 것이다.

그래서 "비슷한 역할을 하는 것을 한곳에 모으자"라고 누구든지 생각된다.

예를 들어, 어떤 데이터 처리를 만드는 것을 생각해 보자. 데이터를 관리하는 함수, 데이터를 추가하는 함수, 데이터를 삭제하는 함수, 데이터를 출력하는 함수 ...... 따위가 쭉 늘어서 있는 것은 그다지 사용하기가 쉽지 않다.

그래서 "데이터를 처리하기 위해 필요한 것"을 모두 한 묶음으로 두자"라고 생각하게 된다. 큰 "데이터 관계 묶음'이라는 것을 만들고, 그 안에 "데이터를 보관할 변수", "데이터를 파일에 읽고 쓰는 함수", "데이터를 추가하거나 삭제하는 함수", "데이터를 출력하는 함수" ......와 같이, 그 데이터 처리에 필요한 변수와 함수를 모두 하나로 모으자는 것이다.

그렇게 되면 데이터의 처리에 관해서는 우선 "이 묶음에 안에 반드시 있다"라는 것이 되기 때문에, 곳곳의 함수를 찾지 않아도 된다.

이것이 "클래스(class)"의 개념이다. 클래스라는 것은 어떤 목적을 위해 필요한 '값'과 '처리'를 모두 한 묶음으로 한 것이다.

이 클래스는 아래와 같은 같은 형태로 만든다. "class 클래스 이름:"라는 것으로 시작하여 그 아래에 클래스가 제공하는 변수와 함수를 들여 쓰기하여 작성한다.

클래스의 정의

class 클래스 이름:
  변수1
  변수2
  ......필요한 만큼 변수를 제공......

  def 메소드1(인수):
     ......메소드의 처리......
   
  def 메소드2(인수):
     ......메소드의 처리......
   
  ......필요한만큼 메소드를 제공......

클래스에 필요한 값을 저장하는 변수를 "멤버 변수", 클래스에 제공하는 함수를 "메소드"라고 한다.

이러한 작성법은 기본적으로 일반 변수와 함수의 작성법 동일하다. 단지 "class OO:"라는 정의에 쓰면 멤버 변수와 메소드로 처리 할 수 있게 된다는 것 뿐이다. 특별한 작성법 등은 없다.


클래스 생성

이제 실제로 클래스를 만들어 사용해 보기로 하자. 이전에 "이름을 사용하여 메시지를 표시한다"는 것을 클래스에 해보기로 한다.

아래에 샘플을 예제를 보도록 하자. 여기에서는 "Member"라는 클래스를 만들었다.

class Member: 
    name = "" 
   
    def showMsg(self): 
        print("Hello, " + self.name + ". How are you?")

멤버 변수는 이름을 저장하는 "name"이라는 변수를 선언하였다. 메소드는 메시지를 표시하는 "showMsg"를 선언하고 있다.

이 클래스의 소스 코드를 보면, 여태 보지 못한 것이 등장하고 있다. "self"라는 것인다. 이것은 showMsg 인수에 사용되고 있다. "뭐야, 단지 인수에 사용한 변수가?"라고 생각하면 그렇지 않다는 것을 알 수 있다.

메소드 안에는 'self.name'라는 식으로 왠지 잘 모르는 사용 법을 사용하고 있다.

사실은 이 "self"라는 것은 "자신"을 나타내는 특별한 값이다. 자신은 클래스가 아니다. 클래스에서 만들어진 "인스턴스"라는 거다.

인스턴스와 self

클래스라는 것은 함수 등과 같이 그대로 클래스에서 메소드를 호출하거나 해서 사용하지는 않는다. 클래스를 이용하기 위해서는 "인스턴스"라는 것을 만들어야 한다.

클래스라는 것은 말하자면 프로그램의 "청사진"이다. 이것 자체를 조작하는 것이 아니라, 이 클래스는 설계도를 바탕으로 실제로 사용할 수 있는 부품을 만들어 그것을 조작하는 거다. 이 부품이 인스턴스이다.

만약 클래스를 그대로 사용하면, 그 클래스의 기능을 몇번이고 사용하고 싶다면 많은 클래스를 만들어야 한다. 예를 들어, 샘플 Member 클래스를 사용하여 "Taro"며 "Hanako"의 데이터를 관리하려 했다고 생각해 보자. 클래스를 그대로 사용하게 되면 그 name에 "Taro"로 설정하면, 또 "Hanako"는 보관할 수 없게되어 버린다.

그래서 클래스를 바탕으로 "인스턴스"라는 부품을 만들고, 그것에 Taro로 설정해 주어야 한다. Hanako가 필요하게 되면, 또한 클래스에서 새로운 인스턴스를 만들고, 거기에 Hanako라고 설정 해준다. 이런 상태로 "Member 클래스를 사용할 필요가 있으면 새로 Member의 인스턴스를 만들고, 이름을 설정해 주어야"입니다. 이렇게 하면 이 클래스를 바탕으로 얼마든지 데이터를 처리할 수 있게 된다.

그리고, 이 인스턴스 자신을 가리키는 데 준비하는 것이 "self"라는 것이다.

예를 들어, 어떤 메소드에서 "이 인스턴스에 저장되어 있는 멤버 변수의 값을 사용해야 한다"고 해보자. 이 예에서 말한다면, Member의 name 값을 showMsg에서 사용하는 경우이다.

이 때, 그냥 'name'변수 이름으로 사용할 수 없다. "이 인스턴스 안에 있는 name" 형태로 지정을 해주지 않으면 안된다.

그래서 Python 클래스에 제공되는 메소드는 반드시 첫 번째 인수에 인스턴스 자신을 나타내는 값을 전달하도록 해야 한다. 이것이 "self"의 정체는 것이다. 이 self 안에있는 멤버 변수와 메소드는

 self."변수"

따라서, self후에 점(.)으로 지정한다. 예를 들어, 여기에 "self.name"라고 하고 name 멤버 변수를 지정하고 이용하면 된다.


클래스 사용

이제 만든 Member 클래스를 사용해 보자. 아래에 사용할 소스 코드 예제가 있다.

class Member: 
    name = "" 
   
    def showMsg(self): 
        print("Hello," + self.name + ".How are you?") 
   
taro = Member() 
taro.name = "Taro"
taro.showMsg() 
   
hanako = Member() 
hanako.name = "Hanako"
hanako.showMsg()

여기에서는 Taro와 Hanako라는 2명의 데이터를 처리하기 위해 2개의 인스턴스를 만들어 사용하고 있다.

인스턴스의 생성은 "클래스 이름()"과 같이 이걸을 호출을 한다. 여기에서는

taro = Member()

이렇게 호출하고 있다. 이제 변수 taro에 Member 클래스의 인스턴스가 만들어 보관된다. 또한 인스턴스의 멤버 변수와 메소드는 점(.)을 사용하여 변수 이름 뒤에 해당되는 변수와 메소드를 작성해서 호출한다. 예를 들어,

taro.name = "Taro"
taro.showMsg()

이것은 taro 인스턴스의 name 멤버 변수에 "Taro"라고 값을 설정 해주고, 그러고 나서 showMsg 메소드를 호출해 실행을 한다. 이런 식으로 인스턴스를 만들어 변수에 할당 해두면 클래스 안에 있는 요소는 자유롭게 사용할 수 있다.

또한, 이 name와 같이 각 인스턴스에 값을 저장하고 사용하는 멤버 변수를 "인스턴스 변수"라고도 한다.

self는 어디 갔지?

그런데 이 사용 예를 보고 무언가 의문을 생기지 않나요? 그것은 showMsg를 호출하는 부분이다. "taro.showMsg()"라고 되어 있다.

어? showMsg는, 첫번째 인수에 "self"가 준비되어 있지 않은가? 그 self는 도대체 어떻게 된 것일까?

사실을 말하면, 메소드의 첫번째 인수로 전달되는 "인스턴스 자신"의 값은 Python의 시스템에 의해 자동으로 넘겨지게 된다. 즉, 첫번째 인수(self) 메서드를 호출할 때 불필요한 거다. 호출할 때, 두 번째 인수 이후만 작성한다. (여기에서는 첫번째 인수밖에 없기 때문에, 호출할 때 인수를 생략했다)

이런 식으로 인스턴스를 만들고 그 안의 멤버 변수를 설정하고 메서드를 호출한다. 이것이 클래스를 사용하는 기본이다. 이러한 기본 작업을 알면 클래스는 쉽게 사용할 수 있다.


생성자 사용

그렇게 하더라도,이 Member클래스 별로 유용하지 않는다. 인스턴스를 만들고, name을 설정하여 showMsg를 호출 ... 결국 하나 하나 다하고 있는 것은 클래스를 사용하지 않는 경우와 별로 차이가 없다. 게다가 인스턴스를 만든 후에 멤버 변수의 설정을 잊으면, 생각대로 움직이지 않게 되어 버린다. 적어도 "필요한 값은 처음부터 제대로 설정 사용"하도록 하고 싶다.

이럴 때에 유용한 것이 '생성자(constructor)'라는 것이 있다. 생성자는 "인스턴스를 만들 때 자동으로 호출되는 인스턴스 초기화를 위한 특별한 방법"이다. 이것은 다음과 같이 만든다.

def __init __ (self 인수 ...):
    ...... 초기화 처리 ......

생성자는 "__init__"와 같은 이름으로 메소드를 작성한다. 만약 어떤 값을 인수로 전달 싶다면, 2번째 인수 이후에 지정한다(첫번째 인수는 반드시 self이다).

이와 같이 생성자를 준비하면 인스턴스를 만들 때, 이 생성자를 사용하게 된다. 아래 예제를 보도록 하자.

class Member: 
   name = "" 
  
   def __init__(self, str): 
       self.name = str
  
   def showMsg(self): 
       print("Hello," + self.name + ".How are you?") 
  
taro = Member("Taro") 
taro.showMsg() 
  
hanako = Member("Hanako") 
hanako.showMsg()

여기에서는 다음과 같이 하여 str라는 인수를 전달하는 형태로 생성자를 제공하고 있다.

def __init__(self, str):

이제 인스턴스를 만들 때 name을 설정해야 한다. 실제로 인스턴스를 만들고 있는 곳을 보면,

taro = Member("Taro")

이런 식으로 ()에 이름을 인수로 넣고 있는 것을 볼 수 있다. 이렇게 해서 인수를 지정하여 인스턴스를 만들 수 있게 하면 필요한 멤버 변수의 설정도 한꺼번에 할 수 있어 매우 편리하다.



상속 클래스

클래스를 정의하는 가장 큰 장점은 "재사용이 가능하다"라는 것이다. 한번 만들면, 이후에는 그것을 그대로 복사하여 여기 저기에서 사용할 수 있다.

그러나 실제로 사용해 보면 "여기는 이렇게 하는 것이 더 좋다"라고 하는 것이 나올 거다. 하지만, 어느 정도의 규모의 프로그램가 되면, 곳곳에서 클래스를 사용하고 있으면 이런 수정은 제공되지 않는다.

이럴 때 정말 편리한 기능이 준비되어 있다. 그것은 "상속"이라는 것이다.

상속이라는 것은 이미 클래스를 그대로 이어 받아 새로운 클래스를 만드는 것이다. "이어 받는다"라는 것은 "클래스의 모든 기능을 그대로 이어 받는다"라는 것이다. 즉, 그 클래스에 있는 것을 통째로 그대로 받아 새로운 클래스를 만드는 거다.

이 상속을 사용하여 클래스를 정의하려면 다음과 같이 클래스를 만듭니다.

def 클래스 이름(상속하는 클래스):
    ...... 클래스의 내용 ......

상속에는, 상속하는 원래 클래스를 "기본 클래스", 새로 만든 클래스를 "파생 클래스"라고 한다.

실제 사용 예를 아래와 같다.

class Member: 
    name = "" 
   
    def __init__(self,str): 
        self.name = str
   
    def showMsg(self): 
        print("Hello," + self.name + ".How are you?") 
   
class PowerMember (Member): 
    mail = "" 
   
    def __init__(self,str1,str2): 
        self.name = str1 
        self.mail = str2 
   
    def showMsg(self): 
        print("Hello," + self.name + ".") 
        print("Your mail address is '" + self.mail + "'.") 
   
   
taro = Member("Taro") 
taro.showMsg() 
   
hanako = PowerMember("Hanako","hanako@flower.com") 
hanako.showMsg() 

여기에서는 Member 클래스를 상속하여 PowerMember라는 파생 클래스를 만들고 있다.

이 PowerMember 클래스는 def __init__(self, str1, str2):이렇게 하여, 2개의 인수를 준비했다. 그리고 self.name과 self.mail에 각각 설정하고 있다. 그런데, 이상한게 있다. 조금 자세히 더 살펴 보도록 하자.

잘 보면 이 PowerMember 클래스는 "mail"밖에 인스턴스 변수가 포함되어 있지 않았다. 그런데 self.name 값은 잘 저장하고 있다. 이것은 기본 클래스인 Member에 name가 준비되어 있기 때문이다. 상속은 기본 클래스의 모든 기능을 이어 받아 사용할 수 있다. 따라서 PowerMember에 name을 준비 할 필요는 없다.

멤버 변수뿐만 아니라 메소드도 모든 슈퍼 클래스에 있는 것은 그대로 사용할 수 있다. 이 상속을 사용하면 이미 있는 클래스를 점점 확장하여 기능 강화해 나갈 수 있다는 것이다.

Python에는 다양한 클래스가 라이브러리에 포함되어 있다. 그리고 그들을 이용할 때, 이 "상속"이 사용되고 있다.

예를 들어, 먼저 "리스트", "튜플", "레인지"라는 컨테이너에 대해 설명을 했었다. 이런 것도 실은 모든 "클래스"로 준비되어 있다. 이 3개의 클래스에 공통된 기능이 많은 것은 '시컨스'라는 인덱스에서 관리하는 컨테이너 클래스가 있고 그것을 계승하고 리스트나 튜플이 있다고 생각하면 이미지화하기 쉬울 것이다.

'Python' 카테고리의 다른 글

[Python] 클래스 사용  (0) 2017.12.22
[Python] 함수(function)  (0) 2017.12.22
[Python] 리스트, 튜플, 레인지, 세트, 사전  (0) 2017.12.22
[Python] 구문(statement)  (0) 2017.12.22
[Python] 우선 값과 계산의 기본  (0) 2017.12.22
[Python] Python 개발 환경  (0) 2017.12.22

스크립트의 일부분을 잘라내어 언제든지 사용할 수 있도록 하는 "함수", 이것을 잘 다루면 긴 프로그램을 구조적으로 조립이 가능하다. 그 기본을 설명한다.


함수란?

스크립트라고 하는 것은, 같은 처리를 여러번 반복하는 경우가 많다. 그 때마다 일일이 같은 스크립트를 여러번 쓰는 것은 매우 귀찮다. 이러한 "정해진 처리"를 언제 어디서나 호출할 수 있도록 하는 것이 "함수"이다.

예를 들어, 아래에 올린 예제(1)과 같은 예를 생각해 보자. 변수에 이름을 넣어 "Hello, OO. How are you?"라고 출력하는 스크립트이다. 이것은 유사한 텍스트를 출력하기 위해, 유사한 print 문을 여러번 작성을 하였다. 어쩐지 너무 바보 같지 않은가?

예제 (1)

a = "Taro" 
b = "Hanako" 
c = "Ichiro" 
   
print("Hello, " + a + ". How are you?") 
print("Hello, " + b + ". How are you?") 
print("Hello, " + c + ". How are you?")

이러한 때 "함수"가 도움이 된다. 예제(2) 함수를 사용하여 다시 작성한 것이다. 첫째로, 정해진 형태로 출력하는 함수를 먼저 준비해두면, 그 후로는 "showMsg("Taro ")"라고 하면 언제든지 호출 할 수 있다. 호출을 하는 것만으로 지정된 형태의 메시지가 표시될 것이다.

예제(2)

def showMsg(str): 
    print("Hello, " + str + ". How are you?") 
   
showMsg("Taro") 
showMsg("Hanako") 
showMsg("Ichiro") 

여기에서는 짧은 메시지를 표시 할 뿐이지만만, 좀 더 복잡한 처리가 되면 "한번 스크립트를 작성해 놓으면, 그것은 언제든지 호출하여 실행할 수 있다"라는 것은 매우 편리하다라는 것을 알 수있을 것이다.

print도 함수?

이 함수라는 것은 이미 실은 여러분은 사용하고 있었다. 값을 출력하는 "print"이다. Python은 기본적으로 많은 기능을 사용할 수 있게 되어 있다. 대부분은 "함수"로 준비되어 있는 것이다.



함수의 정의

이제 이 함수는 어떻게 만드는지, 설명하겠다. 함수는 다음과 같은 형태로 정의한다.

함수의 정의 (1)

def 함수 이름(인수1, 인수 2, ...):
    ...... 수행 할 작업 ......

함수 정의의 기본은 "def 함수 이름"이다. 이전에 샘플(2)는 "def showMsg ~"라고 되어 있기 때문에, showMsg라는 함수가 만들어진 것이다.

그리고 함수 이름 뒤에 ():을 붙이고 그 이후로는 줄 바꿈하고 들여 쓰기를 하여, 수행할 처리를 작성한다.

(): 안에는 '인수'라는 것을 추가할 수 있다. 인수는 함수를 호출할 때에 어떤 값을 받아서 전달하는데 사용한다. 예를 들어 샘플(2)에서

def showMsg(str) :

이렇게 되어 있었다. 이는 ()안에 있는 "str"라는 인수가 포함되어 있다는 것이다.

이것은 "이 함수를 호출 할 때, 어떤 값을 함께 쓰기 때문에, 그것을 str이라는 변수에 넣어 전달한다"라는 의미이다. 샘플에서 호출하는 부분을 살펴 보자.

showMsg( "Taro")

자, 이런 식으로 함수 이름 뒤에 ()를 붙이고, "Taro"라는 값이 작성되어 있는 걸까? 이 "Taro"가 showMsg 함수의 "str" 변수에 전달된다.

실행중인 처리를 보면, 이렇게 되어 있다.

print("Hello, " + str + ". How are you?") 

전달된 변수로 str을 사용하여 메시지를 print하고 있는 것을 알 수 있다.

인수는 하나뿐 아니라 얼마든지 추가할 수 있다. 이 경우 각각의 변수를 쉼표로 구분하여 작성한다.

def showMsg(a, b, c): 

이런 식이다. 인수가 없는 경우에도 ()은 붙이지 않으면 안된다.

함수를 이용하는데 있어서 최소한 기억하지 않으면 안 것은 우선 이것뿐이다. 의외로 간단하지 않는가?


반환 값

함수는 함수 이름과 인수가 제대로 알면 정의 할 수 있다. 사실은 함수 정의 부분에 나타나지 않는 또 하나의 중요한 요소가 있다. 그것은 "반환 값"이다.

반환 값은 함수를 실행한 후, 어떤 값을 호출한 곳에 돌려주는 역할을 한다. 이 반환 값은 "return"이라는 것을 사용하여 설정한다.

함수의 정의 (2)

 def 함수 이름(인수 1, 인수 2, ...):
     ...... 수행 할 작업 ......
     return

이와 같이 처리 한 후, 마지막에 "return 값"으로 인해 값을 반환하고, 호출 곳에 값이 전달된다.

실제로 반환 값을 사용해 보자. 이전에 샘플을 반환 값을 반환하는 형태로 고치면 아래와 같다.

def showMsg(str): 
    return "Hello," + str + ".How are you?"
   
res = showMsg("Taro") 
print(res) 
res = showMsg("Hanako") 
print(res) 

여기에서는 showMsg 함수에서 return을 사용하여 텍스트를 반환한다. 이 함수를 호출하는 부분을 보면,

res = showMsg("Taro")

이렇게 되어있는 것을 확인할 수 있다. showMsg의 결과를 변수 res에 대입하고, 이것으로 반환 값이 res에 할당되게 된다. 그러고 나서는 이 res를 사용하여 결과를 표시하고 있다.



키워드 인수

함수를 구성하는 요소 중에 의외로 다기능 것이 "인수"이다. 이것은 일반적으로 값을 전달 외에 여러가지 옵션을 가지고 있다.

먼저 "키워드 인수"라는 것이 있다. 이것은 인수에 키워드(이름)을 붙여 사용할 수 있도록 하는 기능이다. 무슨 말인가 하면, 아래와 같은 것이다.

함수의 정의 (3)

 def 함수(키1=초기값1, 키2=초기값2, ...):

키와 초기 값을 지정하는 것이다. 그러면 키를 사용하여 인수를 지정할 수 있다. 보통 인수는 순서가 정해져 있지만, 키를 사용하여 순서에 관계없이 값을 작성할 수 있다.

또한, 초기 값을 설정할 수 있기 때문에 값을 생략할 수 있다 (생략하면 기본값이 사용된다). 보통 인수는 반드시 값을 전달하지 않으면 안되지만, 키워드 인수로 하게 되면 옵션 다루는 (없어도 OK) 인수를 만들 수 있다.

그럼 실제 사용 예를 살펴 보자.

def showMsg(name, header='Hello', footer='How are you?'): 
    print(header + "," + name + ". " + footer) 
   
showMsg("Taro") 
showMsg("철수", '안녕', '건강하니?') 
showMsg("영희", footer='잘지내니?', header='야') 

여기에는 세가지 인수의 지정을 해서 showMsg를 호출한다. showMsg("Taro")와 같이, 첫번째 인수에 이름을 지정하는 것만으로도 제대로 동작하고, 두번째와 세번째는 초기값으로 동작한다.

또한 키워드는 붙여도 붙이지 않아도 동작한다. 다만, 키워드를 붙이지 않는 경우는 인수가 정의된 순서대로 지정해야 한다. 키워드를 붙여 인수를 작성할 경우는 어떤 순서라도 상관없다.

여기에서는 키워드가 없는 인수와 있는 인수가 혼재하고 있지만, 이러한 경우에는 반드시 "키워드가 없는 인수"를 먼저 정의하고 키워드 인수는 다음에 정의해야 한다. 키워드 인수 후에 키워드가 없는 인수를 지정하면 문법 오류이다.



가변 인자

인수에 대해 또 다른 설명을 하고 싶은 것이 "가변 인자"라는 것이다. 가변 인자라는 것은 "길이 (인수의 수)가 가변 인수"이다. 즉, "몇 개의 인수를 붙여도 된다"라는 특별한 인수이다.

"인수가 몇개 있어도 된다? 그것을 어떻게 정의하는 걸까?"라고 이상하게 생각 하겠지만, 가능하다. "정의 할 수 있지만 어떻게 값을 받을 거야?"라고 생각 하겠지만, 받을 수 있다.

가변 인자라는 것은 알기 쉽게 말하자면, "많은 인수를 컨테이너에 모와서 받을 인수"이다. 즉, "목록을 인수로 설정한 것"이라고 말할 수 있다. 다만, 목록을 인수에 쓰는 번거로움 때문에, (목록에 보관해 두는 값을) 하나씩 인수에 넣으면 자동으로 그것들을 한꺼번에 넘겨주게 되어 있다.

이 가변 인자는 다음과 같이 정의한다.

함수의 정의 (4)

def 함수(*인수):

인수 정의하는 변수 이름 앞에 별표 (*)를 붙이면, 그 인수가 가변 인자로 설정된다. 이 인수에는 여러 인수로 정의한 값이 n개로 모와서 전달된다. 그 후로는 거기로 부터 필요한 값을 꺼내는 처리만 하면 된다.

그럼 이것도 간단한 예제를 살펴 보자.

def calc(*num): 
    total = 0
    for n in num: 
        total += int(n) 
    print('합계:' + str(total)) 
    print('평균:' + str(total // len(num))) 
   
calc(123, 456, 789, 246, 357, 910) 

여기에서는 calc(* num)와 같이 함수를 정의하고 있다. 이것으로 num이라는 변수에 입력된 모든 인수를 컨테이너에 모와서 전달된다. 그 후에는 이 num을 for문으로 반복해 나가면 된다.

'Python' 카테고리의 다른 글

[Python] 클래스 사용  (0) 2017.12.22
[Python] 함수(function)  (0) 2017.12.22
[Python] 리스트, 튜플, 레인지, 세트, 사전  (0) 2017.12.22
[Python] 구문(statement)  (0) 2017.12.22
[Python] 우선 값과 계산의 기본  (0) 2017.12.22
[Python] Python 개발 환경  (0) 2017.12.22

Python에는 여러 값을 처리하는 것(컨테이너)가 일부 포함되어 있다. 그 기본적인 사용법에 대해 설명한다.


배열 = 리스트?

프로그래밍 언어는 여러 값을 한곳에 모아 처리하는 특별한 변수 같은 것이 대부분 준비되어 있다. 일반적으로 '배열'로 불리는 것으로, 이것은 번호를 붙여 값을 관리 할 수 있다. 예를 들어, "1번의 값을 XX로 변경" 또는 "3번 값을 꺼내기"라고 하여, 많은 값을 번호로 관리한다.

Python에서 제공되는 배열 기능은 "목록"라는 것이다. 이것은 다음과 같은 형태로 작성된다.

변수 = [값1, 값2, ...]

[] 안에는 각각의 값을 쉼표로 구분하여 작성한다. 이것으로 그 값을 순서에 번호를 매긴 목록이 만들어 진다. 이 번호는 일반적으로 "인덱스"라고 한다.

중요한 것은 "인덱스는 0부터 시작한다"라는 점이다. 즉, 첫 번째 값은 "0번"이 되고 두 번째 값이 "1번", 세 번째 값이 "2번" .....와 같은 식으로 넘버링이 된다. 10개의 값이 있었다면, 인덱스 번호는 0~9이다 (1~10이 아니다!).

목록에 있는 개별 요소를 꺼낼 경우, "변수[번호]"라는 식으로 작성한다. 예를 들어,

arr[0] = "OK"
val = arr[1]

이런 식으로 사용할 수 있다. 이것으로 목록에 지정된 번호의 요소를 변경하거나 제거할 수 있다.

아래에는 간단한 사용 예제를 살펴보자.

arr = ['hello','welcome','good-bye']
for n in arr:
    print(n)
 
print("....end.")

여기에서는 이전에 소개한 "for ~ in ..."구문을 사용하여 목록에있는 모든 요소를 반복해 나가고 있다. 이 구문은

for 변수 in 목록 :

이런 식으로 작성하여 목록에서 순서대로 값을 꺼내서 변수로 얻어서 반복을 실행한다. 목록과 for는 매우 잘 사용되므로, 두 세트 꼭 기억하도록 하자.


리스트와 텍스트의 관계

목록은 다양한 곳에서 사용되지만, 실제로는 뜻밖의 곳에서 사용될 수 있다. 그것은 "텍스트"이다.

Python에서 텍스트의 값은 "문자 목록"으로 처리 할 수 있다. 예를 들어 "Hello"라는 텍스트는

str = ['H', 'e', 'l', 'l', 'o']

이런 식으로 5개의 문자 목록으로 생각할 수도 있는 거다. 예를 들면, str[0] 이것으로 'H'의 문자를 꺼낼 수 있다.

그러나 이렇게 하는 경우는 "문자를 꺼낼 때"뿐이다. 같은 방법으로 문자를 변경할 수 없다. 즉, 텍스트와 목록이 같은 것은 아니다. 어디까지나 "텍스트 내의 문자를 꺼내기 위해 목록을 이용할 수 있도록 하고 있다"고 생각하면 된다. 이렇게 하면 매우 알기 쉽게 텍스트 내의 문자를 검색 할 수 있다.

아래에 간단한 사용 예제를 보도록 하자. "Hello"텍스트부터 문자를 제거하고, 새 텍스트를 생성하는 샘플이다.

str = "Hello"
str2 = ""
for n in str:
    str2 = str2 + (n * 2) + '~'
print(str2)

실행해 보면 "HH~ee~ll~ll~oo~"와 텍스트가 표시된다. 텍스트 내의 문자를 다루기 위하여 목록을 사용할 수 있다. 이를 기억해두면 꽤 편리할 거다.


튜플은 변경 불가능한 리스트?

이 목록에 비슷 "튜플"라는 것도 Python에 있다. 이것은 다음과 같이 작성한다.

변수 = (값1, 값2, ...)

값을 꺼낼 때는 변수와 마찬가지로 []에 인덱스를 지정한다. 예를 들어, str[0]와 같은 식으로 쓰면 된다.

그럼 기존 목록과 튜플은 무엇이 다른가? 그것은 "튜플 값을 변경할 수 없다"는 점이다. 즉, '변수'가 아니라 '상수'이다.

프로그래밍 세계에서는 변수처럼 값을 자유롭게 변경할 수 있는 것도 중요하지만, 반대로 "값을 변경할 수 없다"는 것도 중요하다. 어디선가 마음대로 값이 갱신 된다면 문제가 발생하게 된다 ...... 그런 중요한 값을 배열처럼 많이 이용하려면 목록으로는 곤란한다.

튜플은 값이 변하지 않는 것이 보증된 목록이다. 그렇게 생각하면, 이 튜플을 사용하는 경우가 없지 않나?라고 생각 할 수 있을 것이다.

이런 "변경 불가능한 컨테이너"는 튜플 외에도 있다. 앞장에서 잠깐 나온 레인지(range) 등이 있다. 이러한 변경 불가 것을 "불변 객체(immutable)"라고 한다.

이에 대해 변경이 가능한 것은 "가변 객체(mutable)"라고 한다. 목록는 mutable 컨테이너의 대표라고 할 수 있다.

그럼 "튜플로 제공한 값을 나중에 목록으로 사용하고 싶다"라고 하는 경우는 어떻게 해야 하나? 이러한 경우에는 변환을 해주는 함수를 사용하면 된다.

튜플을 목록으로 변환

변수 = list(튜플)

목록을 튜플로 변환

변수 = tuple(목록)

그럼 아래 튜플과 리스트의 사용 예제를 보도록 하자.

tp = (0,1,2,3,4)
ls = list(tp)
for n in range(0,5):
    ls[n] = ls[n] * 2
for n in tp:
    print(ls[n])

튜플 tp를 준비해서 거기에서 목록 ls를 만들고, 목록의 값을 변경한다. 출력 결과를 보면서 ls값과 tp값이 어떻게 사용되고 있는지 생각해 보자.



수열을 다루는 레인지

시퀀스 동료의 마지막은 "레인지(range)"이다. 이것은 이전에 등장 했었다. for 등에서 숫자의 범위를 지정하는데,

for n in range(10)

이런 식으로 쓰기도 했다. 이 range(10)라는 것이 레인지이다. 레인지는 다음과 같이 만든다. 알기 쉽게 예로서, 생성된 레인지에 포함되는 수열을 리스트로 표시해 두었다.

0부터 지정한 값의 직전까지의 범위

변수 = range(종료 값)

예)

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

지정된 값부터 지정한 값의 직전까지의 범위

변수 = range (시작 값, 종료 값)

예)

range(10, 20)
↓
[10, 11, 12, 13, 14, 15, 16, 17, 18, 19]

지정된 값부터 지정된 값의 앞까지 일정한 간격으로 값을 얻는 범위

변수 = range(시작 값, 종료 값 간격)

예)

range(10, 50, 5)
↓
[10, 15, 20, 25, 30, 35, 40, 45]

레인지는 차례를 잘 늘어놓은 수열을 만들기 위한 것이다. 이것은 물론 일반적인 용도로도 사용할 수 있지만, 가장 많이 사용하는 것은 for일 것이다. for문으로 반복 처리할 때, 반복 범위 지정을 위해 레인지를 사용하는 경우가 가장 많은 것이다.

그럼 아래의 목록 란에 간단한 사용 예제를 보도록 하자.

for n in range(10): 
    print(n)

range 값을 for으로 순서대로 출력하고 있다.



시퀀스 기능

목록 등의 "여러 값을 한꺼번에 처리한다"라는 것은 매우 유용하지만, 그러나 처음부터 고정된 수의 요소 밖에 사용할 수 없다면 조금 융통성이 없어 보인다.

하지만 염려하지 말자. 목록에는 요소를 추가하거나 제거하는 기능이 제대로 마련되어 있다.

그 대부분은 "순서"에 포함되어 있는 기능이다. 즉, 리스트, 튜플, 범위 중에서도 사용할 수 있는 기능이라는 것이다. 그럼 시퀀스의 기본적인 기능에 대해 정리해 보자.

새로운 요소를 추가

목록.append(값)

목록의 가장 마지막에 값을 추가한다.

지정된 인덱스 위치에 값을 삽입

목록.insert(인덱스, 값)

목록의 지정된 인덱스 번호의 위치에 값을 삽입한다.

지정된 값을 제거

목록.remove(값)

지정된 값을 목록에서 제거한다.

지정된 인덱스 번호의 요소 제거

del 목록 [번호]

목록 정해진 번호의 값을 제거하는 것이다. 이것은 del 후에 목록 삭제하는 인덱스 번호를 지정하여 작성한다. del 다음에, () 필요 없다.

여기까지 "추가", "삽입", "삭제"라는 것은 시컨스라고 해도 목록 밖에 대응되지 않는다. 왜냐하면, 이것은 목록 수정하는 작업이기 때문이다. 튜플과 레인지는 불변 객체 (변경 불가)이므로 이러한 작업은 할 수 없다.

이후에는 3개의 컨테이너에 공통되는 것이다. "시퀀스 전반에서 사용할 수 있는 기능"이다.

컨테이너의 덧셈

변수 = 컨테이너 + 컨테이너

목록과 튜플은 "+"로 덧셈을 할 수 있다. 2개의 컨테이너를 1개에 연결한 것을 만들 수 있다. 레인지는 수열이라는 이유로 해당하지 않는다.

컨테이너의 곱셈

변수 = 컨테이너 * 정수

컨테이너의 값을 지정한 수 만큼 연결 한 것을 만든다. 예를 들어, 아래와 같다.

[1, 2, 3] * 3
↓
[1, 2, 3, 1, 2, 3, 1, 2, 3]

인덱스의 범위를 반환

변수 = 컨테이너[시작 값:종료 값]

인덱스 번호로 지정된 범위의 값을 꺼내기 위한 것이다. 예를 들어, [2:5]라고 하면 2~5의 인덱스 번호의 요소를 컨테이너에서 반환한다.

값이 포함되어 있는지?

in 컨테이너
값 not in 컨테이너

이것은 값이 컨테이너에 포함되어 있는지 여부를 확인한다. 결과는 부울이다. in은 값이 포함되어 있으면 True, 없으면 False이다. not in 반대로 포함되어 있으면 False, 없으면 True이다.

요소의 개수 얻기

변수 = len(컨테이너)

그 컨테이너에 몇개의 값이 저장되어 있는지를 정수로 반환한다.

최대 값, 최소값 얻기

변수 = max(컨테이너)
변수 = min(컨테이너)

컨테이너에 저장되어 있는 값 중에서 가장 큰 것, 가장 작은 것을 찾아서 반환한다.

대충 이것들을 사용해 리스트 내의 요소를 사용할 수 있게 되면 매우 편리하다.

아래에 간단한 사용 예를 들어 둡니다.

arr = ['hello','bye'] 
arr.append('finish!') 
arr.insert(1, 'welcome') 
arr.remove('bye') 
for n in arr: 
    print(n)

목록 arr에 새로운 값을 추가하거나 삭제한 결과를 출력한다. 최초의 입력한 목록과는 대부분 저장된 값이 변화하고 있는 것을 알 수 있을 거다.



집합을 다루는 세트

여기까지의 리스트, 튜플, 범위는 모든 시퀀스라는 것이었다. 바꿔 말하면, 인덱스 번호를 사용하여 값에 일련 번호를 할당하고 순차적으로 정리하는 컨테이너였다.

하지만, Python에는 "값이 순차적이지 않는 컨테이너"도 있다. 그 중 하나가 "세트(set)"이다.

세트는 집합의 컨테이너이다. 세트는 값을 순서대로 정리하지 않는다. 세트 안에는 동일한 값을 여러개로 가질 수 없다. 저장되어 있는 값과 같은 값은 세트에 존재하지 않는 것이다.

이 세트는 {} 기호를 붙여 작성한다.

변수 = {값1, 값2, ...}

또는 set 함수를 사용하여 만들 수 있다. 인수에는 목록 등의 컨테이너를 제공한다.

변수 = set(값1, 값2, ...)

이제 세트를 만들 수 있다. 그러나! 인덱스가 없기 때문에, 여기에서 필요한 값을 뺄 수 없다. 그럼, 무엇을 위해 있는지?라고 생각할 수도 있다. 세트는 집합이다. 따라서 어떤 값이 이 집합에 포함되어 있는지에 대한 여부를 확인할때 사용을 한다.

세트 조작

세트에서도 세트를 조작하는 기능이 여러가지 준비되어 있다. 그러나 주의하지 않으면 안되는 것은, "세트는 값의 순서가 없다"는 점이다. 이것을 잊지 말고 가보도록 하자.

값 추가

세트.add(값)

값 추가는 "add"라는 것을 사용한다. 이것으로 ()안에 값이 세트에 추가된다. 그러나 이미 세트에 동일한 값이 있는 경우는 아무런 변화가 없다.

값을 삭제

세트.remove(값)

값 삭제는 remove를 사용한다. 이것은 시컨스와 같다. 이것으로 ()안의 값이 세트에서 삭제된다.

요소의 개수 얻기

변수 = len(세트)

그 세트에 몇개의 값이 저장되어 있는지를 정수를 반환다. 이것은 이미 나왔던 거다.

최대 값, 최소값 얻기

변수 = max(세트)
변수 = min(세트)

세트에 저장되어 있는 값 중에서 가장 큰 것, 작은 것을 찾아서 반환해 준다. 이것도 시컨스에서 사용했었다.

세트의 뺄셈

세트1 - 세트2

세트는 덧셈과 곱셈은 없지만, 뺄셈은 있다. 이것으로 "세트1"에서 "세트2" 요소를 제거한 나머지를 새로운 세트로 얻을 수 있다.

세트의 비교 연산

세트1 == 세트2 등

세트는 비교 연산이 가능하다. = <> 등의 기호 류를 사용한 비교 식을 사용하여 두 세트를 비교할 수 있다. 다만, <>는 "어느 쪽의 세트가 크거나 작다"는 의미는 아니다. 이것은 "어딘가가 어딘가에 포함되어 있는지"를 나타낸다. 예를 들어 A>B라고 하면, "A세트에 B세트가 포함되어 있는지"를 나타낸다.

세트의 논리 연산

 세트1 & 세트2
 세트1 | 세트2
 세트1 ^ 세트2

이 논리 연산은 집합인 세트 특유의 것이다. 이것들은 두개의 세트(집합)을 연산하여 새로운 세트를 만든다. 이것은 다음의 연산자를 사용해서 세트를 만들어 낸다.

기호설명
&2개의 세트에 공통되는 요소만을 가진 세트를 생성한다. (논리적)
|2개의 세트에 있는 모든 요소를 가진 세트를 생성한다. (논리합)
^두 세트의 어느 한쪽에만 있는 요소로 구성된 세트를 생성한다. (배타적 논리합)

 

마지막 논리 연산은 조금 이해하기 어려울지도 모른다. 실제 사용 예제를 참고하여 여러가지를 시도해 보자.

a = {'a', 'b'} 
b = {'b', 'c'} 
c1 = a & b 
c2 = a | b 
c3 = a ^ b 
print(c1) 
print(c2) 
print(c3)



키 값을 관리하는 사전

목록도 튜플도 인덱스라는 번호를 사용하여 값을 관리한다는 점에서는 같았다. 이 숫자가 아닌 "이름"을 사용하여 값을 관리하는 것도 Python에는 제공이 되어 있다. 그것은 "사전(dictionary)"이 라는 것이다.

사전은 '키워드'라는 이름을 붙여 값을 관리한다. 그리고 값을 제거하거나 변경하는 경우에는 그 값의 키를 지정한다. 사전은 다음과 같은 형태로 만든다.

변수 = {키1:값1, 키2:값2, ...}

또는 dict라는 것을 사용하여 만들 수 있다. 다만, 이 경우는 작성 방식이 조금 다르기 때문에 주의해야 한다.

변수 = dict (키1=값1, 키2=값2, ...)

사전에서 값을 꺼내는 경우는 시퀀스와 마찬가지로 []를 사용한다. 다만, 인덱스가 아닌 키워드를 []로 지정한다.

변수 = 사전[키]
사전[키] = 값

사전을 이용했을 때, 초보자가 착각하기 쉬운 것은 "사전은 키를 사용해도 다른 것과 동일하게 값을 꺼낼 수 있다"라는 생각이다. 즉, 키로도 꺼낼수 있고, 번호로도 꺼낼 수 있다라고 생각할 수 있다. 하지만 사전은 "키"밖에 사용할 수 없다. 다시 말하면, 사전에 있는 값을 번호 순서대로 추출 할 수 없다.

for in 주의!

모든 요소를 처리하기 위한 "for ~ in"구문은 사전에서도 사용할 수 있지만, 그 동작이 미묘하게 다르므로 주의가 필요하다.

리스트나 튜플에서 "for 변수 in 목록"이라고 하면, 리스트의 값이 변수로 꺼낸진다. 하지만 사전의 경우 꺼내지는 것은 각각의 "값"이 아니고 "키"이다. 즉, 변수에 추출된 키를 사용하여 값을 꺼내 사용하는 형태가 되는 거다.

그럼, 아래에 간단한 사용 예를 보도록 하자.

dic = {'taro':'taro@yamada.com', 
       'hanako':'hanako@flower', 
       'ichiro':'ichiro@baseball'} 
for n in dic: 
    print(n + ' (' + dic[n] + ')') 

여기에서는 각각의 이름을 키로하여 이메일 주소를 설정하고 있다. for를 사용하여 사전의 모든 데이터를 표시하고 있다. 사전을 사용하면, 이 처럼 작은 데이터베이스와 같은 사용이 가능해 진다.

사전 조작

사전도 다른 컨테이너와 같은 방법으로 조작 할 수 있는 기능이 여러가지가 제공되어 있다. 주요한 것에 대해 정리하겠다.

· 값 추가

사전[키] = 값

사전에 새로운 값을 추가하는 것은 간단한다. []으로 추가할 키워드를 지정하고 값을 대입하면 된다. 사전에서 해당 키워드가 아직 사용되지 않고 있다면, 새로운 키워드 항목이 추가된다. append와 add와 같은 기능의 함수가 필요없다.

값 삭제

del 사전[키]

값을 삭제하려면, remove는 사용할 수 없다. del을 사용하여 삭제한다.

모든 키 얻기

변수 = 사전.keys()

모든 값 얻기

변수 = 사전.values()

모든 항목(키, 값) 얻기

변수 = 사전.items ()

사전에 키워드와 값을 함께 얻는 기능이 있다. keys/values는 사전에 저장되어 있는 모든 키워드/값을 컨테이너로 모와서 꺼낸다. 또한, items는 키워드와 값을 튜플에 정리한 것을 얻는 거다.

정리

사전는 목록 등에 비해 '키워드 값을 꺼내기'라는 특성상 다소 특수한 용도로 사용된다. 데이터의 순서가 중요하지 않는 경우라면, 데이터 이름을 붙여 관리하는 것이 번호로 관리하는 것보다 압도적으로 편리하다. 프로그램 작성에 있어서 경우에 따라 "리스트로 하는 것이 좋을지, 사전을 사용하는 편이 편리할지"를 생각해서 이용하도록 하자.

'Python' 카테고리의 다른 글

[Python] 클래스 사용  (0) 2017.12.22
[Python] 함수(function)  (0) 2017.12.22
[Python] 리스트, 튜플, 레인지, 세트, 사전  (0) 2017.12.22
[Python] 구문(statement)  (0) 2017.12.22
[Python] 우선 값과 계산의 기본  (0) 2017.12.22
[Python] Python 개발 환경  (0) 2017.12.22

Python의 가장 큰 특징은 그 독특한 "구문(statement)"스타일에 있다. "들여쓰기(intent)"를 이용한 구문 작성 및 기본적인 제어 구문에 대해 설명한다.


구문과 들여 쓰기의 관계

프로그래밍 언어로는 "값"과 "계산"이 기본라고 했었다. 그럼, 다음에 중요한 것은 무엇일까요? 여러가지 생각나는 것이 있지만, 아마 프로그램의 "제어"일 거다.

단지, 명령을 순차적으로 실행하는 것만으로는 극히 제한된 사용법 밖에 할 수 없다. 프로그램의 상황에 따라 "여기는 이것을 실행", "이것은 OO번 반복"과 같이 프로그램의 흐름을 제어함으로써 보다 복잡한 프로그램이 만들 수 있게 되기 때문이다.

이러한 프로그램의 움직임에 대해 이것 저것 지시하기 위해 준비되어 있는 것이 '구문'이라는 것이다. 그 중에서도 그 흐름을 제어하기 위해 마련된 것을 "제어 구문(control statement)"라고 한다.

Python의 구문 표기법은 매우 독특하다. 그것은 "들여 쓰기"를 사용한 방법이다. "들여 쓰기"란 텍스트의 시작 위치를 탭이나 공백으로 오른쪽으로 보내는 것이다.

Python에서는 다양한 구문이 들여 쓰기를 사용하여 작성한다. 예를 들어, 구문에서 "이런 경우 다음 작업을 수행한다"라는 것을 설명한다고 하자. 그러면, 그 구문 중에 준비하는 처리는 그거보다 오른쪽으로 들여 쓰여져 있다. 그리고 그 들여 쓰기 위치에서 쓰여져 있는 것이 "그 구문 안에 처리"라고 판단되는 것이다.

그 구문을 끝내고 원래대로 돌아가려면 이전 위치로 들여 쓰기를 되돌린다. 즉, Python은 "그 문장이 어떤 위치에서 쓰기 시작하고 있는가"에 따라 어떤 구문의 처리인가를 인식하는 것이다.

간단히 이해를 돕기 위해 아래의 구문 작성 내용을 보도록 하겠다.

Python 구문 작성

보통 문장 ......
구문 그 1
     구문 1의 처리 ......
     구문 1의 처리 ......
     구문의 1에 또한 구문
         그 또한 중 처리 ......
         그 또한 중 처리 ......
     구문 1의 처리 ......
구문 2
     구문 2의 처리 ......
보통 문장
보통 문장
...... 중략 ......

이런 식으로 문장의 시작 위치를 조금씩 다르게 하여 구문이 작성된다. 이는 들여 쓰기 공백 수를 잘못하게 되면 문법적인 에러가 발생하기도 한다는 것을 의미한다.

Python 들여 쓰기는 일반적으로 탭 대신 공백이 사용된다. 표준으로 공백 8개 문자씩 넣는 방식이 많지만, 이것은 특별히 정해져 있는 것은 아니고, 4개 문자도 2개 문자로도 인식하고 동작한다.

단, 너무 공백 수가 적으면 문법의 구성이 알아 보기 힘들어 지거나, 들여 쓰기가 실수가 증가하기도 하고, 공백 수가 너무 많으면 점점 문장이 오른쪽으로 이동하여 문장의 끝이 보이지 않게 될 수도 있다. 그러기에 적당한 폭을 생각하면서 쓰도록 하자.



조건 분기의 기본은 "if"구문

제어 구문을 크게 나누면 "조건 분기"와 "반복"으로 구성되어 있다. 우선 조건 분기부터 살펴 보겠다.

조건 분기는 문자 그대로 "조건에 따라 처리를 분기한다"는 것이다. 그 기본은 양자 택일의 분기하는 "if"구문이다. 이것은 다음과 같은 형태를 하고 있다.

if의 기본형 (1)

if 조건:
    옳았을 때의 처리

if의 기본형 (2)

if 조건:
    옳았을 때의 처리
else:
    잘못된 때의 처리

if 문은 여러가지 작성 방법이 있다. 기본은 조건을 확인하고 그것이 옳았을 때에 작업을 수행한다는 것이다. 이것은 if문 후에 검사 조건이 되는 것을 쓰고 콜론(:)을 쓴다. 그러고 그 이후의 들여 쓰기된 부분을 수행한다.

옳았을 때의 처리와는 별도로 잘못된 경우에도 어떤 처리를 하고 싶다면, 옳았을 때 수행할 처리가 끝나는 곳에, 들여 쓰기를 if 위치로 돌아가서 "else :"라고 쓴다. 그리고 또 오른쪽으로 들여 쓰기하여 수행 할 서치를 작성하면 된다.

또한 조건을 하나뿐만 아니라 차례로 확인하는 "elif:"와 같은 것도 있지만, 일단은 "if ~: ", "else : " 2개만 기억해두면 충분하다.

아래에 간단한 예제는 아래와 같다.

x = 1234
check = x % 2
if check == 0: 
    print(str(x) + "는、짝수입니다.") 
else: 
    print(str(x)  + "는, 홀수입니다.") 
print("....end.")

변수 x가 짝수인지 홀수인지를 검사 프로그램이다. x를 2로 나눈 나머지를 확인하고, 그것이 제로인지 여부에 표시할 텍스트를 변경하고 있다. 변수 x의 값을 다양하게 변경하여 동작을 확인해 보자.



조건은 어떻게 쓰는거야?

이 if문을 사용하기 위해서는 '조건'이라는 것을 어떻게 작성해야 해야 할지가 중요하다. Python이라는 프로그래밍 언어를 이해하는데 있어서 조건은 꼭 알아야 한다. 조건에 대해 정리하면 대략 다음과 같다.

숫자를 비교하는 식

가장 많이 쓰는 것은 숫자를 비교하는 식이다. 이전 페이지 샘플도 숫자 비교 식을 사용했다. 두 값을 비교하여 "어느 쪽이 큰지"라든가 "같은 값인지에 대한 여부"등을 체크하는 식이다. 이것은 다음과 같은 기호가 준비되어 있다.

기호설명
값1 == 값2값1과 값2는 동일하다.
값1 != 값2값1과 값2는 같지 않다.
값1 < 값2값1보다 값2 더 크다.
값1 <= 값2값1보다 값2 쪽이 크거나 같다.
값1 > 값2값1보다 값2 쪽이 작다.
값1 >= 값2값1보다 값2 쪽이 작거나 같다.

부울 값과 변수

"부울"이라는 것은 "옳고 그른지? "라는 양자 택일에 대한 값이었다. 이것은 True 또는 False의 값 중 하나였다. if문에는 그 후의 변수와 값이 True이면 다음의 작업을 수행한다. False라면 작업을 수행하지 않거나 또는 else: 이후의 처리를 실행한다.

결론

사실은 "숫자를 비교하는 식"과 "부울 값과 변수"는 어느 쪽도 같은 것이다. 첫번째에서 "두 값을 비교하는 식"에서는 두개의 식을 비교한 결과를 논리 값으로 반환하는 역할을 한다. 즉, 상세히 따져보면 "True인가? False인가?"에서 모든 if 조건문의 결정이 가능하다는 것이다.



조건에서 반복 while 문

이어서, 또 하나의 제어 구문 "반복"에 대해 알아보겠다. 반복은 2개의 구문으로 되어 있다. 첫번째 조건을 사용하여 반복을 체크하는 "while"구문이라는 것이다.

while 구문의 기본형 (1)

while 조건:
    반복 처리 ......

while 구문의 기본형 (2)

while 조건:
    반복 처리 ......
else :
    반복 종료시 처리 ......

여기에서도 "조건"라는 것이 등장했다. 이것은 if에서 나온 조건과 동일하다. 즉, "True인가? False인가?"를 확인하는 수식 및 변수 혹은 값이다.

이 while 구문은 조건을 확인하고 그것이 True인 동안에는 그 구문의 처리를 반복 실행을 계속한다. 그리고 조건이 False가 되면 반복에서 빠져 나와 다음 처리를 한다. 만약, else:문이 있었다면, 반복을 빠져 나오면서 이를 실행한다.

아래에 간단한 샘플을 보도록 하자.

x = 100
count = 1
total = 0
while count <= x: 
    total = total + count 
    count = count + 1
else: 
    print(str(x) + "까지 합계는 " + str(total) +"이다.") 
print(".....end.")

1에서 변수 x까지의 합을 계산하고 표시하는 샘플이다. x의 값을 다양하게 변경하여 결과를 확인해 보도록 하자.

이 샘플에서는 "while count <= x:"와 같은 while의 조건을 설정한다. 즉, count 값이 x보다 작거나 같은 동안 반복을 계속하고, x보다 커지면 빠져 나오게 된다.

결국은 당연 하지만, 반복 실행하는 과정에서 변수 count 값이 조금씩 커지게 하지 않으면 안된다. 그렇지 않으면, while에서 영원히 벗어날 수 없게 되어 버린다. 이런 경우를 "무한 루프"라고 한다. while을 사용할 때에는 이런 무한 루프에 빠지지 않도록 조건의 내용과 그 결과가 반복에서 어떻게 변화해 가는가를 잘 생각해서 작성을 해야 한다.



많은 값을 순서대로 반복하는 for구문

사실, 반복에는 또 다른 구문이 있다. 그것은 "for"구문이다. 이 for는 "많은 값을 순서대로 처리하는 경우"에 사용한다

for 구문의 기본형 (1)

for 변수 in 많은 값:
    반복 처리 ......

for 구문의 기본형 (2)

for 변수 in 많은 값:
    반복 처리 ......
else :
    반복 종료시 처리

프로그래밍 언어에는 "많은 값을 한곳에 모아 처리하는 기능"이 준비되어 있다. for는 그러한 것들을 위한 전용 반복 구문이다. 즉, 많은 값을 차례로 꺼내 처리를 실행하는 것은 결국에 준비되어 있는 모든 값에 대해 반복을 하는 것이다.

이 "많은 값"이라는 것이 무엇인가 대해 실제 사용 예제로 알아보도록 하자. 아래의 예제는 이전 페이지에서 while에 대한 샘플을 for 구문에 쓰고 다시 작성한 거다.

x = 100
total = 0
for n in range(1, x + 1): 
    total = total + n 
else: 
    print(str(x) + "까지의 합계는 " + str(total)) 
print("....end.")

여기에서는 range(...)는 본 적이 없는 것이 사용되고 있는데, 이것은 "1에서 변수 x까지의 모든 숫자를 하나의 묶음으로 만드는 함수"이다. 이것으로 "1,2, 3 ... 100"는 모든 숫자를 하나의 묶음을 만들고 그 하나 하나를 꺼내 total에 더해 가고 있는 거다.

그런데, 이 for문을 사용하기 위한 포인트는 "많은 값을 모은 것"이라는 게 무엇인가? 라는 점인데, 이것은 일반적으로 "배열"라는 것이다. 다음에는 배열과 그와 관련된 것에 대해 설명하기로 하겠다.

'Python' 카테고리의 다른 글

[Python] 클래스 사용  (0) 2017.12.22
[Python] 함수(function)  (0) 2017.12.22
[Python] 리스트, 튜플, 레인지, 세트, 사전  (0) 2017.12.22
[Python] 구문(statement)  (0) 2017.12.22
[Python] 우선 값과 계산의 기본  (0) 2017.12.22
[Python] Python 개발 환경  (0) 2017.12.22

프로그래밍의 기본이라고 하면, 우선 "값"과 "계산"이다. Python으로 사용되는 기본 값과 계산 방법 등을 설명한다.


값에는 유형이 있다

프로그래밍에 익숙하지 않은 사람이 처음 도전했을 때, 처음에 걸리는 것은 "값은 종류가 있다"는 것이다.

많은 초보자는 Python과 같은 스크립트 언어부터 시작을 공부한다. 이러한 언어에서는 변수(값을 보관 해 두는 곳) 등을 사용하는 경우도 그다지 "값 유형"등을 의식하지 않도록 되어 있다. 따라서 "어떤 값도 변수에 넣어 사용하면 그것으로 움직인다"고 착각해 버린다.

나중에 설명 하겠지만, Python에도 값에는 "유형"이 있다. 숫자, 텍스트, 문자 ......라는 식으로 다양한 종류가 있으며, 그 종류마다 값의 사용법은 달라진다. 하지만, 실제로 프로그램을 쓸 때는 대부분 값의 "유형"을 의식하지 않고 쓸 수 있게 되어 있다.

간단한 예를 살펴 보자. IDLE을 시작하고 아래에 올린 3문장의 스크립트를 한 줄씩 실행하길 바란다.

print(123 + 456)
print('123' + '456')
print(123 + '456')

보면 대체로 비슷한 두 값을 덧셈하고 있다. 어떤 것도 같은 생각이 든다.

그런데 실제로 실행해 보면, 이 3개는 전혀 다르게 동작한다. 첫 번째는 "579"가되고, 두 번째는 "123456"가 되고, 세 번째는 에러가 발생한다.

이것은 첫 번째 숫자로 계산하고, 두 번째는 텍스트로 계산된다. 세 번째는 두 가지의 종류의 다른 값을 억지로 계산하려는 시도했다가 실패를 했다.

즉, Python이라는 언어는 "값 유형"라는 것을 잘 이해해 두지 않으면 사용할 수 없다. 우선, 이 점을 잘 이해해 두자.


주요 값의 유형

그럼 Python에는 어떤 값의 유형이 있을까? 기본적인 사항을 설명한다.

숫자

프로그래밍에서 사용 값이라고 하면, 우선 "숫자"이다. 숫자에 대해서는 Python에서는 많은 유형이 있는데 "정수", "부동 소수점" "복소수"등이 있다.

  • 정수 : 보통의 정수이다. 단지 숫자를 쓰는 것만으로 된다.
  • 부동 소수점 : 소수점 이하의 값이다. 또는 매우 자리수가 많은 숫자에 사용하기도 한다. 이것은 보통 소수의 "."을 붙여 쓴다.
  • 복소수 : 허수이다. 이것은 끝에 "J"를 붙인다.

이 중에 우선 '정수'와 '부동 소수점"만 기억해 두도록 하자. 복소수는 필요하여 사용하게 될때까지 잊고 있어도 된다.

텍스트

텍스트는 값의 전후를 따옴표로 묶어 설명한다. 이것은 "작은 따옴표", "큰 따옴표", "트리플 쿼트"라고 한 것이 사용할 수 있다.

'Hello'    "Welcome"   '''Bye'''

이런 느낌이다. 이 가운데 작은 따옴표(')와 큰 따옴표(")은 동일하다. 일반적으로 텍스트를 쓸 때, 이 중 하나에 쓴다.

마지막 트리플 쿼트(''')은 여러 줄의 텍스트를 쓸 때 사용한다. 작은 따옴표와 큰 따옴표는 텍스트 값의 줄로 할 수 없다. 트리플 쿼트는 도중에 행을 변경해도 된다.

부울

이것은 프로그래밍 특유의 값이다. 이것은 "양자 택일의 값"이다. 참 또는 거짓, yes 또는 no, 올바른 또는 그른지 이런 것을 나타내는데 사용한다. 이것은 "True", "False"라는 Python에 포함되어 있는 키워드를 사용하여 작성한다. 다른 값은 사용할 수 없다.

실제로 이러한 값을 사용한 예제는 아래와 같다. 아래의 코드를 IDLE에서 한 줄씩 실행해 보자.

print(12345) 
print('Hello') 
print('''welcome, 
and bye.''') 
print(True)

조금 이해하기 어려운 것은 트리플 쿼트 텍스트일 것이다. 그 외에 그렇게 어려운 것은 없을 거다.



변수와 계산

값은 그대로 자체를 그대로 사용하는 일은 그다지 많지 않다. 보통은 "변수"에 넣어 사용한다.

변수란 값을 보관할 준비가 된 메모리 영역을 나타내는 것이다. 어째서 이런 것이 있는가? 하고 의문을 가질 수 있는데, 여기서 이를 설명하기에는 너무 내용이 방대하다. 그래서 우선은 알기 쉽게 "여러가지 값을 넣어 두는 용기"라고 생각두면 충분하다. 값은 변수에 넣어 놓고, 계산하고, 그 결과를 다시 변수에 넣고 처리해 나간다.

이 변수는 등호(=)를 사용하여 값을 넣는다. 예를 들면 이런 식이다.

변수명 = 값

이것은, 우변 값이 좌변의 변수에 포함되어 있다(대입라고 한다). 변수 이름을 쓰고, 이런 식으로 값을 대입하면 바로 변수를 만들어 사용할 수 있게 된다. "미리 이런 변수를 만들어 두는 거"라고 해서 귀찮은 일은 아니다.

예를 들어, "a = 10"라고 하면, 변수 a가 바로 만들어 진다. 이 변수는 값과 동일하게 취급 할 수 있다.

a = 10
print(a) 
b = 'Hello'
print(b)

연산에 대해

변수는 단지 값을 보관할 뿐만 아니라, 다양한 계산을 한 결과를 저장하기 위해 많이 사용된다.

숫자 연산

숫자 연산 기호는 이른바 사칙 연산 기호를 그대로 사용할 수 있다. 즉, 덧셈(+), 뺄셈(-), 곱셈(*), 나눗셈(/), 나머지(%)를 말한다. 키보드에 기호가 보이기 때문에 알 것이다. 그런데, 여기서 %는 무엇인가 라고 생각한 사람이 있을 것이다. 이는 "나눗셈을 하고 남은 나머지"를 계산하는 것이다.

a = 10
b = 20
c = a + b 
print(c)

이 밖에 '지수'의 기호 있다. [**]이다. 예를 들어, "10의 제곱"이라면, "10**2" 이렇게 쓴다.

텍스트 연산

또한 "텍스트의 연산 '라는 것도 있다. 연산은 '덧셈'과 '곱셈'을 사용한다.

  • [ + ] 기호 : 왼쪽과 오른쪽 텍스트를 하나로 연결한다.
  • [ * ] 기호 : 왼쪽의 텍스트를 오른쪽 회수 만큼 반복한다.

덧셈은 간단한다. 예를 들어, [ 'A' + 'B' ]라고 하면 "AB"라는 텍스트가 될 것이다. 곱셈은 [ 'A' * 3 ]라고하면 "AAA"이다.

a = 'A'
b = 'B'
c = a + b 
print(c)

print('A' * 3)



값 유형의 변환과 텍스트

값에는 유형이 있다. 다른 유형의 값끼리는 계산할 수 없다. 그렇게 되면, 예를 들어 "텍스트와 숫자를 사용하여 계산한다"라고하는 것은 불가능하다는 걸 의미한다.

그러나 물론 그런 일은 없다. 제대로 된 방법으로 Python에는 값을 다른 유형으로 변환하는 기능이 포함되어있다. 우선 다음의 것만 제대로 배워보도록 하자.

  • int(값) - ()의 값을 정수로 변환한다.
  • float(값) - ()의 값을 실수로 변환한다.
  • str(값) - ()의 값을 텍스트로 변환한다.
  • bool(값) - ()의 값을 부울 값으로 변환한다.

이 처럼 어떤 유형의 값을 다른 유형으로 변환하는 것을 "형 변환"이라고 하고, 영어로는 "캐스팅(casting)"이라고 한다.

그럼 아래 형 변환 간단한 샘플들을 보도록 하겠다. 이런 방법로 텍스트와 정수를 변환하여 사용한다면 될 것이다.

텍스트에 값을 정리

값 형 변환는 "계산 값을 맞출려고"하는 경우도 있지만, 그 보다 자주 사용하는 것이 "print에 값을 출력하기 위해서"이다. print는 어떤 값도 출력할 수 있지만, 텍스트를 사용하여 값을 처리하려는 순간 오류가 발생한다.

a = 123
b = 456
c = a + b
print(c)

이것은 매우 간단한 샘플이다. 그런데 이것을 조금 처리하려는 순간 갑자기 문제가 발생한다.

a = 123
b = 456
c = a + b
print('answer :'+ c)

갑자기 왜 오류가 발생하는가? 라고하면, print()에 쓴 'answer :' + c가 원인이다. 텍스트와 숫자를 연결해 보려고 했기 때문에 "변수 c는 텍스트 아니야"라고 오류가 발생하는 것이다. 텍스트를 + 기호로 연결하기 위해서는 연결 값이 텍스트가 아니면 안된다.

a = 123
b = 456
c = a + b
print('answer :' + str(c))

이제 문제없이 동작하게 되었다. print()를 보면 'answer :'+ str(c)와 같이 되어 있다. 변수 c를 str로 텍스트로 변환한 것으로 정상적으로 동작하게 되는 것이다.

이와 같이, "텍스트으로 값을 연결하려고 하여, 오류가 발생하게 되었다"라는 것은 초보자로써 언제나 하는 실수이기에, print에 오류가 발생하면 먼저 "값 형 변환, 값 형 변환"라고 머릿속에서 반복하자.

'Python' 카테고리의 다른 글

[Python] 클래스 사용  (0) 2017.12.22
[Python] 함수(function)  (0) 2017.12.22
[Python] 리스트, 튜플, 레인지, 세트, 사전  (0) 2017.12.22
[Python] 구문(statement)  (0) 2017.12.22
[Python] 우선 값과 계산의 기본  (0) 2017.12.22
[Python] Python 개발 환경  (0) 2017.12.22

+ Recent posts