07-2 ~ 07-3 (p.293 ~ p.320)

 

이 책의 마지막 부분이다. 그리고 여태 공부했던 분량 중에 가장 많았다...

이 장의 초반에서 '이번 장은 초보자가 이해하기에는 약간 어려운 부분이다.'라고 했었는데 정말로 앞의 내용들보다는 꽤 어려웠다ㅠㅠ 그래도 차분히 공부해 보니 아예 이해를 못 할 정도는 아니라서 오래 걸리긴 했어도 어느 정도 이해는 한 것 같다..!

 

후우...

 


 

07-2 정규 표현식 시작하기

 

정규 표현식의 기초, 메타 문자

정규 표현식에서 사용하는 메타 문자(meta characters)에는 다음과 같은 것이 있다.

. ^ $ * + ? {} [] \ | ()

문자 클래스 [ ]

[ ] 사이의 문자들과 매치

[abc] # a, b, c 중 한 개의 문자와 매치

 

ex) 문자열 : before - 정규식과 일치하는 문자인 "b"가 있으므로 매치
     문자열 : dude - 정규식과 일치하는 문자인 a, b, c 중 어느 하나도 포함하지 않아서 매치X

[ ] 안의 두 문자 사이에 하이픈(-)을 사용하면 두 문자 사이의 범위(From - To)를 의미 ex) [a-c] = [abc]

 

* 자주 사용하는 문자 클래스

정규 표현식 설명
\d 숫자와 매치, [0-9]와 동일한 표현식이다.
\D 숫자가 아닌 것과 매치, [^0-9]와 동일한 표현식이다.
\s whitespace 문자(space나 tab처럼 공백을 표현하는 문자)와 매치, [ \t\n\r\f\v]와 동일한 표현식이다.
맨 앞의 빈 칸은 공백 문자(space)를 의미한다.
\S whitespace 문자가 아닌 것과 매치, [^ \t\n\r\f\v]와 동일한 표현식이다.
\w 문자+숫자와 매치, [a-zA-Z0-9_]와 동일한 표현식이다.
\W 문자+숫자가 아닌 문자와 매치, [^a-zA-Z0-9_]와 동일한 표현식이다.

 

Dot(.)

줄바꿈 문자인 \n을 제외한 모든 문자와 매치됨

a.b # a와 b사이에 줄바꿈 문자를 제외한 어떤 문자가 들어가도 모두 매치
# = "a + 모든 문자 + b"

ex) 문자열 : aab - 가운데 문자 "a"가 모든 문자를 의미하는 .과 일치하므로 정규식과 매치

     문자열 : a0b - 가운데 문자 "0"이 모든 문자를 의미하는 .과 일치하므로 정규식과 매치

     문자열 : abc - "a" 문자와 "b" 문자 사이에 어떤 문자라도 하나는 있어야 하는 이 정규식과 일치하지 않으므로 매치X

 

반복(*)

ca*t # * 문자 바로 앞에 있는 a가 0번 이상 반복되면 매치

ex) 문자열 : ct - "a"가 0번 반복되어 매치

     문자열 : cat - "a"가 0번 이상 반복되어 매치(1번 반복)

     문자열 : caaat - "a"가 0번 이상 반복되어 매치(3번 반복)

 

반복(+)

ca+t # + 문자 바로 앞에 있는 a가 1번 이상 반복되면 매치

ex) 문자열 : ct - "a"가 0번 반복되어 매치되지 않음

     문자열 : cat - "a"가 1번 이상 반복되어 매치(1번 반복)

     문자열 : caaat - "a"가 1번 이상 반복되어 매치(3번 반복)

 

반복({m,n}, ?)

# {m}
ca{2}t # a가 2번 반복되면 매치 = "c + a(반드시 2번 반복) + t"

# {m,n}
ca{2,5}t # a가 2~5번 반복되면 매치 = "c + a(2~5번 반복) + t"

# ?
ab?c # b가 0~1번 사용되면 매치 = "a + b(있어도 되고 없어도 된다) + c"

 

파이썬에서 정규 표현식을 지원하는  re 모듈

re 모듈은 파이썬을 설치할 때 자동으로 설치되는 기본 라이브러리이다.

# re 모듈 사용법
import re # 정규 표현식 컴파일
p = re.compile('ab*') # p는 컴파일된 패턴 객체

 

정규식을 사용한 문자열 검색

컴파일된 패턴 객체는 다음과 같은 4가지 메서드를 제공한다.

메서드 목적
match() 문자열의 처음부터 정규식과 매치되는지 조사한다.
search() 문자열 전체를 검색하여 정규식과 매치되는지 조사한다.
findall() 정규식과 매치되는 모든 문자열을 리스트로 돌려준다.
finditer() 정규식과 매치되느 모든 문자열을 반복 가능한 객체로 돌려준다.

match, search는 정규식과 매치될 때는 match 객체를 돌려주고, 매치되지 않을 때는 None을 돌려준다.

import re
p = re.compile('[a-z]+')

# match
m = p.match("python")
print(m)
>>> <re.match object; span=(0, 6), match='python'> # match 객체를 돌려줌
m = p.match("3 python")
print(m)
>>> None

# search
m = p.search("python")
print(m)
>>> <re.Match object; span=(0, 6), match='python'>
m = p.search("3 python")
print(m)
>>> <re.Match object; span=(2, 8), match='python'>

# findall
result = p.findall("life is too short")
print(result)
>>> ['life', 'is', 'too', 'short']

# finditer
result = p.finditer("life is too short")
print(result)
>>> <callable_iterator object at 0x01F5E390>
for r in result: print(r)
...
<re.Match object; span=(0, 4), match='life'>
<re.Match object; span=(5, 7), match='is'>
<re.Match object; span=(8, 11), match='too'>
<re.Match object; span=(12, 17), match='short'>

 

match 객체의 메서드

메서드 목적
group() 매치된 문자열을 돌려준다.
start() 매치된 문자열의 시작 위치를 돌려준다.
end() 매치된 문자열의 끝 위치를 돌려준다.
span() 매치된 문자열의 (시작, 끝)에 해당하는 튜플을 돌려준다.
import re
p = re.compile('[a-z]+')
m = p.match("python")
m.group()
>>> 'python'
m.start()
>>> 0
m.end()
>>> 6
m.span()
>>> (0, 6)

m = p.search("3 python")
m.group()
>>> 'python'
m.start()
>>> 2
m.end()
>>> 8
m.span()
>>> (2, 8)

* 모듈 단위로 수행하기

p = re.compile('[a-z]+')
m = p.match("python")

# =
m = re.match('[a-z]+', "python")

 

컴파일 옵션

옵션 이름 약어 설명
DOTALL S dot 문자(.)가 줄바꿈 문자를 포함하여 모든 문자와 매치한다.
IGNORECASE I 대, 소문자에 관계 없이 매치한다.
MULTILINE M 여러 줄과 매치한다. (^, $ 메타 문자의 사용과 관계가 있는 옵션이다.)
VERBOSE X verbose 모드를 사용한다.
(정규식을 보기 편하게 만들 수도 있고 주석 등을 사용할 수도 있다.)
# DOTALL, S
import re
p = re.compile('a.b', re.DOTALL)
m = p.match('a\nb')
print(m)
>>> <re.Match object; span=(0, 3), match='a\nb'>

# IGNORECASE, I
p = re.compile('[a-z]', re.I)
p.match('python')
>>> <re.Match object; span=(0, 1), match='p'>
p.match('Python')
>>> <re.Match object; span=(0, 1), match='p'>
p.match('PYTHON')
>>> <re.Match object; span=(0, 1), match='p'>

# MULTILINE, M
import re
p = re.compile("^python\s\w+", re.MULTILINE)

data = """python one
life is too short
python two
you need python
python three"""

print(p.findall(data))
>>> ['python one', 'python two', 'python three'] # ^, $ 메타 문자를 각 줄마다 적용

# VERBOSE, X
charref = re.compile(r"""
 &[#]                     # Start of a numeric entity reference
 (
    0[0-7]+               # Octal form
  | [0-9]+                # Decimal form
  | x[0-9a-fA-F]+         # Hexadecimal form
 )
 ;                        # Trailing semicolon
 """, re.VERBOSE)

 

백슬래시 문제

정규 표현식을 파이썬에서 사용할 때 혼란을 주는 요소가 한 가지 있는데, 바로 백슬래시(\)이다.

예를 들어 어떤 파일 안에 있는 "\section"문자열을 찾기 위한 정규식을 만든다고 가정해 보자.

이 정규식은 \s 문자가 whitespace로 해석되어 의도한 대로 매치가 이루어지지 않는다.

이 표현은 다음과 동일한 의미이다.

[ \t\n\r\f\v]ection # \s 문자가 이스케이프 코드 \t, \n, \r, \f, \v로 해석됨

의도한 대로 매치하고 싶다면 다음과 같이 변경해야 한다.

\\section

즉 위 정규식에서 사용한 \ 문자가 문자열 자체임을 알려주기 위해 백슬래시 2개를 사용하여 이스케이프 처리를 해야 한다.

따라서 위 정규식을 컴파일하려면 다음과 같이 작성해야 한다.

p = re.compile('\\section')

그런데 여기에서 또 하나의 문제가 발견된다. 위처럼 정규식을 만들어서 컴파일하면 실제 파이썬 정규식 엔진에는 파이썬 문자열 리터럴 규칙에 따라 \\\로 변경되어 \section이 전달된다.

결국 정규식 엔진에 \\ 문자를 전달하려면 파이썬은 \\\\처럼 백슬래시를 4개나 사용해야 한다.

하지만 이는 너무 복잡해서 파이썬 정규식에는 Raw String 규칙이 생겨나게 되었다. 즉 컴파일해야 하는 정규식이 Raw String임을 알려 줄 수 있도록 파이썬 문법을 만든 것이다. 그 방법은 다음과 같다.

p = re.compile(r'\\section')

 

07-3 강력한 정규 표현식의 세계로

 

메타 문자

아직 살펴보지 않은 메타 문자에 대해서 모두 살펴보자. 여기에서 다룰 메타 문자는 앞에서 살펴본 메타 문자와 성격이 조금 다르다. 앞에서 살펴본 +, *, [ ], { } 등의 메타문자는 매치가 진행될 때 현재 매치되고 있는 문자열의 위치가 변경된다(보통 소비된다고 표현한다.) 하지만 이와 달리 문자열을 소비시키지 않는 메타 문자도 있다. 이번에는 이런 문자열 소비가 없는(zero-width assertions) 메타 문자에 대해 살펴보자.

 

|

| 메타 문자는 or과 동일한 의미로 사용된다. A|B라는 정규식이 있다면 A 또는 B라는 의미가 된다.

p = re.compile('Crow|Servo')
m = p.match('CrowHello')
print(m)
>>> <re.Match object; span=(0, 4), match='Crow'>

 

^

^ 메타 문자는 문자열의 맨 처음과 일치함을 의미한다. 앞에서 살펴본 컴파일 옵션 re.MULTLINE을 사용할 경우에는 여러 줄의 문자열일 때 각 줄의 처음과 일치하게 된다.

print(re.search('^Life', 'Life is too short'))
>>> <re.Match object; span=(0, 4), match='Life'>
print(re.search('^Life', 'My Life'))
>>> None

 

$

$ 메타 문자는 ^ 메타 문자와 반대의 경우이다. 즉 $는 문자열의 끝과 매치함을 의미한다.

print(re.search('short$', 'Life is too short'))
>>> <re.Match object; span=(12, 17), match='short'>
print(re.search('short$', 'Life is too short, you need python'))
>>> None

* ^ 또는 $ 문자를 메타 문자가 아닌 문자 그 자체로 매치하고 싶은 경우에는 \^, \$ 로 사용하면 된다.

 

\A

\A는 문자열의 처음과 매치됨을 의미한다. ^ 메타 문자와 동일한 의미이지만 re.MULTILINE 옵션을 사용할 경우에는 다르게 해석된다. re.MULTILINE 옵션을 사용할 경우 ^은 각 줄의 문자열의 처음과 매치되지만 \A는 줄과 상관없이 전체 문자열의 처음하고만 매치된다.

 

\Z

\Z는 문자열의 끝과 매치됨을 의미한다. 이것 역시 \A와 동일하게 re.MULTILINE 옵션을 사용할 경우 $ 메타문자와는 달리 전체 문자열의 끝과 매치된다.

 

\b

\b는 단어 구분자(Word boundary)이다. 보통 단어는 whitespace에 의해 구분된다.

p = re.compile(r'\bclass\b')
print(p.search('no class at all'))
>>> <re.Match object; span=(3, 8), match='class'>
# '\bclass\b' 정규식은 앞뒤가 whitespace로 구분된 class라는 단어와 매치됨을 의미한다.
# 따라서 no class all의 class라는 단어와 매치됨을 확인할 수 있다.

print(p.search('one subclass is'))
>>> None # class 앞에 sub 문자열이 더해져 있으므로 매치되지 않는다.

* \b 는 파이썬 리터럴 규칙에 의하면 백스페이스를 의미하므로 메타 문자로 사용할 때에는 백스페이스가 아닌 단어 구분자임을 알려 주기 위해 r'\bclass\b'처럼 Raw string임을 알려주는 기호 r을 반드시 붙여 주어야 한다.

 

\B

\B 메타 문자는 \b 메타 문자와 반대의 경우이다. 즉 whitespace로 구분된 단어가 아닌 경우에만 매치된다.

p = re.compile(r'\Bclass\B')
print(p.search('no class at all'))
>>> None
print(p.search('the declassified algorithm'))
>>> <re.Match object; span=(6, 11), match='class'>
print(p.search('one subclass is'))
>>> None

class 단어의 앞뒤에 whitespace가 하나라도 있는 경우에는 매치가 안되는 것을 확인할 수 있다.

 

그루핑

ABC 문자열이 계속해서 반복되는지 조사하는 정규식을 작성하고 싶다고 하자. 어떻게 해야 할까? 지금까지 공부한 내용으로는 위 정규식을 작성할 수 없다. 이럴 때 필요한 것이 바로 그루핑(Grouping)이다.

(ABC)+

그룹을 만들어 주는 메타문자는 ( )이다.

p = re.compile('(ABC)+')
m = p.search('ABCABCABC OK?')
print(m)
>>> <re.Match object; span=(0, 9), match='ABCABCABC'>
print(m.group(0))
>>> ABCABCABC
p = re.compile(r"(\w+)\s+\d[-]\d+[-]\d+")
m = p.search("park 010-1234-1234")
print(m.group(1))
>>> park
group(인덱스) 설명
group(0) 매치된 전체 문자열
group(1) 첫 번째 그룹에 해당하는 문자열
group(2) 두 번째 그룹에 해당하는 문자열
group(n) n 번째 그룹에 해당하는 문자열
p = re.compile(r"(\w+)\s+((\d+)[-]\d+[-]\d+)")
m = p.search("park 010-1234-1234")
print(m.group(3))
>>> 010
# 그루핑된 문자열 재참조하기(Backreferences) (재참조 메타문자 = \1)
p = re.compile(r'(\b\w+)\s+\1') # = '(그룹) + " " + 그룹과 동일한 단어'
p.search('Paris in the the spring').group() # 2개의 동일한 단어 연속적으로 사용해야만 함
>>> 'the the'

 

그루핑된 문자열에 이름 붙이기

(\w+) -> (?P<name>\w+)
# (?P<그룹 이름>...)
p = re.compile(r"(?P<name>\w+)\s+((\d+)[-]\d+[-]\d+)")
m = p.search("park 010-1234-1234")
print(m.group("name"))
>>> park

 

전방 탐색

정규식 종류 설명
(?=...) 긍정형 전방 탐색 ...에 해당하는 정규식과 매치되어야 하며 조건이 통과되어도 문자열이 소비되지 않는다.
(?!...) 부정형 전방 탐색 ...에 해당하는 정규식과 매치되지 않아야 하며 조건이 통과 되어도 문자열이 소비되지 않는다.
# 긍정형 전방 탐색
p = re.comile(".+(?=:)") # 긍정형 전방 탐색 기법 적용(?=:)
m = p.search("http://google.com")
print(m.group())
>>> http # :에 해당하는 문자열이 정규식 엔진에 의해 소비되지 않아서 결과에서는 :이 제거

# 부정형 전방 탐색
.*[.](?!bat$).*$ # 확장자가 bat가 아닌 경우에만 통과

 

문자열 바꾸기

sub 메서드를 사용하면 정규식과 매치되는 부분을 다른 문자로 쉽게 바꿀 수 있다.

p = re.compile('(blue|white|red)')
p.sub('colour', 'blue socks and red shoes')
>>> 'colour socks and colour shoes'

p.sub('colour', 'blue socks and red shoes', count=1) # 바꾸기 횟수 제어 : count
>>> 'colour socks and red shoes'

# subn
p = re.compile('(blue|white|red)')
p.subn( 'colour', 'blue socks and red shoes')
>>> ('colour socks and colour shoes', 2) # 튜플로 반환(변경된 문자열, 바꾸기가 발생한 횟수)

sub 메서드의 첫 번째 매개변수는 '바꿀 문자열(replacement)'이 되고, 두 번째 매개변수는 '대상 문자열'이 된다.

위 예에서 볼 수 있듯이 blue 또는 white 또는 red라는 문자열이 colour라는 문자열로 바뀌는 것을 확인할 수 있다.

 

Greedy vs Non-Greedy

s = '<html><head><title>Title<\title>'
len(s)
>>> 32
print(re.match('<.*>', s).span())
>>> (0, 32)
print(re.match('<.*>', s).group())
>>> <html><head><title>Title<\title>

위 코드에서 * 메타 문자는 매우 탐욕스러워서 매치할 수 있는 최대한의 문자열인 <html><head><title>Title</title> 문자열을 모두 소비해 버렸다. 어떻게 하면 이 탐욕스러움을 제한하고 <html> 문자열 까지만 소비하도록 막을 수 있을까?

print(re.match('<.*?>', s).group())
>>> <html>

*다음과 같이 non-greedy 문자인 ?를 사용하면 *의 탐욕을 제한할 수 있다.

non-greedy 문자인 ?는 *?, +?, ??, {m,n}?와 같이 사용할 수 있다. 가능한 한 가장 최소한의 반복을 수행하도록 도와주는 역할을 한다.

06-4 ~ 07-1 (p.278 ~ p.292)

 

어제부터 시작한 6장을 모두 끝내고 7장의 '정규표현식'을 배우기 시작했는데, 필자의 말로는 정규표현식은 현직자들도 잘 모를 수 있는 고급 주제라서 가벼운 마음으로 읽어달라고 했다. 하지만 책에도 소개되어 있고 이해하면 매우 유용하게 사용할 수 있다고 하니 어렵더라도 최대한 이해하려고 노력해봐야겠다!

 

유용한데 안배우는건 못참지 ㅋㅋ

 


 

06-4 간단한 메모장 만들기

 

문제 : 원하는 메모를 파일에 저장하고 추가 및 조회가 가능한 메모장을 만들어라.

 

필요한 기능은? : 메모 추가하기, 메모 조회하기
입력받는 값은? : 메모 내용, 프로그램 실행 옵션

출력하는 값은? : memo.txt

1. 우선 다음과 같이 입력으로 받은 옵션과 메모를 출력하는 코드를 작성한다.

# C:/doit/memo.py
import sys

option = sys.argv[1] # sys.argv : 프로그램을 실행할 때 입력된 값을 읽어 들임
                     # sys.argv[0]은 프로그램 이름인 memo.py이므로 [1]부터 시작
memo = sys.argv[2]

print(option)
print(memo)

 

2. memo.py를 작성했다면 다음 명령을 수행해 본다.

# cmd
C:\doit>python memo.py -a "Life is too short"
-a
Life is too short

 

3. 이제 입력으로 받은 메모를 파일에 쓰도록 코드를 변경해 본다.

# C:/doit/memo.py
import sys

option = sys.argv[1]

if option == '-a': # 옵션이 -a인 경우에만 memo 값을 읽고 memo.txt 파일에 그 값을 씀
    memo = sys.argv[2]
    f = open('memo.txt', 'a')
    f.write(memo)
    f.write('\n')
    f.close()

 

4. 이제 다음과 같은 명령을 수행해 본다.

# cmd
C:\doit>python memo.py -a "Life is too short"
C:\doit>python memo.py -a "You need python"

# 결과
C:\doit>type memo.txt
Life is too short
You need python

 

5. 이번에는 작성한 메모를 출력하는 부분을 만들 차례이다.

# C:/doit/memo.py
import sys

option = sys.argv[1]

if option == '-a':
    memo = sys.argv[2]
    f = open('memo.txt', 'a')
    f.write(memo)
    f.write('\n')
    f.close()
elif option == '-v': # -v : 메모 출력 옵션
    f = open('memo.txt')
    memo = f.read()
    f.close()
    print(memo)
# 결과
C:\doit>python memo.py -v
Life is too short
You need python

 

06-5 탭을 4개의 공백으로 바꾸기

 

문제 : 문서 파일을 읽어서 그 문서 파일 안에 있는 탭(tab)을 공백(space) 4개로 바꾸어 주는 스크립트를 작성해라.

 

필요한 기능은? : 문서 파일 읽어 들이기, 문자열 변경하기
입력받는 값은? : 탭을 포함한 문서 파일

출력하는 값은? : 탭이 공백으로 수정된 문서 파일
# 다음과 같은 형식으로 프로그램 수행
python tabto4.py src dst
# src : 탭을 포함하고 있는 원본 파일 이름
# dst : 파일 안의 탭을 공백 4개로 변환한 결과를 저장할 파일 이름

1. 우선 다음과 같이 tabto4.py 파일을 작성한다.

# C:/doit/tabto4.py
# sys.argv를 사용하여 입력값을 확인하도록 한 코드
import sys

src = sys.argv[1]
dst = sys.argv[2]

print(src)
print(dst)

 

2. 다음과 같이 수행했을 입력값이 정상적으로 출력되는지 확인해 본다.

# cmd
C:\doit>python tabto4.py a.txt b.txt
a.txt
b.txt

 

3. 테스트를 위한 원본 파일(탭을 포함하는 파일)인 a.txt를 다음과 같이 작성한다. 각 단어는 탭(\t) 문자로 분리되도록 입력해야 한다.

Life    is         too         short
You    need    python

 

4. 테스트를 위한 원본 파일(탭을 포함하는 파일)인 a.txt를 다음과 같이 작성한다. 각 단어는 탭(\t) 문자로 분리되도록 입력해야 한다.

# C:/doit/tabto4.py
import sys

src = sys.argv[1]
dst = sys.argv[2]

f = open(src)
tab_content = f.read()
f.close()

space_content = tab_content.replace("\t", " "*4) # replace 함수를 사용해서 탭을 4개의 공백으로 변경
print(space_content)

 

5. tabto4.py를 위와 같이 변경한 후 다음과 같은 명령을 수행해 본다.

# cmd
C:\doit>python tabto4.py a.txt b.txt
Life  is  too  short
You   need   python
# 탭 문자가 공백 4개로 변경되어 출력

 

6. 이제 변경된 내용을 b.txt. 파일에 저장할 수 있도록 다음과 같이 프로그램을 변경해 본다.

# C:/doit/tabto4.py
import sys

src = sys.argv[1]
dst = sys.argv[2]

f = open(src)
tab_content = f.read()
f.close()
space_content = tab_content.replace("\t", " "*4)

f = open(dst, 'w') # 탭이 공백으로 변경된 space_content를 출력 파일인 dst에 쓰도록 함
f.write(space_content)
f.close()

 

7. 프로그램을 실행하기 위해 다음 명령을 수행한다.

# cmd
C:\doit>python tabto4.py a.txt b.txt
# 결과 : C:\doit 디렉터리에 b.txt 파일 생성

 

06-6 하위 디렉터리 검색하기

 

문제 : 특정 디렉터리부터 시작해서 그 하위 모든 파일 중 파이썬 파일(*.py)만 출력해 주는 프로그램을 만들어라.

 

1. 다음과 같이 sub_dir_search.py 파일을 작성해 본다.

# C:/doit/sub_dir_search.py

def search(dirname):
    print(dirname)
    
search("C:/")
# search 함수를 만들고 시작 디렉터리를 입력받도록 코드 작성

 

2. 이제 이 디렉터리에 있는 파일을 검색할 수 있도록 소스를 변경해 본다.

# C:/doit/sub_dir_search.py
import os

def search(dirname):
    filenames = os.listdir(dirname) # os.listdir : 디렉터리에 있는 파일들의 리스트 구해줌
    for filename in filenames:
        full_filname = os.path.join(dirname, filename) # 디렉터리를 포함한 전체 경로를 구해주는 함수
        print(full_filename)
        
search("C:/")

# 결과 : C:/ 디렉터리에 있는 파일 목록 출력

 

3. 이제 C:/ 디렉터리에 있는 파일들 중 확장자가 .py인 파일만을 출력하도록 코드를 변경해 본다.

# C:/doit/sub_dir_search.py
import os

def search(dirname):
    filenames = os.listdir(dirname)
    for filename in filenames:
        full_filname = os.path.join(dirname, filename)
        ext = os.path.splitext(full_filname)[-1] # 파일 이름에서 확장자만 추출하기 위해 함수 사용
                                                 # 파일 이름을 확장자를 기준으로 두 부분으로 나눔
        if ext == '.py':
            print(full_filname)
        
search("C:/")

 

4. 하지만 우리가 원하는 것은 C:/디렉터리 바로 밑에 있는 파일뿐만 아니라 그 하위 디렉터리(sub directory)를 포함한 모든 파이썬 파일을 검색하는 것이다. 하위 디렉터리도 검색이 가능하도록 다음과 같이 코드를 변경해야 한다.

# C:/doit/sub_dir_search.py
import os

def search(dirname):
    try: # os.listdir를 수행할 때 권한이 없는 디렉터리에 접근하더라도 프로그램이 오류로 종료되지 않고 그냥 수행토록 하기 위해 사용
        filenames = os.listdir(dirname)
        for filename in filenames:
            full_filname = os.path.join(dirname, filename)
            if os.path.isdir(full_filname): # full_filename이 디렉터리인지 파일인지 구별하기 위해 함수 사용
                search(full_filname) # 디렉터리일 경우 해당 경로를 입력받아 다시 search 함수 호출(재귀 호출)
            else:
                ext = os.path.splitext(full_filname)[-1]
                if ext == '.py':
                    print(full_filname)
    except PermissionError:
        pass
        
search("C:/")

* 하위 디렉터리 검색을 쉽게 해주는 os.walk

import os

for (path, dir, files) in os.walk("C:/"):
    for filename in files:
        ext = os.path.splitext(filename)[-1]
        if ext == '.py':
            print("%s/%s" % (path, filename))
# os.walk : 시작 디렉터리부터 시작하여 그 하위 모든 디렉터리를 차례대로 방문하게 해주는 함수

 

 

7장 : 정규 표현식

 

7장에서는 정규 표현식을 배운다.

 

07-1 정규 표현식 살펴보기

 

정규 표현식(Regular Expressions)은 복잡한 문자열을 처리할 때 사용하는 기법으로, 파이썬만의 고유 문법이 아니라 문자열을 처리하는 모든 곳에서 사용한다. 정규 표현식을 배우는 것은 파이썬을 배우는 것과는 또 다른 영역이다.

 

정규 표현식은 왜 필요한가?

주민등록번호를 포함하고 있는 텍스트가 있다. 이 텍스트에 포함된 모든 주민등록번호의 뒷자리를 * 문자로 변경해 보자.

이 문제를 정규식을 모른다면 다음과 같은 순서로 프로그램을 작성해야 할 것이다.

1. 전체 텍스트를 공백 문자로 나눈다(split).
2. 나뉜 단어가 주민등록번호 형식인지 조사한다.
3. 단어가 주민등록번호 형식이라면 뒷자리를 *로 변환한다.
4. 나뉜 단어를 다시 조립한다.

이를 구현한 코드는 다음과 같다.

data = """
park 800905-1049118
kim  700905-1059119
"""

result = []
for line in data.split("\n"):
    word_result = []
    for word in line.split(" "):
        if len(word) == 14 and word[:6].isdigit() and word[7:].isdigit():
            word = word[:6] + "-" + "*******"
        word_result.append(word)
    result.append(" ".join(word_result))
print("\n".join(result))

# 결과
park 800905-*******
kin 700905-*******

하지만 정규식을 사용하면 다음처럼 훨씬 간편하고 직관적으로 코드를 작성할 수 있다.

import re # 정규 표현식을 사용하기 위한 모듈

data = """
park 800905-1049118
kim  799905-1059119
"""

pat = re.compile("(\d{6})[-]\d{7}")
print(pat.sub("\g<1>-*******", data))

# 결과
park 800905-*******
kin 700905-*******

* 정규 표현식을 사용하면 간단한 예제에서도 코드가 상당히 간결해진다. 만약 찾으려는 문자열 또는 바꾸어야 할 문자열의 규칙이 매우 복잡하다면 정규식의 효용은 더 커지게 된다.

 

첫째 마당 복습 ~ 06-3 (p.266 ~ p.277)

 

드디어 첫째 마당을 끝내고 둘째 마당에 돌입했다. 이 책의 여정도 거의 끝나간다.

문법을 다 배우고 이제 프로그램을 어떻게 작성해야 할지 막막했는데, 둘째 마당에서는 이러한 독자들의 고충을 잘 이해하고 프로그램 작성 방식을 차근차근 알려준다. 다시 생각해도 이 책으로 사길 잘한 것 같다!

 

잘했어 과거의 나!

 


 

6장 : 파이썬 프로그래밍, 어떻게 시작해야 할까?

 

6장에서는 짤막한 스크립트(에디터로 작성한 파이썬 프로그램 파일)와 함수를 만들어 보면서 프로그래밍 감각을 키운다.

 

06-1 내가 프로그램을 만들 수 있을까?

 

문법을 어느 정도 익혔지만 프로그램을 작성하는 것에 자신이 없다면 다른 사람들이 만든 프로그램 파일을 자세히 들여다보도 분석하는 것이 좋다. 그러다 보면 자신만의 새로운 아이디어가 떠오를 수도 있다.

하지만 여기에서 가장 중요한 것은 자신의 수준에 맞는 소스를 찾는 것이다.

처음에는 구구단 프로그램을 작성해 보자.

* 프로그램을 만들려면 가장 먼저 '입력'과 '출력'을 생각하라.

 

구구단 프로그램
함수 이름은? : GuGu
입력받는 값은? : 2
출력하는 값은? : 2단(2, 4, 6, 8, ..., 18)
결과는 어떤 형태로 저장? : 연속된 자료형이므로 리스트

 

1. 먼저 이렇게 입력한다. GuGu라는 함수에 2를 입력값으로 주면 result라는 변수에 결괏값을 넣으라는 뜻이다.

result = GuGu(2)

 

 

2. 이제 결괏값을 어떤 형태로 받을 것인지 고민해 본다. 2단이니까 2, 4, 6, ..., 18까지 갈 것이다.

이런 종류의 데이터는 리스트 자료형이 딱이다. 따라서 result = [2, 4, 6, ..., 18] 같은 결과를 얻는 것이 좋겠다는 생각을 먼저 하고 나서 프로그래밍을 시작하는 것이 필요하다.

 

3. 어떻게 만들지 생각해 봤으니 1번에서 입력한 문장은 지우고 진짜 프로그램을 짜 보자. 일단 이름을 GuGu로 지은 함수를 다음과 같이 만든다.

def GuGu(n):
    print(n) # 실행하면 2를 출력

 

4. 이제 결괏값을 담을 리스트를 하나 생성하자. 앞에서 작성한 print(n)은 입력이 잘되는지를 확인하기 위한 것이므로 지워도 좋다.

def GuGu(n):
    result = []

 

5. 다음으로 result에 2, 4, 6, ..., 18을 어떻게 넣어야 할지 생각해보자.

def GuGu(n):
    result = []
    result.append(n*1) # append 내장 함수 사용
    result.append(n*2)
    result.append(n*3)
    result.append(n*4)
    result.append(n*5)
    result.append(n*6)
    result.append(n*7)
    result.append(n*8)
    result.append(n*9)
    return result
print(GuGu(2))
>>> [2, 4, 6, 8, 10, 12, 14, 16, 18]

 

6. 하지만 위 함수는 반복이 너무 많다. 가만 보면 result.append(n*'숫자')의 '숫자' 위치에 1부터 9까지 숫자만 다르게 들어가 있다는 것을 알 수 있다. 똑같은 일을 반복할 때는 '반복문'을 사용한다고 했다. 그렇다면 1부터 9까지 출력해 주는 반복문을 만들면 되지 않을까?

def GuGu(n):
    result = [] # 결괏값을 저장할 리스트 result
    i = 1
    while i < 10:
        result.append(n * i)
        i = i + 1
    return result
    
print(GuGu(2))
>>> [2, 4, 6, 8, 10, 12, 14, 16, 18]

* 프로그래밍을 할 땐 매우 구체적으로 접근해야 머리가 덜 아프다.

 

06-2 3과 5의 배수 합하기

 

문제 : 10 미만의 자연수에서 3과 5의 배수를 구하면 3, 5, 6, 9이다. 이들의 총합은 23이다. 1000 미만의 자연수에서 3의 배수와 5의 배수의 총합을 구하라.

 

입력하는 값은? : 1부터 999까지(1000미만의 자연수)
출력하는 값은? : 3의 배수와 5의 배수의 총합
생각해 볼 것은? : 1. 3의 배수와 5의 배수는 어떻게 찾지?
                                        2. 3의 배수와 5의 배수가 겹칠 때는 어떻게 하지?

 

1. 먼저 1000 미만의 자연수는 어떻게 구할 수 있을지 생각해 보자. 다음과 같이 변수에 초깃값 1을 준 후 루프를 돌리며 1씩 증가시켜서 999까지 진행하는 방법이 가장 일반적인 방법일 것이다.

n = 1
while n < 1000:
    print(n)
    n += 1

또는 다음과 같이 좀 더 파이썬다운 range 함수를 사용할 수도 있다.

for n in range(1, 1000):
    print(n)

 

2. 1000까지의 자연수를 차례로 구하는 방법을 알았으니 3과 5의 배수를 구하는 방법을 알아보자.

1부터 1000까지의 수 중 3으로 나누었을 때 나누어 떨어지는 경우, 즉 3으로 나누었을 때 나머지가 0인 경우가 바로 3의 배수이다.

for n in range(1, 1000):
    if n % 3 == 0:
        print(n)
# 5의 배수도 동일한 방법으로 작성

 

3. 이러한 내용을 바탕으로 최종 작성하면 다음과 같다.

result = 0
for n in range(1, 1000): # 1부터 999까지 n에 대입하며 반복
    if n % 3 == 0 or n % 5 == 0: # n을 3으로 나눈 나머지가 0이거나 n을 5로 나눈 나머지가 0이라면
        result += n
print(result)

이 문제에는 한 가지 함정이 있는데 3으로도 5로도 나누어지는 15와 같은 수를 이중으로 더해서는 안 된다는 점이다.

따라서 15와 같이 3의 배수도 되고 5의 배수도 되는 값이 이중으로 더해지지 않기 위해 or 연산자를 사용하였다.

 

06-3 게시판 페이징하기

 

문제 : 게시물의 총 건수와 한 페이지에 보여 줄 게시물 수를 입력으로 주었을 때 총 페이지 수를 출력하는 프로그램을 작성하라.

 

함수의 이름은? : getTotalPage
입력받는 값은? : 게시물의 총 건수(m), 한 페이지에 보여 줄 게시물 수(n)

출력하는 값은? : 총 페이지 수

 

1. 총 건수(m)를 한 페이지에 보여 줄 게시물 수(n)로 나누고 1을 더하면 총 페이지 수를 얻을 수 있다.

총 페이지 수 = (총 건수 / 한 페이지당 보여 줄 건수) + 1

 

2. 이러한 공식을 적용했을 경우 총 페이지 수가 표의 값처럼 구해지는지 확인해 보자(m을 n으로 나눌 때 소수점 아래 자리를 버리기 위해 / 대신 // 연산자 사용)

def getTotalPage(m, n):
    return m // n + 1
    
print(getTotalPage(5, 10)) # 1이 출력
print(getTotalPage(15, 10)) # 2가 출력
print(getTotalPage(25, 10)) # 3이 출력
print(getTotalPage(30, 10)) # 3이 출력되어야 하는데 4가 출력

 

3. 실패 케이스는 총 게시물 수와 한 페이지에 보여 줄 게시물 수를 나눈 나머지 값이 0이 될 때 발생함을 유추할 수 있을 것이다. 이 실패 케이스를 해결하려면 다음과 같이 코드를 변경해야 한다.

def getTotalPage(m, n):
    if m % n == 0:
        return m // n # 나누었을 때 나머지가 0인 경우는 몫만 돌려줌
    else:
        return m // n + 1

print(getTotalPage(5, 10))
print(getTotalPage(15, 10))
print(getTotalPage(25, 10))
print(getTotalPage(30, 10))

 

05-6 ~ 05장 연습문제 (p.247 ~ p.265)

 

슬슬 기본 문법은 마무리되고 응용 부분 공부를 시작하니 머리를 더 쓰면서 공부를 해야 하지만 전보다 더 재미있어지는 것 같다. 본격적으로 프로그램을 작성해 보기 전 단계를 밟고 있는 느낌이다.

이대로 쭉 흥미를 잃지 말고 전진하자아~~

 

흥 미 진 진

 


 

05-6 라이브러리

 

라이브러리는 말 그대로 원하는 정보를 찾아보는 곳이다. 전 세계의 파이썬 사용자들이 만든 유용한 프로그램을 모아 놓은 것이다. (파이썬 라이브러리는 파이썬을 설치할 때 자동으로 컴퓨터에 설치한다.)

밑에 적는 것은 자주 사용되고 꼭 알아 두면 좋은 라이브러리들이다.

 

sys

sys 모듈은 파이썬 인터프리터가 제공하는 변수와 함수를 직접 제어할 수 있게 해주는 모듈이다.

# 명령 행에서 인수 전달하기 - sys.argv
# 명령 프롬프트 창에서 모듈명 뒤에 또 다른 값을 함께 넣어 주면 sys.argv 리스트에 그 값 추가
#argv_test.py
import sys
print(sys.argv)

# cmd
C:/doit/Mymod>python argv_test.py you need python
['argv_test.py', 'you', 'need', 'python']


# 강제로 스크립트 종료하기 - sys.exit
# Ctrl + z처럼 대화형 인터프리터를 종료하는 것과 같은 기능
>>> sys.exit()


# 자신이 만든 모듈 불러와 사용하기 - sys.path
# 파이썬 모듈들이 저장되어 있는 위치 나타냄
# 이 위치에 있는 파이썬 모듈은 경로에 상관없이 어디에서나 불러올 수 있음
import sys
sys.path
>>> ['', 'C:\\Windows\\SYSTEM32\\python37.zip...'] # ''는 현재 디렉터리

# path_append.py # sys.path.append를 사용해 경로 이름 추가
import sys
sys.path.append("C:/doit/Mymod")

 

pickle

pickle은 객체의 형태를 그대로 유지하면서 파일에 저장하고 불러올 수 있게 하는 모듈이다.

import pickle
f = open("test.txt", 'wb')
data = {1: 'python', 2: 'you need'}
pickle.dump(data, f)
f.close()
# dump 함수 사용해서 딕셔너리 객체인 data를 그대로 파일에 저장

import pickle
f = open("test.txt", 'rb')
data = pickle.load(f)
print(data)
>>> {1:'python', 2:'you need'}
# pickle.dump로 저장한 파일을 pickle.load를 사용해 원래 있던 딕셔너리 객체(data) 상태 그대로 불러옴

 

OS

OS 모듈은 환경 변수나 디렉터리, 파일 등의 OS 자원을 제어할 수 있게 해주는 모듈이다.

# 내 시스템의 환경 변수 값을 알고 싶을 때 - os.environ
import os
os.environ
>>> environ({'PROGRAMFILES': 'C:\\Program Files', 'APPDATA': ... 생략 ...})
# 환경 변수 값은 컴퓨터 마다 다름

os.environ['PATH'] # PATH 환경 변수 내용
>>> 'C:\\ProgramData\\Oracle\\Java\\javascript; ... 생략 ...'


# 디렉터리 위히 변경하기 - os.chdir
os.chdir("C:\WINDOWS")


# 디렉터리 위치 돌려받기 - os.getcwd
os.getcwd()
>>> 'C:\\WINDOWS' # 현재 디렉터리 위치에 따라 결과가 다를 수 있음


# 시스템 명령어 호출하기 - os.system
os.system("dir")


# 실행한 시스템 명령어 결괏값 돌려받기 - os.popen
# 실행한 결괏값을 읽기 모드 형태의 파일 객체로 돌려줌
f = os.popen("dir")
print(f.read()) # 읽어 들인 파일 객체의 내용 보기


# 기타 유용한 os 관련 함수
os.mkdir(디렉터리) # 디렉터리를 생성
os.rmdir(디렉터리) # 디렉터리 삭제(단 디렉터리가 비어 있어야 함)
os.unlink(파일 이름) # 파일을 지움
os.rename(src, dst) # src라는 이름의 파일을 dst라는 이름으로 변경

 

shutil

shutil은 파일을 복사해 주는 파이썬 모듈이다.

import shutil
shutil.copy("src.txt", "dst.txt") # src라는 이름의 파일을 dst로 복사
# dst가 디렉터리 이름이면 src라는 파일 이름으로 복사
# dst 디렉터리에 복사하고 동일한 파일 이름이 있으면 덮어씀

 

glob

특정 디렉터리에 있는 파일 이름 모두를 알려준다.

*, ? 등 메타 문자를 써서 원하는 파일만 읽어 들일 수도 있다.

# 디렉터리에 있는 파일들을 리스트로 만들기 - glob(pathname)
import glob
glob.glob("C:/doit/mark*") # mark로 시작하는 파일을 모두 찾음(* 사용)
>>> ['C:/doit\\marks1.py', 'C:/doit\\marks2.py', 'C:/doit\\marks3.py']

 

tempfile

파일을 임시로 만들어서 사용할 때 유용한 모듈이다.

import tempfile
filename = tempfile.mkstemp() # 중복되지 않는 임시 파일의 이름을 무작위로 생성
filename
>>> 'C:\WINDOWS\TEMP\~-275151-0'

import tempfile
f = tempfile.TemporaryFile() # 임시 저장 공간으로 사용할 파일 객체를 돌려줌
                             # 기본적으로 바이너리 쓰기 모드(wb)를 가짐
f.close() # 생성한 임시 파일이 자동으로 삭제됨

 

time

시간과 관련된 모듈이다.

# time.time - 현재 시간을 수 형태로 돌려주는 함수
import time
time.time()
>>> 988348015.73417199


# time.localtime - time.time()이 돌려준 실수 값을 사용해서 연도, 월, 일, 시, 분, 초, ... 형태로 변환
# import time
time.localtime(time.time())
>>> time.stuct_time(tm_year=2013, tm_mon=5, tm_mday21, ...)


# time.asctime - time.localtime에 의해서 반환된 튜플 형태의 값을 인수로 받아서 날짜와 시간을 알아보기 쉬운 형태로 돌려줌
time.asctime(time.localtime(time.time()))
>>> 'Sat Apr 28 20:50:20 2001'


# time.ctime - 위 함수를 간편하게 표시, 항상 형재 시간만을 돌려줌
time.ctime()
'Sat Apr 28 20:56:31 2001'


# time.strftime
time.strftime(' 출력할 형식 포맷 코드', time.localtime(time.time()))
# strftime 함수는 시간에 관계된 것을 세밀하게 표현하는 여러 가지 포맷 코드 제공
import time
time.strftime('%x', time.localtime(time.time()))
>>> '05/01/01'


# time.sleep - 일정한 시간 간격을 두고 루프 실행 가능(주로 루프 안에서 사용)
import time
for i in range(10):
    print(i)
    time.sleep(1) # 1초 간격으로 0부터 9까지의 숫자 출력

 

calendar

파이썬에서 달력을 볼 수 있게 해주는 모듈이다.

# calendar.calendar(연도, 월) - 해당 월 달력을 보여줌, 월 생략하면 연도 전체를 보여줌
import calendar
print(calendar.calendar(2022))


# calendar.prcal(연도) - 위와 똑같은 결괏값
calendar.prcal(2022)


# calendar.weekday(연도, 월, 일) - 그 날짜에 해당하는 요일 정보를 보여줌
calendar.weekday(2022, 2, 15)
>>> 1 # 월요일부터 0, 화요일 1, 수요일 2... 순서


# calendar.monthrange(연도, 월) - 입력받은 달의 1일이 무슨 요일인지, 그 달이 며칠까지 있는지 튜플 형태로 보여줌
calendar.monthrange(2022, 2)
>>> (1, 28)

 

random

난수(규칙이 없는 임의의 수)를 발생시키는 모듈이다.

import random
random.random() # 0.0 ~ 1.0 사이의 실수 중에서 난수 값을 돌려줌
>>> 0.5345324654543467

random.randint(1, 10) # 1 ~ 10 사이의 정수 중에서 난수 값을 돌려줌
>>> 7


# random.shuffle - 리스트의 항목을 무작위로 섞음
import random
data = [1, 2, 3, 4, 5]
random.shuffle(data)
data
>>> [5, 1, 3, 4, 2]

 

webbrowser

자신의 시스템에서 사용하는 기본 웹 브라우저를 자동으로 실행하는 모듈이다.

import webbrowser
webbrowser.open("http://google.com")

webbrowser_new.open("http://google.com") # 웹브라우저가 이미 실행된 상태라도 새로운 창으로 열림

 

threading

한 프로세스 안에서 2가지 또는 그 이상의 일을 동시에 수행할 수 있게 해주는 모듈이다.

# thread_test.py
import time
import threading # threading 모듈 import

def long_task(): # 5초의 시간이 걸리는 함수
    for i in range(5):
        time.sleep(1) # 1초 간격으로 실행
        print("working:%s\n" % i)
        
print("Start")

threads = []
for i in range(5): # long_task를 5회 수행
    t = threading.Thread(target=long_task) # 스레드 생성
    threads.append(t)
for t in threads:
    t.start() # 스레드 실행
for t in threads:
    t.join() # join으로 스레드가 종료될 때까지 기다림
    
print("End")
>>> Start
    working:0 # long_task가 동시에 5번씩 실행
    working:0
    working:0
    working:0
    working:0
    
    working:1
    working:1
    working:1
    working:1
    working:1
    .
    .
    .
    working:4
    working:4
    working:4
    working:4
    working:4
    End

 

05-4 ~ 05-5 (p.222 ~ p.246)

 

어느덧 10일 차로 절반 이상을 달려왔다.

10일 동안 교재의 3분에 2 이상을 공부하다 보니 앞에서 배웠던 부분들을 들춰가면서 공부를 하게 된다.

이 교재를 다 보더라도 틈틈이 복습을 해야겠다. 그래도 지금까지 배운 것들이 있다 보니 처음보다는 수준이 오른 것이 느껴진다..!

 

미약하지만 이런 느낌

 


 

05-4 예외처리

 

파이썬은 try, except를 사용해서 예외적으로 오류를 처리할 수 있게 해 준다.

 

오류 예외 처리 기법

try, except문

# try, except문의 기본 구조
try:
   ...
except [발생오류[as 오류 메시지 변수]]: # []는 괄호 안의 내용을 생략할 수 있다는 관례 표현 기법
   ...
# 1. try, except만 쓰는 방법
# 오류 종류에 상관없이 오류가 발생하면 except 블록 수행
try:
    4 / 0
except:
    print("오류 발생")
>>> 오류 발생


# 2. 발생 오류만 포함한 except문
# 오류가 발생했을 때 except에 미리 정해 놓은 오류 이름과 일치할 때만 except 블록을 수행
try:
    4 / 0
except ZeroDivisionError: # except 발생 오류:
    print("오류 발생")
>>> 오류 발생


# 3. 발생 오류와 오류 메시지 변수까지 포함한 except문
# 두 번째 경우에서 오류 메시지의 내용까지 알고 싶을 때 사용
try:
    4 / 0
except ZeroDivisionError as e: # except 발생 오류 as 오류 메시지 변수:
    print(e)
>>> division by zero

 

try... finally

finally 절은 try문 수행 도중 예외 발생 여부에 상관없이 항상 수행된다. 보통 사용한 리소스를 close 해야 할 때에 많이 사용된다.

f = open('foo.txt', 'w')
try:
    f .write("Hello") # 무언가를 수행한다.
finally:
    f.close()
# foo.txt 파일을 쓰기 모드로 연 후, try문을 수행하고 예외 발생 여부와 상관없이 finally절 수행

 

여러 개의 오류 처리하기

try:
    a = [1, 2]
    print(a[3])
    4 / 0
except ZeroDivisionError:
    print("0으로 나눌 수 없습니다.")
except IndexError:
    print("인덱싱할 수 없습니다.")
>>> 인덱싱할 수 없습니다. # 인덱싱 오류가 먼저 발생했으므로 ZeroDivisionError 오류는 발생하지 않음

# 두 오류를 함께 처리도 가능
try:
    a = [1, 2]
    print(a[3])
    4 / 0
except (ZeroDivisionError, IndexError) as e:
    print(e)

 

 

오류 회피하기

try:
    f = open("나없는파일", 'r')
except FileNotFoundError:
    pass # 파일이 없더라도 오류를 발생시키지 않고 통과한다.

 

오류 일부러 발생시키기

파이썬은 raise 명령어를 사용해 오류를 강제로 발생시킬 수 있다.

class Bird:
    def fly(self):
        raise NotImplementedError # 꼭 작성해야 하는 부분이 구현되지 않았을 경우 일부러 오류를 일으키기 위해 사용하는 내장 오류
         
class Eagle(Bird): # Eagle 클래스는 Bird 클래스를 상속 받음
        pass # Eagle 클래스에서 fly 함수를 구현하지 않았기 때문에 Bird 클래스의 fly 함수 호출
             # Bird 클래스의 fly 함수에 있는 raise문에 의해 NotImplementedError 오류 발생

eagle = Eagle()
eagle.fly()
>>> NotImplementedError 오류 발생

# 오류가 발생되지 않게 하려면 Eagle 클래스에 fly 함수를 반드시 구현해야 함
class Eagle(Bird):
    def fly(self):
        print("vert fast")

eagle = Eagle()
eagle.fly()
>>> very fast

 

예외 만들기

예외는 파이썬 내장 클래스인 Exception 클래스를 상속하여 만들 수 있다.

class MyError(Exception):
    def __str__(self):
        return "허용되지 않는 별명입니다."

def say_nick(nick):
    if nick == '바보':
        raise MyError()
    print(nick)
    
try:
    say_nick("천사")
    say_nick("바보")
except MyError as e:
    print(e) # 오류 메시지를 출력했을 때 오류 메시지가 보이게 하려면 오류 클래스에 __str__ 메서드 구현해야 함

 

05-5 파이썬 내장 함수

 

# 앞에서 살펴본 함수들은 생략
# abs : 어떤 숫자를 입력받았을 때, 절댓값을 돌려줌
abs(3) 
>>> 3

# all : 인수에 반복 가능한 자료형을 받고 모두 참이면 True, 거짓이 하나라도 있으면 Fals 반환
all([1, 2, 3])
>>> True
all([1, 2, 3, 0])
>>> False

# any : 하나라도 참이 있으면 True, 모두 거짓이면 False
any([1, 2, 3, 0])
>>> True
any([0, ""])
>>> False

# chr : 아스키 코드 값을 입력받아 그 코드에 해당하는 문자 출력
chr(97)
>>> 'a'

# dir : 객체기 자체적으로 가지고 변수나 함수를 보여줌
dir([1, 2, 3])
>>> ['append', 'count', ...]

# divmod : 두 숫자를 나눈 몫과 나머지를 튜플 형태로 반환
divmod(7, 3)
>>> (2, 1)

# enumerate : 순서가 있는 자료형을 입력으로 받아 인덱스 값을 포함하는 enumerate 객체 반환
for i, name in enumerate(['body', 'foo', 'bar']):
    print(i, name)
    
>>> 0 body
    1 foo
    2 bar

# eval : 실행 가능한 문자열을 입력으로 받아 문자열을 실행한 결괏값 반환
eval('1+2')
>>> 3
eval("'hi' + 'a'")
>>> 'hia'

# filter : 첫 번째 인수로 함수 이름, 두 번째 인수로 그 함수에 차례로 들어갈 반복 가능한 자료형을 받는다.
#          그리고 두 번째 인수인 반복 가능한 자료형 요소가 첫 번째 인수인 함수에 입력되었을 때 반환 값이
#          참인 것만 묶어서(걸러서) 반환
def positive(x):
    return x > 0

print(list(filter(positive, [1, -3, 2, 0, -5, 6])))
>>> [1, 2, 6]

# hex : 정수 값을 입력받아 16진수로 변환
hex(234)
>>> '0xea'

# id : 객체를 입력받아 객체의 고유 주소 값 반환
a = 3
id(3)
>>> 135072304

# input : 사용자 입력을 받는 함수
a = input("Enter : ")
>>> Enter : hi
a
>>> 'hi'

# int : 문자열 형태의 숫자나 소수점이 있는 숫자 등을 정수 형태로 반환
int('3') # 문자열 형태 3
>>> 3
int(3.4)
>>> 3

# isinstance : 첫 번째 인수로 인스턴스, 두 번째 인수로 클래스 이름
#              입력으로 받은 인스턴스가 그 클래스의 인스턴스인지를 판단
#              참이면 True, 거짓이면 False
class Person: pass # 아무 기능이 없는 Person 클래스 생성
...
a = Person() # Person 클래스의 인스턴스 a 생성
isinstance(a, Person) # a가 Person 클래스의 인스턴스인지 확인
>>> True

# len : 입력값의 길이 반환
len("python")
>>> 6
len([1, 2, 3])
>>> 3

# list : 반복 가능한 자료형을 입력받아 리스트로 반환
list("python")
>>> ['p', 'y', 't', 'h', 'o', 'n']
list((1, 2, 3)) # 튜플 자료형
>>> [1, 2, 3]

# map : 함수와 반복 가능한 자료형을 입력으로 받음
#       입력 받은 자료형의 각 요소를 함수 f가 수행한 결과를 묶어서 돌려주는 함수
def two_times(x): return x*2
...
list(map(two_times, [1, 2, 3, 4]))
>>> [2, 4, 6, 8]

# max : 인수로 반복 가능한 자료형을 입력받아 그 최댓값 반환
max([1, 2, 3])
>>> 3
max("python")
>>> 'y'

# min : 인수로 반복 가능한 자료형을 입력받아 그 최솟값 반환
min([1, 2, 3])
>>> 1
min("python")
>>> 'h'

# oct : 정수 형태의 숫자를 8진수 문자열로 반환
oct(34)
>>> '0o42'

# open : '파일 이름'과 '읽기 방법'을 입력받아 파일 객체를 반환
#         읽기 방법을 생략하면 기본값인 읽기 전용 모드(r)
# w : 쓰기 모드, r : 읽기 모드, a : 추가 모드, b : 바이너리 모드(b는 w, r, a와 함께 사용)
f = open("binary_file", "rb") # b를 추가하면 바이너리 모드 추가(바이너리 읽기 모드)

# ord : 문자의 아스키 코드 값 반환
ord('a')
>>> 97

# pow : x의 y 제곱한 결괏값 반환
pow(2, 4)
>>> 16

# range : 입력받은 숫자에 해당하는 범위 값을 반복 가능한 객체로 만들어 반환
list(range(5))
>>> [0, 1, 2, 3, 4]
list(range(5, 10))
>>> [5, 6, 7, 8, 9]
list(range(1, 10, 2)) # 세 번째 인수는 숫자 사이의 거리
>>> [1, 3, 5, 7, 9]

# round : 숫자를 입력받아 반올림
round(4.6)
>>>5

# sorted : 입력값을 정렬한 후 결과를 리스트로 반환
sorted([3, 1, 2])
>>> [1, 2, 3]
sorted("zero")
>>> ['e', 'o', 'r', 'z']

# str : 문자열 형태로 객체를 변환
str(3)
>>> '3'
str('hi'.upper())
>>> 'HI'

# sum : 입력받은 리스트나 튜플의 모든 요소의 합을 반환
sum([1, 2, 3])
>>> 6

# tuple : 반복 가능한 자료형을 입력받아 튜플 형태로 바꾸어 반환
tuple("abc")
>>> ('a', 'b', 'c')
tuple([1, 2, 3])
>>> (1, 2, 3)
tuple((1, 2, 3))
>>> (1, 2, 3)

# type : 입력값의 자료형이 무엇인지 알려줌
type("abc")
>>> <class 'str'> # "abc"는 문자열 자료형

# zip : 동일한 개수로 이루어진 자료형을 묶어 주는 역할
list(zip([1, 2, 3], [4, 5, 6]))
>>> [(1, 4), (2, 5), (3, 6)]
list(zip("abc", "de"))
>>> [('a', 'd'), ('b', 'e')]

 

05-2 ~ 05-3 (p.207 ~ p.221)

 

점점 진도가 나갈수록 심화되는 내용이 나오다 보니 확실히 전보다 더 공부하는 시간이 오래 걸린다.

공부 시간이 오래 걸리는 데는 교재에 나와 있지 않은 예들을 실행되는지 테스트해 보느라고 그런 것도 있는 것 같다.

하지만 공부한 것들을 이해하게 되면 지나간 시간이 아깝지 않다. 프로그래밍 공부는 될 때까지 시도해 보는 근성도 중요한 것 같다.

 

아무도 나를 막을 수 없으셈!

 


 

05-2 모듈

 

모듈이란 함수나 변수 또는 클래스를 모아 놓은 파일이다.

다른 파이썬 프로그램에서 불러와 사용할 수 있게끔 만든 파이썬 파일이다.

다른 사람이 이미 만들어 놓은 모듈을 사용할 수도 있고 직접 만들어서 사용할 수도 있다.

# 모듈의 예
# mod1.py
def add(a, b):
    return a + b
    
def suv(a, b):
    return a - b
# .py로 만든 파이썬 파일은 모두 모듈

 

모듈 불러오기

# 명령프롬프트(cmd)
C:\Users\a_jb97> cd C:\doit # mod1.py를 저장한 디렉터리로 이동
C:\doit>dir # C:\doit 디렉터리에 파일이 있는지 확인
...
2022-02-10 오후 09:10 50 mod1.py
...
C:\doit> python # 대화형 인터프리터 실행
>>> import mod1 # import는 이미 만들어 놓은 파이썬 모듈을 사용할 수 있게 해주는 명령어
                # import는 현재 디렉터리에 있는 파일이나 파이썬 라이브러리가 저장된 디렉터리에 있는 모듈만 불러올 수 있음
                # 모듈 이름은 .py를 제거한 mod1만 작성
>>> print(mod1.add(3, 4)) # add 함수를 사용하기 위해서는 모듈 이름 뒤에 '.'(도트 연산자)를 붙이고 함수 이름
7
>>> print(mod1.sub(4, 2))
2

# 모듈 이름 없이 함수 이름만 쓰고 싶을 때
>>> from mod1 import add
>>> add(3, 4)
7

# add, sub 함수 둘 다 사용하고 싶을 때
1. from mod1 import add, sub
2. from mod1 import* # *는 '모든 것'이라는 뜻

 

if __name__ = "__main__": 의 의미

# mod1.py
def add(a, b):
    return a + b
    
def suv(a, b):
    return a - b
    
print(add(1, 4))
print(sub(4, 2))

# cmd
C:\doit> python
>>> import mod1
5
2 # mod1.py 파일의 add와 sub 함수만 사용하려고 했는데 결괏값이 출력


# 이러한 문제 방지
# mod1.py
def add(a, b):
    return a + b
    
def suv(a, b):
    return a - b

if __name__ == "__main__": # 직접 파일을 실행하면 참이 되어 if문 다음 문장 수행
                           # 대화형 인터프리터나 다른 파일에서 이 모듈을 불러서 사용할 때는 거짓이 되어 if문 다음 문장이 수행되지 않음
    print(add(1, 4))
    print(sub(4, 2))
   
import mod1
>>> # 아무것도 수행되지 않음

 

클래스나 변수 등을 포함한 모듈

# mod2.py
PI = 3.141592

class Math:
    def solv(self, r):
        return PI * (r ** 2)
    
def add(a, b):
    return a + b

# cmd
C:\doit>python
>>> import mod2
>>> print(mod2.PI) # mod2.py에 있는 PI 변수 값 사용

>>> a = mod2.Math()
>>> print(a.solv(2)) # mod2.py에 있는 Math 클래스 사용
12.566368

>>> print(mod2.add(mod2.PI, 4.4)) # mod2.py에 있는 add 함수 사용
7.541592

 

다른 파일에서 모듈 불러오기

# modtest.py
import mod2 # mod2.py 파일을 불러옴
            # 현재 모듈과 불러올 모듈이 동일한 디렉터리에 있어야 함
result = p212.add(3, 4)
print(result)

* 모듈을 불러오는 또 다른 방법(모듈을 저장한 디렉터리로 이동하지 않고 모듈을 불러와서 사용)

# cmd
C:\doit>mkdir mymod # 새 디렉터리 생성
C:\doit>move mod2.py mymod # 지정한 디렉터리로 파일 이동
        1개 파일을 이동했습니다.

# 1. sys.path.append(모듈을 저장한 디렉터리) 사용하기
# cmd
C:\doit>python
>>> import sys # sys 모듈 불러옴
>>> sys.path # 파이썬 라이브러리가 설치되어 있는 디렉터리를 보여줌
>>> sys.path.append("C:\doit\mymod")
>>> sys.path
sys.path(파이썬 라이브러리)에 'C:\doit\mymod' 디렉터리 추가
>>> import mod2
>>> print(mod2.add(3, 4))
7

# 2. PYTHONPATH 환경 변수 사용하기
# cmd
C:\doit>set PYTHONPATH=C:\doit\mymod # set 명령어 사용해 PYTHONPATH 환경변수에 mod2.py 파일이 있는 C:\doit\mymod 디렉터리 설정
C:\doit>python
>>> import mod2
>>> print(mod2.add(3, 4))
7

 

05-3 패키지

 

패키지는 도트'.'를 사용하여 파이썬 모듈을 계층적(디렉터리 구조)으로 관리할 수 있게 해준다.

모듈 이름이 A.B인 경우, A는 패키지 이름이 되고 B는 A 패키지의 B 모듈이 된다.

패키지는 디렉터리, 파이썬 모듈로 이루어져 있다.

# 패키지의 구조 (ex.게임)
game/ # 디렉터리
  __init__.py # 모듈
  sound/
    __init__.py
    echo.py
    wav.py
  graphic/
    __init__.py
    screen.py
    render.py
  play/
    __init__.py
    run.py
    test.py

* 패키지 구조로 프로그램을 만들면 공동 작업이나 유지 보수가 용이하다.

 

패키지 만들기

패키지 기본 구성 요소 준비하기

  1. 디렉터리 생성(game, graphic, sound)

디렉터리 생성

  2. 각 디렉터리에 __init__.py 파일 생성

 

  3. 각 디렉터리에 필요한 모듈(.py파일) 생성

 

  4. game 패키지를 참조하도록 명령 프롬프트 창에서 set 명령어로 PYTHONPATH 환경 변수에 C:\doit 디렉터리 추가

 

패키지 안의 함수 실행하기

다음 예제들은 import 예제이므로 하나의 예제를 실행하고 나서 다음 예제를 실행할 때는 인터프리터를 종료하고 다시 실행. (윈도우는 인터프리터 종료 : Ctrl + z)

# cmd
# 1. echo 모듈을 import하여 실행
C:\doit>python
>>> import game.sound.echo
>>> game.sound.echo.echo_test()
echo

# 2. echo 모듈이 있는 디렉터리까지를 from...import 하여 실행
>>> from game.sound import echo
>>> echo.echo_test()
echo

# 3. echo 모듈의 echo_test함수를 직접 import하여 실행
>>> from game.sound.echo import echo_test
>>> echo_test()
echo

# 실행 불가능 예
>>> import game
>>> game.sound.echo.echo_test()
오류 발생 # import game을 수행하면 game 디렉터리의 모듈 또는 game 디렉터리의 __init__.py에 정의한 것만 참조 가능

>>> import game.sound.echo.echo_test
오류 발생 # 도트 연산자'.'를 사용해서 import a.b.c처럼 import할 때 가장 마지막 항목인 c는 반드시 모듈 또는 패키지여야만 함

 

__init__.py 의 용도

__init__.py 파일은 해당 디렉터리가 패키지의 일부임을 알려주는 역할을 한다.

만약 패키지에 포함된 디렉터리에 __init__.py 파일이 없다면 패키지로 인식되지 않는다.

* Python 3.3 버전부터는 __init__.py 파일이 없어도 패키지로 인식하지만 하위 버전 호환을 위해 생성하는 것이 좋다.

# cmd
C:\doit>python
>>> from game.sound import*
>>> echo.echo_test()
오류 발생

# 특정 디렉터리의 모듈을 *를 사용하여 import할때는 다음과 같이 해당 디렉터리의 __init__.py 파일에
  __all__ 변수를 설정하고 import할 수 있는 모듈을 정의해 주어야 한다.

# C:/doit/game/sound/__init__.py
__all__ = ['echo', 'ringtone']

# cmd
C:\doit>python
>>> from game.sound import*
>>> echo.echo_test()
echo

 

relative 패키지

# render.py
from game.sound.echo import echo_test # 추가하여 echo_test 함수를 사용할 수 있게 함
def render_test():
    print("render")
    echo_test()
    
# cmd
C:\doit>python
>>> from game.graphic.render import render_test
>>> render_test()
render
echo


# relative하게 import
# render.py
from ..sound.echo import echo_test # ..은 부모 디렉터리를 뜻함
                                   # graphic과 sound 디렉터리는 동일한 깊이이므로 사용 가능

def render_test():
    print("render")
    echo_test()
# ..같은 relative한 접근자는 모듈 안에서만 사용 가능

 

04장 연습문제 ~ 05~1 (p.179 ~ p.206)

 

오늘 공부한 클래스는 처음 접하는 개념이라서 이해를 하는데 전보다는 시간이 더 걸렸다.

클래스를 공부하기 전에 4장 연습문제를 풀었는데 한 문제씩 정답을 맞히니 기분이 좋았다.

이 기분을 양분으로 삼아서 더 열심히 해야겠다.

 

기분이 조크든요

 


 

5장 : 파이썬 날개 달기

 

5장에서는 프로그래밍의 꽃이라 할 수 있는 클래스와 모듈, 예외 처리 및 파이썬 라이브러리에 대해서 배운다. 여기까지 끝내면 파이썬 프로그램을 작성하기 위해 알아야 할 대부분의 내용을 배우게 된다고 한다.

 

05-1 클래스

 

클래스는 프로그램 작성을 위해 꼭 필요한 건 아니지만, 적재적소에 사용하면 매우 편리한 요소이다.

# 클래스의 예
class Calculator:
    def __init__(self):
        self.result = 0
    
    def add(self, num):
        self.result += num
        return self.result

cal1 = Calculator()
cal2 = Calculator()

* 클래스(class)란 똑같은 무언가를 계속해서 만들어 낼 수 있는 설계 도면(과자 틀)이고, 객체(object)란 클래스로 만든 피조물(과자)을 뜻한다.

클래스는 함수처럼 구조는 여러 로직들을 모아놓은 모양새지만 가장 큰 특징은 같은 클래스로 생성한 여러 가지 다른 이름의 객체(cal1, cal2)들은 서로 영향을 받지 않고 독립적인 값을 유지한다는 것이다.

 

* 클래스로 만든 객체를 인스턴스라고도 한다. 객체와의 차이는 'cal1 = Calculator()'에서 cal1은 객체이고 a객체는 Calculator의 인스턴스이다.

 

사칙연산 클래스 만들기

클래스는 무작정 만드는 것보다 클래스로 만든 객체를 중심으로 어떤 식으로 동작하게 할 것인지 미리 구상을 한 후에 생각한 것들을 하나씩 해결하면서 완성해 나가는 것이 좋다.(메서드 = 클래스 안에 구현된 함수)

사칙연산 클래스(FourCal)
setdata 메서드 두 숫자 입력
add 메서드 더하기 기능
mul 메서드 곱하기 기능
sub 메서드 빼기 기능
div 메서드 나누기 기능

 

# 사칙연산 클래스
class FourCal: # 클래스 구조
    def setdata(self, first, second): # 객체에 숫자 지정 
                                      # a 객체를 만들면(a.setdata(4, 2)) 첫 번째 매개변수 self에는 setdata메서드를 호출한 객체 a가 자동으로 전달
        self.first = first
        self.second = second
    def add(self): # 매개변수 = self, 반환값 = result
        result = self.first + self.second # = result = a.first + a.second
        return result
    def mul(self):
        result = self.first * self.second
        return result
    def sub(self):
        result = self.first - self.second
        return result
    def div(self):
        result = self.first / self.second
        return result
        
a = FourCal()
b = FourCal()
a.setdata(4, 2)
b.setdata(3, 8)

a.add()
>>> 6
a.mul()
>>> 8
a.sub()
>>> 2
a.div()
>>> 2

b.add()
>>> 11
b.mul()
>>> 24
b.sub()
>>> -5
b.div()
>>> 0.375

 

생성자(Constructor)

객체에 초깃값을 설정해야 할 필요가 있을 때는 setdata와 같은 메서드를 호출하여 초깃값을 설정하기보다는 생성자를 구현하는 것이 안전한 방법이다.

 

* 생성자(Constructor)란 객체가 생성될 때 자동으로 호출되는 메서드를 의미한다. 파이썬 메서드 이름으로 __init__를 사용하면 생성자 생성

# FourCal 클래스에 생성자 사용
class FourCal:
    def __init__(self, first, second): # 생성자로 인식
        self.first = first
        self.second = second
    def setdata(self, first, second):
        self.first = first
        self.second = second
    def add(self):
        result = self.first + self.second
        return result
    def mul(self):
        result = self.first * self.second
        return result
    def sub(self):
        result = self.first - self.second
        return result
    def div(self):
        result = self.first / self.second
        return result
        
a = FourCal(4, 2) # 생성자로 인해 a.first = 4, a.second = 2 바로 대입
print(a.first)
>>> 4
print(a.second)
>>> 2

a.add()
>>> 6
a.div()
>>> 2.0

 

클래스의 상속

'재산을 상속받다'의 상속과 같은 의미로 어떤 클래스를 만들 때 다른 클래스의 기능을 물려받을 수 있게 한다.

class FourCal:
    def __init__(self, first, second):
        self.first = first
        self.second = second
    def setdata(self, first, second):
        self.first = first
        self.second = second
    def add(self):
        result = self.first + self.second
        return result
    def mul(self):
        result = self.first * self.second
        return result
    def sub(self):
        result = self.first - self.second
        return result
    def div(self):
        result = self.first / self.second
        return result

# 클래스의 상속
class MoreFourCal(FourCal): # class 클래스 이름(상속할 클래스 이름):
    def pow(self):
        result = self.first ** self.second # 제곱
        return result
# FourCal 클래스를 상속했으므로 FourCal 클래스의 모든 기능 사용 가능

a = MoreFourCal(4, 2)
a.add()
>>> 6
a.mul()
>>> 8
a.sub()
>>> 2
a.div()
>>> 2
a.pow() # 제곱
>>> 16

# 메서드 오버라이딩(상속한 클래스에 있는 메서드를 동일한 이름으로 다시 만드는 것)
a = FourCal(4, 0)
a.div()
>>> ZeroDivisionError 오류 # 4를 0으로 나누려고 하기 때문에 오류 발생

class SafeFourCal(FourCal):
    def div(self):
        if self.second == 0:
            return 0 # div 메서드에서 0으로 나누면 0 반환
        else:
            return self.first / self.second
            
a = SafeFourCal(4, 0)
a.div()
>>> 0

 

클래스 변수

# 클래스 변수
class Family:
    lastname = "김"

print(Family.lastname)
>>> 김

a = Family()
b = Family()
print(a.lastname)
>>> 김
print(b.lastname)
>>> 김

Family.lastname = "박"
print(a.lastname)
>>> 박
print(b.lastname)
>>> 박
# 클래스 변수는 클래스로 만든 모든 객체에 공유됨

 

04-2 ~ 04-3 (p.168 ~ p.178)

 

이제 반 정도 왔다. 여기까지 오니까 슬슬 초반에 배웠던 부분들이 가물가물해지려고 한다. 매일마다 복습도 꾸준히 해야겠다.

아직까지는 지금까지 배운 걸로 프로그램을  만들 수 있을 것 같은 느낌은 들지 않는다. 그래서 아직 불안한 느낌이 드는지도 모르겠다. 계속해서 공부를 하는 수밖에 없을 거 같다.

 

열심 또 열심히..!

 


 

04-2 사용자 입력과 출력

 

사용자의 입력을 받는 방법과 출력하는 방법은 여러 가지가 있다.

 

사용자 입력

사용자가 입력한 값을 어떤 변수에 대입하려고 할 때는 이런 방법들이 있다.

# input의 사용
a = input()
>>> Life is too short, you need python
a
>>> 'Life is too short, you need python'
# input은 입력되는 모든 것을 문자열로 취급한다.


# 프롬프트 값을 띄워서 사용자 입력받기
# input("내용 입력")
number = input("숫자를 입력하세요 : ")
>>> 숫자를 입력하세요 : 3
print(number)
>>> 3

 

print 자세히 알기

print문으로 할 수 있는 일은 여러 가지가 있다.

# 큰따옴표(")로 둘러싸인 문자열은 + 연산과 동일하다
print("life" "is" "too short")
>>> lifeistoo short
print("life" + "is" + "too short")
>>> lifeistoo short

# 문자열 띄어쓰기는 콤마로 한다
print("life", "is", "too short")
>>> life is too short

# 한 줄에 결괏값 출력하기
for i in range(10):
    print(i, end=' ') # end 사용

>>> 0 1 2 3 4 5 6 7 8 9 >>>

 

04-3 파일 읽고 쓰기

 

이제까지는 값을 '입력'받을 때는 사용자가 직접 입력하는 방식을 사용했고 '출력'할 때는 모니터 화면에 결괏값을 출력하는 방식으로 프로그래밍해 왔다. 하지만 입출력 방법에는 꼭 이것만 있는 것은 아니다. 여기에서는 파일을 통한 입출력 방법을 배웠다.

 

파일 생성하기

f = open("새파일.txt", 'w')
f.close()

# 파일 객체 = open(파일 이름, 파일 열기 모드)
# open 함수는 '파일 이름'과 ' 파일 열기 모드'를 입력값으로 받고 결괏값으로 파일 객체를 돌려준다.

# C:/doit 디렉터리에 생성하고 싶을 때
f = open("C:/doit/새파일.txt", 'w')
f.close()
파일 열기 모드 설명
r 읽기 모드 - 파일을 읽기만 할 때 사용
w 쓰기 모드 - 파일에 내용을 쓸 때 사용
a 추가 모드 - 파일의 마지막에 새로운 내용을 추가할 때 사용

* 파일을 쓰기 모드로 열면 해당 파일이 이미 존재할 경우 원래 있던 내용이 모두 사라지고, 해당 파일이 존재하지 않으면 새로운 파일이 생성된다.

* 프로그램을 종료할 때 파이썬 프로그램이 열려 있는 파일의 객체를 자동으로 닫아 주기 때문에 close()는 생략해도 되지만 되도록이면 사용해 주는 것이 좋다. 쓰기 모드로 열었던 파일을 닫지 않고 다시 사용하려고 하면 오류가 발생하기 때문이다.

 

파일을 쓰기 모드로 열어 출력 값 적기

이번에는 프로그램의 출력 값을 파일에 직접 써본다.

f = open("C:/doit/새파일.txt", 'w')
for i in range(1, 11):
    data = "%d번째 줄입니다.\n" % i
    f.write(data)
f.close()

# 실행하면 1번째 줄입니다. ~ 10번째 줄입니다. 라고 적혀있는 메모장 파일이 생성된다.

 

프로그램의 외부에 저장된 파일을 읽는 여러 가지 방법

파이썬에는 외부 파일을 읽어 들여 프로그램에서 사용할 수 있는 여러 가지 방법이 있다.

# readline 함수 사용하기
f = open("C:/doit/새파일.txt", 'r')
line = f.readline()
print(line)
f.close()
# readline 함수는 파일의 첫 번째 줄을 읽어 출력하는 함수이다.
# 실행하면 새파일.txt의 가장 첫 번째 줄인 '1번째 줄입니다.'가 출력된다.

# readlines 함수 사용하기
f = open("C:/doit/새파일.txt", 'r')
lines = f.readlines()
for line in lines:
    print(line)
f.close()
# readlines 함수는 파일의 모든 줄을 읽어서 각각의 줄을 요소로 갖는 리스트로 돌려준다.

# read 함수 사용하기
f = open("C:/doit/새파일.txt", 'r')
data = f.read()
print(data)
f.close()
# f.read()는 파일의 내용 전체를 문자열로 돌려준다.

 

파일에 새로운 내용 추가하기

원래 있던 값을 유지하면서 새로운 값을 추가해야 할 경우에는 파일을 추가 모드('a')로 열면 된다.

# 추가 모드를 사용한 예
f = open("C:/doit/새파일.txt", 'a')
for i in range(11, 20):
    data = "%d 번째 줄입니다.\n" % i
    f.write(data)
f.close()
# 실행하면 '10번째 줄입니다.'의 뒤를 이어 '11번째 줄입니다' ~ '19번째 줄입니다'까지 출력된다.

 

with문과 함께 사용하기

지금까지는 항상 코드 마지막에 f.close()를 추가해서 파일을 닫아왔다. 파일을 열면 항상 close 해 주는 것이 좋다.

하지만 with문을 사용하면 파일을 열고 닫는 것을 자동으로 처리할 수 있다.

# with문을 사용한 예
with open("foo.txt", "w") as f:
    f.write("Life is too short, you need python")
# with문을 사용하면 with 블록을 벗어나는 순간 열린 파일 객체 f가 자동으로 close 된다.

 

+ Recent posts