파이썬을 배워보자 10일차 - 함수

0

Python

목록 보기
10/18

점프 투 파이썬 : https://wikidocs.net/book/1
파이썬 기본을 갈고 닦자 : https://wikidocs.net/16031

함수

함수는 매우 유용한 기능을 제공해준다. 우리가 일련의 작업을 반복해야한다면 함수로 만들어 도구처럼 사용할 수 있고, 다른 이에게 기능을 제공하되 다른이는 내부의 구현을 알지 못해도 된다면 함수로 만들어주면 된다.

1. 파이썬의 함수 구조

파이썬의 함수 구조는 다음과 같다.

  1. def 예약어로 함수임을 나타낸다.
  2. 값을 반환할 때는 return을 사용하면 된다. 여러 개의 값을 반환할 수도 있다. 아무것도 반환하지 않을 때는 return을 안써줘도 된다.
  3. 매개변수의 수는 0개 이상이다. 아무것도 받지 않으려면 매개변수를 안써주면 된다.
def 함수명(매개변수):
    함수본문
    함수본문
    return 리턴값

ret = 함수명(인수)

함수인 것을 나타내기 위해서 다른 언어는 func, function등 다양한 표현을 사용하는데 python은 def만 사용하면 된다.

다음의 예제를 보자,

def add(a, b):
    return a + b

print(add(1,2)) # 3
print(add(3,4)) # 7

add라는 함수를 만들었고, 여기에는 a,b라는 매개변수가 들어간다. 반환값으로 a + b를 리턴하며 사용방법은 add(1,2)이런식으로 쓴다.

생각보다 굉장히 쉽다. 어려운게 없다.

매개변수(parameter) 와 인수(arguments) : 매개변수는 함수 선언할 때 함수에 들어가는 것들을 말한다. 인수는 함수를 호출할 때 매개변수에 들어가는 값을 말한다.

def add(a, b): # a, b 매개변수
    return a + b

print(add(1,2)) # 1, 2인수

다음의 경우를 고려해보면 된다.

이 둘은 자주 혼동되고 혼용하는데, 사실 별로 딱딱 구별할 필요는 없다. 구별해서 의사소통 흐름 끊는게 더 불편한 자리를 만들기 때문이다.

1.1 입력값이 없는 함수

즉, 매개변수가 없는 함수이다.

def say():
    return "hello"

print(say()) # hello

1.2 리턴값이 없는 함수

분명 매개변수는 있지만, 반환하는 값이 없는 함수이다. return이 없기 때문에 함수에게서 무언가 받을 수 있는 게 없다.

def add(a, b):
    print(a + b)

add(1,2) # 3

1.3 매개변수도 리턴값도 없는 함수

매개변수도 리턴값도 없는 함수를 만들 수 있다.

def say():
    print("hello")

say() # hello

1.4 여러 개의 리턴값을 낼 수 있는 함수

튜플 형식으로 여러 개의 리턴 값을 낼 수 있는 함수도 있다.

def add(a, b):
    return a + b, a - b , a * b, a // b

print(add(10,2)) # (12, 8, 20, 5)

plus, minus, mul, div = add(10,2)
print(plus, minus, mul, div, end=" ") # 12 8 20 5 

다음과 같이 여러 개의 값을 반환하면 튜플 형식으로 반환된다. 따라서, 이를 튜플의 할당구조로 받아낼 수 있고, plus, minus, mul, div가 바로 이에 해당하는 예제이다.

따라서 함수가 리턴값을 여러개 반환하는 것 처럼보여도, 사실은 하나의 return값만 반환하는 것이다. 즉 여러 개의 값들을 하나의 튜플로 묶어서 반환하는 것이다.

1.5 매개변수를 지정하여 호출

함수를 호출할 때 어떤 매개변수 어떤 값을 줄지 확실하게 선택하여 호출할 수도 있다.

def add(a, b):
    return a - b

print(add(b= 10, a= 32)) # 22

둘의 순서가 다름에도 매개변수를 지정하여 값을 주니 상관없이 구동되는 것을 볼 수 있다.

1.6 매개변수의 수를 모를 때

다른 언어들도 매개변수의 수를 가변적으로 받을 수 있듯이, python도 가변적으로 매개변수들을 받을 수 있다. 가변 매개변수로 사용할 매개변수 앞에 asterisk(*)를 붙여주면 된다.

사용방법은 다음과 같다.

def 함수이름(*매개변수):
    본문
    return 반환값

함수이름(인자1, 인자2 , 인자3, ... , 인자n)

이렇게 선언하면 가변적으로 매개변수를 받을 수 있다. 한 번 예제를 만들어보도록 하자

def add_many(*args):
    result = 0
    print(args)
    for i in args:
        result = result + i
    return result

ret = add_many(10,3,-2,0,1,321,-3,12) # (10, 3, -2, 0, 1, 321, -3, 12)
print(ret) # 342

ret = add_many(1,2,3) # (1, 2, 3)
print(ret) # 6

다음과 같이 몇 개의 인자든지 다 받아서 하나로 합칠 수 있다.

args를 출력해보면 입력된 값들이 튜플 형식으로 들어온 것을 볼 수 있다. 이렇게 가변 매개변수는 입력을 가변적으로 받아 이를 튜플로 만들어 처리하는 것이다.

물론 가변 매개변수를 사용한다해서 일반 매개변수를 같이 사용못하는 것은 아니다. 다만 같이 사용하려면 가변 매개변수를 맨 뒤에 써야한다.

def add_many(*args, choice):
    pass
    
add_many(1,2) # add_many() missing 1 required keyword-only argument: 'choice'

가변 매개변수를 앞에 써버리는 바람에 choice가 어디에 있는 지 못찾는 것이다. 그래서 일반 매개변수인 choice를 맨 앞에 써주고 가변 매개변수인 *args를 맨 뒤에 써준다.

def add_many(choice, *args):
    pass

add_many(1,23,32)

문제없이 구동된다.

1.7 키워드 파라미터 사용

우리가 이전에 print(end="") 이런거를 쓰는 것을 보았을 것이다. 사실 이것은 키워드 파라미터를 말한다. 즉, 파라미터로 넘어가되 그냥 순서가 아닌 키워드가 맞아야지만 들어가는 파라미터인 것이다.

키워드 파라미터를 사용하기 위해서는 asterisk를 두 번 써주면 된다. **키워드파라미터 이렇게 말이다.

def print_kwargs(**kwargs):
    print(kwargs)
    return kwargs["name"]

ret = print_kwargs(a=1, b=2, name="hello") # {'a': 1, 'b': 2, 'name': 'hello'}
print(ret) # hello

kwargs는 딕셔너리 형식으로 입력받은 매개변수를 저장한다. 즉, 인자로 name="hello"로 넘겨주면 kwargs["name"] = "hello" 이런식으로 저장하는 것이다.

그래서 함수의 리턴값으로 kwargs["name"]으로 리턴시켜 값이 나오는 것이다. 만약 name="hello"를 안넘겨주면 해당하는 키가 저장된 적이 없다고 에러를 반환할 것이다.

1.8 매개변수에 default값 설정하기

매개변수에 기본값을 설정할 수 있다. 이는 해당 매개변수에 어떠한 값이 들어가지 않으면 해당 기본값이 들어가도록 하는 것이다.

def retValues(a = 10, b = 2, c = "hello"):
    print(a, b , c)

retValues() # 10 2 hello
retValues(100) # 100 2 hello
retValues(100, 1000) # 100 1000 hello
retValues(100 ,1000 ,"????") # 100 1000 ????

매개변수 값을 넣어주지 않으면 기본값이 들어가는 것을 확인할 수 있다. 특정 매개변수를 건너띄고 다른 매개변수에 값을 넣을 수는 없다. 즉

retValues(100, ,"??") 

이런 식으로는 안된다는 것이다. 그래서 기본값을 설정해야할 것이 있다면 우선 순위별로 뒤에 두는 게 맞다.

2. 함수와 변수

2.1 함수 안에서 선언한 변수의 효력 범위

다음의 코드는 어떤 결과값이 나올까??

a = 1

def funcTest(a):
    a = a + 1

funcTest(a)
print(a) # 1

위의 예제를 보면 전역변수 a가 함수의 매개변수로 들어가고 함수안에서 더해지므로 2가 될거라고 생각한다.

그러나, 이전에도 말했듯이 파이썬의 정수형은 불변이다. 즉, 매개변수 a에 들어간 값은 전역변수 a의 값을 똑같이 배낀 새로운 변수라는 것이다.

즉, 서로 완전 다른 변수이기 때문에 함수 안에서 선언된 매개변수 a는 함수 안에서만 유효범위를 갖는다. a = a + 1을 해도 전역변수 a에 한 것이 아니고, 매개변수 a에 대해 +1을 한 것이기 때문에 전역변수 a는 관계가 아예 없다는 것이다.

때문에 매개변수 a는 함수 안에서만 유효범위를 가지며 함수 안에서만 호출할 수 있고 변경가능하다.

def funcTest(a):
    a = a + 1

funcTest(4) # NameError: name 'a' is not defined

함수 밖에서 함수안에서 선언된 변수에 접근하면 에러가 발생한다.

반대로, 함수 안에서는 함수 밖에서의 전역변수를 바꿀 수 없다. 정확히는 접근은 하여 값을 읽을 수는 있지만, 바꿀 수는 없다. 이유가 무엇일까?? 그건 파이썬의 변수 선언 방식 때문이다.

b = 3
def funcTest():
    print(b)
    
funcTest() # 3

위와 같이 전역변수 b값을 읽어올 수는 있지만 변경은 못한다.

b = 3
def funcTest():
    b = 5
    print(b)
    
funcTest() # 5

왜 변경이 안될까?? 그건 b = 5를 '전역변수 b를 읽어서 값 5로 바꿔라'가 아니라 함수에 로컬 변수로 b = 5를 선언과 동시에 초기화한 것이다. 그렇기 때문에 파이썬 코드는 혼란을 겪게 되는 것이다.

2.2 함수 안에서 함수 밖의 변수를 변경하는 방법

그래서 지금 내가 참조하고 있는 변수가 전역변수이다. 를 알려주기 위해서 우리는 global 키워드를 사용하면 된다.

global 변수라고 적으면 이 변수가 전역 변수다! 라는 것을 확인하게 된다.

a = 1
def vartest():
    global a
    a = a + 1

vartest()
print(a) # 2 

이제는 global을 통해서 전역변수에 있는 변수도 가져올 수 있다.

그러나, 해당 방법은 조금 위험한 문제점이 있다. 전역변수를 함수 안에서 바꾸는 코드는 그닥 좋지 못하기 때문이다. 나중에 디버깅이 어려워지는 문제점도 있고 유지 보수성이 좋지 않다.

그래서 return으로 전역 변수 값을 바꾸어주는 것이 가장 괜찮은 방법이다.

a = 1
def vartest(a):
    return a + 1

a = vartest(a)
print(a) # 2

지역변수 a전역변수 a의 값을 가져와서 복사한 다음 지역변수 a+1연산을 하고 반환하여 전역변수 a에 복사하는 방식이다. 이 방식이 유지보수성이나 디버깅에 있어 좋다.

3. lambda

람다는 함수를 생성할 때 사용하는 키워드로 def와 동일하다. 그러나 def와는 달리 한줄 정도의 굉장히 간단한 함수를 만들 때 사용한다.

사용방법은 다음과 같다.

lambda 매개변수1, 매개변수2, ..., 매개변수N : 표현식

이를 이용하여 '두 매개변수를 받아 더하는 함수'를 간단히 만들어보자

add = lambda a, b : a + b
result = add(3,4)
print(result) # 7

람다식에서 바로 실행까지도 할 수 있다. 함수를 실행하듯이 ()로 인자를 주면된다.

print((lambda a, b : a + b)(3,5)) # 8

잘보면 return이 없는데도 return을 하는 것을 볼 수 있다. 람다는 return을 자동으로 한다.

0개의 댓글