티스토리 뷰

함수는 거의 모든 언어에서 지원되는 기본적인 기능이고 기본적인 함수정의와 함수호출의 관계는 동일하다. 여기서는 자바 언어와 다른 파이썬의 함수에 대해 정리해 보려고 한다. 

파이썬에서 함수는 이름으로 어떤 동작을 하는 코드 부분을 나타내는 방법이다. 함수를 정의하는 방법은 def 키워드를 이용한다.

def get_max(a, b):
   return a if a >= b else b
삼중 연산자(ternary operator)를 이용해서 큰 값을 돌려주는 함수다. 다음은 주어진 수가 소수인지 아닌지 판별하는 함수다.
def is_prime(n):
    for i in range(2, n): #
        if n % i == 0:
            return False
    return True

이 함수는 n을 나누어 떨어지는 약수가 있으면 거짓을 반환하고 아니면 참을 반환한다. 함수 코드를 파이썬 IDE의 대화형 콘솔에서 실행시켜 보면 한 가지 이상한 점이 있다. 함수 코드에 구문 상의 오류가 있어도 오류 메시지가 발생하지 않는다는 점이다. 다음의 예를 보자. 함수 코드에 문제가 있다. (파이썬은 True/False를 키워드로 가진다)

>>> def is_prime(n):
    for i in range(2, n):
       if n % i == 0:
           return false
    return true

>>> is_prime(55)
Traceback (most recent call last):
  File "<pyshell#11>", line 1, in <module>
    is_prime(55)
  File "<pyshell#10>", line 4, in is_prime
    return false
NameError: name 'false' is not defined

그러나 함수 def 부분(정의부 코드)에서는 조용히 넘어가고 실행할 때 되서야 true나 false는 없다라는 메시지가 나온다. 이것은 자바라면 컴파일 단계에서 오류가 났어야 하는 것인데, 파이썬에서는 컴파일 단계에 이것을 검사하지 않음을 알 수 있다. 이 코드를 파일로 저장하여 실행시켜도 마찬가지다. 함수 코드는 호출되기 전에는 이 오류가 검출되지 않는다. 왜냐하면 불려질 시점에 true나 false 라는 이름이 정의될 수 있기 때문이다(이름이 동적바인딩이므로). 파이썬에서는 괄호 짝이 안 맞거나 :이 틀렸거나 들여쓰기가 틀린 경우 구문 오류를 일으키고 그 이외의 경우는 거의 실행시가 되어야 오류가 검출된다. (TypeError, NameError, ...)

함수 호출부는 이러한 함수를 이름과 매개변수로 실행시키는 부분이다. 위의 get_max 함수는 다음과 같이 사용될 수 있다.

print("{}와 {} 중 큰 값은 {}입니다.".format(a, b, max(a, b))

그러면 max(a, b)를 호출한 자리는 그 반환값으로 계산되어 채워지게 된다. 또한 다음과 같이 is_prime 함수를 호출하는 코드를 작성할 수 있다.

if is_prime(a):
    print(a)

여기서는 is_prime(a)의 결과값이 True일 때만 a를 print하게 된다. 즉 함수 호출의 결과는 True 또는 False가 된다.

파이썬이 자바와 가장 크게 다른 점은 타입 선언이 없다는 점이다. 함수 선언에서도 똑같은데 매개변수와 반환값의 타입이 정해지지 않는다. 매개변수는 어떤 타입의 값이든 들어올 수 있다(함수의 다형성). 그리고 함수는 매개변수의 타입에 상관없이 자기가 하고 싶은 일을 한다. 그렇지만 파이썬에서도 함수의 매개변수 개수는 정해져 있다. 또한 함수의 코드는 매개변수로 넘어온 값을 가지고 연산을 해야 하므로, 매개변수 값은 어떤 성질을 만족해야 한다. 매개변수의 타입 오류는 다음과 같은 방식으로 처리된다.

매개변수의 개수가 맞지 않으면? 위의 함수에서 get_max는 두 개의 매개변수를 받아야 하고 is_prime은 한 개의 매개변수를 받아야 한다. 함수 호출부에서 개수가 맞지 않으면 오류를 발생시킨다.

매개변수 타입이 잘못 되면? 파이썬은 타입 검사가 없으므로 매개변수가 어떤 것이 넘겨지든 그것을 해당하는 이름의 자리에 넣어서 계산을 하게 된다. (또는 매개변수 이름이 그 값(또는 객체)를 가리킨다고 표현할 수 있다). 그런데 그 값으로 처리할 수 없는 코드나 연산을 만난다면? if a < b:에서 a와 b가 다른 타입이면 오류(TypeError)가 발생한다. a가 가리키고 있는 값과 b가 가리키고 있는 값이 무엇이냐에 따라 이 코드는 잘 실행되기도 하고 오류를 일으키기도 한다. 이러한 타입 처리 방식을 duck typing이라고 한다. 뭐가 됐든 오리의 역할을 할 수 있으면 오리가 된다는 것이다. 마찬가지로 is_prime에서도 매개변수 n이 range(n) 자리에 들어갈 수 있으면 문제가 없고 아니면(Float나 스트링이면)? 예외를 일으키게 된다. 그러므로 이 함수를 이용하고 싶으면 is_prime을 호출하는 사람이 어떤 값을 줘서 뭘 해야 할지를 알고 호출해야 된다는 것이다. 

다른 예로 파이썬의 빌트인 함수인 max() 함수를 생각해 보자. 이 함수는 매개변수로 넘어온 sequence 타입의 값에 대해 최대인 것을 찾아 돌려준다. 아마도 다음과 같은 코드가 될 것이다.

def max(list_arg):
   t_max = list_arg[0]
   for (x : list_arg):
       if t_max < x:
          t_max = x;
    return t_max

이러한 코드가 동작하기 위해서는 list_arg가 sequence 타입이어야 한다. 또한 list_arg의 원소들이 if 문에서 <로 비교될 수 있어야 한다. 그러므로 이러한 조건을 만족하지 않으면 max 함수는 예외를 일으킬 것이다. 

즉 함수를 호출할 때  그 함수가 어떤 매개변수를 받아 무슨 일을 하고 어떤 값을 돌려주는지 알고 호출부에서 그에 맞게 호출해야 한다. 그런데 이것은 사실 어떤 언어에서나 마찬가지다.

다음으로 변수의 범위규칙에 대해 살펴보자. 파이썬에서 변수는 전역(global)변수와 지역(local)변수로 나뉜다. 지역변수는 함수 안에서 선언한 이름이고 전역변수는 함수 밖에서 선언한 이름이다. 아직까지는 클래스를 고려하지 않으므로 파이썬 코드는 함수 안이냐 밖이냐로 나누어진다.

변수란 이름이 처음으로 지정문의 왼쪽에 나타날 때 선언되었다고 한다(이름을 모아둔 사전에 등록한다). 이름의 사전은 범위마다 새로 만들어진다. 즉 전역 범위로 하나, 함수마다 하나의 새로운 범위로 하나가 생긴다. 그러므로 전역범위에 있던 이름이라도 함수에서 새로 지정되면 함수 범위의 사전(지역변수)에 새로 등록된다. 이미 선언된 이름에 다시 뭔가가 지정되면 그 이름이 다른 것을 가리키게 된다. 이미 있던 이름이라도 함수 안에서 처음 지정되면 지역변수라고 보고 새로 등록한다. 또 하나 특징은 함수 안에서 전역변수의 값을 읽기 참조하는 것은 가능하다는 점이다. 전역 변수 이름의 읽기 접근은 자바와 동일하다.

파이썬의 범위 규칙도 동일한데, 자바와의 차이라면 이름이 지정문의 왼쪽에 나오는 것이 선언이므로 전역변수와 같은 이름이 지정문의 왼쪽에 나오면 지역변수로 새로 선언된다는 점이다. [주의] 같은 코드에 대해 자바에서는 따로 선언되지 않았으므로 전역변수를 그대로 사용하는 것으로 처리된다. 파이썬에서는 지정문의 왼쪽에 나오는 것이 새로운 선언이므로 지역변수로 선언되기 때문이다. (매개변수의 이름은 그대로 선언의 역할을 한다. 호출부에서 실매개변수가 넘어올 때 지정되면서 선언)

def sum_avg(n): sum = 0    # 지역변수 sum 선언 avg = 0.0  # 지역변수 avg 선언 for i in range(n): sum += int(input()) avg = sum / n print(avg) return sum sum = 0 avg = 0.0 for i in range(count): sum += sum_avg(3) avg = sum / (count*3) print(avg)

위의 코드에서 sum과 avg 변수는 전역에 하나씩 존재하고 sum_avg() 함수에도 하나씩 존재한다. 두 이름은 다른 변수를 나타낸다. 즉 파이썬에서는 함수 안에서 전역변수의 값을 변경하려면 그냥 이름으로 지정해서는 안된다는 점이다. 그럼 전역변수의 값을 함수 안에서 바꾸어야 할 때 어떻게 해야 할까? global 키워드를 써서 명시적으로 그 이름을 사용함을 나타내야 한다. (일종의 import 기능이라고 생각할 수 있다)

def sum_avg(n):
    global sum    # 전역변수 global을 이 범위에서 사용
    local_sum = 0
    avg = 0.0  # 지역변수 avg 선언
    for i in range(n):
        local_sum += int(input())
    avg = local_sum / n
    print(avg)
    sum += local_sum

sum = 0
avg = 0.0
for i in range(count):
    sum_avg(3)
avg = sum / (count*3)
print(avg)

마지막으로 함수의 반환형에 대해 살펴보자. 자바와 달리 파이썬은 모든 반환값이 같은 타입일 필요가 없다.

def get_number():
   n = random.randint(1, 100)
   if n % 2 == 0:
       return n % 20
   return (n % 20) / 2

print(get_number())

이 함수는 랜덤 수가 짝수일 때는 int를 반환하고 홀수면 소수점 수를 반환한다. 그런가 하면 반환값이 없는 것도 가능하다.

파이썬에서는 반환값이 없는 함수를 호출하면 None 값이 돌아온다. 이것은 변수에 지정될 수도 있다. None 값은 자바의 null과는 다른 개념이다.

def get_number():
   n = random.randint(1, 100)
   if n % 2 == 0:
       return n % 20
   #return (n % 20) / 2

for _ in range(5):
    print(get_number())

이 코드를 실행한 결과는 다음과 같다.

None
12
16
None
None

한가지 더. 함수의 반환형은 튜플 타입이 될 수 있다. 튜플은 값을 여러 개 콤마로 나열한 자료형이다.

def sum_avg(n):
    local_sum = 0
    avg = 0.0  # 지역변수 avg 선언
    for i in range(n):
        local_sum += int(input())
    avg = local_sum / n
    return local_sum, avg

이것은 의외로 상당히 편리한 기능이다. 함수가 항상 반환값을 하나만 돌려보낼 수 있다는 점 때문에 C 언어에서 포인터 매개변수로 값을 돌려받는 코드를 쓰곤하는데 이런 점을 해결해 준다. 튜플을 이용하면 반환값을 몇개든 제한없이 돌려보낼 수 있다.

sum, avg = sum_avg()
m, n = min_max(mylist)


'파이썬 프로그래밍' 카테고리의 다른 글

컴프리헨션과 제너레이터 수식  (0) 2019.02.21
파이썬 함수 - (2) 빌트인 함수  (0) 2019.02.20
파이썬 리스트 컴프리헨션  (0) 2019.01.28
파이썬 리스트  (0) 2019.01.28
파이썬 스트링  (0) 2019.01.27
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/11   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
글 보관함