< 함수와 인수 >
> 반복되는 코드
프로그램이 늘 하는 일은 늘상 비슷해서 유사한 작업을 계속 반복한다.
한번 처리한 일을 다시 처리할 경우가 빈번하며 그러다 보니 비슷한 코드가 계속 반복되는데 다음 예제를 보자
# repeat
sum = 0
for num in range(5):
sum += num
print("~ 4 =", sum)
sum = 0
for num in range(11):
sum += num
print("~ 10 =", sum)
~ 4 = 10
~ 10 = 55
* 4 까지의 합계 range(5) 와 10까지의 합계 range(11)를 구해 출력
일정 범위의 정수 합계를 구하는 방법은 sum 변수를 초기화하고 for 루프를 돌리며 누적합을 구하는 것
위와 같은 방식은 합계를 구하는 범위만 다를 뿐 같은 코드가 두번 반복 된다.
따라서 이처럼 반복적으로 사용되는 코드는 한번 정의해 놓고 계속 사용하는 것이 좋다.
함수는 일련의 코드 블록에 이름을 붙여 정의한 것이다.
def 함수명(인수 목록):
본체
* def 키워드(define)를 앞에 쓴 후 함수의 이름을 정의한다.
함수명은 명칭이므로 자유롭게 붙일 수 있되 동작을 잘 표현하는 동사로 붙이는 것이 좋다.
# calc sum
def calcsum(n):
sum = 0
for num in range(n + 1):
sum += num
return sum
print("~ 4 =", calcsum(4))
print("~ 10 =", calcsum(10))
~ 4 = 10
~ 10 = 55
* 합계를 계산하는 동작을 하므로 calcsum 으로 함수의 이름을 정함.
인수 n은 합계를 구할 범위를 전달 받으며 본체에서 n 까지 루프를 돌며 정수의 합계를 구함.
n 을 범위에 포함시키기 위해 range 함수의 끝 범위를 n + 1 로 지정
sum을 0으로 초기화하고 루프를 돌며 누적합을 구한 후 sum을 리턴
파이썬은 소스를 처음부터 순서대로 읽어 실행하는 인터프리터 언어이다.
그래서 함수를 호출하기 전에 먼저 함수가 정의되어 있어야 한다.
> 인수 (parameter)
인수는 호출원에서 함수로 전달되는 작업거리이며 호출하는 쪽과 함수를 연결한다는 의미로 매개변수라고도 부른다.
호출원에서 어떤 인수를 넘기는가에 따라 함수의 동작이 달라진다.
개수 제한이 없어 얼마든지 많이 전달할 수 있고 아예 없을 수도 있다.
def calcsum10():
sum = 0
for num in range(11):
sum += num
return sum
* 위 calcsum10 함수는 인수가 없으며 합계를 구할 범위가 10으로 고정되어 있어 딱 10까지의 합계만 구함.
인수가 없으면 함수의 동작이 항상 똑같아 활용성이 떨어진다.
이에 비해 앞에서 만든 calcsum(n) 함수는 끝 범위를 인수로 전달받아 range 함수에 적용하기 때문에 다양한 범위의 합계를 구할 수 있다.
인수는 함수의 동작에 변화를 주어 활용성을 높인다.
함수 정의문의 인수를 형식인수라고 하며,
함수 호출문에서 전달하는 인수를 실인수라고 한다.
calcsum( n ) 함수 정의문의 " n " 이 형식 인수이며,
호출문의 print( ... , calcsum( 4, 10 )) 4 와 10 이 실인수이다.
함수를 호출할 때 실인수의 값이 형식 인수에 대입되어 본체에서 사용한다.
함수는 입력에 따라 일정한 계산을 통해 출력을 리턴하는 기계에 비유된다.
호출문에서 어떤 인수를 전달하는가에 따라 함수의 동작과 리턴값이 달라진다.
형식 인수는 실인수값을 잠시 대입받는 임시 변수여서 이름은 아무래도 상관없지만 가급적이면 설명적인 이름을 붙이는 것이 좋다.
def calcsum(end):
sum = 0
for num in range(end + 1):
sum += num
return sum
* n 대신 범위의 끝이라는 뜻으로 end 로 붙이면 함수를 처음 보는 사람도 이 인수의 의미를 쉽게 파악할수 있어 직관적이다.
def calcrange(begin, end):
sum = 0
for num in range(begin, end + 1):
sum += num
return sum
print("3 ~ 7 =", calcrange(3, 7))
3 ~ 7 = 25
* 위 함수는 범위의 시작과 끝을 두개의 인수로 전달받아 두 정수 범위의 합계를 구함
끝 값만 전달받는 calcsum 은 범위의 시작이 1 로 고정되어 있는데 비해
시작과 끝을 모두 전달받는 calcrange 함수는 임의 범위의 합계를 구할 수 있어 활용성이 더 높다.
필요하다면 건너뛸 값을 step 인수로 전달받아 3, 5, 7 씩으로 하나씩 건너뛰며 합계를 구할 수도 있다.
> 리턴값
리턴값은 함수의 실행 결과를 호출원으로 돌려주는 값이다.
인수는 호출원에서 함수로 전달되는 작업거리인데 비해 리턴값은 실행 결과를 보고하는 값이다.
함수의 입장에서 볼 때 " 인수는 입력 값" 이고 "리턴은 출력 값" 이다.
인수는 여러 개 있을 수 있지만 리턴값은 딱 하나 밖에 없다.
리턴 값을 반환할 때 return 명령 뒤에 반환할 값을 지정한다.
return sum
* caclsum 함수는 인수로 전달 받은 범위에 대한 합계를 sum 에 계산하여 이 값을 리턴한다.
# print sum
def printsum(n):
sum = 0
for num in range(n + 1):
sum += num
print("~", n, "=", sum)
printsum(4)
printsum(10)
~ 4 = 10
~ 10 = 55
* 이 예제의 printsum 함수는 합계를 구해주는 것이 아니라 직접 출력한다.
내부에서 모든 것을 다 처리해 버리기 때문에 반납할 값이 없으며 따라서 함수의 본체에서 return 문을 쓸 필요가 없다.
> pass 명령
pass 명령은 파이썬의 모든 명령 중 가장 단단하며 아무 동작도 하지 않는다.
함수가 아닌 키워드이며 해석기가 직접지원한다.
해석기는 pass 명령을 만나면 그냥 무시하고 건너뛴다.
print("타잔이 10원짜리 팬티를 입고")
pass
print("20원짜리 칼을 차고 노래를 한다.")
타잔이 10원짜리 팬티를 입고
20원짜리 칼을 차고 노래를 한다.
* 이런 코드를 작성하면 print 함수만 두번 실행될 뿐 pass는 아무것도 하지 않는다.
동작은 없지만 앞으로 코드를 작성할 위치를 지정하는 역할을 한다.
예를 들어 calctotal 이라는 함수를 만들 예정이라면 다음과 같이 작성한다.
def calctotal():
pass
def calctotal(): #Error
윗줄이 : 로 끝났는데 아래줄에 코드가 없으면 에러처리가 나기 때문에,
일단 함수를 만들어 놓고 본체는 천천히 완성할 계획이라면 본체 자리에 pass 라고 적어 놓는다.
차후 이 위치에 구현코드를 천천히 작성하면 된다.
if score >= 90:
pass
else:
pass
함수뿐만 아니라 클래스나 조건문 등을 작성할 때도 빈 코드를 일단 만들어 둘 때는 pass 명령을 사용한다.
나중에 작성할 코드라도 일단 구문을 맞춰야 하기 때문에 빈 명령인 pass 가 꼭 필요하다.
< 인수의 형식 >
> 가변 인수
함수를 호출할 때 정의문의 형식 인수 개수만큼 실인수를 전달해야 한다.
def calcrange(begin, end):
sum = 0
for num in range(begin, end + 1):
sum += num
return sum
print("3 ~ 7 =", calcrange(3, 7))
print("3 ~ 7 =", calcrange(3)) # Error
print("3 ~ 7 =", calcrange(3, 7, 10)) # Error
* calcrange 함수는 두 개의 인수를 요구하므로 두 인수 모두 넘거야 한다.
하나만 전달하거나 세 개를 전달하면 에러 처리된다.
calcrange 함수는 범위의 시작과 끝을 모두 전달해야 제대로 동작한다.
일반적인 함수는 정의문에 필요한 인수의 개수가 명시되어 있고 호출할 때 이 개수에 맞게 실인수를 넘겨야 한다.
이에 비해 가변 인수는 임의 개수의 인수를 받는다.
인수 이름 앞에 * 기호를 붙이면 이 자리에 여러 개의 인수가 올 수 있다.
# var arg
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))
6
45
54
* intsum 함수는 정수의 합계를 구하되 개수는 미리 정해져 있지 않다.
ints 인수 앞에 * 기호 가 있어 이 인수로 여러 개의 실 인수를 한꺼번에 전달받는다.
파이썬은 호출문에 나타난 실인수를 튜플로 묶어 전달 하는데 본체에서 for 루프를 돌며 튜플의 요소를 하나씩 꺼내 사용한다.
intsum(1, 2, 3) → def intsum(*ints)
intsum(5, 7, 9, 11, 13) → def intsum(*ints)
* 정수 3개를 넘기든 5개를 넘기든 모두 ints 튜플로 묶여 전달되므로 얼마든지 많은 정수의 합을 구할 수 있다.
가변인수는 이후의 모든 인수를 다 포함하기 때문에 인수 목록의 마지막에 와야 한다.
가변 인수 앞에는 일반 인수가 올 수 있지만 뒤쪽에 올 수는 없다.
가변 인수 뒤에 일반 인수가 더 있으면 어디까지 가변 인수인지 잘 구분되지 않기 때문이다.
intsum(s, *ints) # Correct
intsum(*ints, s) # Error
intsum(*ints, *nums) # Error
같은 이유로 가변 인수가 2개 이상 있어서도 안 된다.
제일 마지막에 딱 하나만 와야 한다.
intsum(1, 2, 3, 4, 5) 로 호출 할 때 앞쪽에 일반 인수가 있으면 1이 s로 전달되지만 뒤쪽에 일반 인수가 더 있거나 다른 가변 인수가 있으면
어디까지가 어떤 인수 소속인지 애매하다.
가변 인수를 사용하는 대표적이고 실용적인 함수가 기본 출력문인 print 이다.
개수와 타입에 제한이 없어 문자열이든 숫자든 콤마로 구분하여 전달하면 된다.
출력 대상이 무엇이든, 어떤 형태이든 구분할 필요 없이 여러 개의 정보를 한 줄에 같이 출력할 수 있다.
print("순수한 선택, 성실한 실천")
print(a, b)
print("문자열", 1234, 3.14)
print 함수는 모든 인수를 가변 인수로 전달받아 하나씩 순서대로 꺼내 문자열 형태로 바꾼 후 화면으로 출력한다.
> 인수의 기본값
인수가 많으면 호출원에서 작업 지시를 섬세하게 전달할 수 있어 활용성이 높아진다.
인수를 하나 받는 calcsum 함수보다 인수 두 개를 받는 calcrange 함수가 더 범용적이다.
calcrange 함수에 증감값을 인수로 전달하면 범위 내의 수를 건너뛰며 합계를 구할 수도 있다.
이런 함수를 calcstep으로 다시 정의해 보자.
# calcstep
def calcstep(begin, end, step):
sum = 0
for num in range(begin, end + 1, step):
sum += num
return sum
print("1 ~ 10 =", calcstep(1, 10, 2))
print("2 ~ 10 =", calcstep(2, 10, 2))
1 ~ 10 = 25
2 ~ 10 = 30
* 1 에서 10 까지 2 씩 건너뛰면 홀수의 합계이고, 2 에서 10 까지 2 씩 건너뛰면 짝수의 합계이다.
인수가 추가되면 더 세밀한 작업 지시가 가능해져 다양하게 활용할 수 있지만 증감값이 1인 경우에도 꼭 1을 전달해야 하는 불편함이 있다.
예를 들어 1 ~ 100 의 모든 정수 합계를 구하려면 다음과 같이 호출한다.
calcstep(1, 100, 1)
* 증감값은 웬만하면 1인 경우가 대부분인데 일일이 이 값을 전달해야 하니 오히려 불편해지는 면이 있다.
이처럼 잘 바뀌지 않는 인수는 인수 목록에 기본값을 저장해 둔다.
기본값이 있는 인수는 일종의 옵션으로 취급되어 생략 가능하다.
실인수를 생략하면 기본값을 전달한 것으로 가정한다.
# default arg
def calcstep(begin, end, step=1):
sum = 0
for num in range(begin, end + 1, step):
sum += num
return sum
print("1 ~ 10 =", calcstep(1, 10, 2))
print("1 ~ 100 =", calcstep(1, 100))
1 ~ 10 = 25
1 ~ 100 = 5050
* calcstep 함수의 세 번째 인수에 = 1 초기값으로 주어 기본값 1을 지정했다.
calcstep(1, 10, 2) 식으로 3개의 인수를 지정하면 step 은 2가 되고,
calcstep(1, 100) 식으로 2개의 인수만 전달하면 step 은 기본값이 1 이 된다.
전달할 필요 없이 생략해버면 되니 간편하고 꼭 지정하고 싶으면 별도로 전달할 수 있어 활용성도 높다.
어떤 인수든지 기본값을 지정할 수는 있다.
calcstep의 end 인수에 기본값 100을 지정하면 끝값 생략 시 자동으로 100이 된다.
begin 도 마찬가지로 기본값을 지정할 수 있다.
그러나 기본값을 가지는 인수 뒤에 일반 인수가 올수는 없다.
중간의 인수가 기본값을 가지면 호출문에서 이를 생략할 수 없기 때문이다.
> 키워드 인수
함수를 호출할 때 함수 정의문에 선언된 순서대로 실인수를 전달하며
호출문에 나타나는 순서에 따라 형식 인수에 차례대로 대입된다.
순서대로 인수를 전달하는 방식을 위치 인수 (Positional Argument) 라고 하는데
호출문이 짧고 간단하지만 인수의 수가 많아지면 은근히 헷갈린다.
다음 예를 보자.
getscore(grade, clano, stuno, subject, month)
* 한 학생의 성적을 구하는 함수인데 학년, 반, 학생 번호, 과목, 시기 등 인수가 무척 많다.
2학년 3반 15번 학생의 4번 과목 6월 성적을 구하려면 getscore(2, 3, 15, 4, 6) 이라고 호출한다.
비슷한 숫자가 반복되어 순서를 외우기 어렵고 때로는 무척 헷갈리기도 한다.
위치 인수가 헷갈리는 이유는 등장 순서대로 형식 인수와 대응되며 정확하게 호출하려며 인수의 순서와 의미를 숙지해야 하기 때문이다.
그래서 순서와 무관하게 인수를 전달하는 방법을 제공한다.
인수의 이름을 지정하여 대입 형식으로 전달하는 방식을 키워드 인수 (Keyword Argument) 라고 한다.
이름으로 구분 가능하므로 순서가 바뀌어도 상관없다.
# keyword arg
def calcstep(begin, end, step):
sum = 0
for num in range(begin, end + 1, step):
sum += num
return sum
print("3 ~ 5 =", calcstep(3, 5, 1))
print("3 ~ 5 =", calcstep(begin=3, end=5, step=1))
print("3 ~ 5 =", calcstep(step=1, end=5, begin=3))
print("3 ~ 5 =", calcstep(3, 5, step=1))
print("3 ~ 5 =", calcstep(3, step=1, end=5))
3 ~ 5 = 12
3 ~ 5 = 12
3 ~ 5 = 12
3 ~ 5 = 12
3 ~ 5 = 12
* 이름으로 구분 가능하므로 순서가 바뀌어도 상관없다.
print 함수의 sep, end 가 대표적인 키워드 인수이며 기본값까지 지정되어 있다.
sep의 기본값이 공백이고 end의 기본값이 개행이어서 변수 사이에 공백이 삽입되고 다 출력한 후 자동 개행된다.
> 키워드 가변 인수
키워드 인수를 가변 개수 전달할 때는 인수 목록에 ** 기호를 붙인다.
호출원에서 여러 개의 키워드 인수를 전달하면 인수의 이름과 값의 쌍을 사전으로 만들어 전달한다.
함수 내부에서는 사전을 읽듯이 인수값을 꺼내 사용한다.
# keyword va arg
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 ~ 5 =", calcstep(begin=3, end=5, step=1))
print("3 ~ 5 =", calcstep(step=1, end=5, begin=3))
3 ~ 5 = 12
3 ~ 5 = 12
* calcstep 은 ** 인수 하나로 모든 작업거리를 다 전달받는다.
사전은 원래 순서가 없는 집합이어서 인수의 전달 순서는 아무래도 상관 없으며 함수 내부에서도 전달 순서를 알수 없다.
또한 위 예제의 calcstep 함수는 키워드 인수만 받겠다고 선언되어 있으므로 반드시 키워드 인수만 넘겨야 한다.
calcstep(3, 5, 1) 식으로 위치 인수를 넘기면 에러 처리된다.
또 세 개의 인수를 다 사용하고 있으므로 세 인수를 모두 전달해야 한다.
사전의 키로부터 인수값을 추출하여 사용하면 그만이다.
begin | 3 |
end | 5 |
step | 1 |
* calcstep(begin = 3, end = 5, step = 1) → calcstep(**args) // 사전으로 만들어 전달
위치 인수와 키워드 인수를 동시에 가변으로 취할 수도 있다.
이 때는 위치 인수가 먼저 오고 키워드 인수가 뒤에 온다.
일반 인수도 있다면 제일 앞에 온다.
# calc score
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("김상형", 88, 99, 77, avg=True)
calcscore("김한슬", 99, 98, 95, 89, avg=False)
김상형
총점: 264
평균: 88.0
김한슬
총점: 381
* 일반인수인 name 은 반드시 첫 번째 인수로 전달해야 한다.
name 다음에 가변 위치 인수 score 가 오는데 점수의 개수는 몇 개든 상관없다.
함수 호출문의 이름이 없는 모든 인수가 튜플로 묶여 score 인수로 전달된다.
마지막으로 option 가변 키워드 인수에는 성적을 처리하는 방식을 지정하는 옵션 값이 온다.
임의의 이름으로 얼마든지 많은 옵션을 포함할 수 있으며 옵션의 이름과 같이 사전으로 묶여 전달된다.
예제에서는 평균값을 출력할 것인지를 지정하는 avg 인수만 정의했다.
< 변수의 범위 >
> 지역 변수 (Local Variable)
함수는 동작에 필요한 변수를 얼마든지 사용할 수 있는데 함수 내부에서 선언하는 변수를 지역 변수라고 한다.
복잡한 동작을 처리하려면 많은 변수가 필요하다.
앞에서 만들었던 calcsum 함수는 누적 합계를 저장하기 위해 sum 이라는 지역 변수를 사용한다.
def calcsum(n):
sum = 0 # initialize local variable
for num in range(n + 1):
sum += num # accumulate
return sum # return
* 함수 선두에서 0으로 초기화하고 루프 내부에서 정수를 sum에 누적시켜 합계를 구한다.
정해진 범위를 다 돈 후 구해진 합계인 sum을 호출원으로 리턴한다.
지역변수는 함수안에서만 사용될 뿐 밖으로는 알려지지 않는다.
# local var
def kim():
temp = "김과장의 함수"
print(temp)
kim()
print(temp)
Traceback (most recent call last):
File "c:\Users\mnkyu\Desktop\pytest\python.py", line 9, in <module>
print(temp)
NameError: name 'temp' is not defined
* kim 함수 안에서 temp 라는 지역 변수에 초기 값을 대입하고 출력한다.
호출원에서 kim 함수를 호출하는데 이 과정에서 kim 은 내부적으로 temp 변수를 만들고 초기화하여 사용한다.
지역 변수는 함수의 동작을 처리하기 위해 잠시 사용하는 것이어서 함수가 종료되면 사라진다.
kim 함수를 호출한 후 temp 변수를 출력했는데 이는 에러이다.
temp 지역 변수는 kim 함수가 실행 중인 동안에만 존재하며 따라서 사용 범위는 함수 내부로 국한된다.
지역 변수의 사용 범위를 함수 내부로 제한하는 이유는 이름 충돌을 피하고 함수의 동작에 필요한 모든 것을
내부에 포함시켜 독립성을 향상시키기 위해서이다.
# local var_2
def kim():
temp = "김과장의 함수"
print(temp)
def lee():
temp = 2 ** 10
return temp
def park(a):
temp = a * 2
print(temp)
kim()
print(lee())
park(6)
김과장의 함수
1024
12
* 세 함수에서 temp 라는 지역 변수를 똑같은 이름으로 사용하고 있다.
이름이 같고 타입도 제각각이지만 각 지역 변수는 자신이 속한 함수 내부에서만 사용되므로 문제되지 않는다.
또 지역 변수는 함수의 동작에 필요한 모든 것을 내부에 완벽하게 소유할 수 있도록 하여 재활용성을 향상시킨다.
이 외에도 지역 변수는 여러가지 이 점을 제공한다.
지속 시간이 함수 실행 중으로 제한되어 필요할 때만 기억장소를 차지하므로 메모리를 절약한다.
지역 변수로 인해 골치 아픈 문제가 생겨도 사용 범위가 좁아 문제를 금방 찾을 수 있으며 디버깅이 쉽다.
> 전역 변수
지역 변수는 함수 내부에서 선언하여 초기화하는 변수이다.
이와 반대로 함수 바깥에서 선언하는 변수를 전역 변수라고 한다.
지역 변수는 함수 소속이어서 함수 내부에서만 참조할 수 있는데 비해 전역 변수는 프로그램 소속이어서 어디에서나 참조할 수 있다.
# global var
salerate = 0.9 # global var
def kim():
print("오늘의 할인율 :", salerate)
def lee():
price = 1000
print("가격 :", price * salerate)
kim()
salerate = 1.1
lee()
오늘의 할인율 : 0.9
가격 : 1100.0
* 소스 선두에 선언하고 초기화한 salerate 가 전역 변수이다.
어디에서나 참조할 수 있어 kim, lee 두 함수는 물론이고 함수 외부의 코드에서도 언제든지 읽고 쓸 수 있다.
kim 에서 salerate 를 출력하고 외부에서 이 값을 1.1로 변경하며 lee 는 가격에 할인율을 곱해 가격을 계산한다.
함수에서 전역 변수를 읽는 것은 아무 문제가 없지만 쓰는 것은 약간 주의해야 한다.
함수 내부에서 처음 대입하는 변수는 항상 지역 변수로 간주된다.
파이썬은 명시적인 선언이 없고 대입에 의해 변수를 생성하다 보니 초기화하는 장소에 따라 범위가 결정된다.
그래서 함수에서 외부 변수를 쓰기 어려운 문제가 있다.
# global var_2
price = 1000 # global variable declaration
def sale():
price = 500 # local variable declaration in "sale function"
sale()
print(price)
1000
* price는 함수 외부에서 1000 으로 초기화된 전역 변수이며 프로그램 전 영역에서 참조할 수 있다.
sale 함수는 price 변수를 500원으로 낮추어 반값 할인 행사를 준비한다.
sale 함수를 호출하고 가격을 다시 출력했는데 이상하게도 가격은 500원이 되지 않고 여전히 1000원이다.
이렇게 되는 이유는 함수 내부에서 초기화하는 변수는 전역 변수와 이름이 같더라도 항상 지역 변수로 간주되기 때문이다.
함수 내부에서 전역 변수를 읽을 수는 있지만 대입하여 초기화하면 새로운 지역 변수가 생성된다.
그래서 sale 함수에서 선언한 price 는 전역 변수 price 와 아무 상관이 없는 별도의 지역 변수이다.
그냥 우연히 이름이 같을 뿐이며 값을 저장하는 메모리 위치가 다른 별개의 변수이다.
# id
price = 1000
def sale():
price = 500
print("sale", id(price))
sale()
print("global", id(price))
sale 1595904452912
global 1595904452880
* id 함수는 변수의 고유한 식별자를 조사하는데 주로 값을 저장하는 주소를 리턴한다. (변수의 주소)
이름은 같은 price 이지만 sale 함수 내의 지역 변수 price 와 전역 변수 price 의 주소가 다름을 알 수 있다.
전역 변수와 같은 이름의 지역 변수를 선언할 수 없다면 함수를 재활용할 때 전역 변수와의 이름 충돌이 빈번해 불편해진다.
calcsum 함수를 다른 프로젝트로 가져갔는데 이미 sum이라는 전역 변수가 있다면 지역 변수 sum 의 이름을 바꿔야 하는 문제가 있다.
그래서 지역, 전역끼리는 이름이 같아도 상관없되 함수 내부에서는 지역 변수가 우선이라는 규칙을 적용한다.
이 규칙에 의해 sale 함수 내에서 price = 500 대입문은 전역 price 의 값을 바꾸는 것이 아니라 지역 price 변수를 초기화하는 문장이다.
앞 예제의 sale 함수에서 참조하는 price 는 의도상 새로운 지역 변수를 의미하는 것이 아니라 전역 price의 값을 변경하고자 하는 것이다.
이런 경우는 이 변수가 지역이 아니라 전역임을 가르쳐 주어야 하며 이때 사용하는 명령이 global 이다.
# global var_3
price = 1000
def sale():
global price
price = 500
sale()
print(price)
500
* 함수 선두에 global price 라고 써 주면 함수 내부에서 price 변수를 사용할 때 지역 변수 price를 새로 만들지 않고 전역의 price 변수를 참조한다.
변수의 이름을 자유롭게 붙일 수 있고 또 여러 명이 팀을 이루어 작업하다 보니 이름 충돌은 어쩔 수 없이 발생한다.
충돌 시 어떤 변수를 의미하는지 지정하는 global 명령이 제공되는데 가급적 사용하지 않은 것이 좋다.
원칙적으로는 전역 변수를 자제하는 것이 좋고 정필요하다면 g_price 식으로 이름에 전역임을 표시하여 충돌을 원천적으로 방지하는 것이 합리적이다.
( 한군데가 잘못되어 생긴 오류가 프로그램 전체에 영향을 끼침, 될 수 있는 한 지역 변수를 사용하고 전역 변수는 최소화하는 프로그래밍 습관 필요)
> docstring
함수는 내가 쓰기 위해 만들기도 하지만 잘 만든 함수는 여러 사람이 재사용하기도 한다.
모든 것을 직접 만들 수는 없으니 다른 개발자가 만든 함수를 구해 쓰는 경우도 빈번하다.
별도의 문서나 주석으로 설명을 남길 수도 있지만 공식적인 방법은 docstring 을 작성하는 것이다.
docstring 은 함수 선언문과 본체 사이에 작성하는 문자열인데 보통 삼겹 따옴표로 여러 줄의 긴 설명을 작성하고 서식까지 지정할 수 있다.
docstring 을 읽을 때는 help 함수를 사용하며 인수로 함수명을 전달한다.
# docstring
def calcsum(n):
"""1 ~ n 까지의 합계를 구해 리턴한다."""
sum = 0
for num in range(n + 1):
sum += num
return sum
help(calcsum)
Help on function calcsum in module __main__:
calcsum(n)
1 ~ n 까지의 합계를 구해 리턴한다.
* help 는 함수의 소속과 호출 형식 그리고 개발자가 작성해 놓은 docstring 을 출력한다.
아주 짧은 설명이지만 calcsum 함수의 기능이 무엇이고 어떻게 사용하는지 알 수 있다.
> 함수의 장점
함수는 엄격한 형식에 따라 작성해야 하며 규칙이 다소 복잡한데다 호출 과정을 거쳐야 하므로 필요한 코드를 바로 작성하는 것보다 번거로운 면이 있다.
그러나 한번 잘 작성해 놓으면 여러가지 장점을 제공한다.
'Language > Python' 카테고리의 다른 글
Python_09. List and Tuple (0) | 2022.08.01 |
---|---|
Python_08. String management (0) | 2022.07.31 |
Python_06. Iteration (0) | 2022.07.27 |
Python_05. Conditional Statement (0) | 2022.07.27 |
Python_04. Operator (0) | 2022.07.27 |