python

파이썬 - 절차지향 프로그래밍

EDWARD IS OPTIMISTIC 2021. 3. 4. 17:46
프로그래밍을 배우다 보면 절차 지향 프로그래밍(procedural programming)이나 객체 지향 프로그래밍(object-orient-programming) 혹은 함수형 프로그래밍(functional programming) 같은 다양한 용어를 만난다. 이들 모두 프로그래밍 패러다임의 한 종류다. 패러다임이란 어떤 사물을 바라보는 사고의 틀이나 체계를 말한다. 그렇다면 프로그래밍 패러다임이란 프로그래밍을 어떻게 바라볼 것인지, 어떻게 프로그래밍할 것인지에 대한 인식이나 체계라고 말할 수 있다.

절차 지향 프로그래밍 

절차를 의미하는 procedure은 서브 루틴, 메서드, 함수라고도 불린다. 함수라는 용어가 익숙하니 절차 대신 함수라는 단어를 사용한다. 함수는 입력을 받아 일련의 연산 과정을 거쳐 출력을 내보낸다. 한 번 정의해 두면 어디서든 다시 호출해 사용할 수 있고 이름만 봐도 이 함수가 어떤 일을 하는지 쉽게 알 수 있다. 또한 함수를 만든 사람과 사용하는 사람이 다르다면 사용하는 사람은 함수의 내부 구현은 알 필요 없이 사용법(인터페이스)만 익혀 사용하면 된다. 어떤 일을 수행하는 긴 코드를 기능별로 나누어 함수로 정의하고, 함수 호출을 사용해 코드를 작성하면 다른 프로그래머도 쉽게 프로그램을 이해하고 유지, 보수할 수 있다.

 

"이 프로그램은 어떤 일을 하는가?"에 대한 질문에 쉽게 답할 수 있도록 함수(procedure)를 사용해 프로그래밍하는 것, 이를 절차 지향 프로그래밍이라고 한다. 

 

함수를 사용하지 않고 프로그램을 만든 후 절차 지향 패러다임에 따라 코드를 변경해보자.

#cmd
pip install openpyxl

exam.xlsx 엑셀 파일

openpyxl 모듈을 불러와 exam.xlsx 파일을 열고 현재 활성화된 sheet를 받아온다.  

>>> from openpyxl import *
>>> wb = load_workbook('exam.xlsx')
>>> wb.sheetnames
['Sheet1']
>>> ws = wb.active
>>> ws
<Worksheet "Sheet1">

 

다음으로 엑셀 파일의 셀들을 모두 읽어 들인다. rows는 데이터가 있는 모든 행을 발생자 객체로 반환한다. 그리고 next() 함수로 첫번째 행을 가져와 출력하면 엑셀 파일의 셀들이 들어 있는 것을 확인할 수 있다.

>>> g = ws.rows
>>> cells = next(g)
>>> cells
(<Cell 'Sheet1'.A1>, <Cell 'Sheet1'.B1>, <Cell 'Sheet1'.C1>, <Cell 'Sheet1'.D1>)

 

exam.xlsx에는 1행에 학생 이름과 과목명이 들어있다. 1행을 학생 데이터를 담을 딕셔너리의 키(keys)로 사용한다.

셀의 value에 접근해서 실제 값을 가져온다.

>>> keys = []
>>> for cell in cells:
...     keys.append(cell.value)
...
>>> keys
['name', 'math', 'literature', 'science']

 

이제 학생들의 데이터를 가져와 딕셔너리에 저장한다.

>>> student_data = []
>>> for row in g:
...     dic = {k:c.value for k , c in zip(keys, row)}
...     student_data.append(dic)
...
>>> student_data
[{'name': 'john', 'math': 25, 'literature': 30, 'science': 55}, {'name': 'yang', 'math': 50, 'literature': 45, 'science': 40}, 
{'name': 'timothy', 'math': 15, 'literature': 65, 'science': 90}, {'name': 'melisa', 'math': 100, 'literature': 100, 'science': 100}, 
{'name': 'thor', 'math': 10, 'literature': 15, 'science': 20}, {'name': 'elen', 'math': 25, 'literature': 50, 'science': 100}, 
{'name': 'mark', 'math': 80, 'literature': 75, 'science': 80}, {'name':'steve', 'math': 95, 'literature': 100, 'science': 95}, 
{'name': 'anna', 'math': 20, 'literature': 20, 'science': 20}]

딕셔너리 컴프리헨션을 이요해 학생 한 명의 데이터를 모은 딕셔너리를 만들고, 전체 학생 데이터를 저장하는 리스트에 추가하였다.

 


평균•분산•표준편차를 함수로 만들기

지금 만드는 프로그램에서는 점수의 평균(average)과 점수가 얼마나 퍼져 있는지(dispersion, 산포도)를 분석하기 위한 분산(variance)과 표준편차(standard deviation)만 구한다.

 

우선 성적 평가에 필요한 반의 엑셀 파일을 만든다.

class.xlsx

먼저 함수를 사용하지 않고 코드를 작성한다.

#sequential.py

import openpyxl
import math
# 학생 전체 학생의 평균 : 50점

raw_data = {}
wb = openpyxl.load_workbook('class.xlsx')
ws = wb.active
g = ws.rows

for name, score in g:
    raw_data[name.value] = score.value

scores = list(raw_data.values())

s = 0

for score in scores:
    s += score

avrg = round(s/len(scores) , 1)

s = 0

for score in scores:
    s += (score - avrg) ** 2

variance = round(s/len(scores), 1)

std_dev = round(math.sqrt(variance), 1)

print(
    "평균 : {0}, 분산 : {1}, 표준편차 : {2}".format(avrg, variance, std_dev) 
)

if avrg < 50 and std_dev > 20:
    print("성적이 너무 저조하고 학생들의 실력 차이가 너무 크다")
elif avrg > 50 and std_dev > 20:
    print("성적은 평균 이상이지만 학생들의 실력 차이가 크다. 주의 요망!")
elif avrg < 50 and std_dev < 20:
    print("학생들의 실력차이는 크지 않지만 성적이 너무 저조하다. 주의 요망!")
elif avrg > 50 and std_dev < 20:
    print("성적도 평균 이상이고 학생들의 실력 차이도 크지 않다.")
#실행 결과
평균 : 51.5, 분산 : 1240.2, 표준편차 : 35.2
성적은 평균 이상이지만 학생들의 실력 차이가 크다. 주의 요망!

위의 프로그램을 절차 지향으로 바꿔보자.

 

실행에 필요한 함수를 모아 둘 function.py모듈을 만든 다음 sequential.py를 기능별로 쪼개 함수로 만든다. 

import openpyxl
import math

#엑셀에서 데이터를 가져오는 함수
def get_data_from_excel(filename):
    dic = {}
    wb = openpyxl.load_workbook(filename)
    ws = wb.active
    g = ws.rows

    for name, score in g:
        dic[name.value] = score.value

    return dic

#평균, 분산, 표준편차를 구하는 함수
def average(scores):
    s = 0
    for score in scores:
        s += score
    return round(s / len(scores), 1)

def variance(socres, avrg):
    s = 0
    for score in socres:
        s += (score - avrg) ** 2
    return round(s / len(socres), 1)

def std_dev(variance):
    return round(math.sqrt(variance), 1)

#평가 출력
def evaluateClass(avrg, total_avrg, std_dev, sd):
    if avrg < total_avrg and std_dev > sd:
        print("성적이 너무 저조하고 학생들의 실력 차이가 너무 크다")
    elif avrg > total_avrg and std_dev > sd:
        print("성적은 평균 이상이지만 학생들의 실력 차이가 크다. 주의 요망!")
    elif avrg < total_avrg and std_dev < sd:
        print("학생들의 실력차이는 크지 않지만 성적이 너무 저조하다. 주의 요망!")
    elif avrg > total_avrg and std_dev < sd:
        print("성적도 평균 이상이고 학생들의 실력 차이도 크지 않다.")

함수를 모아 둔 모듈을 완성했으므로 실제 프로그램이 실행될 메인 파일의 코드를 작성한다.

from functions import *

if __name__ == "__main__":
    raw_data = get_data_from_excel('class.xlsx')
    scores = list(raw_data.values())

    avrg = average(scores)
    variance = variance(scores, avrg)
    standard_deviation = std_dev(variance)

    print("평균 : {0}, 분산 : {1}, 표준편차 : {2}".format(avrg, variance, standard_deviation))
    evaluateClass(avrg, 50, standard_deviation, 20)
#실행 결과
평균 : 51.5, 분산 : 1240.2, 표준편차 : 35.2
성적은 평균 이상이지만 학생들의 실력 차이가 크다. 주의 요망!

 

메인 프로그램에선 함수의 이름만 봐도 이 프로그램이 무슨 일을 하는지 알 수 있다. 또한 함수를 어떻게 구현했는지 알 필요 없이 인터페이스만 알면 필요한 함수를 가져다 쓰면 되므로 다른 프로그램도 쉽게 작성할 수 있다.

'python' 카테고리의 다른 글

파이썬 - 인수(argument)  (0) 2021.03.16
클래스 - 메서드 오버라이딩과 다형성  (0) 2021.03.11
클래스 - 클래스 관계  (0) 2021.03.11
객체 지향 프로그래밍  (0) 2021.03.05
함수  (0) 2021.03.03