파이썬은 동적 객체 지향 언어입니다. 동적이라는 말의 의미는 이미 파이썬 일기의 초장에 정리해놨으니, 이번엔 객체 지향이라는 말의 의미를 알아봅시다. 객체, 지향, 이 두 단어는 따로 떼어 놓으면 전혀 어려운 뜻이 아닌데 붙여 놓으니까 무슨 말인지 하나도 이해가 안 갑니다.

이럴 때는 위키 피디아님에게 도움을 청하는 것도 하나의 방법입니다.

Object-oriented programming (OOP) is a programming paradigm that uses "objects" — data structures consisting of datafields and methods — and their interactions to design applications and computer programs. Programming techniques may include features such as information hiding, data abstraction, encapsulation, modularity, polymorphism, and inheritance. It was not commonly used in mainstream software application development until the early 1990s. Many modern programming languages now support OOP.

라는군요.

요약하자면, 객체 지향 프로그래밍은 프로그래밍을 할 때 "객체"를 중심으로 사고, 생각을 한다는 것입니다. 객체 지향 프로그래밍 이전의 프로그래머들은 명령을 중심으로 생각했었죠. 그나저나 여기에서 말하는 객체는 또 무슨 의미일까요? 그냥 단순하게 우리가 앞에서 살펴봤던 변수와 메쏘드를 함께 갖고 있는 "무엇"이라고 생각하면 됩니다. 앞에서 인용한 설명대로, 객체 내의 변수와 메쏘드는 긴밀하게 상호 동작합니다(그렇지 않으면 한 객체 안에 엮어놓을 이유가 없겠죠!).

인용구의 나머지 부분은 나중에 다시 한번 이야기하기로 하고, 이제는 클래스에 대해 이야기를 시작하겠습니다.

클래스와 객체의 관계는 자료형과 변수의 관계라고 생각하면 딱 맞습니다. 파이썬에서는 모든 것이 객체이므로 자료형이 클래스이고 클래스가 자료형이며, 변수가 객체고 객체가 변수라고 할 수 있습니다. 우리가 지금까지 만들어 왔던 객체들은 모두 파이썬에 내장되어 있는 클래스들을 이용한 것입니다. int, float, string, tuple, list 는 파이썬의 내장 클래스의 일부입니다. 이들은 다시 새로운 클래스를 만들 때 재료로 사용되곤 합니다.

파이썬의 클래스는 다음과 같이 class 키워드를 이용하여 선언합니다. 그리고 그 안에 클래스의 멤버들(변수, 메쏘드)를 선언하면 기본적인 클래스가 완성됩니다.

>>> class A :
    a = 0
    b = 0
    def printMembers(self) :
        print( self.a, self.b )

         >>> obj = A() >>> obj.a = 3 >>> obj.b = 4 >>> obj.printMembers() 3 4

위 코드에서 중요한 건 class 키워드입니다. 우리는 위에서 A라는 클래스를 선언했고, A 클래스 변수 두 개(a와 b), 함수 하나(printMembers)를 정의했습니다. a와 b에 대해서는 특별히 볼 것이 없습니다. 클래스 안에서 선언되었다는 것 말고는 특별한 것이 없습니다. 하지만 printMembers()는 보통의 함수와 다른 것을 갖고 있습니다. 보이나요?

예, self 입니다. self 는 객체 자신을 가리키는 변수로써, self라는 이름은 파이썬에 키워드로 예약되어 있습니다. (즉, 우리는 self라는 변수를 임의로 만들어 쓸 수 없다는 뜻입니다. ) 파이썬은 클래스의 메쏘드로 하여금 반드시 self 매개 변수를 받는 꼴로 구현하도록 강요합니다. 대신 이 메쏘드를 실제로 호출할 때는 self 에 아무것도 대입하지 않아도 됩니다. 마치 self 매개 변수는 없는 것처럼 사용하면 됩니다.

self가 객체 자신을 가리킨다고 했는데, 그럼 self를 통하면 객체의 멤버와 메쏘드 모두에게 접근이 가능하겠죠? self 뒤에 마침표(.)를 찍고 변수 또는 메소드의 이름을 적어주면 됩니다. 다음과 같이 말입니다.

>>> obj = A()
>>> obj.a = 3
>>> obj.b = 4
>>> obj.printMembers()

아, 중요한 것을 빼먹었네요. 위 코드에서 obj를 생성하기 위해 A()라는 함수를 호출해서 그 결과를 대입하는 부분이 있죠? 여기에서 A()를 생성자(Constructor)라고 합니다. 생성자는 클래스와 같은 이름을 가지며, 클래스를 만들어주는 역할을 하는 함수입니다. 생각해보니, 우리는 A() 생성자를 구현한 적이 없습니다. 하지만 우리는 호출을 했고, A() 생성자는 우리가 원하는 대로 객체를 만들어 반환했습니다. 이것은 기본 생성자(Default Constructor) 때문입니다. 우리가 생성자를 따로 만들지 않아도 파이썬에서 기본 생성자를 제공합니다.

클래스를 사용하다 보면 어떤 때는 객체를 생성할 때 일부 멤버 변수를 원하는 값으로 초기화하고 싶을 때가 있습니다. 이런 경우에는 생성자를 직접 구현하는 것이 좋은 답이 됩니다. 생성자를 호출할 때는 클래스의 이름 뒤에 ()만 붙이면 되지만, 구현할 때의 생성자는 __init__()라는 이름을 가집니다. __init__()을 구현하면, 우리는 클래스() 로 호출해서 사용하는 것이죠.

한편, __init__() 도 클래스의 메쏘드이기 때문에 self 매개 변수는 반드시 있어야 합니다. 추가적인 매개 변수는 self 뒤에 콤마(,)로 구분해서 열거하면 됩니다. 다음 코드의 NameCard 클래스를 보면 이해가 금방 갈 겁니다.

>>> class NameCard : 
           name = "" 
           cp_number = "" 
           def __init__(self, name, cp_number) : 
               self.name = name 
               self.cp_number = cp_number 
>>> sean = NameCard( "Sean", "010-5555-7777" ) 
>>> sean.name 
'Sean' 
>>> sean.cp_number 
'010-5555-7777' 

자, 이렇게 해서 간단한 클래스를 만드는 데까지 공부를 했습니다. 본격적인 객체 지향 프로그래밍을 위해서는 상속이나 오버로딩, 오버라이딩에 대해 공부를 해야 하는데, 벌써 여기까지 오는 데도 상당한 시간이 걸렸습니다. 앞으로 지금처럼 일기 형식으로 글을 남길지는 고민입니다. 파이썬 일기는 온라인 문서만으로 공부했을 때 어느 정도 시간이 걸리는지, 얼마나 스트레스를 덜 받으면서 새 언어를 익힐 수 있는지를 보고자 하는 개인적인 실험이었습니다.

실험의 중간 결과를 말씀드리자면, 온라인 문서만으로는 제대로 공부하는 것은 어렵다는 판단이 섰습니다. 이런 식으로는 제 스스로 공부하는 데에도 어려움이 있고 의미 있는 문서를 남기고자 하는 취지도 살리질 못할 것 같습니다(그래서 #6 파이썬 일기부터는 일기 형식이 아닌 강좌 형식을 취했습니다.).

뭔가 더 좋은 방법을 찾아봐야겠습니다. 방법이... 있겠죠? ^^;

저작자 표시 변경 금지
Posted by seanlab

continue와 break는 루프의 실행을 건너뛰거나 멈추게 하는 구문이다. 이름을 보면 그 의미를 짐작할 수 있는데, continue는 루프를 한 회 건너 뛰게 하고 break는 루프를 중단하게 만든다. 사용방법은 각각 다음과 같다.

먼저 continue 문 예제.

>>> for i in range(0, 5) :
        if i == 2 :
            continue
        print( i )
        
0
1
3
4
 

다음은 break문 예제.

>>> for i in range(0, 5) :
        if i == 2 :
            break;
        print( i )
0
1 

continue와 break는 for 문에서만 사용할 수 있는 것이 아니다. while 루프에서도 사용할 수 있다.

>>> i = 0
>>> while (1) :
        i += 1
        if ( i == 3 ) :
            continue
        if ( i == 5 ) :
            break
        print( i )
1
2
4 

이렇게 해서 파이썬의 기본 초식은 익힌 것 같다. 다음에는 코드를 간추려 함수로 만드는 방법을 정리해야겠다.

여러 가지 일을 병행해서이기도 하지만, 현재 진행 방식이 너무 느린 것 같아 고민이다. 함수까지 익히고 나면 뭔가를 만들면서 부딪히는 방식으로 속도를 높여볼 계획이다. 점심시간이 벌써 끝났네. T-T 이제 일해야겠다.

저작자 표시 비영리 변경 금지
Posted by seanlab

파이썬에서 지원하는 루프문은 for와 while 두 가지다. 우선 for 문에 대해 공부해보자.

#5.2. for문

for 문은 코드를 작성할 때 프로그래머가 루프의 반복 횟수를 미리 정해놓았을 때 주로 사용하며, 다음의 꼴로 쓰면 된다.

for 변수 in 순서열 :
    명령1
    명령2
    … 

여기에서 순서열은 리스트, 튜플, 문자열, 바이트 배열 등 어떤 것을 사용해도 괜찮다. for 문은 순서열의 각 원소를 처음부터 차례로 순회하면서 변수에 담아낸다. for문에 가장 많이 사용되는 순서열은 레인지(Range)인데, range() 함수에 최소, 최대값을 매개 변수로 넘기면 바로 최소값부터 최대값 사이의 정수를 원소로 갖는 레인지 순서열을 만들 수 있기 때문이다. 만약 순서열이 range(0, 5) 함수를 이용하여 생성된다면 변수에는 0, 1, 2, 3, 4 가 차례대로 담겨질 것이다. 다음 예제 코드를 보면 이게 무슨 말인지 금방 이해할 수 있을 것이다.

>>> for x in range(0, 5) :
    print( x )
    
0
1
2
3
4 

for를 한번 중첩해서 추억의 별찍기를 해보자.

>>> for i in range(0, 6) :
        for j in range(0, i) :
            print( "*", end = "", )
        print()
*
**
***
****
***** 

#5.3. while 문

while 문도 for문이랑 사용법이 비슷하다.

문은 코드를 작성할 때 프로그래머가 루프의 반복 횟수를 미리 정해놓았을 때 주로 사용하며, 다음의 꼴로 쓰면 된다.

while 수행 조건 :
    명령1
    명령2
    … 

백번 읽어보는 것보다 하나의 예제 프로그램을 만들어 보는 것이 훨씬 기억도 잘되고 이해도 잘된다. 예제 코드를 작성해 보자.

>>> i = 100
>>> while i > 0 :
        print( i, end=' ' )
        i = i -1
    
100 99 98 97 96 95 94 93 92 91 90 89 88 87 86 85 84 83 82 81 80 79 78 77 76 75 74 73 72 71 70 69 68 67 66 65 64 63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 
저작자 표시 비영리 변경 금지
Posted by seanlab

프로그램은 컴퓨터가 할 일의 목록이다. 프로그래머가 컴퓨터에게 할 일의 목록을 내주면, 컴퓨터는 프로그램에 기록되어 있는 일 목록을 보고 그 대로 수행한다. 프로그래밍에서 "흐름(Flow)"라고 하면 컴퓨터에 내려지는 명령의 순서를 가리키는 말이다. 다음과 같이 프로그램을 작성하면 컴퓨터는 1, 2, 3번의 작업을 순서대로 처리할 것이다.

  1. a 에 사용자로부터 입력 받은 수를 저장하라.
  2. b에 3/a의 결과를 저장하라.
  3. a와 b를 출력하라.

이 프로그램은 간단하긴 하지만 문제가 한가지 있다. 만일 a가 가지면 안 되는 수, 예를 들어 0을 사용자로부터 입력 받게 되면 이 프로그램은 오류를 일으키게 된다. 이것을 방지할 수 있는 방법이 있어야 하지 않겠는가? 다음과 같이 말이다.

  1. a에 사용자로부터 입력 받은 수를 저장하라.
  2. a가 0 이라면 프로그램을 종료하고, 그렇지 않으면 다음 명령을 수행하라.
  3. b 에 3/a의 결과를 저장하라.
  4. a와 b를 출력하라.

2번의 "a가 0이라면 ~하고, 그렇지 않다면 ~하라"를 주목하자. 프로그램이 직선으로 흐르다가 이 문장을 통해 방향을 이리 저리 틀게 되었다. 오늘은 이와 같이 프로그램 흐름의 방향을 틀거나 반복할 수 있도록 하는 파이썬의 "흐름 제어(Control Flow)"를 살펴볼 계획이다.

흐름 제어에는 크게 두 가지가 있다. 하나는 프로그램의 흐름을 좌/우로 가르는 분기(branch)문, 또 다른 하나는 프로그램의 일부를 반복 동작하게 하는 반복(loop)문이다. 파이썬이 제공하는 분기문에는 if 문이 있고 반복문에는 for와 while문이 있다. 다음에서 먼저 if 문을 알아보자.

#5.1. if 문

초등학교를 졸업했다면 if가 무슨 뜻인지 알고 있을 것이다 if는 '~라면'이라는 뜻이다. 신라면, 너구리 이런 라면이 아니고 어떤 상황을 가정할 때 사용하는 라면 말이다. 예를 들어 '내가 산 로또가 1등이라면' 같은 것 말이다. 파이썬 코드에서도 if 문을 이용해서 어떤 상황을 가정하고 그 상황에 필요한 명령을 사용할 수 있다.

if 조건 문장 :
    명령1
    명령2
    ….
else :
    명령2
    명령3
    …

if문의 조건은 콜론( : )으로 끝나며, 그 밑 줄부터는 조건이 참(True)인 경우 실행할 명령들이 위치한다. 만약 if에서 명시한 조건이 거짓(False)인 경우에는 else : 이후의 명령들이 실행된다. 여기에서 잘 봐야 할 것이 있다. 바로 if 문에 명시한 조건 하에서 실행되어야 하는 명령들(그리고 그 조건이 맞지 않아 else : 밑에서 실행되어야 하는 명령들)은 모두 들여쓰기로 쓰여있어야 한다는 것이다. 예를 한번 보자.

>>> a = int( input( "수를 입력하세요 : " ) )
수를 입력하세요 : 78
>>> if a != 0 :
    print( "a는 크다!" )
    print( "a는 크다!" )
    print( "a는 크다!" )
else :
    print( "a는 0이다.")
    print( "a는 0이다.")
    
a는 크다!
a는 크다!
a는 크다!

위 코드에서 print("a는 크다!")가 쓰여 있는 세 줄이 바로 a가 0이 아닌 경우 실행되어야 하는 부분이다. 이 세 줄처럼 하나의 묶음으로 처리되는 코드를 가리켜 "블록(Block)"이라 한다. 파이썬은 코드가 연속한 줄에 위치한다고 해서 같은 블록으로 인지하지는 않는다. 같은 깊이로 들여쓰기가 되어 있어야 블록으로 인지한다. 그래서 위 예제에서 if 블록이라 하면 세 줄의 print( "a는 크다!" )를 가리키는 것이고, else 블록이라 하면 print("a는 0이다.")의 두 줄을 가리키는 말이 되는 것이다.

조금 전에 살펴 봤던 예제처럼 조건이 하나만 있을 수도 있지만, 조건이 여러 개 필요한 경우도 다반사다. 이런 경우를 위해 if와 함께 쓸 수 있는 elif 문이 있다. elif는 else if 의 줄임말로, if문과 똑같이 조건을 명시하면 된다. 사용 예는 다음과 같다.

>>> a = int ( input( "숫자를 입력하세요 : ") )
숫자를 입력하세요 : 1
>>> if a == 0 :
    print("a는 0")
elif a == 1 :
    print("a는 1")
elif a == 2 :
    print("a는 2")
else :
    print("a는 0도, 1도, 2도 아님.")

a는 1

파이썬의 분기문은 if 하나 뿐이다. C나 Java, C#에서 제공하는 것처럼 switch 문 등은 제공하지 않는다. 아쉽긴 하지만 이건 어디까지나 파이썬을 만든 귀도형님의 취향이니 존중을 해드려야겠다. 내일은 반복문에 해당하는 for문과 while 문을 공부해야겠다.

저작자 표시 비영리 변경 금지
Posted by seanlab
TAG If, 파이썬

불변/가변 바이트 배열은 바이트와 또 하나의 문자열 자료형이라고 생각하면 이해가 쉽다. 문자열 객체가 갖고 있는 대부분의 메소드를 바이트 배열도 거의 다 갖고 있다. 단, 문자열에서는 문자 코드를 유니코드로 사용하는데 반해, 바이트 배열에서는 문자의 범위가 정수 0~256 코드로 한정된다는 점이 다르다. 원래의 이름은 bytes, byte array지만 내 나름대로 의역해서 불변 바이트 배열, 가변 바이트 배열이라 이름 붙였다(이의 제기 환영합니다. 굽신굽신). 불변/가변 바이트 배열은 불변/가변이라는 속성 말고는 큰 차이가 없기 때문에 어느 한쪽을 알아두면 나머지는 자동으로 알게 되는 것이나 다름 없다. 그래서 오늘은 불변 바이트 배열에 대해 정리를 해보고, 그 다음에 가변 바이트 배열을 다루면서 불변 바이트 배열과 다른 점을 다룰 계획이다.

불변 바이트 배열은 문자열 상수 앞에 b를 붙이거나 문자열의 bytes() 내장함수 또는 문자열의 encode() 메소드를 이용해서 만들 수 있다. 먼저 b를 이용하는 선언은 다음과 같다.

>>> b = b'abc'
>>> b
b'abc'
>>> b[0]
97
>>> b[1]
98
>>> b[2]
99

bytes() 내장 함수는 튜플, 리스트, 문자열 등으로부터 바이트 배열을 만들어 준다.

>>> b = bytes( (97, 98, 99) ) #튜플로부터 바이트 배열 생성
>>> b
b'abc'
>>> b = bytes( 'abc', 'ascii' ) # 문자열로부터 바이트 배열을 생성할 때에는 인코딩을 매개 변수를 같이 넣어줘야 한다.
>>> b
b'123'

문자열에서 바이트 배열을 만들어냈다. 그렇다면 문자열을 바이트 배열로부터 만들어 낼 수 있을까? 있다. decode() 메소드를 이용하면 된다.

>>> b = bytes( 'abc', 'ascii' )
>>> b.decode()
'abc'

앞서 bytes() 내장 함수를 이용해서 문자열 객체를 바이트 배열로 바꿨는데, 문자열의 encode() 메소드를 사용해도 같은 결과를 얻을 수 있다.

>>> s = 'abc'
>>> b = s.encode()
>>> b
b'abc'

불변 바이트 배열은 그야말로 '불변'이다. 일부를 바꾸려고 시도하면 파이썬 인터프리터가 그런 짓 하지 말라고 나를 나무랄 것이다. 한번 해보자.

>>> b = b'123'
>>> b[0] = 100
Traceback (most recent call last):
File "<pyshell#11>", line 1, in <module>
b[0] = 100
TypeError: 'bytes' object does not support item assignment

혹시나 했는데 역시나였다. 이제 가변 바이트 배열을 살펴보자. 가변 바이트 배열은 bytearray() 내장 함수를 이용해서 생성한다. byte() 내장 함수처럼 순서열을 매개 변수로 받아 가변 바이트 배열을 만들어낸다. 한번 사용해 보자.

>>> b = bytearray( b'123' ) #불변 바이트 배열로부터 가변 바이트 배열 만들기
>>> b[0]
49
>>> b[0] = 54 # 첫 번째 원소에 '6' 쓰기
>>> b
bytearray(b'623')

여기까지가 내가 오늘 불변/가변 바이트 배열에 대해 공부한 전부다. 마지막으로 가변 바이트 배열의 메소드를 몇 가지 테스트 하는 것으로 오늘 일기를 마감해야겠다.

>>> b = bytearray( b'123' )
>>> b
bytearray(b'123')
>>> b.append ( 99 ) # 가변 바이트 배열에 덧붙이기.
>>> b
bytearray(b'123c')
>>> b.index( b'23' ) # 일치하는 바이트 배열 찾기
1
>>> b.replace( b'23', b'ab' ) # '23'을 'ab'로 바꾸기
bytearray(b'1abc')
>>> b.remove ( ord('1') ) # '1'을 찾아 제거하기
>>> b
bytearray(b'abc')

저작자 표시 비영리 변경 금지
Posted by seanlab

순서열의 세부 자료형인 튜플과 리스트는 서로 닮은 꼴인 자료형이다. 하지만 서로의 성질은 완전히 반대이다. 튜플은 불변 객체고, 리스트는 가변 객체이기 때문이다. 튜플은 완전히 새로 할당하지 않는 한 객체의 일부를 변경하는 것이 불가능하다. 리스트는 가능한데 말이다.

튜플은 둥근 괄호( '('와 ')' )를 이용해서 선언할 수도 있고, tuple() 내장 함수를 이용할 수도 있다.

예를 들어 보자. 먼저 둥근 괄호를 이용해서 튜플을 만들어 보자.

>>> t = (1, 2, 3)
>>> t
(1, 2, 3)
>>> type(t)
<class 'tuple'>

앞에서도 이야기했지만 튜플은 읽기만 가능하고 쓰기(변경)는 허용되지 않는다. 쓰기를 시도해봤더니 인터프리터가 에러를 내뱉는다.

>>> t[0]
1
>>> t[1]
2
>>> t[1] = 4
Traceback (most recent call last):
File "<pyshell#16>", line 1, in <module>
t[1] = 4
TypeError: 'tuple' object does not support item assignment

자, 이번엔 tuple() 내장 함수를 보자. 이 놈은 쓸모가 많다. 문자열도, 리스트도, 튜플도 다 튜플로 만들어준다.

우선 문자열.

>>> tuple( '123' )
('1', '2', '3')
>>> tuple( '홍길동')
('홍', '길', '동')

잘된다. 이젠 리스트하고 튜플로부터 튜플을 만들어 보자. tuple() 함수를 이용하면 된다.

>>> tuple ( ( 1, 2, 3 ) )
(1, 2, 3)
>>> tuple ( [ 1, 2, 3 ] )
(1, 2, 3)

리스트는 변경이 가능한 순서열이다. 현재 있는 내용에 덧붙일 수도 있고(append), 끼워넣을 수도 있고(insert), 일부를 없앨 수도 있다(remove).

>>> l = [ 1, 2, 3 ]
>>> l.append( 4 )
>>> l
[1, 2, 3, 4]
>>> l.insert(3, 3.5)
>>> l
[1, 2, 3, 3.5, 4]
>>> l.remove(3.5)
>>> l
[1, 2, 3, 4]

리스트에는 정렬(sort), 뒤집기(reverse) 등의 기능도 있다.

>>> l = [ 1, 3, 4, 2 ]
>>> l.sort()
>>> l
[1, 2, 3, 4]
>>> l.reverse()
>>> l
[4, 3, 2, 1]

한편, 리스트는 [와 ] 사이에 원소를 나열해서 생성할 수 있지만, list() 내장 함수를 이용하는 것도 가능하다. list() 함수는 tuple() 함수처럼 다른 순서열로부터 리스트를 만들어 준다.

>>> l = list( (1, 2, 3) )
>>> l
[1, 2, 3]
>>> l = list( [1, 2, 3] )
>>> l
[1, 2, 3]
>>> l = list ( '123' )
>>> l
['1', '2', '3']

이쯤이면 튜플과 리스트에 대해 대강은 이해한 것 같다. 마지막으로 'Reverse'라는 문자열을 뒤집는 테스트 코드를 만들어 보고 오늘은 일기를 끝내야겠다.

>>> l = list( 'Reverse' )
>>> l.reverse()
>>> s = ''.join( l )
>>> s
'esreveR'

저작자 표시 비영리 변경 금지
Posted by seanlab

사전에서 Sequence를 찾아보면 순서, 연속물 이런 뜻으로 나온다. 파이썬의 Sequence는 이런 뜻으로는 설명이 어렵다. 누군가 이것을 이 바닥 용어로 적절하게 의역했다. "순서열"이다. C/C++/C# 또는 Java 경험이 있다면 순서열이 배열과 비슷하고 생각하면 된다. 아니, 비슷하다는 표현이 적절하지 않은 것 같다. 생김새는 비슷하지만 사용하기는 더 쉽고 더 강력한 기능을 제공하기 때문이다.

한편, 파이썬의 순서열 밑에는 여러 세부 자료형들이 존재한다. 순서열은 불변 순서열과 가변 순서열로 구분 되는데, 불변 순서열에는 문자열(String), 튜플(Tuple), 불변 바이트 배열이 있고 가변 순서열에는 리스트, 가변 바이트 배열이 있다. 종류가 6가지나 된다. 하지만 걱정하진 않아도 될 것 같다. Numeric이 그랬던 것처럼 같은 범주 안에 있는 자료형들은 대부분의 속성들을 공유하기 때문이다. 순서열의 기본 속성을 문자열을 통해 알아보자.

문자열을 선언하는 방법은 작은 따옴표(')나 큰 따옴표(")의 짝으로 문자열을 감싸는 것이다. 다음과 같이 말이다.

>>> s = "This is a string."
>>> t = 'This is a string, too.'
>>> s
'This is a string.'
>>> t
'This is a string, too.'
>>> type(t)
<class 'str'>
>>>

파이썬의 문자열은 문자의 집합이기 전에 순서열임을 잊지 말자. 순서열은 두 순서열을 결합하는 + 연산자를 제공한다. 따라서 순서열의 세부 자료형인 문자열도 + 연산자를 이용하여 두 문자열을 결합할 수 있다.

>>> hello = "Hello"
>>> world = ", World"
>>> hello_world = hello + world
>>> hello_world
'Hello, World'

순서열은 + 연산자 뿐만 아니라 쓸모가 많은 여러 연산자를 지원한다. 다음 코드에 그 예가 있다.

>>> s = 'Hello, World.'
>>> s[3:5]         # s의 3번째부터 5번째까지 자르기(Slicing)
'lo'
>>> 'o' in s         # 'o' 가 순서열 s 안에 들어 있는가
True
>>> 'x' in s         # 'x' 가 순서열 s 안에 들어 있는가
False
>>> s * 3        # 순서열 s를 3번 복사해 붙여 넣기
'Hello, World.Hello, World.Hello, World.'
>>> s[8]         # 8번째 문자
'o'
>>> len(s)         # s의 길이
13
>>> min(s)         # s의 최소값
' '
>>> max(s)         # s의 최대값
'r'

파이썬을 사용하다가 다시 C나 Java를 사용하게 되면 답답해질 것 같아 벌써부터 걱정이 든다. 기존에 다뤄봤던 언어에서 제공하던 배열에 비해 훨씬 유연하고 강력하다. 그 중에서도 특히 자르기(slicing)가 아주 백미다. 그런데 자르기라고 하니 용어가 쉽게 와 닿지 않는다. 그냥 슬라이싱이라고 해야겠다.

앞에서는 매개 변수를 한 개, 두 개만 넣었지만 슬라이싱은 원래 아래와 같이 세 개의 매개 변수를 받는다.

[시작인덱스, 종료인덱스, 보폭(step)]

보폭이 왜 필요한지 잘 이해가 안됐는데 실제로 사용해보니까 상당히 쉽다.

>>> s = 'Hello, World.'
>>> s[0:len(s):1] #0번부터 문자열의 끝까지 1개의 요소마다 1개를 골라 슬라이싱
'Hello, World.'
>>> s[0:len(s):2] #0번부터 문자열의 끝까지 2개의 요소마다 1개를 골라 슬라이싱
'Hlo ol.'
>>> s[0:len(s):3] #0번부터 문자열의 끝까지 3개의 요소마다 1개를 골라 슬라이싱
'Hl r.'
>>> s[0:len(s):4] #0번부터 문자열의 끝까지 4개의 요소마다 1개를 골라 슬라이싱
'Hoo.'
>>> s[0:len(s):20] #0번부터 문자열의 끝까지 20개의 요소마다 1개를 골라 슬라이싱

그러고 보니 아까 예로 사용했던 s[3:5]는 s[3:5:1]을 줄인 표현이었다. s[8]은 s[8:8:1]을 줄인 표현이고 말이다. 슬라이싱은 아주 유연하다. 다음과 같이 앞/뒤 매개 변수를 생략해서 사용할 수도 있다.

>>> s = 'Hello, World.'
>>> s[:5]         #s[0:5:1] 과 같음
'Hello'
>>> s[7:]        #s[7:len(s):1] 과 같음
'World.'

재미있다. 이제 겨우 자료형을 공부해 나가고 있는 중인데 문법이 손에 착 감기는 느낌이다. 다음에는 순서열의 세부 자료형들을 차근차근 살펴봐야겠다.

저작자 표시 비영리 변경 금지
Posted by seanlab

변수가 데이터를 담는 그릇이라면, 자료형은 이 그릇의 모양을 결정하는 거푸집과 같다고 할 수 있다. 오늘은 수치형 변수를 만드는 파이썬의 거푸집, Numeric 형에 대해 살펴보려 한다. 파이썬은 수치 자료형으로 정수(int), 부동소수형(float), 그리고 복소수형(complex)를 지원한다.

IDLE을 켜고 쉘에 아무 수나 입력해보자.

>>> 7
7

7을 입력했더니 7을 출력한다. 그럼 7을 변수에 담아보자. 그런데 변수는 어떻게 만들고, 어떻게 담을까?

먼저 변수에는 변수를 구분할 수 있는 이름이 있어야 한다. 파이썬 인터프리터는 각 변수(객체)를 identity로 구분하지만, 이것은 파이썬 인터프리터가 정하는 것이기 때문에 프로그래머는 이것으로 변수를 코드 내에서 구분할 수 있는 도리가 없다. identity는 프로그램을 실행할 때마다 달라질 것이기 때문이다. 변수의 이름은 프로그래머가 붙여야 한다. 알파벳, 밑줄(_), 그리고 숫자의 조합이면 아무것이나 괜찮다. 단, 숫자가 변수 이름의 첫 글자여서는 안 된다. 가령 abc123_과 _abc123은 정상적인 변수 이름이지만, 123abc_는 변수 이름으로는 사용할 수 없다.

다시 우리가 하던 얘기로 돌아와서, 7을 변수에 담아보자. 변수의 이름은 a로 하자.

>>> a = 7
>>> a
7

7을 a에 담았고, a를 다시 쉘에 입력했더니 IDLE이 a에 담겨져 있던 7을 출력했다. 파이썬에서는 변수를 선언할 때 프로그래머가 직접 자료형을 명시하지 않는다. 대신 파이썬 인터프리터가 값을 보고 필요한 자료형을 스스로 결정한다. 7은 양의 정수이기 때문에 파이썬 인터프리터는 마땅히 a를 int형으로 간주해야 한다. 정말 그렇게 할까? 내장 함수 type()을 이용해서 확인해보자.

>>> type(a)
<class 'int'>

A는 정수형이 맞는단다. 그런데 'int' 앞의 class는 무슨 의미일까? 아까 변수를 그릇, 자료형은 그릇을 만들 때 사용하는 거푸집이라고 했는데, 객체는 변수이고 클래스는 자료형이다. 이건 비유가 아니고 사실이 그렇다. 따라서 class 'int'라는 메시지는 말그대로 a의 자료형이 int 클래스라는 이야기다. 아, 그러고보니 파이썬의 객체를 처음 공부할 때 각 객체는 자료형, 값, 그리고 identity로 이루어진다고 했던 게 생각난다. 그럼 우리의 a도 identity가 있을 터, 한번 확인해보자. Identity를 알아내는 것은 id() 함수를 이용하면 된다.

>>> id(a)
505300216

희한한 숫자 값이 나왔다. 이 값에는 특별한 의미가 없다. 단지 파이썬 인터프리터가 a에게 붙여준 또 다른 이름일 뿐이다.

이번에는 몇 가지 계산을 해보자. 간단한 사칙 연산을 해보는 게 좋겠다. 뺄셈을 해보자. (혹시나 싶어서 한글로 변수 이름을 만들어 봤는데, 정상적으로 동작한다. 파이썬 매뉴얼을 다시 찾아보니 파이썬 3.0부터는 변수 이름에 알파벳 뿐 아니라 유니코드까지 사용할 수 있도록 한다고 되어 있다.)

>>> 형의나이=11
>>> 동생나이=8
>>> 형과_동생의_나이차 = 형의나이 – 동생나이
>>> 형과_동생의_나이차
3

뺄셈이 잘 동작한다. 이번엔 사칙연산을 한번에 해보자.

>>> a=3
>>> b=7
>>> c=(111-a)*b/11+2
>>> c
70.727272727272734

사칙 연산도 무리 없이 잘되는 것으로 보인다. 어, 그런데 c가 소수다. 혹시? 타입을 확인해보자.

>>> type(c)
<class 'float'>

c를 파이썬 인터프리터가 알아서 부동 소수형으로 만들었다. 정수끼리의 덧셈, 뺄셈, 곱셈은 계산 결과를 소수로 만들 일이 없다. c가 저 지경이 된 것은 나눗셈의 책임이다. 깨끗하게 나눗셈의 몫만 구할 수는 없을까? 또는 나머지만 구할 수는 없을까? 나눗셈의 몫을 구하는 것은 // 연산자, 나머지를 구하는 것은 % 연산자를 사용하면 된다. 다음은 그 예다.

>>> d = 3/7
>>> d
0.42857142857142855
>>> e = 3 // 7
>>> e
0
>>> f = 3 % 7
>>> f
3

파이썬은 친절하게 divmod()라는 함수를 지원해서 몫과 나머지를 동시에 구할 수 있도록 하고 있다. divmod() 함수는 몫과 나머지를 차례대로 튜플(tuple)에 넣는다. 튜플은 불변 객체로, 객체의 목록을 다룬다. 이 함수가 반환하는 튜플은 몫과 나머지를 원소로 가진다. 튜플은 나중에 차차 볼 기회가 있을 것 같으니 지금은 대충 이렇게 넘어가는 게 낫겠다. 지금은 Numerics에만 집중하자. 그래도 divmod() 함수는 한번 써보자.

>>> g = divmod(3, 7)
>>> type(g)
<class 'tuple'>
>>> g
(0, 3)
>>> g[0]
0
>>> g[1]
3

부동 소수형은 거의 대부분의 연산을 정수형과 공유한다. 그래서 특별히 노트해둘 것도 없다.

그런데 갑자기 궁금한게 생겼다. 부동소수형 3.00을 정수로 바꿀 수 있을까? 거푸집(자료형)의 모양에 따라 변수가 갖는 값이 한정되는 정적 형 언어(Static Typed Language)와는 달리 파이썬은 변수가 갖고 있는 값에 따라 거푸집의 모양을 결정짓는다. 그렇다면 아무리 3.00은 아무리 그릇을 바꿔 옮겨 담아도 영원히 부동 소수형으로 남을 수밖에 없다. 이런 상황을 위해 파이썬은 int() 함수를 제공한다. int() 함수 안에 매개 변수를 입력하면 그 결과로 정수를 얻을 수 있다. 이 때 주의할 점은 부동 소수형의 소수점 이하는 버려진다는 사실이다. 파이썬 쉘에서 테스트를 해보자.

>>> a = 3.00
>>> type(a)
<class 'float'>
>>> a = int(3.00)
>>> a
3
>>> type(a)
<class 'int'>
>>> a = int(3.7)
>>> a
3

복소수형은 별로 쓸 일이 없을 것 같지만, 지금 정리를 안 해두면 어쩌면 영원히 다시 만날 일이 없을 것 같다. 복소수는 실수와 허수로 이루어지는 수이며, 실수처럼 사칙 연산에 대해 닫혀 있다("닫혀 있다"라는 표현, 정말 오랜만에 써본다.). 따라서 파이썬의 정수와 부동 소수형에 대해 사용했던 사칙 연산들을 복소수형에 대해서도 그대로 사용할 수 있다. 복소수는 complex() 함수를 이용하여 만들며, 사용 예는 다음과 같다.

>>> a = complex(1, 3)
>>> a
(1+3j)
>>> type(a)
<class 'complex'>
>>> a.real
1.0
>>> a.imag
3.0

복소수도 Numeric의 하위 자료형임을 기억해야 한다. 실수부와 허수부로 이루어져 있다고 해서 실수부만 변경하거나 허수부만 변경하는 짓은 허용되지 않는다.

>>> a.real = 3
Traceback (most recent call last):
File "<pyshell#27>", line 1, in <module>
a.real = 3
AttributeError: readonly attribute

마지막 메시지를 보니 Attribute Error : readonly attribute란다. 파이썬에서 객체의 멤버 필드를 attribute라고 부르는 모양이다. 이렇게 해서 오늘은 정수형, 부동 소수형 그리고 잘 안 쓸 것 같은 복소수형까지 정리했다. 이제 수치 자료형만 공부했을 뿐인데 파이썬하고 벌써 많이 친해진 느낌이다(아마 이런 걸 두고 설레발이라고 하지?).

저작자 표시 비영리 변경 금지
Posted by seanlab
사전에 공부를 해 두고 기록을 하는 것이 아니라 공부를 하면서 기록을 하고 있다.
거시적인 공부 계획 같은 것은 없다. 한 걸음 앞으로 나아가면 조금 더 시야가 넓어지리라는 생각으로 공부를 하고 있다. 지금 내 눈 앞에 보이는 다음 계획은 다음과 같다.

# 파이썬 인터프리터 사용법.
    - 인터프리터 사용하기 
    - 파이썬 스크립트 실행하기
   
# 파이썬의 기본 타입 시스템

# 파이썬의 기본 흐름 제어문

# 파이썬에서 코드 블록을 간추리는 방법(함수)

# 파이썬의 클래스

점심 시간 끝났다. 다시 일해야지.
저작자 표시 비영리 변경 금지
Posted by seanlab

아직 파이썬을 안다고 할 수 없다. 아직 문법 공부를 시작한 것도 아니니 당연하다고 할 수 있지만, 내가 지금 이야기하는 것은 파이썬을 완벽하게 정의할 수 없다는 뜻이다.
다 좋은데, "동적 객체 지향 언어"가 무슨 말인지 모르겠다. 나는 10년 가까이 C++, C#, Java와 같은 객체 지향 언어로 프로그래밍 해왔다. 그런데 "동적"이라는 게 무슨 뜻인가?
 
이럴 때는 요즘 애들 말마따나 "닥치고 구글질"이다. "Dynamic Programming Language"라는 키워드로 검색을 하니 위키피디아의 링크가 제일 먼저 올라온다. 한페이지 분량으로 동적 프로그래밍 언어에 대해 설명하고 있는데, 이 문서가 정의하는 동적 프로그래밍 언어는 다음과 같다. 
 

Dynamic programming language is a term used broadly in computer science to describe a class of high-level programming languages that execute at runtime many common behaviors that other languages might perform during compilation, if at all. These behaviors could include extension of the program, by adding new code, by extending objects and definitions, or by modifying the type system, all during program execution. These behaviors can be emulated in nearly any language of sufficient complexity, but dynamic languages provide direct tools to make use of them. Most dynamic languages are dynamically typed, but not all.


안되는 영어를 동원해서 번역을 해봤다 
 

동적 프로그래밍 언어는 여타 프로그래밍 언어들이 컴파일 과정 중에 수행하는 다양한 일반적인 동작들을 런타임에 실행하는 고수준 프로그래밍 언어의 한 부류를 묘사하기 위해 컴퓨터 과학 분야에서 폭넓게 사용되는 용어다. 여기에서 "동작"이란 프로그램의 실행 중 새로운 코드의 추가, 객체와 정의의 확장, 또는 타입 시스템의 변경 등을 통한 프로그램의 확장을 아우른다. 거의 어떤 언어에서나 상당한 복잡성을 통해 이러한 동작들을 모방할 수 있지만, 동적 언어는 이들을 사용할 수 있는 직접적인 도구를 제공한다. 대부분의 동적 언어들이 동적 형 언어이긴 하지만, 모두 그런 것은 아니다.


한글로 써놔도 무슨 말인지 알아먹기가 힘들다. 요약하자면 동적 프로그래밍 언어는 프로그램 실행 도중에 코드를 추가하거나 수정할 수 있다는 말인데, 이게 장점일까? 잘 모르겠다. C++, C#, Java 같은 언어에서는 형을 엄격하게 관리하는 것이 장점이었다.
 
예를 들어 C++에서는 정의되어 있지 않은 멤버 함수를 객체로부터 호출하려 하면 컴파일 에러를 낸다. 컴파일러가 알려주는 에러를 보고 프로그래머는 자신이 멤버 함수의 구현을 빠뜨렸음을 깨닫고 소스 코드를 수정할 수 있다. 이렇게 함으로써 프로그램이 엉뚱한 짓을 수행하는 것을 막을 수 있다는 게 정적 언어(Static Language)의 장점이 아니던가.

동적 언어의 정의는 알겠다. 파이썬이 동적 객체 지향 언어라는 것은 프로그램을 실행하고 이는 중에 객체를 확장하거나 변경할 수 있고, 심지어 추가할 수도 있다는 뜻이다.

하지만 이것이 장점인지는 파이썬을 직접 만져봐야 알 수 있을 것 같다.

저작자 표시 비영리 변경 금지
Posted by seanlab