반응형

URL 복사  통계 

 

 

1. 함수와 인수

1.1 반복되는 코드

ex) 해당 숫자까지의 합 구하기

sum = 0
for num in range(1, 5):
    sum += num
print("1 ~ 4 까지의 합 =", sum)

sum = 0
for num in range(1, 11):
    sum += num
print("1 ~ 10 까지의 합 =", sum)

코딩을 하다보면 이런식으로 코드가 반복되는 경우가 있다. 물론 복사,붙여넣기로 범위만 변경해주면 되긴 하지만 매번 이런식으로 수정하려고 하면 귀찮을 뿐만 아니라 소스도 길어지고 구조상으로 보기 좋지 않다. 따라서 이런식으로 반복적으로 사용되는 코드는 한번 정의해 놓고 계속 사용하는 것이 좋다. 함수는 일련의 코드 블록에 이름을 붙여 정의한 것이다.

<기본 형식>

def 함수명(인수 목록):

    본체

def 키워드(define)를 앞에 쓴 후 함수의 이름을 정의한다. 함수명은 명칭이므로 자유롭게 붙일 수 있되 동작을 잘 표현하는 동사로 붙이는 것이 좋다. 인수 목록은 호출원이 함수로 전달하는 작업거리이며 함수 내부에서 사용한다. 본체는 함수의 동작을 처리하는 코드를 기술한다. 이렇게 함수를 정의해 놓고 다음 호출문으로 실행한다.

함수(인수목록)

함수의 이름으로 호출하고 괄호 안에 작업거리인 인수를 전달한다. 호출문에 의해 함수의 본체로 인수가 전달되어 실행된다

ex) 함수를 사용해서 해당 숫자까지의 합 구하기

def add(x):
    sum = 0
    for num in range(1,x+1):
        sum += num
    return sum

print("1 ~ 4 까지의 합 =",add(4))
print("1 ~ 10 까지의 합 =",add(10))

파이썬은 소스를 처음부터 순서대로 읽어 실행하는 인터프리터 언어이다. 그래서 함수를 호출하기 전에 먼저 함수가 정의되어 있어야 한다. 아래와 같이 작성한다면 에러가 발생한다.

print("1 ~ 4 까지의 합 =",add(4))
print("1 ~ 10 까지의 합 =",add(10))

def add(x):
    sum = 0
    for num in range(1,x+1):
        sum += num
    return sum

# ==>> 에러발생!!!

해석기는 함수의 코드를 기억해 두었다가 호출원에서 호출할 때 함수를 실행한다.

 

1.2 인수

인수는 호출원에서 함수로 전달되는 작업거리이며 호출하는 쪽과 함수를 연결한다는 의미로 매개변수라고도 부른다. 호출원에서 어떤 인수를 넘기는가에 따라 함수의 동작이 달라진다. 개수 제한이 없어 얼마든지 많이 전달할 수 있고 아예 없을 수도 있다.

def add(x): # x라는 매개변수로 함수의 동작에 변화를 주어 활용성을 높힌다.

    sum = 0

    for num in range(1,x+1):

        sum += num

    return sum # x값에 따라 다양한 값을 리턴한다.

호출문에서 어떤 인수를 전달하는가에 따라 함수의 동작과 리턴값이 달라진다.

1.3 리턴 값

리턴값은 함수의 실행 결과를 호출원으로 돌려주는 값이다. 인수는 호출원에서 함수로 전달되는 작업거리인 데 비해 리턴값은 실행 결과를 보고하는 값이다. 함수의 입장에서 볼 때 인수는 입력값이고 리턴은 출력값이다. 인수는 여러개 있을 수 있지만 리턴값은 딱 하나밖에 없다. 리턴값을 반환할 때 return 명령 뒤에 반환할 값을 지정한다. add 함수는 인수로 전달받은 범위에 대한 합계를 sum에 계산하여 이 값을 리턴한다.

리턴값이 꼭 있어야 하는 것은 아니며 반환할 값이 없는 경우도 있다.

ex) 해당 숫자까지의 합 구하기

def printadd(x):
    sum = 0
    for num in range(1, x+1):
        sum += num
    print("1 ~ ", x, "까지의 합 =", sum)

printadd(4)
printadd(10)

printadd 함수는 인수를 받으면 sum을 구하고 출력까지 해주기 때문에 반납할 리턴값이 없다. 리턴 값이 없는 함수는 동작만 처리할 뿐 값이 아니기 때문에 단독으로 호출해야한다. 변수에 대입하거나 수식 내에서 사용해서는 안된다.

a = printadd(4) # None
print(printadd(10)*4) # 에러

printadd 함수의 리턴값이 없어 a에는 아무것도 아니라는 의미의 None 값이 대입된다. 리턴값이 없는 함수 호출문을 수식 내에서 사용하면 None과 숫자를 연산할 수 없어 에러 처리된다.

 

1.4 pass 명령

pass 명령은 파이썬의 모든 명령 중 가장 간단하며 아무 동작도 하지 않는다. 함수가 아닌 키워드이며 해석기가 직접 지원한다. 해석기는 pass 명령을 만나면 그냥 무시하고 건너뛴다.

def calculator():
    pass

일단 함수를 만들어 놓고 본체는 천천히 완성할 계획이라면 본체 자리에 pass라고 적어 놓는다. 차후 이 위치에 구현 코드를 천천히 작성하면 된다.

def calculator():

만약 위의 코드처럼 함수의 이름만 덜렁 만들어 두면 본체가 없다는 에러로 처리된다. 왜냐하면 윗줄이 : 으로 끝났으니 아랫줄에 뭔가 코드가 와야 하는데 그렇지 않으니 모양이 이상하고 불완전해 보인다. 그래서 빈 코드임을 나타내는 pass 명령이 필요하다.

void calculator()
{

}

C나 Java 같은 경우 위의 코드 처럼 괄호안에 내용이 비었기 때문에 따로 명령어를 써주지 않아도 되지만 파이썬의 경우 { } 괄호를 쓰지 않기 때문에 빈 코드를 의미하는 별도의 키워드가 필요하다. 함수뿐만 아니라 클래스나 조건문 등을 작성할 때도 빈코드를 일단 만들어 둘 때는 pass 명령을 사용한다.

2. 인수의 형식

2.1 가변인수

함수를 호출할 때 함수 정의문의 형식 인수 개수만큼 실인수를 전달해야 한다. 일반적인 함수는 정의문에 필요한 인수의 개수가 명시되어 있고 호출할 때 이 개수에 맞게 실인수를 넘겨야 한다. 이에 비해 가변 인수는 임의 개수의 인수를 받는다. 인수 이름 앞에 * 기호를 붙이면 이자리에 여러 개의 인수가 올 수 있다.

def intsum(*ints):
    sum = 0
    for num in ints:
        sum += num
    return sum

print(intsum(1, 2, 3))
print(intsum(5, 7, 9, 11, 13))
print(intsum(8, 9, 6, 2, 9, 7, 5, 8))

intsum 함수는 정수의 합계를 구하되 개수는 미리 정해져 있지 않다. ints 인수 앞에 * 기호가 있어 이 인수로 여러 개의 실인수를 한꺼번에 전달받는다. 파이썬은 호출문에 나타난 실인수를 튜플로 묶어 전달하는데 본체에서 for 루프를 돌며 튜플의 요소를 하나씩 꺼내 사용한다. 모두 ints 튜플로 묶여 전달되므로 얼마든지 많은 정수의 합을 구할 수 있다.

가변 인수는 이후의 모든 인수를 다 포함하기 때문에 인수 목록의 마지막에 와야 한다. 가변 인수 앞에는 일반 인수가 올 수 있지만 뒤쪽에 올 수는 없다. 가변 인수 뒤에 일반 인수가 더 있으면 어디까지 가변 인수인지 잘 구분되지 않기 때문이다.

intsum(s, *ints)      # 가능
intsum(*ints, s)      # 에러
intsum(*ints, num*)   # 에러

같은 이유로 가변 인수가 2개 이상 있어서도 안 된다. 제일 마지막에 딱 하나만 와야한다. 예를들어 intsum(1, 2, 3, 4, 5)로 호출할 때 앞쪽에 일반 인수가 있으면 1이 s로 전달되지만 뒤쪽에 일반 인수가 더 있거나 다른 가변 인수가 있으면 어디까지가 어떤 인수 소속인지 애매하다.

가변 인수를 사용하는 대표적이고 실용적인 함수가 기본 출력문인 print 이다. 개수와 타입에 제한이 없어 문자열이든 숫자든 콤마로 구분하여 전달하면 된다. 출력 대상이 무엇이든, 어떤 형태이든 구분할 필요 없이 여러개의 정보를 한 줄에 같이 출력할 수 있다.

print("Hell World")
print(a, b)
print("문자열", 1234, 3.14)

print 함수는 모든 인수를 가변 인수로 전달받아 하나씩 순서대로 꺼내 문자열 형태로 바꾼 후 화면으로 출력한다. 던지는 족족 출력해 주니 쓰기 편하다.

2.2 키워드 인수

함수를 호출할 때 함수 정의문에 선언된 순서대로 실인수를 전달하며 호출문에 나타나는 순서에 따라 형식 인수에 차례대로 대입된다. 순서대로 인수를 전달하는 방식을 위치 인수(Positional Argument)라고 하는데 호출문이 짧고 간단하지만 인수의 수가 많아지면 은근히 헷갈린다.

getscore(grade, clano, stuno, subject, month)

한 학생의 성적을 구하는 함수인데 학년, 반, 학생 번호, 과목, 시기 등 인수가 무척 많다. 예를들어 2학년 3반 15번 학생의 4번 과목 6월 성적을 구하려면 getscore(2, 3, 15, 4, 6)이라고 호출한다. 비슷비슷한 숫자가 반복되어 순서를 외우기 어렵고 때로는 무척 헷갈리기도 한다. 위치 인수가 헷갈리는 이유는 등장 순서대로 형식 인수와 대응되며 정확하게 호출하려면 인수의 순서와 의미를 숙지해야 하기 때문이다. 그래서 순서와 무관하게 인수를 전달하는 방법을 제공한다. 인수의 이름을 지정하여 대입 형식으로 전달하는 방식을 키워드 인수(Keyword Argument)라 한다. 이름으로 구분 가능하므로 순서가 바뀌어도 상관없다.

def getscore(grade, clano, stuno, subject, month):
    print(grade, "학년", clano, "반", stuno, "번 학생", subject, "번 과목", month, "월 성적")


getscore(2, 3, 15, 4, 6)
getscore(2, subject = 4, month = 6, clano = 3, stuno = 15)
getscore(2, 3, 15, month = 6, subject = 4)
getscore(2, 3, subject = 4, month = 6, stuno = 15)
getscore(clano = 3, stuno = 15, subject = 4, month = 6, grade = 2)

첫 번째 호출문은 위치 인수만 사용하여 차례대로 형식 인수 grade, clano, stuno, subject, month 에 대입되었다. 두 번째 호출문 부터는 각 인수에 이름을 지정하여 전달했는데 이름을 분명히 지정했으므로 순서와 상관없이 정확하게 전달된다. 대신 이름이 지정되지 않은 위치 인수는 순서를 정확히 하여야 한다.

그러나 아래와 같은 경우는 에러가 발생한다.

getscore(grade = 2, 3, 15, 4, 6)  # 에러
getscore(2, 3, month = 6, 4, stuno = 15)  # 에러

위치 인수가 항상 먼저 와야 하며 앞쪽에 키워드 인수가 왔으면 뒤쪽에는 위치 인수가 올 수 없다. 앞쪽에 이미 키워드 인수가 왔으면 뒤쪽 인수도 이름을 지정해야 정확한 짝을 찾을 수 있다.

print 함수의 sep, end 가 대표적인 키워드 인수이며 기본값까지 지정되어 있다.

print("Hell world", sep = '/', end = ' ')

sep 의 기본값이 공백이고 end의 기본값이 개행이여서 변수 사이에 공백이 삽이되고 다 출력 한 후 자동 개행된다. 키워드 인수를 생략하면 디폴트가 적용되지만 두 인수를 원하는 대로 변경하여 구분자와 끝 문자를 바꿀 수 있다.

print(a, b)                           # 3 4
print(a, b, sep = ' ')                # 3,4
print(a, b, sep = ',', end = '$')     # 3,4$
print(a, b, end = '$', sep = '.')     # 3,4$

 

2.1 키워드 가변 인수

키워드 인수를 가변 개수 전달할 떄는 인수 목록에 ** 기호를 붙인다. 호출원에서 여러 개의 키워드 인수를 전달하면 인수의 이름과 값의 쌍을 사전으로 만들어 전달한다. 함수 내부에서는 사전 읽듯이 인수값을 꺼내 사용한다.

def calcstep(**args):
    begin = args['begin']
    end = args['end']
    step = args['step']

    sum = 0
    for num in range(begin, end + 1, step):
        sum += num
    return sum


print("3 + 4 + 5 =", calcstep(begin = 3, end = 5, step = 1))
print("3 + 4 + 5 =", calcstep(step = 1, end = 5, begin = 3))

calcstep은 **args 인수 하나로 모든 작업거리를 다 전달받는다. 사전은 원래 순서가 없는 집합이여서 인수의 전달 순서는 상관없으며 함수 내부에서도 전달 순서를 알 수 없다. 사전의 키로부터 인수값을 추출하여 사용한다.

def calcscore(name, *score, **option):
    print(name)
    sum = 0
    for s in score:
        sum += s
    print("총점 :",sum)
    if option['avg'] == True:
        print("평균 :", sum / len(score))


calcscore("A", 88, 99, 77, avg = True)
calcscore("B", 99, 98, 95, 89, avg = False)

일반 인수인 name 은 반드시 첫 번째 인수로 전달해야 한다. 그리고 가변 위치 인수 score가 오는데 점수의 개수는 몇개든 상관없다. 함수 호출문의 이름이 없는 모든 인수가 튜플로 묶여 score 인수로 전달된다.

마지막으로 option 가변 키워드 인수에는 성적을 처리하는 방식을 지정하는 옵션 값이 온다. 임의의 이름으로 얼마든지 많은 옵션을 포함할 수 있으며 옵션의 이름과 같이 사전으로 묶여 전달된다.

def calcscore(name, *score, **option):
    print(name)
    sum = 0
    for s in score:
        sum += s
    print("총점 :",sum)
    if option['avg'] == True:
        print("평균 :", sum / len(score))
    print("평균 평점 :", option['GPA'])
    print()

calcscore("A", 88, 99, 77, avg = False, GPA = "3.8")
calcscore("B", 99, 98, 95, 89, avg = True, GPA = "4.4")

 

3. 변수의 범위

3.1 지역 변수

함수는 동작에 필요한 변수를 얼마든지 사용할 수 있는데 함수 내부에서 선언하는 변수를 지역변수라고 한다. 아래의 함수는 누적 합계를 저장하기 위해 sum 이라는 지역 변수를 사용한다.

def calcsum(n):
    sum = 0                 # 지역변수 초기회
    for num in range(n+1):
        sum += sum          # 누적
    return sum              # 리턴

<예제>

def kim():
    temp = "김준영의 한숨"
    print(temp)

kim()
print(temp)

지역 변수는 함수의 동작을 처리하기 위해 잠시 사용하는 것이어서 함수가 종료되면 사라진다. kim 함수를 호출 하고난 뒤 temp 변수를 출력하려고 하면 에러가 발생하게된다.

def kim():
    temp = "김준영의 한숨"
    print(temp)

def lee():
    temp = 2
    return temp

def park(a):
    temp = a * 2
    print(temp)

kim()
print(lee())
park(6)

세 함수에서 temp 라는 지역 변수를 똑같은 이름으로 사용하고 있지만 각 지역변수는 자신이 속한 함수 내부에서만 사용되므로 문제가 발생하지 않는다.

 

3.2 전역 변수

지역변수와는 반대로 함수 바깥에서 선언하는 변수를 전역 변수라고 한다. 지역 변수는 함수 소속이여서 함수 내부에서만 참조할 수 있는데 비해 전역 변수는 프로그램 소속이어서 어디에서나 참조할 수 있다.

price = 1000

def sale():
    price = 500

sale()
print(price)

위의 예제 처럼 sale 함수에서 선언한 price 는 전역 변수 price 와 아무 상관이 없는 별도의 지역 변수이므로 print(price) 에 영향을 주지 않는다.

price = 1000

def sale():
    price = 500
    print("지역 변수", id(price))

sale()
print("전역 변수", id(price))

id 함수는 변수의 고유한 식별자를 조사하는데 주로 값을 저장하는 메모리 주소를 리턴한다. 보이는 바와 같이 주소 또한 다르다.

3.3 Docstring

docstirng은 다른 개발자가 코드를 보았을때 자세한 함수 사용법이나 인수의 의미, 주의사항등을 문서로 남긴 것이다. docstring은 다른 사람을 위한 단순한 설명일 뿐 코드가 아니어서 실행에는 아무런 영향을 미치지 않는다. docstring을 읽을 때는 help 함수를 사용하며 인수로 함수명을 전달한다.

def calcsum(n):
    """1 ~ n 까지의 합계를 구해 리턴한다."""
    sum = 0
    for i in range(n+1):
        sum += i
    return sum

help(calcsum)

def calcsum(n):
    """1 ~ n 까지의 합계를 구해 리턴한다."""
    sum = 0
    for i in range(n+1):
        sum += i
    return sum

print(calcsum.__doc__)

help는 함수의 소속과 호출 형식 그리고 개발자가 작성해 놓은 docstion을 출력한다.

3.4 내장 함수 몰록

대표적인 예

- 입력과 출력 : print, input

- 타입 변환 : int, str, float

- 진법 변환 : hex, oct, bin

Built-in Functions

abs()

dict()

help()

min()

setattr()

all()

dir()

hex()

next()

slice()

any()

divmod()

id()

object()

sorted()

ascii()

enumerate()

input()

oct()

staticmethod()

bin()

eval()

int()

open()

str()

bool()

exec()

isinstance()

ord()

sum()

bytearray()

filter()

issubclass()

pow()

super()

bytes()

float()

iter()

print()

tuple()

callable()

format()

len()

property()

type()

chr()

frozenset()

list()

range()

vars()

classmethod()

getattr()

locals()

repr()

zip()

compile()

globals()

map()

reversed()

__import__()

complex()

hasattr()

max()

round()

delattr()

hash()

memoryview()

set()

감사합니다.

 

반응형

+ Recent posts