* 프로그램 요청이나 산출물 요청은 댓글로 달아주세요!

 

기다리고 기다리던 CVE SCRAPPER의 시현 시간이다. 개발정보는 -계획,진행-편에 들어있다.

2021.06.16 - [Python/Python Project] - [Python Project] CVE Scrapper 만들기 - 계획,진행-

 

[Python Project] CVE Scrapper 만들기 - 계획,진행-

블로그에 올릴 장기적인 프로그램을 만들고자 한다. 정보보안에 관심있는 필자이다 보니, 당연히 취약점에 눈이 들어오는건 당연하다. 속내를 말하자면 "와 이거 하루에 하나씩만 해도 한 3년은

tutoreducto.tistory.com

 


디렉토리 구조

이렇다. 코드 각각의 설명은 -기능도-편을 참고하자

2021.06.19 - [Python/Python Project] - [Python Project] CVE Scrapper 만들기 - 기능도-

 

[Python Project] CVE Scrapper 만들기 - 기능도-

코드별 클래스, 함수명에 대한 설명이다.

tutoreducto.tistory.com

 


시현

CveMain은 다음과 같이 실행된다. vendor의 목록은 cve_list directory에서 긁어온다. 

 

Make Report로 보고서를 만들 수 있다. Exported?가 True인 행은 Information dialog가 띄워지며 export가 안된다. exported 된 여부 판단은 export_cve directory에 해당 cve보고서가 존재하는지 여부로 판단한다.

export 된 보고서는 export_cve directory에서 찾을 수 있다.


OUTPUT

 

보고서는 다음과 같이 제작되어있다.

CVE-2010-4218.pdf
0.18MB


후기

짧은 프로젝트였다. 정식으로 뿌리는 것도 아니고, 디자인을 하나도 안 한 프로젝트였다. 찾아보니 TISTORY API가 있는 거 같은데, 이거 자동화해서 블로그에 보고서를 올리면 좋을 거 같다. 크롤링 코드와 gui를 연습할 수 있는 좋은 경험이었던 거 같다. fpdf를 사용하다 보니 편하다는 것을 느꼈다. body부분의 글자별 스타일 적용이 어렵다는 느낌은 받았지만 다음 자동화 프로젝트도 fpdf로 출력할 거 같다.

주석은 프로그래밍에서 어려운 문법을 가지고 있는 것도 아니고, 프로그램의 실행시간의 지대한 영향을 끼치는 것도 아니다. 그런데 왜 필자는 주석을 Python과 친해지기 포스팅이 아닌 Advanced버전인 Python과 매우 친해지기에 올렸을까? 아래의 두 가지 이유가 있다.

 

1. 주석은 프로그래머들 사이의 협업의 규약이기에 매우 중요하므로, 이는 어느 정도 실력이 보증된 프로그래머가 작성해야된다.
2. 필자는 까먹었다.

 

자 그럼 Python세계의 주석을 알아보도록 하자


주석(Comment)

주석은 프로그램의 실행에 영향을 미치지 않으며, 프로그래머가 필요에 의해서 코드에 추가적인 정보를 입력하는 것이다.

Python에서는 다음과 같은 방법으로 주석을 남길 수 있다.

 

한 줄 주석(#)

def comment(value) :
    print(value)	# 한줄의 주석을 추가합니다.

 

여러 줄 주석(''' ~ ''')

def comment(value) :
    print(value)

'''
여러줄의 주석을 추가하는
방법은 작은따옴표
세개를 연달아 쓰는것입니다.
끝내는 방법도 동일합니다.
'''

 

훌륭한 주석에 대한 고찰

좋은 주석은 무엇일까? 보기에 번잡스럽지 않으면서도, 필요한 내용을 정확하게 알려주는 주석이 좋은 주석이라고 할 수 있겠다. 대표적으로 아래와 같은 주석이 많이 쓰인다.

 

① 머리글 : 프로그램의 제작자, 저작권, 생성일, 변경 일등을 기술한다.

#######################
# Python calc program
# Python 3.8.4
# written by 리덕토 at 2021_04_10
# This code is protected by XXX
# 
# revise-1 2021_04_XX : add sub function
# revise-2 2021_05_XX : add div function
#######################

def add(val1, val2):
    return val1 + val2

def sub(val1,val2) :
    return val1-val2

def div(val1,val2) :
    return val1/val2

 

② 함수 동작 주석 : 함수의 파라미터, 리턴 값, 동작 방식을 설명한다.(프로그래머도 사람이라, 자연어 기술이 더 읽기 좋다.)


# function_name : do_something
#
# purpose : to get property of user's request 
#
# parameters : 
#  - req : String, should include ":"
#  - parameter : Integer, 1 = length of seperated req, 2 = sum of seperated req
#
# return : Integer, length of handled data or sum of that
#
# Warn : req should only contained Integer value and ":" 
 
def do_something(req, parameter) :
   	data = data.strip(":")
    if(parameter == 1) :
        return len(data)
    elif(parameter == 2) :
    	sum_data = 0 
        for i in range(len(data)) :
        	sum_data += data[i]
        return sum_data

좋은 주석을 단다는 건 어려운 일이다. 주석을 다는 규약이 없는 조직이라는 필연적으로 프로그래머 자기 자신 나름대로의 주석 습관이 생기기 마련이다.

누군가는 모든 코드에 주석을 달 것이고(이건 아닌 거 같다.) 누군가는 나만 보려고 주석 없는 코드를 만들 수도 있다.

다만 좋은 형상관리 도구들(Github)들을 가보면 모두가 사용하는 코드에 주석이 한 줄도 없는 경우는 찾아보기 힘들다.

매너 있는 프로그래머가 되기 위해서, 또 게으른 우리 자신을 위해서 주석을 달아두는 건 분명 도움되는 습관이다. 다음 시간은 Iterator에 대해서 알아보자

오랜만이다. 한 3일 만에 글을 쓰는 것 같은데 바쁜 현생을 살고 왔다. 이번 시간에는 Python에 존재하는 여러 가지 예약어에 대해서 알아보도록 하자. 이미 우리가 배운 것도 많이 있으며 어? 이건 새로 보는 건데? 하는 건 사용법으로 알아보자. 

 

※ 주의 ※ 이번글은 굳이 다 읽은 필요가 없다.


and

논리연산 and, 2개의 bool값을 받아 둘 다 참이면 True, 하나라도 False인 경우 False 가 된다. 

더보기
>>> a = 3
>>> b = 5
>>> print(a>2 and b>4)
True
## a>2와 b>4는 둘다 True라 둘을 and 한 결과도 True이다.

>>> print(a<2 and b>4)
False 
## a<2가 거짓이기 때문에 비록 b>4가 True더라도 and의 결과는 False이다.

 

as

별칭을 지정한다. f객체를 open하며 사용하거나, import 되는 모듈의 이름을 별칭으로 만들기도 한다.

더보기
import random as r

print(r.randrange(0,10))

with open("filename.txt","r") as f:
    print(f.read())

 

assert

가정설정문, 특별한 경우를 지정하여 Exception을 발생시킨다.(값 검증에 사용된다.) 여기서 발생되는 Exception은 AssertionError라고 한다. 

더보기
## 코드 : 
fruits = ["apple","orange","strawberry","kiwi"]

loc = int(input("몇 번째 과일을 출력하시겠습니까? : "))
try :
    assert len(fruits)>loc-1, "배열초과"
    print(fruits[loc])
except AssertionError :
    print("올바른 값을 입력")

## 실행 :
몇 번째 과일을 출력하시겠습니까? : 2
strawberry

## 재실행 :
몇 번째 과일을 출력하시겠습니까? : 5
올바른 값을 입력

 

async - await

import asyncio와 같이 사용되며, 비동기 처리방식을 만들 때 사용된다. 함수를 정의할 때 async를 지정하여 네이티브 코 루틴을 생성할 수 있고, await로 해당 코 루틴이 끝나는 것을 기다릴 수 있다.(추후에 자세한 내용 포스팅)

더보기
## 코드 : 
import asyncio

async def add_five(val) :
    print(val,"is val")
    await asyncio.sleep(1.0)
    return val+5

async def add_print(val) :
    print(await add_five(val))

loop = asyncio.get_event_loop()
loop.run_until_complete(add_print(4))
loop.close()

## 출력 : 9는 1초후에 출력된다.
4 is val
9

 

break

가장가까운 반복문을 중단한다

더보기
## 코드 :
for i in range(10) :
	if(i>2) :
		break
	print("HELLO",i)

## 출력 :    
HELLO 0
HELLO 1
HELLO 2

 

class

클래스를 정의한다.(성의 없다.)

더보기
class dog :
    name = ""
    owner = ""

 

continue

가장 가까운 반복문을 다음 step으로 강제 이동시킨다.

더보기
## 코드 : 
for i in range(5) :
	print("loop is not end")
	if(i>2) :
		continue
	print("HELLO",i)

## 출력 : 
loop is not end
HELLO 0
loop is not end
HELLO 1
loop is not end
HELLO 2
loop is not end
loop is not end

 

def

함수를 정의한다.(성의 없다.)

더보기
def my_foo(value):
    print(value)

 

del 

변수 / 객체를 삭제한다. 삭제된 변수 / 객체는 접근할 수 없다.(원칙상 그렇다. 취약점으로 접근하는 경우는 논외로 하자)

더보기
## 코드 :
a=3
print(a)

del a
print(a)

## 출력 :
3
Traceback (most recent call last):
  File "C:/Users/82109/Desktop/123.py", line 5, in <module>
    print(a)
NameError: name 'a' is not defined

 

finally - else

try - except과 같이 사용되며, finally 예약어는 try 나 except에서 실행되고 실행될 블록을, else는 try에서 에러가 검출되지 않을때 실행될 블럭을 지시한다.

더보기
## 코드 :
fruits = ["apple","orange","strawberry","kiwi"]

try :
    print(fruits[3])
except IndexError:
    print("인덱스 에러입니다.")
else :
    print("인덱스 에러가 아닙니다.")
finally :
    print("2케2케해서 출력은 끝입니다.")
## else - finally 중 하나만 써도 되지만, 둘다 쓴다면
## else 다음 finally가 나와야한다.

## 출력 : 
kiwi
인덱스 에러가 아닙니다.
22케해서 출력은 끝입니다.

 

from - import

다른 모듈을 import 한다. from과 같이 사용하여 대상을 지정할 수 있고, 이렇게 가져온 모듈은 앞의 클래스 명의 생략이 가능하다.

더보기
## 코드 :
import random
print(random.randrange(0,10))

from random import *
print(randrange(0,10))

from random import randrange
print(randrange(0,10))

## 출력 : 
3
8
8

 

for

반복을 지시하는 예약어, iteratable 한 객채를 대상으로 반복자를 지정하여 반복한다. 

더보기
## 코드 : 
fruits = ["apple","orange","strawberry","kiwi"]

print("일반적인 for문")
for i in range(len(fruits)) :
    print(fruits[i])

print("\nAdvanced for문")
for fruit in fruits :
    print(fruit)

## 출력 :
일반적인 for문
apple
orange
strawberry
kiwi

Advanced for문
apple
orange
strawberry
kiwi

 

global

변수를 전역 변수로 지정한다.

더보기
## 코드 :
fruits = ["apple","orange","strawberry","kiwi"]

def print_fruits() :
    global fruits
    print(fruits)

print_fruits()
   
## 실행 : 
['apple', 'orange', 'strawberry', 'kiwi']

 

if - elif - else

프로그램 제어문으로 프로그램의 분기를 생성한다.

더보기
## 코드 :
value = int(input("값을 입력하세요 : "))

if(value>50) :
    print("50이상의 값")
elif(value>30) :
    print("30이상의 값")
else :
    print("뭐죠")
   
   
## 실행 : 
값을 입력하세요 : 60
50이상의 값

## 재실행
값을 입력하세요 : 40
30이상의 값

## 재실행
값을 입력하세요 : 20
뭐죠

 

in 

포함을 검사하는 예약어. [원소] in [자료구조]로 사용하여 bool값(True, False)을 반환한다. not과 같이 사용하여 not in으로 사용할 수 있다.

더보기
## 코드 : 
fruits = ["apple","orange","strawberry","kiwi"]

if("apple" in fruits) :
    print("apple은 있다")
    if("app" in fruits[fruits.index("apple")]) :
        print("물론 app도 있다")

## 출력 : 
apple은 있다
물론 app도 있다

 

is

동등함을 검사하는 예약어 == 과 다른 점은 같은 값을 가리키느냐(==), 객채를 가리키느냐(is) 차이이다. not과 같이 사용하여 is not으로 쓸 수 있다.

더보기
## 코드 : 
fruits = ["apple","orange","strawberry","kiwi"]

if("apple" is fruits[0]) :
    print("그렇죠 같습니다")

## 출력 :
그렇죠 같습니다

 

lambda

Python에서 익명 함수를 생성하는 예약어이다. (자세한 사용법은 포스팅 예정)

더보기
my_foo = lambda x:x+3
print(my_foo(3))

 

None

값이 없다는 의미이다. NoneType이라는 자료형에 하위 객체인데, 다른 언어의 null처럼 사용된다

 

nonlocal

인라인 함수에서(Nested Function) 자기 위에 존재하는 변수를 지정할 때 사용된다.(추후 자세한 내용 포스팅)

더보기
## 코드 : 
def outer_fun(crit) :
    data = "outer_fun_data"
    def inter_fun() :
        nonlocal data
        data = "inter_fun_data"
        print(data)
    if(crit == 1) :
        inter_fun()
    print(data)

crit = int(input())
outer_fun(crit)

## 실행 :
기준을 입력하세요(1:nonlocal) : 0
outer_fun_data

## 재실행 :
기준을 입력하세요(1:nonlocal) : 1
inter_fun_data
inter_fun_data

 

not

논리 연산 not, 2개의 bool값을 받아 True - False를 반전 시킨다. 

더보기
>>> a=3
>>> a>3
False

>>> not a>3
True
## not의 결과는 반전이다.

 

or

논리연산 or, 2개의 bool값을 받아 둘 중 하나라도 True인 경우 True, 나머지는 False가 된다.

더보기
>> a = 3
>> b = 5
>> print(a>2 or b>4)
True
## b<4가 False이지만, a>2가 True 이기때문에 둘의 or는 True이다.

>> print(a<2 or b<4)
False 
## 둘다 False인 경우 or의 결과또한 False이다.

 

pass

실행할 코드가 없음을 명시한다. 이는 except같이 필수적으로 무언가 실행을 지시하여야 하나, 아무 처리를 하지 않을 때 사용된다.

더보기
## 코드 : 
fruits = ["apple","orange","strawberry","kiwi"]

loc = int(input("몇 번째 과일을 출력하시겠습니까? : "))
try :
    print(fruits[loc])
except IndexError :
    pass

## 실행 : 
몇 번째 과일을 출력하시겠습니까? : 1
orange

## 재실행 :
몇 번째 과일을 출력하시겠습니까? : 5

 

raise

강제 Exception을 발생시킨다.

더보기
## 코드 : 
fruits = ["apple","orange","strawberry","kiwi"]

loc = int(input("몇 번째 과일을 출력하시겠습니까? : "))
try :
    if(loc-1 > len(fruits)) :
        raise IndexError
    print(fruits[loc])
except IndexError :
    print("올바른 값을 입력")

## 실행 : 
몇 번째 과일을 출력하시겠습니까? : 1
orange

## 재실행 :
몇 번째 과일을 출력하시겠습니까? : 5
올바른 값을 입력

 

return 

함수 실행결과의 반환 값을 지정한다.

더보기
## 코드 : 
def add_five(value) :
    return value+5

print(add_five(3))

## 출력 :
8

 

try - except

try 코드 블록에서 에러가 발생할 블록을 검사하여 에러를 except 코드블록에서 처리한다.

더보기
## 코드 :
fruits = ["apple","orange","strawberry","kiwi"]

try :
    print(fruits[6])
except IndexError:
    print("인덱스 에러입니다.")

## 출력 :
인덱스 에러입니다.

 

with

객체에 대한 __enter__()와 __exit__()을 자동으로 실행시켜준다. 정말 훌륭한 기능은 file 객체의 f.open()과 f.close()를 자동 호출해준다는 점이다.

더보기
with open("filename.txt","r") as f :
    print(f.read())

 

while 

반복을 지시하는 예약어 조건이 참 일동 안 loop를 반복한다.

더보기
## 코드 : 
fruits = ["apple","orange","strawberry","kiwi"]

cnt = 0
while(cnt<len(fruits)) :
    print(fruits[cnt])
    cnt+=1

## 출력 : 
apple
orange
strawberry
kiwi

 

yield

generator 객체의 다음 반환될 값을 설정한다.(추후에 자세히 포스팅 예정) yield from으로 다른 iteratable 한 객체를 반환할 수 있다.

더보기
## 코드 :
def my_generator() :
    a=[1,2,3]
    for i in range(len(a)) :
        yield a[i]

my = my_generator()
print(next(my))
print(next(my))
print(next(my))

## 출력 :
1
2
3

파이선 3.8을 기준으로 만든 35개의 예약어 사전이다(맞겠지? 다 적긴 한 거 같은데...). 필요한 게 생길 때 와서 보면 좋을 것 같다. Python에는 내장 모듈로 keyword를 제공한다. keyword는 파이선에서 제공되는 여러 가지 예약어와 관련된 함수를 정의해두었으니, 사용해보는 것을 추천한다. 

우리는 저번 학생 관리 프로그램을 만들 때 보다 문자열을 보다 세부적으로 다룰 수 있게 되었고, 정의된 모듈을 가지고 올 수 있게 되었으며, 사용자와 상호작용할 수 있는 방법을 알았으고 이를 파일로 입력 / 출력할 수 있는 방법을 알았다. (WOW 이렇게나 발전하다니, 오늘 저녁은 치킨이닭)

 

이제 이러한 새로 배운 지식으로 우리의 학생관리 프로그램을 다음의 조건들에 맞게 한 단계 진화시켜보자

 

추가된 개발조건

① 같은 폴더에 new_student.txt를 생성하고 그곳에서 학생정보를 가져와서 입력받는 기능을 추가할 것
② 통과한 학생에 대해서만 콘솔로 성적 정보를 입력받는 기능을 추가할 것

③ 모든 학생정보를 출력할 수 있는 기능을 students로 출력할 수 있게 기존 기능을 개선할 것
④ 파라미터는 students만 받아 랜덤 한 학생의 정보를 출력하는 기능을 추가할것(random 모듈의 randrange함수를 사용할 것)

 

* new_student.txt는 [학생 번호] [학생 이름]이 여러 줄로 구성된 파일이다.
* 이제 입출력을 배웠으니, students는 초기화 하자

 

우선 자신이 코딩할 수 있는 부분을 코딩해보고 아래의 필자의 코드를 참고하도록 하자(아직도 우리는 최상의 코드를 만들 수 없다 ㅠㅠ)


학생관리 프로그램 ver1보다는 훨씬 쉬워 보인다. 처음 조건부터 개발 착수해보자

① 같은 폴더에 new_student.txt를 생성하고 그곳에서 학생정보를 가져와서 입력받는 기능을 추가할 것

예시로 다음과 같은 new_students.txt를 생성하자

P_01 CHEELL
R_03 GLAADOS
T_04 SOMEONE

①은 다음과 같이 구현하면 될 것 같다.

students = dict()
## 새마음 새출발 새Dictionary

def add_students_file(target_dict) :
    with open("new_students.txt","r") as f:
        data = f.readlines()
        for i in range(len(data)) :
            temp_data = data[i].strip()
            student_num = temp_data.split()[0]
            student_name = temp_data.split()[1]
            target_dict[(student_num,student_name)] = list()
    with open("new_students.txt","w") as f :
        f.write("")

코드를 해석해보자. 저번과 비슷한 느낌으로 target_dict은 함수 내부에서 수정이 가능하니 파라미터로 받아왔고, with open("new_students.txt","r") as f를 통해서 파일 핸들을 읽기 모드로 열었다. 그 후 data에 f.readlines()로 나온 리스트를 저장했고, (현재 data = ["P_01 CHEELL", "R_03 GLAADOS"]) for문을 통해 data의 공백을 strip() 함수로 제거해 준후, 공백을 기준으로 나누어 이를 튜플로 target_dict;의 key로 삼아 list로 value를 초기화했다. 추가로 받아온 내용은 지우기 위해 다시 한번 f를 덮어쓰기 모드("w")로 열어 빈 값을 넣어주었다.

 

② 통과한 학생에 대해서만 콘솔로 성적 정보를 입력받는 기능을 추가할 것

②는 다음과 같이 구현할 수 있다.

def add_scores(target_dict) :
    keys = list(target_dict.keys())
    values = list(target_dict.values())
    stop = [False for i in range(len(keys))]

    for i in range(len(stop)) :
        if(len(value[i]) == 0 ):
            continue
        else :
            if(value[i][-1] <= 70) :
                stop[i] = True

    for i in range(len(keys)) :
        if(stop[i] == False) :
            score = int(input(keys[i][1]+"의 성적은? : "))
            value[i].append(score)

우선 target_dict의 key와 value를 list화 해서 각각 keys와 values에 저장하였다. 또한 저번처럼 stop을 만들어 응시한 마지막 시험이 70 이하인 학생들에 대해서는 입력을 받지 않도록 하였다. 중간 for문의 if(len(value[i]) ==0)는 맨 처음 응시하는 경우 value [해당 학생의 인덱스][-1]을 하면 배열의 길이가 0이라 마지막을 찾을 수 없어 에러가 발생하기 때문에 continue를 지시하였다. 그 후 stop[i]값이 False인 학생들을 int(input(score = int(input(keys[i][1]+"의 성적은? : "))을 하여 응시결과를 입력받았다.

* stop을 생성한 stop = [False for i in range(len(keys))] 은 Python의 고급 기능인 내포(Comprehension)인데, 조만간 볼 것이다.(아니 미 X놈아 그러면 지금 어떻게 하라고!)

 

③ 모든 학생정보를 출력할 수 있는 기능을 students.txt로 출력할 수 있게 기존 기능을 개선할 것

③은 다음과 같이 구현할 수 있다.

def show_students(target_dict,show_type) :
    keys = list(target_dict.keys())
    values = list(target_dict.values())
    if(show_type == 1) :
        for i in range(len(target_dict)) :
            print("학생번호 "+keys[i][0]+": "+keys[i][1])
    elif(show_type == 2) :
        with open("student.txt","w") as f:
            for i in range(len(target_dict)) :
                f.write("학생번호 "+keys[i][0]+": "+keys[i][1]+"\n")

ver1에서 만들었던 show_students함수를 개량했다. 콘솔로 출력할지(show_type : 1), 파일로 출력할지(show_type : 2)를 지정하였고, show_type에 따라 출력을 달리하였다. 파일로 출력할 땐 마지막에 개행을 함으로 가독성을 높였다.(write() 함수는 end="\n"뭐 이런 파라미터가 없다. print와 다르게 개행을 명시적으로 해주어야 된다.)

 

④ 파라미터는 students만 받아 랜덤한 학생의 정보를 출력하는 기능을 추가할 것(random 모듈의 randrange함수를 사용할 것)

마지막 조건인 ④는 일단 random이라는 모듈을 불러오고 randrange()의 사용을 보아야 한다. randrange()는 2개의 정수 파라미터를 받아 그 사이의 값(인덱스처럼 계산된다. randrange(0,10)이면 0~9중 하나 반환)을 랜덤으로 반환하는 함수이다. 다음과 같이 구현할 수 있다.

import random

def rshow_students(target_dict) :
    limit = len(target_dict)
    keys = list(target_dict.keys())
    o_index = random.randrange(0,limit)
    print("학생번호 "+keys[o_index][0]+": "+keys[o_index][1])

우선 random 모듈을 import 했다. 함수 내부에서는 randrange의 사용을 위해서 범위를 설정할 limit정수를 len(target_dict)으로 초기화했고, 출력했다.

 

이제 우리 학생관리 프로그램의 전체 코드는 다음과 같다.

import random

students = dict()

def add_students(target_dict, stu_num, stu_name) :
    target_dict[(stu_num, stu_name)] = list()

    
def show_students(target_dict,show_type) :
    keys = list(target_dict.keys())
    values = list(target_dict.values())
    if(show_type == 1) :
        for i in range(len(target_dict)) :
            print("학생번호 "+keys[i][0]+": "+keys[i][1])
    elif(show_type == 2) :
        with open("student.txt","w") as f:
            for i in range(len(target_dict)) :
                f.write("학생번호 "+keys[i][0]+": "+keys[i][1]+"\n")

def rshow_students(target_dict) :
    limit = len(target_dict)
    keys = list(target_dict.keys())
    o_index = random.randrange(0,limit)
    print("학생번호 "+keys[o_index][0]+": "+keys[o_index][1])
    
                

def add_score(target_dict, score_list) :
    keys = list(target_dict.keys())
    values = list(target_dict.values())
    stop = [False,False,False,False,False]

    for i in range(4) :
        add = True
        for j in range(len(values)) :
            if(stop[j] == True) :
                continue
            else :
                values[j].append(score_list[i][j])
                if(values[j][-1] <= 70) :
                    stop[j] = True
                    
def add_students_file(target_dict) :
    with open("new_students.txt","r") as f:
        data = f.readlines()
        for i in range(len(data)) :
            temp_data = data[i].strip()
            student_num = temp_data.split()[0]
            student_name = temp_data.split()[1]
            target_dict[(student_num,student_name)] = list()
    with open("new_students.txt","w") as f :
        f.write("")

def add_scores(target_dict) :
    keys = list(target_dict.keys())
    values = list(target_dict.values())
    stop = [False for i in range(len(keys))]
    print(keys,stop)

    for i in range(len(stop)) :
        if(len(values[i]) == 0 ):
            continue
        else :
            if(values[i][-1] <= 70) :
                stop[i] = True

    for i in range(len(keys)) :
        if(stop[i] == False) :
            score = int(input(keys[i][1]+"의 성적은? : "))
            values[i].append(score)
    

뿌듯한 마음이 들지 않는가? 이번 시간이 완벽하게 이해가 되었다면 어디 가서 이제 제가 Python좀 씁니다라고 자랑해도 된다. 이번 시간까지 해서 길다면 길고 짧다면 짧았던 Python과 친해지기가 끝이 났다. 아직 얼굴만 좀 아는 사이이고, 조금 더 깊은 관계를 맺기 위해서는 가야 할 길이 태산처럼 많이 남아있다. 학습을 할 때 조바심을 내는 것은 정말 좋지 않다. 빠르게 흥미가 떨어지고, 두 번 다시 안 보게 되는 것만큼 학습을 좀먹는 것도 드물다고 생각한다. 이제 필자는 Python과 "매우"친해지기 포스팅의 클래스와 객채를 준비하여 다시 한번 여러분들의 Python의 세계의 길잡이를 역할을 하겠다.

사실 입출력(콘솔)의 바로 다음 포스팅을 입출력(파일)으로 하려 했는데, 명절 부모님의 마음으로 "얘 이것도 가져가렴"하다 보니 문자열 기본 다지기와 모듈에 관한 이야기를 하고 드디어 입출력(파일)을 포스팅한다.  사실 개발하는 사람들이나 Python콘솔의 화면을 보고 있지(히히 콘솔 까맣다 이쁘다.) 컴퓨터사용자들이 Python콘솔을 보고 있진 않다. 프로그램에서 입출력 화면을 구현해주거나, 파일을 통한 입출력을 하는 것이 많기에 이번 시간은 우리의 능력을 더욱 끌어올려주는 내용으로 준비했다.


파일 입출력 - 읽기

다음의 글을 복사해서 text.txt를 준비하자

Hello! I'm Python study
THIS IS My FIRST TIME TO PYTHON
PYTHON IS FUN
IM LIAR

Python에서 파일을 처리하기 위해서는 우선 파일을 지정하는 변수가 있어야한다. 관습적으로 f를 변수명으로 사용한다. 다음의 코드를 text.txt가 있는 곳 같은 폴더 안에 생성하자.

 

f = open("text.txt","r")

* open함수는 len(), sum()같은 built-in 함수로 첫 번째 파라미터로 파일의 이름(절대 / 상대 경로 다 된다.)과 두 번째 파라미터로 모드를 지정한다.(모드 목록 : r(읽기), w(덮어쓰기), a(이어 쓰기), b(바이너리), t(텍스트), +(업데이트), x(생성 검사))

(두 번째 파라미터는 기본값 읽기로 필수는 아니지만 지정하는 것이 명시적으로 좋다.)

 

이렇게 open함수로 f는 파일 "text.txt"에 대한 읽기 작업을 할 수 있다.(모드가 "r"이다) 주로 사용하는 함수는 다음의 것들이 있다.

>> f = open("text.txt","r")
>> data = f.read()
>> print(data)
Hello! I''m Python study
THIS IS My FIRST TIME TO PYTHON
PYTHON IS FUN
IM LIAR

>> f = open("text.txt","r")
>> data = f.readline()
>> print(data)
Hello! I''m Python study\n

>> f = open("text.txt","r")
>> data = f.readlines()
>> print(data)
["Hello! I''m Python study\n", 'THIS IS My FIRST TIME TO PYTHON\n', 
'PYTHON IS FUN\n', 'IM LIAR']

## f = open("text.txt","r")을 계속 써주는 이유는 f는 자기가 읽은 마지막 부분을 
## 기억하기 때문이다. 만약 f.read()를 한 후 f.readline()할 경우 이미 마지막까지 읽었기에 
## f.readline()은 아무값도 가져오지 않는다.
## 물론 지금은 함수의 사용을 보여주기위해 계속 f를 열었고, 보통 이렇게 안한다.

 

파일 입출력 - 쓰기

쓰기 또한 f을 열고 f에 대한 쓰기 함수를 통해서 작성할 수 있다. 대표적인 쓰기 함수는 write, writeline들이 있지만, wrtie만 다루면 보통 문제 될 건 없다.

>> f = open("text.txt","a")
>> f.write("hello!")
## 지금은 이어쓰기(a)모드로 text.txt를 열었기에 마지막줄에 hello!가 추가되어 있다.
## write함수는 파라미터로 전달된 값을 쓰고(이건 무조건 문자열만 받는다)
## write함수도 반환이 있는데, 쓴 글자수를 정수로 반환한다.

>> f = open("text.txt","w")
>> f.write("hello!")
## 이번에는 덮어쓰기(w)모드로 text.txt를 열었기에 모든입력되 있던 글자는 없어지고
## hello! 만 text.txt에 있다.

 

파일 입출력 - 유의사항

* 파일에 대한 작업이 끝난 다음에는 close() 함수를 호출해서 꼭 파일 핸들(처리하는 부분)을 닫아주어야 한다! [아래 참조]

>> f = open("text.txt","a")
>> f.write("Do something")
>> f.close()
## 그렇지 않으면 python에서 계속 파일의 접근권을 가지고 있어 그 파일에 대한 작업이 
## 프로그램 실행 끝까지 불가할 수도 있다.

이러한 것을 방지하기 위해 with문을 소개한다.

 

with ~ as

with문의 사용방법은 다음과 같다.

with open("text.txt","r") as f:
    data = f.read()

이런 식으로 with는 실행할 함수와 as는 그 결과를 받아줄 변수를 정의한다. 이 변수는 with문 안에서 밖에 사용을 못하기 때문에 data = f.read()까지 with블록이 끝나고 난 후에는  f.close()가 자동으로 실행되어 파일 핸들 관리가 용이해진다.

 

* 이 as에 대해서는 할 말이 조금 더 있는데, 이름이 긴 모듈들에 대해서도 as를 많이 사용한다. 예를 들면 데이터 처리 모듈인 pandas는 import pandas as pd로 사용하고 크롤링 모듈인 BeautifulSoup4도 from bs4 import BeautifulSoup as bs로 사용하기도 한다.

 


이제 우리는 파일 입출력을 자유롭게 할 수 있게 되었다! 사실 입출력은 버퍼를 이용한 더 효율적인 방법이 존재하고, 입출력을 도와주는 FileInput같은 모듈들도 많이 이용하고 있다. 그러나 open함수를 통한 기본적인 파일입출력을 정확하게 이해하는 것이 추가적인 학습 및 프로그래밍에 많은 도움을 줄 것이라고 확신한다. 다음 시간에는 Python과 친해지기 마지막시간으로 저번에 만들었던 학생관리 프로그램ver1을 조금 더 업그레이드하는 시간을 가져보자

Python은 정말 어마어마한 사용자가 있다. 보통의 경우 우리가 "와 이런 함수를 만들어야겠다!"라고 생각한 건 이미 만들어져 있다고 보면 된다.  물론 이런 것들이 Python을 설치했을 때 기본적으로 들어와 있는 것들도 있지만, 인터넷에 올라가 있기에 다운로드하여야 되는 것들도 있다. 이렇게 정의되어 우리가 사용할 수 있는 친구들을 모듈(module)이라고 부르는데 이번 시간에는 모듈 사용으로 사전에 다른 곳에 정의된 함수를 사용하는 방법에 대해서 간단하게 알아보자


모듈의 사용법

우리를 도와줄 모듈은 os라는 모듈이다. 우리의 개발 콘솔에 다음과 같은 명령어를 입력하자

>> import os

os라는 모듈은 시스템의 작업을 도와주는 역할을 한다. (파일 시스템 작업이란 파일의 이동 / 복사 / 삭제 및 현재 작업 경로의 파악과 변경 등의 일을 말한다. os에 내장된 기능은 훨씬 많지만, 복잡한 기능은 오히려 다른 모듈이 잘 구현되어 있는 경우가 많다.)

 

모듈을 import 했으면 끝이다...

??? 이게?

맞다 사용하기만 하면된다. 파이선은 이렇게 간단하게 모듈을 사용할 수 있다. 

 

지금은 이해하기 어렵겠지만 os모듈에서 가져온 함수들은 os에 속한 함수를 사용한다는 의미로 앞에 os. 을 붙여야 한다.

다음의 예시를 보자

>> import os
>> os.getcwd()
'C:\\Users\\[사용자명]\\AppData\\Local\\Programs\\Python\\Python38'
## os.getcwd()는 현재 작업환경이 어디에 있는지를 문자열로 반환한다.
## 이 경로는 당연히 사용자마다 다르다.

>> from os import *
## 위와같은 방법으로 함수사용에 앞 os.을 생략할 수 있다. import 예약어는 "사용하겠다"이고 
## from 예약어는 당연히 "어디에서 가져온"을 의미하기에 명시적으로 from os 를 하면 함수명만으로 
## 아래처럼 사용할 수 있다. 모듈이 많아지면 많아질수록 헷갈리기에 선택적으로 잘 사용해야된다.
>> getcwd()
'C:\\Users\\[사용자명]\\AppData\\Local\\Programs\\Python\\Python38'

* 아래처럼 from으로 위치를 정해주면 os. 를 생략할 수 있다는 것도 알아두면 좋다.

 

이렇게 바로 가져올 수 있는 모듈은 Python이 설치될 때 기본적으로 들어있는 모듈(built-in module)들로 자신의 Python설치경로 Lib(라이브러리의 약자다) 폴더에 정의되어있다. 이 외에도 다른 사람들이 만든 모듈의 자신의 Python환경에서 사용할 수 있는 방법 또한 당연히 존재한다.

 

 

외부 라이브러리 가져오기 - pip

pip( Pip installs Packages or Pip Installs Python" 재밌는 이야기 해줄까요? pip는 약자는 pip가 들어가요 히히히히힣) 우리 환경에 패키지(일련의 코드나 폴더들의 집합체)를 설치하고 관리해주는 도구이다. 리눅스를 사용하는 사람이라면 Python버전의 apt-get이나 dpkg라고 생각해주시면 되겠다. pip에 관한 자세한 설명은 별도의 포스팅에서 하는 것을 하고(훌륭한 프로그램이라 사용법을 조금 집중해서 설명하고 싶다.) 바로 라이브러리를 한번 가져와보자

 

환경변수 설정 후 pip로 모듈 가져오기

* 환경변수 세팅이 끝나 있는 사람은 만 보면 된다.

* 본 설명은 Window를 기준으로 되어있다.

① [윈도 키] + [Pause Break]로 컴퓨터 설정으로 들어와서 [고급 시스템 설정] - 환경변수를 선택한다.

② 시스템 변수[S]의 PATH를 편집하여 [새로 만들기]로 설치한 Python경로의 scripts 폴더를 넣어주면 끝!

* 자신이 Python을 어따설치한지 모르는 분들을 위해 : Python 3.X 사용자는 사용자 폴더 아래 있는 appdata\local\programs 아래, Python 2.X 사용자는 C아래의 Programs Files(X86), anaconda사용자는 anaconda 설치경로 아래 anaconda3/bin에 pip가 있다.

 

③ 명령 프롬포트(cmd)를 실행하고 다음의 코드를 입력하자

* Python2 사용자의 경우 pip3가 아닌 pip를 사용하시면 되겠다.

C:\> pip3 install requests

에러 없이 정상적으로 설치가 되었다면 아까 확인한 Lib(라이브러리) 안 site-package폴더 아래 requests 관련된 패키지가 설치된 것을 확인할 수 있을 것이다. 이럴 것도 없이 Python 개발 콘솔에 import requests를 입력해서 에러가 없으면 끝이다.

 

 

모듈의 순서와 유효 범위

당연하게도 아무 데나 둔다고 import 할 수 있는 것은 아니다. Python은 다음의 순서로 import를 시도한다.

① sys.modules(이미 import 된 모듈을 dictionary로 관리)

② built-in modules(Python에서 제공하는 공식 라이브러리)

③ sys.path

 

①과 ③은 sys라는 모듈을 import 해서 확인할 수 있다.

>> import sys
>> sys.modules
## 보통 이거 조금 길다 예시는 생략한다.

>> sys.path
## 이건 환경변수 PATH값을 가져온다.

 

내가 만든 함수도 모듈로 물론 import 할 수 있다. 다음과 같은 a.py를 만들고 같은 폴더에서 b.py를 작업하고 있다고 하자

## a.py ##
def some_function() :
    print("a에서 실행된 파일입니다.")

 

코드 : 
## b.py ##
import a
a.some_function()

출력 :
"a에서 실행된 파일입니다."

이런 식으로 말이다. 


이번 시간에는 모듈의 사용방법을 알아보았다 이제 다른 사람이 작성한 훌륭한 코드들을 루팡 할 수 있게 되었다. 자유자재로 사용할 수 있게 된 것이다. Python에는 너무나도 훌륭한 모듈이 많기에 친해지기 강의가 끝나면 모듈별로 알아보는 module 집중탐구 시간을 가지고자 한다. 다음 시간에는 파일 입출력에 대해서 알아보도록 하자

+ Recent posts