Stargazer

[Python] 모두가 쉬운 파이썬 - 함수 본문

프로그래밍/Python

[Python] 모두가 쉬운 파이썬 - 함수

COM2IT 2021. 1. 6. 23:44
반응형

안녕하세요? COM2IT 입니다.

 

오늘은 함수에 대해서 알아보겠습니다.


함수란 무엇인가

 

우리는 학생 시절 수학시간에 함수라는 개념을 배웠습니다.

대충 y = f(x)  의 형태를 가진 것을 함수라고 부릅니다.

 

"어떤 x의 값을 f 라는 함수에 넣었더니 y라는 값이 나온다."

 

이 함수의 개념은 파이썬의 함수의 개념과 동일합니다.

 

"어떤 매개변수 x를 함수 f 에 넣었더니 y라는 반환값이 나온다"

 

즉, 함수는 어떤 값을 받으면 그에 따라 특정 기능을 수행하기 위한 일종의 명령어 집합과 같습니다.

이전에 자료형 강좌에서 사용했던 set() 이나 list의 .append() 등이 함수에 해당합니다.

(이 둘은 약간의 차이가 있으나 추후 클래스/메소드 강좌에서 언급하겠습니다.)

 


함수 구조 및 정의

 

그렇다면 파이썬에서 함수를 어떻게 정의 할까요?

함수를 사용해보면서 함수를 배워봅시다.

1
2
3
4
5
6
>>> def say_hello():
    print("Hello world!") #출력함수
 
    
>>> say_hello()
Hello world!
cs
↑ 예제

 

위 예제를 보면 def say_hello(): 라고 되어있는 부분이 있습니다.

def 는 함수를 정의 할때 앞에 적어주는 예약어 부분입니다.

그뒤에 say_hello 라고 돼있는 부분은 함수의 이름입니다.

그리고 () 라고 돼있는 부분은 매개변수를 전달하는 부분입니다. (앞 키워드가 함수라는 것을 의미하기도 합니다.)

 

매개변수란 내가 주고자 하는 값을 함수로 전달하는 변수라고 보시면 됩니다.

함수마다 다르지만 어떤 함수는 매개변수가 필요한 함수가 있습니다.

print()는 안에 매개변수로 출력할 문구를 주면 출력합니다.

1
2
3
4
5
6
>>> def sum(a, b):
    return a+b
 
>>> print(sum(a,b))
3
>>> 
cs

위 예제에서 보면,

괄호 안에 2개의 매개변수가 들어간 것을 볼 수 가 있습니다.

함수를 정의할때 매개변수 이름은 임의로 적어도 되지만 예약어, 즉, def, return, print 와 같은 예약어는

이러한 매개변수를 통해서 함수 내부에 필요한 값을 전달 할 수 있습니다.

 

여기서 return 이라는 키워드가 보시이나요?

return 이란 함수의 어떤 결과 값을 함수를 불러온 시점에 반환(return) 하는 명령어입니다.

만약 이 함수가 반환 할 것이 있다면 return을 통해서 반환하고 없다면 그냥 쓰지 않으면 됩니다.

 

그리고 여기서 반드시 주의 할것이 있는데,

어디부터 어디까지 함수인지를 파이썬 인터프리터에게 전달하기 위해서

함수를 정의하는 부분을 들여쓰기(Tab)를 해야합니다.

 

이 들여쓰기하는 것은 추후에 배울 분기문, 반복문에서 사용하는 문법이 필히 숙지하세요!

왜냐하면 들여쓰기가 어디부터 어디까지인지를 정해주는 문법이기 때문입니다

만약 들여쓰기를 하지 않으면 파이썬이 어디까지가 정의 범위인지 가늠이 안되기 때문에 오류가 발생합니다.

 

그리고 들여쓰기가 끝나는 부분은 엔터를 쳐서 개행을 한번 해주십시오.

IDLE(인터프리터)에서는 엔터를 한번 더 쳐주셔야지 쉘이 활성화됩니다.

(쉘은 쉽게 말해 명령을 할 수 있는 창입니다(>>>) ) 

 

그리고 함수는 매개변수를 받을때 항상 명시된 갯수만큼 받아야합니다.

위의 예제에서 보면 매개변수 a,b를 받는데 내가 1개나 3개를 넣고 싶어도 무조건 2개만 가능 합니다.

안그러면 매개변수의 개수가 맞지 않다면서 오류를 일으키죠.


함수를 실행하면 작동 순서는 어떻게 되나요?

 

만약 함수를 불러오게 된다면 코드는 어떤식으로 작동하게 될까요?

예제를 보도록 하겠습니다.

 

1
2
3
4
5
6
7
8
9
10
11
12
>>> def func1():
    print("func1 is called")
 
    
>>> def func2():
    print("func2 is called")
    func1()
 
>>> def func3():
    func2()
    print("func3 is called")
    func1()
cs

 

조금 시간을 드리겠습니다. 제가 func3()을 실행하면 함수의 작동 순서와 결과는 어떻게 나올까요?

 

30초 드리겠습니다.

 

 

 

 

...

 

 

 

 

결과를 보도록 하겠습니다.

 

1
2
3
4
5
>>> func3()
func2 is called
func1 is called
func3 is called
func1 is called
cs

우선 함수를 불러오는 순서는 func3 -> func2 -> func1 ->func1 입니다

왜 결과값은 3,2,1,1 이 아니고 2,1,3,1 순서로 나왔냐면 출력하는 시점과 함수를 불러오는 시점이 다르기 때문입니다.

func3 에서 보면 출력이 먼저가 아니고 함수를 먼저 불러왔고,

함수가 완벽히 불러와진 후에야 func3 의 출력문으로 넘어갈 수 있습니다.

 

즉, 순서는 이렇습니다.

함수3을 부른다 -> 함수3 내부에서 함수2를 불렀다. -> 함수2는 출력 후에 함수1을 불렀다 - > 함수1은 출력후 함수를 종료 했다 -> 함수2는 종료했다 -> 함수3으로 돌아와서 출력 후에 함수 1을 불렀다  -> 함수1은 출력후 함수를 종료 -> 함수3은 종료했다.

 

함수가 종료하게 되면, 함수를 불러왔던 그 시점으로 다시 돌아갑니다. 즉, 큰 상자안에 작은 상자를 까보고 작은 상자를 다 확인 하면 큰상자로 다시 돌아와 확인하는 것과 같습니다.

 

여기서 질문 만약 함수의 매개변수에 함수를 넣는다면 어떻게 될까요?

당연히 매개변수를 건네주기 위해선 값이 필요하므로 매개변수로 들어갈 함수를 먼저 실행하고, 나온 결과물을 바깥 함수에 전달합니다.

 

 


함수 심화 및 응용

 

이번에는 함수에 대해서 깊게 살펴 보도록 하겠습니다.

 

예제를 보도록 하죠

아래 예제는 이름과 나이를 입력 받고, 맨 앞글자와 태어난 년도를 출력하는 함수입니다.

 

1
2
3
4
5
6
7
8
9
10
>>> def birth_year(name, age):
    print("Hello, my name is", name)
    print("i'm " + str(age) + " years old")
    return name[0], 2021 - age + 1
 
>>> print(birth_year("John Lenon",23))
Hello, my name is John Lenon
i'm 23 years old
('J', 1999)
>>> 
cs

하나하나 차근차근 보도록 하죠

 

줄:1에서 보면 def birth_year(name, age): 이라고 정의를 시작합니다.

매개변수로 name , age 를 받는다고 명시했습니다.

줄:2에서는 2가지를 출력합니다. "Hello, my name is" 라는 부분과 name 이라는 변수를 출력을 하죠

여기서 알 수 있는 점은 print 함수는 2개 이상의 매개변수를 주어도 상관 없음을 알 수 있습니다.

print(값1, 값2, ..., 값 10213, ... )  이와 같이 값이 얼마정도 들어오던 상관 없이 모두 출력합니다.

 

그런데 어떻게 이런게 가능할까요? 분명히 아까 전에는 매개변수의 개수가 명시된 것 만큼만 받을 수 있다고 했는데?

 

사실 매개변수를 받을때 튜플의 형태로 받으면 문제가 없습니다. 즉, 배열의 형태를 1개를 받으면 매개변수 1개만 설정해도 유동적으로 매개변수를 받을 수 있습니다.

 

매개변수를 튜플의 형태로 받으려면 매개변수 앞에 *(asterisk) 를 붙입니다.

 

다음의 예제를 살펴보겠습니다.

 

1
2
3
4
5
>>> def multi(*n):
    return n
 
>>> multi(1,2,3,4,5)
(12345)
cs

 

보시면 여러가지 매개변수를 준 걸 그대로 출력하는 것을 볼 수 있습니다.

그리고 출력된 값을 보면 튜플의 형태임을 알 수 있습니다.

받은 매개변수가 튜플이기 때문에 함수 내부에서 인덱싱을 통해서 모든 매개변수에 접근이 가능합니다.

 

 

이어서 줄:3에서 보면 "i'm " + str(age) + " years old" 라는 걸 출력한다는 의미인데,

자료형에서 말씀드렸지만 문자열은 덧셈이 가능합니다. 그래서 합치는게 가능하다는 의미입니다.

그런데 str() 함수는 무엇일까요? str() 함수는 문자열이 아닌 자료형을 문자열로 바꾸어주는 역할을 하는 함수입니다.

보통 숫자형에서 문자열로 넘어갈때 자주 사용합니다. 왜냐하면 숫자형 1과 문자형 '1'은 다른 값이기 때문입니다.

그래서 나이는 숫자형이므로 덧셈을 해주기 위해 문자열로 변경해주는 겁니다.

 

줄:4에서 보면 return name[0], 2021 - age + 1 라 돼있는데,

name[0] 은 name 문자열의 맨 앞글자, 즉, J를 인덱싱을 한 것이고,

2021- age +1 은 현재 나이를 받으면 태어난 년도가 언제인지 계산한 것 입니다.

그런데 return을 할때 이렇게 2개를 반환 해도 될까요?

매개변수도 맞춰서 넣어야되는데, 반대로도 그래야 될까요?

사실 return 되는 값은 단 한개 밖에 없습니다. 

 

그런데 2개를 반납하는건 오류 아닌가요? 라고 반문 할 수 있는데,

아까 매개변수에서 힌트가 있습니다.

바로 반납할때도 튜플의 형태로 반납하면됩니다.

예제를 보시면 반납된 형태가 튜플인것을 확인 할 수 있습니다.

 

또는, 반환되는 개수에  맞춰서 변수에 대입하는 방법도 있습니다.

1
2
3
4
5
6
7
8
>>> init , year = birth_year("John Lenon"23)
Hello, my name is John Lenon
i'm 23 years old
>>> init
'J'
>>> year
1999
>>> 
cs
↑예제

줄:6 은 반환된 값을 출력하는 것입니다.

 

자 여기서, 함수가 실행되는 순서 잊지 않으셨죠?

먼저 바깥 함수 매개변수로 들어가는 함수를 먼저 실행해야하기때문에 

birth_year 함수가 실행되서 자기소개가 출력이 되고,

그 다음 나온 결과물을 반환해서 print 함수에 전달해주고 있습니다.

그래서 출력 순서는 자기소개 출력 후 결과값 출력 순입니다.

 


매개변수 초기화

 

자, 그럼 다른 유용한 것들을 배워보겠습니다.

이 함수를 매번 사용할 일 있다고 해봅시다.

그런데 매번 입력하는 사람들의 나이가 대체로 20살에 분포되있다고 가정 해봅시다.

그렇다면 매번 나이를 입력하지 않아도 자동으로 들어가면 좋겠다고 생각하지 않겠습니까?

 

이때 사용하는 개념이 매개변수 초깃값 설정입니다.

사용법은 아래 예제로 확인해보겠습니다.

 
1
2
3
4
5
6
7
8
9
>>> def birth_year(name, age=20): #age 의 초깃값을 20으로 초기화
    print("Hello, my name is", name)
    print("i'm " + str(age) + " years old")
    return name[0], 2021 - age + 1
 
>>> birth_year("John Lenon")
Hello, my name is John Lenon
i'm 20 years old
('J', 2002)
cs

아까와 다른점은 age부분이 age=20 이라고 되어있습니다.

이게 바로 매개변수 초기화라고 합니다.

 

여기서 주의해야할 점은 매개변수 초기화는 항상 매개변수의 맨 오른쪽부터 해야합니다.

갑자기 중간에  초기화를 하거나 왼쪽부터하면, 오류가 발생합니다. 왜냐하면

함수에 매개변수를 하나하나 대입할 때 왼쪽부터 넣기 때문에,

왼쪽부터 초기화를 하게 되면 초기화된 값은 대입한 값으로 바뀌게 되고 맨끝에 남게되는 매개변수는 초기화가 되지 않아서 값이 대입되지 않는 현상이 발생합니다. 

 

그래서 초기화해야할 변수가 있다면 오른쪽으로 옮기거나 오른쪽부터 채워주듯이 작성해야합니다.

 

파이썬은 call by object reference(객체 참조에 의한 호출) 방식을 사용합니다.

갑자기 어려운 용어가 나와서 당황 하셨을 텐데, 차근차근 설명해보겠습니다.

 

파이썬은 모든 것이 객체입니다. 그중에는 immutable(불변) 객체와 mutable(가변) 객체가 있습니다.

Immutable : int, float, str, tuple

mutable: list,dict,set

 

파이썬은 함수를 불러올때 각 객체에 특성에 맞게 작동합니다.

불변 객체를 매개변수로 사용하면 아무리 함수 내에서 그 값을 바꾸려해도 바꿀 수 없습니다.

반대로 가변 객체를 매개변수로 주게 되면 함수내에서 값을 바꾸면 그 값은 바뀝니다.

 

만약 프로그램 작성하시다가 잘 기억이 안나시면 가변인지 불변인지 대략 파악하는 꿀팁이 있습니다.

변수 파트에서 설명할때 파이썬의 변수는 메모리 값을 가리키는 형태입니다.

변수라는 것은 최적화가 될 수록 프로그램이 안정해집니다.

그렇기 때문에 int,float, str 같은단일 값으로 사용가능하면 함수내에서 새로운 객체를 복사를 해서 사용을 합니다.

tuple은 이전에 말했듯이 변경이 불가능한 상수라고 말씀드렸으니 생략하겠습니다.

 

반대로 list나 dict, set 같은 복수의 값을 가지는 자료형들은 값을 복사하기에는 양이 많을 수도 있기 때문에

그 주소를 그대로 사용하는 것이 더 효율적일 것입니다.

 

 


매개변수의 순서 상관없이 전달하고 싶을때

매개변수의 순서가 헷갈려서 문제가 생길 것을 방지하여 순서없이 매개변수를 확실하게 전달하고 싶을때가 있습니다.

그럴때는 매개변수를 보낼때 명시를 해주면 됩니다.

사용법은 함수(매개변수1 = 값, 매개변수2 = 값, ...)

다음은 예제입니다.

 

1
2
3
4
5
6
7
8
9
10
def birth_year(name, age=20): #age 의 초깃값을 20으로 초기화
    print("Hello, my name is", name)
    print("i'm " + str(age) + " years old")
    return name[0], 2021 - age + 1
 
>>> birth_year(age=22, name="John Lennon")
Hello, my name is John Lennon
i'm 22 years old
('J', 2000)
>>> 
cs

함수를 정의 하고, age=22 , name= "John Lennon" 로 매개변수를 지정해서 

순서와 상관 없이 매개변수를 전달하고 있습니다.

만약 매개변수를 정해서 전달 하고 싶다면 웬만하면 매개변수 모두 명시지정해서 사용하는 것이 좋습니다.

(물론 섞어서 사용이 가능합니다. 왼쪽부터 함수 정의 포지션대로 하다가 그 뒤에 있는 매개변수들에 대하여 순서 바꿔서 명시지정이 가능합니다.)


람다(lambda) 형식

 

함수 중에 단순 계산같은 간단한 함수 같은 경우가 있습니다. 이런 경우에는 굳이 함수를 정의 해가면서 쓰지 않고,

일시적으로 사용하는 경우가 있는데 이때 lambda(람다) 형식을 사용하면 됩니다.

사용법은 변수 = lambda 인자 : 표현식 로 정의 하고 변수(매개변수) 방식으로 사용합니다. 

 

1
2
3
>>> hap = lambda a,b : a+b
>>> print(hap(10,23))
33
cs

위 예제는 람다를 사용하는 방법입니다.

인자 부분에 매개변수를 지정하고, 표현식에서 인자를 이용한 명령어를  적고,

이를 변수에 대입하면, 그변수는 함수처럼 사용하면 됩니다.

 

다음 시간에는 연산자에 대해서 배워보도록 하겠습니다.

반응형
Comments