위코드에서 2개월 간 코딩을 배우면서 함수와 변수를 사용하기만 했지 정확히 왜 사용하고, 왜 함수가 만들어졌고, 언제 사용해야 할 지 등은 모르고 되는데로 사용한 것 같다. 정확히 모르기 때문에 코딩이 더 안된 탓도 있는거 같아서 함수에 대해 자세히 알아보고자 한다.
들어가기 전(순차적 프로그래밍, 비구조적 프로그래밍)
함수에 대해서 알아보기 전에 함수와 클래스가 왜 생긴건지부터 알아보자. (간단하게 이런게 있다고만 알면 된다고 한다.)
1) 순차적(비구조적) 프로그래밍
2) 절차적(구조적) 프로그래밍
3) 객체지향적 프로그래밍
1)순차적 프로그래밍
- 말 그대로 순차적으로 흘러가는 프로그래밍 구조를 의미한다.
- 여기서 말하는 순차적이란 순차를 중점으로 보는 프로그램을 의미한다.(즉 코드의 흐름, 순서에 기바한 프로그래밍이라는 것임)
- 가장 초기에 등장한 코딩 패러다임이다.(모든 프로그램들이 순서대로 읽기 때문에)
- 구조라는 개념이 없기 때문에 goto문을 사용해서 코드를 짜게 된다. 하지만 규모가 커질수록 코딩에 집중하기 보단 서로간의 관계와 흐름만 신경쓰다가 시간만 버리고 있었다고 하며, 따라서 코드의 중복을 최대한 피하기 위해서 코드를 단위화할 방법을 모색하게 되었다.
goto문 : 특정 줄 번호나 레이블로 건너뛰거나 돌아갈때 쓰는 명령어
프로그래밍 패러다임
프로그래밍 패러다임
2) 절차적 프로그래밍, 구조적 프로그래밍
절차적 프로그래밍을 절차지향 프로그래밍이라고 하는데, 절차지향프로그래밍이란 존재하지 않는다. 절차란 함수를 의미한다. 따라서 반복될 가능성이 있는 모듈을 재사용 가능한 단위(함수)로 나눈 프로그래밍이라고 할 수 있다.
절차적 프로그래밍이 발전된게 구조적 프로그래밍이다. (절차적 프로그매밍이 함수를 기준으로 나뉜다면, 구조적 프로그래밍은 모듈을 기준으로 나뉘기 떄문에 동의어는 아니다. 하지만, 모듈은 물리적인 소스파일을 이야기하는 것이기 떄문에 완전히 다르다고 하긴 애매모호하다.)
프로시저라는 것은 추상적인 단위기 때문에 절차적(구조적) 프로그래밍의 문제였다.(물리적인 요소(변수나 상수등의 값들을 저장하기 위한 메모리)를 관리하는 방법에 대해서 깊게 논하지 않기에 그것까지 관리하는데는 문제가 생기기 마련이다.)
프로시저: 메모리에 저장되는 것을 관리하는 논리적 단위
예를 들자면,
도서관리 프로그램을 작성한다고 하면 책이라는 자료형이 필요하다.
그리고 그 책에 대한 함수역시 필요할 것이다. 구조적 프로그래밍에서는 이 둘을 따로 생각할 수 밖에 없다.
즉 책 자료형은 책 자료형이고 그것을 사용하는 함수는 함수대로 따로 있는것이다.
이를 소스파일에 묶어 두더라도 사용자 입장에서 코드가 길어진다면 그것을 알기는 굉장히 힘들다.
즉 책에 대한 자료형과 책에 대한 함수가 물리적으론(기록되는 곳)같이 있을 수(모듈) 있지만 논리적으로는(개념)
함께 할 수 없는 구조이기 때문이다. 이를 묶기 위한 새로운 패러다임이 필요했다.
3) 객체지향적 프로그래밍(Object-Oriented Programming)
절차적(구조적) 프로그래밍의 문제점을 해결하는 것은 생각보다 간단했었고, 특정 개념과 자료형을 함께 묶어서 관리하자 해서 탄생된 것이 객체지향 프로그래밍이다.
여기서 중요한 점은 모든 객체는 그 내부에 자료형(Field)와 함수(Method)가 존재한다.
이렇게 객체 지향 프로그램은 가능한 모든 물리적, 논리적 요소들을 객체로 만드려고 한다.
일단 객체라는 한 개체로 완성되면 그 객체는 다른 객체로 부터 높은 수준의 독립성을 완성했다고 할 수 있다.
이렇게 하면 코딩할때 극 초반에만 조금 불편해진다.
그러나 시간이 지날수록 중복코딩이 굉장히 줄게되며 객체와 객체간에 독립성이 확립되므로 유지보수에 도움이된다.
반응형, 함수형 프로그래밍이 부상하면서 객체지향 프로그래밍인 파이썬도 저무는 날이 언젠가 올것이다.
요약
정확히 객체지향적 프로그래밍이 어떤거고, 원래 이렇게 간단하게 할 수 있는 건 줄 알았는데, 함수와 클래스가 생기면서 편해졌다는 걸 느꼈고, 불편한 것들을 해소하고 편한 방식을 찾기 위해 연구하는 사람들을 응원한다.
그럼 이제 본론으로 들어와서 파이썬에서 함수를 어떻게 사용하는지, 함수가 무엇인지를 알아보자.
함수(Funtion)
- 쉽게 설명하면, 어떤 재료를 넣어 새로운 값을 만드는 틀이라고 생각하면 쉬워진다.
- 본래 구조적 프로그래밍은 어떤 입력에 대한 작업의 결과를 출력해주는 것
- 함수(function)은 일정한 작업을 수행하는 코드블럭으로 반복적으로 계속 사용되는 코드들을 함수로 정의하여 사용하게 된다.
- 미리 구현된 함수들을 라이브러리 함수라고 하고, 본인이 만든 함수를 사용자 함수라고 부른다.
함수 자체로는 프로그래밍의 연산이나 논리에 직접적으로 관여하진 않으나 방대한 코드를 줄여주고, 유지보수를 쉽게하고, 버그가 발생할 여지를 줄여주는 것이라고 할 수 있다. 또한, 함수를 사용하지 않아도 개발을 할 수 있으나 함수를 사용하는 이유는 규모가 큰 소프트웨어를 만든다는 것이 현실적으로 어렵다.
결과물을 확인하기 위해 자주 사용하고 있는 print()도 함수이다.
함수를 사용하는 이유와 주의사항
-
사용하는 이유
- 중복된 여러개의 코드를 함수에 담아 사용하게 될 경우 가독성이 좋아진다.(코드가 깔끔해지고, 어디에서 어떤 기능을 사용하는지 알아보기 쉽다.)
- 프로그램에 문제가 발생하거나 기능을 수정해야 할 경우 손쉽게 유지보수 할 수 있다.
- 코드를 작성 후 추후에 검토를 하거나 다른사람이 봤을 때 분석하기 쉬워진다.
- 기존에 작성된 함수만 복사하면 같은 기능을 하는 함수만 복사해 와서 사용할 수 있다.
-
주의사항
- 별도의 규칙은 없으나 하나의 기능을 하나의 함수로 만드는 것이 좋다.
- 같은 이름으로 함수를 정의 할 경우 마지막에 정의된 함수가 실행된다. 하지만 중복, 일반화가 가능하며, 디스패치 함수를 작성하여 관련된 함수의 집합에 디스패치 하면 된다.
중복된 함수를 제거해서 가독성을 높이고, 유지보수하기 쉬워지고, 같은기능을 가진 함수를 다른곳에서도 사용할 수 있다. 이제 함수를 어떻게 사용하면 될지 알아보자.
함수의 사용법
기본적인 사용법
- 아래와 같이
def
로 정의하고 알맞은 함수명을 작성하여 사용할 수 있으며, 파이썬은 띄어쓰기에 굉장히 민감하므로 문장 앞에는 2칸, 혹은 4칸의 공백을 두어 사용해야 한다.(파이썬 공식문서에서는 탭이나 2칸이 아닌 공백 4칸을 권장한다고 작성되어 있다.) - 함수 이름 뒤 괄호 안의 매개변수는 호출된 인자를 받기 위한 것이며,
값이 없는 변수
이다. 이렇게 정의한 후 if, while, for문 등으로 함수에서 수행할 문장을 입력한다.(입력되는 값은 함수의 매개변수가 몇개인지에 따라 나뉘는게 보통이며(먼저 정의된 매개변수가 없을 수도 있다.) -
매개변수와 인수라는 단어를 혼용해서 사용하거나 헷갈릴 수 있으니 잘 알고 사용해야 한다.
매개변수는 함수에 입력으로 전달된 값
인수는 함수를 호출할 떄 전달하는 입력 값 - 함수의 기본 틀
- 덧셈을 위한 예시
- 매개변수에 직접 값을 입력한 함수 정의
- 매개변수가 몇개일지 모를 경우 사용하는 매개변수명(*args)
- 매개변수값을 Dictionary 형태로 리턴해주는 매개변수(**kwargs)
- 매개변수의 값을 미리 설정
- 함수의 외부와 내부에 같은명의 변수가 있을 경우
- 함수 외부에서 선언된 변수의 값 변경(return, global)
- lambda
- 재귀함수
#함수의 기본적인 틀
>>> def 함수명(매개변수): #parameter
... <문장1>
... <문장2>
... ...
print(함수명(인수)) # argument
- 매개변수(입력값)가 없는 함수는 아래와 같이 작성되며, 입력된 매개변수가 없어서 별도의 값을 받진 않지만 호출하게 될 경우
hello world
를 리턴해준다. -
호출했을 떄 return을 통해 결과값을 돌려받거나(오직 return으로만 돌려받을 수 있음) print와 같이 바로 어떤 문장이나 값을 출력해 줄 수도 있다.(print()문은 아래의 <문장1>과 같이 수행할 문장에 해당하는 부분이여서 별도의 결과값이 존재하지 않는다고 한다.)
>>> def test(): ... print("hello world") ... >>> a = test() >>> print(a) hello world
-
매개변수에 직접 값을 지정하여 호출을 할 수 있다.
>>> def add(a, b): ... return a+b ... >>> result = add(a=3, b=7) # a에 3, b에 7을 전달 >>> print(result) 10
- 몇개의 매개변수를 받을 지 모를 경우
*args
, 딕셔너리 자료형으로 결괏값을 저장하려면**kwargs
를 사용한다.)
>>> def add_mul(choice, *args):
... if choice == "add":
... result = 0
... for i in args:
... result = result + i
... elif choice == "mul":
... result = 1
... for i in args:
... result = result * i
... return result
...
>>> result = add_mul('add', 1,2,3,4,5)
>>> print(result)
15
>>> result = add_mul('mul', 1,2,3,4,5)
>>> print(result)
120
위 예제를 보면, 매개변수 choice
에 add
를 받으면 덧셈이 되고, mul
을 받으면 곱셈을 한 값을 리턴해준다.
>>> def add_and_mul(a,b):
... return a+b, a*b
>>> add_and_mul(3,4)
(7, 12)
- 매개변수에 초깃값을 미리 설정해둔 함수를 만들 수 있다.
lambda
lambda는 함수와 같은 역할을 하며, 간단한 함수를 작성하거나 함수를 사용할 수 없을 때 사용된다.
lambda 매개변수1, 매개변수2, ... : 매개변수를 이용한 표현식
>>> add = lambda a, b: a+b
>>> result = add(3, 4)
>>> print(result)
7
재귀함수
- 함수 내부에서 함수 자신을 호출하는 경우를 흔히 볼수 있으며 이러한 유형은 재귀함수 호출이라고 한다.
- 재귀함수에서 알아야 할 것은 언제까지 반복할 건지와 언제 멈출 것인지를 잘 설정해두지 않으면 무한루프에 빠질 수 있다.
- 팩토리얼이나 일정한 규칙을 가진 함수를 통해 원하는 결과값을 return해야 할 때 많이 사용된다.
내가 지금까지 사용한 함수들은 극소수에 불과하고, 함수의 기능들을 자유자재로 사용하기 위해 제대로와 꾸준히 여러가지 코드를 짜봐야 할 거 같다.