사실 입출력(콘솔)의 바로 다음 포스팅을 입출력(파일)으로 하려 했는데, 명절 부모님의 마음으로 "얘 이것도 가져가렴"하다 보니 문자열 기본 다지기와 모듈에 관한 이야기를 하고 드디어 입출력(파일)을 포스팅한다.  사실 개발하는 사람들이나 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 집중탐구 시간을 가지고자 한다. 다음 시간에는 파일 입출력에 대해서 알아보도록 하자

문자열이란 무엇인가? 

훗. 이제 우리에게는 쉬운 소리다. 다들 알고 있다시피 문자열은 변하지 않는 리터럴이면서 큰따옴표(")나 작은따옴표(')로 둘러싸인 값을 의미한다. 이번 시간에는 파일 입출력을 제대로 공부하기 전에 문자열에 대한 여러 가지 이야기를 하고자 한다.

* 굳이 이걸 파일 입출력 전에 하는 이유는 파일 입출력과 파싱은 해당 언어에서 문자열에 대한 이해를 강요하기 때문이다. 필자 탓하지 말아 주라. 나도 파싱 할 때면 한숨부터 나온다.

 


문자열 함수

문자열에 대해서 처리되는 함수는 특정한 목적의 프로그램을 만드는 것이 아닌 이상 자주 사용되는 것이 정해져 있다.

 

아래 코드는 필자가 생각하는 적어도 이 정도는 알아야 문자열을 핸들링하는데 문제가 없을 것이라고 생각하는 수준의 함수들이다. * 가독성을 위해서 print문은 제외하고 함수의 반환문을 실행결과로 적어두었다.

>> my_label="PUT YOUR ILLINAIRE SIGN"
>> my_label.count("L")
2
## count함수는 문자열에서 셀 문자를 파라미터로 받고 그 수를 리턴한다. 추가적으로 int정수를 주어 
## count를 시작, 끝낼 인덱스를 지정할 수 있다.

>> my_label.encode("UTF-8")
b"PUT YOUR ILLINAIRE SIGN"
## 문자열을 UTF-8로 인코딩한다. 인코딩이란 컴퓨터 문자열의 집합인데, 우리가 보고 있는 글자,
## 심지어 이 글도 어떠한 문자열 집합으로 인코딩되어 있는 것이다.
## 리턴은 UTF-8로 인코딩된 문자열값(바이트) 이다.
## 문자열 앞에 b가 붙은건 이것이 바이트값임을 의미하고, 문자열이 아니다.

>> my_label.endswith("SIGN")
True
## 문자열이 파라미터로 넘긴 값으로 끝나는지 확인하여 bool값(True / False)를 리턴한다.
## 파일 확장자(.txt .png 등)를 검사할 때 유용한 기능이다.

>> my_label.find("YOUR")
4
## 파라미터로 넘긴 문자열이 몇번째 인덱스에서 시작하는지를 반환한다. 못찾으면 -1반환

>> my_label.join("aaa")
'aPUT YOUR ILLINAIRE SIGNaPUT YOUR ILLINAIRE SIGNa'
## 파라미터로 넘긴 값을 호출한 문자열(이경우에는 my_label)로 이어붙인다.
## 이 join함수는 문자열뿐만아니라 배열도 파라미터로 받을 수 있는데, 그 배열을 이어붙여 문자열로 
## 캐스팅하는 형태를 많이 쓴다.(ex : a=["1","2","3"]  >> "".join(a)의 출력은 "123"이 된다.)

>> my_label2 = "    s "
>> my_label2.strip()
"s"
## 문자열 앞뒤로의 공백을 제거한다. 공백또한 len함수에 포함이 되거나, 인덱스관련 작업을 방해
## 하는 경우가 많아 파일의 입출력 / 사용자의 입출력을 필터링하는데 주로 사용된다.

>> my_label.replace("YOUR","MY")
'PUT MY ILLINAIRE SIGN'
## 철수 교실에서 봤던 replace함수이다. 파라미터1의 문자를 파라미터2의 문자로 변환하여 리턴한다.

>> my_label.split(" ")
['PUT', 'YOUR', 'ILLINAIRE', 'SIGN']
## 문자열을 파라미터로 넘겨진 문자기준으로 나누어 배열을 생성하고 그 배열을 리턴한다.
## 위에는 " "으로 공백을 명시적으로 적어두었지만 생략하면 자동으로 공백을 기준으로 실행된다.

이 외에도 문자열의 처음을 대문자로 해주는. capitalize()나 모든 문자를 대소문자로 만드는 upper()와 lower(), 이들을 검사하는. is뭐 함수, tab을 space로 변환해주는. expandtabs() 등 여러가지 함수들도 많다. 한 번씩 찾아보는 것도 실력 향상을 위해서 알아두면 좋다.

 

특수문자의 사용

애초에 큰따옴표(")나 작은따옴표(')가 문자열의 시작과 끝으로 인식이 되고, 저번 시간에 본 \n NewLine처럼 예약된 특수문자가 있다 보니 문자열을 사용할 때 특수문자를 처리하고 싶으면 다음 표를 참고해서 처리해야 된다.

특수문자 해석
\ 다음 줄의 연속
\' 작은 따옴표(')
\" 큰 따옴표(")
\\ \문자
\n New Line
\t TAB

* 이 특수문자들은 문자열 안에서 쓰일 때 해석으로 변환되어 사용된다.

* 문자열 앞에 r을 붙이면"(ex : r"PUT YOUR \t ILLINAIRE \n SIGN") raw문자열로 해석되어 이러한 특수문자가 해석으로 치환되지 않고 그대로 출력된다. 한 번씩 해보길 바란다. 이게 파일 경로를 처리할 때 도움이 많이 된다. (ex : C\USERS\USER은 \가 들어가 특수문자 처리를 위해 \\로 입력을 해야 되는데 raw로 변환하면 그냥 경로 그대로 처리 가능하다.)

 

예약어 in, is, not

딱히 어디에 넣기 애매해서 문자열 기본 포스팅에 in의 사용법을 올리게 되었다. 나중에 적당한 포스팅이 생기면 is의 설명은 이곳의 설명 범위를 벗어나기에 이동할 예정이다. in과 is 그리고 not은 Python에 이미 정의된 예약어인데, 논리 연산자와 비슷한 역할을 한다.

>> a = [1,2,3]
>> 1 in a
True

>> 3 not in a
False

>> my_val = "ILLINAIRE"
>> "ILL" in my_val
True

>> b = [1,2,3]
>> a is b
False

in의 사용법은 위와 같이 in뒤의 값이 in앞의 값을 포함하고 있는지를 판단하여 True, False로 반환한다는 점이다. is, in, not모두 조건문의 조건으로 많이 사용한다.

 

*a is b가 False인 이유(이 시간의 내용을 벗어난다, 더보기 참조)*

더보기

이는 is라는 예약어가 비교하는 두 대상이 같은 메모리를 가리키는지를 판단해서 그렇다.

 

변수를 생성을 하면 변수의 이름은 특정한 메모리 공간과 매칭이 되는데, (예를 들면 위 상황에서 a라는 변수는 호텔방 101호를 배정받은 거고 b라는 변수는 102호를 배정받은 거다)  같은 곳을 가리키고 있는지 검사하는 is의 특성상 값이 같더라도 객체가 달라 다르다고 판단을 한다.(101호에 치킨이 있고 102에 치킨이 있어도 이는 다른 방이기에 is는 False를 반환한다.)

 

따라서 값을 비교하는 경우라면 ==을 사용하는 것이 현명하다. (단 리터럴 문자와 변수를 비교할 경우 값이 같을 경우 True를 반환한다. 이는 리터럴에 대한 메모리 재활용 덕분인데, 이는 이 포스팅의 설명 범위를 한참 벗어난다.)

 


이번 시간에는 파일 입출력에 들어가기 전 문자열을 조금 더 자유자재로 다룰 수 있는 기초 다지기 시간을 가져보았다. 함수를 모두 외울 필요는 없다. 그걸 다 머리에 넣고 있다고 좋은 프로그래머라고 단정할 수 있는 사람은 없다.(오히려 프로그래머라면 메모리 낭비라고 화낼 것이다.) 필요한 기능이 생기면 Python 공식 API 등을 찾아봐서 찾아 쓰는 것을 추천한다. 실력을 늘릴 수 있는 좋은 방법이다.

우리는 성공적으로 첫 번째 프로그램인 학생관리 프로그램을 개발했다. 이번 개발의 잘생긴 발주자는 다행히도 학생의 리스트와 분기별 성적을 주었지만, 문제는 얼마 가지 않아 발생한다. 

새로운 정보를 입력할 수는 없는 거야?

아... 우리의 치부를 들켰다. 아직 우리는 Python의 입력과 출력에 대해서 자세히 모르는 거 같다. 하지만 걱정 마시라, 이번 강의가 끝나면 새로운 정보의 콘솔 입력, 출력을 자유자재로 할 수 있을 것이다.


입력 - input

일단 입력 또한 함수로 진행된다. 변수 my_val을 사용자의 입력을 받아서 저장하는 예시로 이를 실험해보자

>> a = input("정보를 입력하세요 : ")
정보를 입력하세요 : 나는 최고의 Python 프로그래머

>> print(a)
"나는 최고의 Python 프로그래머"

와우 이렇게 간단할 수가. 그럼 이번에는 사용자에게 정수를 입력받아서 저장해보자

>> a = input("정수를 입력하세요 : ")
정보를 입력하세요 : 3

>> print(a)
"3"

어? 이건 문자열 3이다. 이를 a = int(a)로 캐스팅해도 되지만, 좀 더 Pythonic 하게 받는 순간 캐스팅을 해보자

>> a = int(input("정수를 입력하세요 : "))
정보를 입력하세요 : 3

>> print(a)
3

좋아 이제 완벽하다. 

* 눈치 빠른 사람들은 알겠지만, input의 parameter로 전달된 "~를 입력하세요"는 입력 직전 콘솔 창에 띄워줄 정보이다. 물론 파라미터를 전달하지 않고 a=input()처럼 사용할 수 있다. 이런 경우 입력 직전 아무말도 없이 입력받는다.

 

출력 - print 심화

우리가 평소 사용하던 print함수, 돋보기로 들여다보자 print함수는 사실 복수개의 파라미터를 전달받을 수 있다.

>> my_val = 3

>> print(my_val)
3

>> my_list = [1,2]

>> print("우리의 정수는",my_val,"우리의 배열은",my_list)
우리의 정수는 3 우리의 배열은 [1,2]
## 복수개의 파라미터 전달 가능

이렇게 파라미터를 사실상 제한 없이 전달받을 수 있다. 우리도 이런 함수를 만들 수 있지 않을까??(물론 가능하다 Python과 매우 친해지기까지 열심히 공부하자)

 

또한 print에는 다음과 같은 추가적인 파라미터들이 있다.

>> my_val = 3

>> print(my_val)
3

>> my_list = [1,2]

>> print("우리의 정수는",my_val,"우리의 배열은",my_list,sep="W")
우리의 정수는W3W우리의 배열은W[1, 2]
## sep="X"를 하면 복수개의 파라미터를 전달했을 때 구분자를 "X"로 한다.

>> print("우리의 정수는",my_val,"우리의 배열은",my_list,end="W")
우리의 정수는 3 우리의 배열은 [1, 2]W
## end="X"로 하면 출력의 마지막 글자를 W로 한다. 기본적으로 \n가 지정이 되어있는데,
## \n은 New Line이라는 지정된 문자열이다. print후 자동으로 다음줄로 넘어가는것은,
## print에 기본적으로 end="\n"이 되어있기 때문이다. end="X"등으로 \n를 바꾸면 
## 다음줄로 넘어가지않고 콘솔(>>>)이 이어서나오는것을 확인할 수 있다.

이 외에도 어디로 출력을 지정할지 선택하는 file(기본값 : std.stdout)이나 출력 후 버퍼를 플러싱할지를 선택하는 flush(기본값 : False)등이 있다.

* 파라미터 중에는 함수가 정의될 때 생략되면 기본으로 정해지는 형태도 있다. 우리가 print를 출력할 값만으로 사용할 수 있는 이유도 원래 전달되어야 하는 end, sep, file 등의 파라미터는 default 값(생략될 시 선택되는 값)을 가지고 있기 때문이다.

 


이렇게 사용자로부터 입력을 받는 방법인 input함수와 Python의 print함수의 조금 더 깊은 이해를 가질 수 있게 된 시간이었다. 다음 시간에는 파일 입출력에 들어가기전에 문자열 기본을 다지고, 모듈에 대해서 알아보도록하자

자 즐거운 코딩 시간이다. 우리는 이제 Python을 할 줄 아니, 간단한 프로그램을 통해서 우리가 배운 것을 검증하고 복습하는 시간을 가져보자.

<출처 : https://www.clien.net/service/board/park/12262874>

우리가 만들 영광스러운 첫 번째 프로그램은 학생을 관리하는 프로그램이다. 이 프로그램은 다음과 같은 특징이 있다.(프로그램을 발주한 사람은 필자라고 치자)

 

학생관리 프로그램의 조건

① 학생의 명단은 발주자가 미리 제공한다.

② 새로운 학생을 추가할 수 있는 기능이 있어야 한다.

③ 학생의 전체 명단을 "학생 번호 X : AAA"로 표현하는 기능이 있어야 한다.

④ 성적 저조자는 잘라야 되니, 성적 검사 후 70점 이하인 학생은 다음 분기 시험을 치더라도 입력하지 않게 한다.

⑤ 4분기 별 성적을 학생들에게 한 번에 추가하는 기능

② ~ ⑤는 students를 매개변수로 하는 함수로 생성할 것

⑥ 학생이 0명이 되면 교수님이 위너위너치킨디너

 

다음의 학생정보는 미리 제공된다.(이곳에는 5명의 학생밖에 없다 6명을 만들고 싶지만 불편하게 남기겠다.)

학생번호 이름
P_01 CHEELL
O_02 GLADOOS
R_03 WHEETLEY
T_04 TUULET
A_05 CAAVE

 

다음은 분기별 성적 정보이다.

  1분기 2분기 3분기 4분기
P_01 80 70 60 90
O_02 20 80 70 100
R_03 70 75 80 60
T_04 40 20 30 50
A_05 80 85 90 95

 

실력 향상을 위해서 다음 하나의 변수만 저장하도록 하자. 자료구조도 지정해 준 것을 사용하도록 하자

 

students : ([학생번호], [학생])의 tuple을 key로 사용하고 분기별 점수 배열을 value로 사용하는 dictionary
score_set : 주어지는 분기별 성적, 이중 LIST형태로 관리하자

 

⑥에 의해서 함수로 구현을 하면 되는데, 다행히도 dictionary는 자료구조이기 때문에 파라미터로 전달되면 함수의 내부에서 변경이 가능하다. 

또한 이번 프로그램에 한정해서 dictionary를 반복할 때는 dictionary.keys()와 dictionary.values()를 강제로 list()로 캐스팅해서 사용하도록 하자. 이는 우리가 아직 dictionary 반복을 모르기 때문이다.

 

어느 정도 프로그램에 윤곽이 잡힌 해리들은 아래 더보기를 눌러 아래 필자의 프로그램과 비교해보자

 


 

 

우선 하나하나 요구사항을 보며 무엇을 구현할지 생각해 보자

① 학생의 명단은 발주자가 미리 제공한다.

이것으로 명단을 막일로 적어주자(타이핑도 연습이야 이거 왜 이래)

students = { ("P_01","CHELL") : list(), ("O_02", "GLADOOS") : list(), ("R_03", "WHEETLEY") : list(),\
             ("T_04", "TUULET") : list(), ("A_05", "CAAVE") : list() }

아마 정상적으로 본 포스팅을 따라온 사람이라면, 한 줄에 모든 코드를 입력했을 것이다. 사실 Python은 줄이 너무 길다 싶으면 역 슬래쉬(\)를 통해서 다음 줄에 이어서 변수 정의 등을 할 수 있다.

 

① 조건은 완료했다.

② 새로운 학생을 추가할 수 있는 기능이 있어야 한다.

②에 따르면 새로운 학생을 추가할 수 있는 "기능"이 있어야 한다. 조건대로 함수로 구현해보자

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

추가할 dictionary와 학생번호, 학생 이름을 매개변수로 받아 매개변수로 받은 dictionary에 기존 테이블 형식으로 원소를 추가하는 함수를 생성하였다. 이렇게 ②조건을 완료했다.

 

③ 학생의 전체 명단을 "학생 번호 X : AAA"로 표현하는 기능이 있어야 한다.

③은 단순 출력이니 반복문을 통해서 어렵지 않게 구현할 수 있을 거 같다. 다음의 코드대로 구현해보자

def show_students(target_dict) :
    keys = list(target_dict.keys())
    values = list(target_dict.values())
    for i in range(len(target_dict)) :
        print("학생번호 "+keys[i][0]+": "+keys[i][1])

어려워 보여도 구현을 완료했다.

* dictionary를 반복할 수 있는 방법은 당연히 있다. 단 아직 배우지 않았다. 이는 Python과 친해지기가 끝나면 다음 올릴 예정인 Python과 매우 친해지기에서 알 수 있다.(Advanced-for나 dictionary for를 구글링 해서 봐도 된다.)

④ 성적 저조자는 잘라야 되니, 성적 검사 후 70점 이하인 학생은 다음 분기 시험을 치더라도 입력하지 않게 한다.
⑤ 4분기 별 성적을 학생들에게 한 번에 추가하는 기능

일단 score_set을 등록하자

score_set = [ [80, 20, 70, 40, 80], [70, 80, 75, 20, 85], [60, 70, 80, 30, 90], [90, 100, 60, 50, 95] ]

다음은 score_set을 studetns에 입력하는 함수를 만들어보자

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) :
        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

이번에는 조금 복잡해 보인다. 하나하나 살펴보도록 하자. 일단 stop=[False,False,False,False,False] 까지는 함수에서 사용할 변수를 정의한 것이다. 사용하는 변수는 다음과 같다.

 

keys : 전달받은 dictionary의 key를 저장하는 list

values : 전달받은 dictionary의 value를 저장하는 list(따라서 맨 처음에는 [ [], [], [], [], [] ] 의 이중 LIST가 된다.)

stop : 학생들의 정보를 추가할지 말지는 결정하는 배열, (ex : stop[2] 가 True면 3 학생은 이제 점수 입력을 아니한다.)

 

다음은 for문의 분석이다 for는 i를 반복자로 하는 반복문(반복1)과 j를 반복자로 하는 반복문(반복2) 2개의 반복문이 중첩되어 작성되어있다. 반복1는 각 분기별로 동작할 것을 지시하며, 반복2는 각 분기에 대해(반복1의 블럭) 학생 수만큼 돌며 stop이 True면 넘어가고 아니라면 점수를 등록하고 등록한 점수에 대해서 70점 이하이라면 stop을 True로 하는 역할을 한다.

 

이렇게 구현을 완료했다.

 

전체 코드는 다음과 같다.

 

students = { ("P_01","CHELL") : list(), ("O_02", "GLADOOS") : list(), ("R_03", "WHEETLEY") : list(),\
             ("T_04", "TUULET") : list(), ("A_05", "CAAVE") : list() }
score_set = [[80, 20, 70, 40, 80], [70, 80, 75, 20, 85], [60, 70, 80, 30, 90], [90, 100, 60, 50, 95]]

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

    
def show_students(target_dict) :
    keys = list(target_dict.keys())
    values = list(target_dict.values())
    for i in range(len(target_dict)) :
        print("학생번호 "+keys[i][0]+": "+keys[i][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
    
        

이번 시간에는 지금까지 배운 지식을 동원해서 나만의 조그마한 학생관리 프로그램을 만들어 보았다. 여기까지 오느라 수고 많았다. 이제 여러분은 Python의 기초적인 지식을 학습한 상태이다. 다음 시간에는 입출력에 대해서 알아보도록 하자

함수(Method, Function)

으악! 함수다! 수학 시간에 열심히 보았던 그 f(x) 맞다. 단 혹시 그 f(x)의 구조와 함께 이런 그림이 기억나는가?

<출처 : 위키백과 https://ko.wikipedia.org/wiki/%ED%95%A8%EC%88%98>

함수가 동작하는 방식은 다음과 같다.

① 0개이상의 입력을 받는다.

② 무언가를 하고

③ 1개이상의 출력을 한다.

 

Python에도 함수(메서드)라는 개념이 있다. 위에 함수의 개념과 대응을 하면

① 0개이상의 파라미터를 받는다.

② 무언가를 하고

③ 0개이상의 값을 리턴한다.

 

파라미터라는 것은 입력, 리턴이라는 건 출력 같기도 한데??? 아닌가?? 헷갈려도 괜찮다. 다음 함수의 문법을 보자

이렇게 간단하다니! 함수의 사용은 함수이름과 소괄호() 안에 파라미터를 전달해주면 된다. 가령 우리가 줄기차게 사용해온 print("Hello World")라는 건 print라는 함수에 "Hello World"라는 문자열 파라미터를 전달한 것이다.(파라미터라고 어렵게 생각하지 말자, 단순하게 python에서 파라미터는 메서드에 전달되는 값으로, 무엇이든 파라미터가 될 수 있다.)

 

사용자 정의 함수

함수는 프로그래머가 직접 만들 수 있다. 아래의 성적처리 프로그램을 보자

def calculate(score) :
    if(score > 90) :
        return "A"
    elif(score > 80) :
        return "B"
    elif(score > 70) :
        return "C"
    else :
        return "Shit"

def [함수명](파라미터...)로 원하는 함수를 만들 수 있다. if ~ elif ~ else는 저번시간에 배웠던 제어문인데, 중간에 return "A" 같은 것은 어떤 의미일까? 파라미터로 전달한 score는 임의로 정한 이름이다. 전달되는 score에 대해서 함수 내부적으로 '이러한 일을 하세요'라는 의미이기에 실제로 사용할 때는 score라는 이름보다는 프로그램에서 사용되는 변수나 상수가 전달될 것이다. 위 calculate함수를 만든 다음(컴파일하고) 아래 예시를 따라 해 보자

>> print(calculate(75))
"C"

>> print(calculate(50))
"shit"

>> print(calaculate(score))
## 에러! 함수를 정의할때 사용한 score는 함수사용시에는 존재하는 값으로 치환되어야한다!
## score는 지금 없는 값이다!

 

독특한 것은 calculate(75)가 그대로 print() 함수의 파라미터로 넘어갔다는 점이다. 여기서 우리는 return에 의미에 대해서 알 수 있다.  return이란 함수의 결과이며, 함수가 종료되는 순간 바뀌는 값이다.(즉! 여기서 print(calculate(75))에서 calculate(75)는 score>70에서 return "C" 되기에 결과적으로 "C"가 되어 print(calculate(75))는 print("C")와 같은 코드가 된다.)

 

이러한 반환 값은 지정이 될 수도 안될 수도 있다. 아래의 예시를 보자

def non_return(val) :
    print(val)
    
def yes_return(val) :
    print(val) 
    return val

>> non_return(3)
3

>> a=yes_return(3)
3
>> print(a)
3

 

한 가지 재미있는 점은 자료형이 아닌 고정된 값들은 함수내부에서 변경이 되지 않는다는 점이다. 아래의 예시를 보자

def cal_val(val) :
    val = 5
    
def cal_ref(val) :
    val[0] = 100

>> a = 3
>> cal_val(a)
>> print(a)
3
## 비록 cal_val에서 val=5가 실행되도 a는 3으로 남아있다.

>> my_list = [0,1,2,3]
>> cal_ref(my_list)
>> print(my_list)
[100,1,2,3]
## 자료구조로 전달된 my_list의 0번째 원소는 cal_ref함수의 val[0]=100에 의해 100으로수정되었다.

??? 아니 어떻게 a는 바뀌지 않고 my_list는 바뀔 수 있는 것일까? 이에 구분은 파라미터가 전달될 때 값에 의한 전달(call by value)인지 참조에 의한 전달(call by reference)인지에 따라 구분된다. 자세한 내용은 c언어를 포스팅할 때 이야기하도록 하겠으며(구글링도 도움된다. 컴퓨터에 대한 지식을 높일 수 있는 좋은 기회이다), 지금은 단순하게 자료구조는 함수 내부에서도 변경이 가능하다는 것을 알아두면 좋을 거 같다.

 


이번 시간에는 프로그래밍의 꽃인 함수에 대해서 알아보았다. 함수란 기능을 의미하며, 사용자 정의 함수를 작성하는 방법도 알았으니, 다음 시간에는 지금까지 배운 내용을 통해서 간단한 프로그램을 만들어 보도록 하자

+ Recent posts