함수
특정 작업을 수행하기 위한 재사용
가능한 코드 묶음, ⇒ 모듈화를 시킨다.
함수가 많아지면, 느려진다. 그럼에도 불구하고 유지보수 등을 고려했을 때 사용함
왜? 사용하는 가
코드의 중복을 방지
재사용성이 높아지고, 코드의 가독성과 유지보수성 향상
함수 call하고 return 하면 복사되면서 사라짐
내장 함수 Built-in
파이썬이 기본적으로 제공 = 별도의 import 필요 없음
ex) print()
, abs()
외장 함수
공식적으로는 존재하지 않음, 단지 내장함수의 반댓말로 말하기 위함
사용자정의함수
개발자가 만든 함수, 내장함수 반댓말 중 하나
함수 호출 (function call)
function_name(arguments)
함수를 실행하기 위해 함수의 이름을 사용하여 해당 함수의 코드 블록을 실행하는 것
구조
def make_sum(pram1, pram2):
# prameter : 매개변수
# ':'하단부터 'return' 까지를 body라고 함
return pram1 + pram2 #
return 값은 무조건 하나 <= return 은 한번만 나오기 때문
여러 줄 주석 할 때도 “”” 대신에 ‘#’ 을 사용하자
""" """ Docstring : Document String
함수를 사용할 가이드를 제공해서 함수에 넣어줌
특수한 역할을 함, 하지만 필수적인 것은 아님
=> 이런 이유로 여러 줄 주석할 때 # 으로 다 처리하는걸 권장 함
정의
#함수 정의
def greet(name): # def 키워드로 시작
"""여러줄 # 클론(:) 다음에 들여쓰기 된 코드 블록
주석""" # Docstring은 함수 body 앞에 선택적으로 작성
message = 'Hello, ' + name
return message # 함수는 필요한 경우 결과를 반환 가능 # return 문은 함수의 실행을 종료 # => return 뒤로 나오는 문자들 무시함
- 값을 반드시 받아야 한다? ⇒ 매개변수(name)를 입력해둔다
- return 문은 함수의 실행을 종료 ⇒ return 뒤로 나오는 문자를 무시함
호출
- 함수를 호출하기 위해서는 함수의 이름과 필요한 인자(argument)를 전달해야 함
- 호출 부분에서 전달된 인자는 함수 정의 시 작성한 매개변수에 대입됨
매개변수와 인자
책마다 동일 시 하기도 함 but 다르다
기본적으로 매개변수의 수와 인자의 수는 같아야만 한다.
- 매개변수 parameter
함수를 정의할 때, 함수가 받을 값을 나타내는 변수, 지역변수로 취급 됨
- 인자 argument
함수를 호출할 때, 실제로 전달되는 값
def add_numbers(x,y): #x와 y는 매개변수(parameter)
result = x + y
return result
a = 2
b = 3
sum_result = add_numbers(a,b) # a와 b는 인자(argument)
print(sum_result)
위치 인자 Positional Arguments
- 함수 호출 시 인자의 위치에 따라 전달되는 인자
- 위치 인자는 함수 호출 시 반드시 값을 전달해야 한다.
- 파이썬은 다이나믹 타이핑(변수의 타입을 선언해두지 않음)이기 때문에 위치 인자가 더 중요하다.
def greet(name, age): print(f'안녕하세요, {name}님! {age}살이시군요.')
greet('Alice', 25) # 안녕하세요, Alice님! 25살이시군요
greet('Bella') # age인자가 없어서 실행 안됨
greet(30) # age인자가 없어서 실행 안됨, 30이 name의 인자로 들어갔음
greet(30,'bella','aaa') # 3개의 인자가 들어가서 에러
return은 필수 X, 하지만 위치 인자 age 필수
기본 인자 값 Default Argument Values
- 함수 정의에서 매개변수에 기본 값을 할당하는 것
- 함수 호출 시 인자를 전달하지 않으면, 기본값이 매개변수에 할당됨
- 반드시 위치 인자 다음에 나와야 한다.
def greet(age = 30, name):
은 안됨 - 인수를 전달할 때 매개변수에 할당된 기본 값의 타입을 맞춰줘야 한다.
def greet(name, age=30):
print(f'안녕하세요, {name}님! {age}살이시군요.')
greet('Bob') # 안녕하세요, Bob님! 30살이시군요.
greet('Charlie', 40) # 안녕하세요, Charlie님! 40살이시군요.
키워드 인자 Keyword Arguments
- 함수 호출 시 인자의 이름과 함께 값을 전달하는 인자
- 매개변수와 인자를 일치 시키지 않고, 특정 매개변수에 값을 할당할 수 있음
- 인자의 순서는 중요하지 않으며, 인자의 이름을 명시하여 전달
단, 호출 시 키워드 인자는 위치 인자 뒤에 위치함
greet(age=35, name='Dave') #정상
grret(age=35,'Dave') # 에러
임의의(가변) 인자 목록 Arbitrary Argument Lists
- 정해지지 않은 개수의 인자를 처리하는 인자
⇒ 유연하게 처리할 필요성이 생기기 때문
print()
함수의 인자가 몇 개 나올지 모르지만 여러개를 넣어둘 수 있는 이유 - 함수 정의 시 매개변수 앞에
*
를 붙여 사용하며, 여러 개의 인자를 tuple 로 처리
def calculate_sum(*args): # 인자가 몇개가 나오던지 묶어버려라 print(args) total = sum(args) print(f'합계: {total}') """ (1, 2, 3) 합계: 6 """
calculate_sum(1, 2, 3)
임의의 키워드(가변 키워드) 인자 목록 Arbitrary Keyword Argument Lists
- 정해지지 않은 개수의 키워드 인자를 처리하는 인자
⇒ 0개이상의 키워드 목록을 처리해야 한다
- 함수 정의시 매개변수 앞에
**
를 붙여 사용하며, 여러 개의 인자를 dictionary로 묶어 처리
def print_info(**kwargs):
print(kwargs)
print_info(name='Eve', age = 30) #{'name':'Eve','age':30}
함수 인자 권장 작성순서
- 위치 인자는 반드시 높은 우선순위 (가장 앞에 와야함)
- 위치 → 기본 → 가변 → 키워드인수 → 가변 키워드
⇒ 키와 값을 가진 인수(키워드 인수)들은 위치와 무관하게 잘 찾아갈 수 있다.
⇒ 하지만, 위치인수, 기본인수는 정확한 위치를 요구받기때문에 우선순위가 높다.
- 호출 시 인자를 전달하는 과정에서 혼란을 줄일 수 있도록 함
- 단, 절대적인 규칙 아님, 유연하게 조정해야함
def func(pos1, pos2, age='default', *args, **kwargs):
print(pos1, pos2, age, args, kwargs)
func(1,2,3,4,5) # 1 2 3 (4,5) {}
func(1,2,3,1=100,b=200) # 1 2 3 () {'a':100,'b':200}
범위 Scope
함수는 코드 내부에 local scope를 생성하며, 그 외의 공간인 global scope로 구분
scope
- global scope : 코드 어디에서든 참조할 수 있는 공간
- local scope : 함수가 만든 scope(함수 내부에서만 참조 가능)
⇒ ‘함수’ 를 기준으로 나뉜다
variable
- global variable : global scope 에 정의된 변수
- local variable : local scope에 정의된 변수
Scope 예시
local scope에 존재하는 변수 global 에서 사용할 수 없음
변수의 수명주기와 연관이 있음
변수 수명주기(lifecycle)
- 변수의 수명주기는 변수가 선언되는 위치와 스코프에 따라 결정됨
- built_in scope
파이썬이 실행된 이후부터 영원히 유지
- global scope
모듈이 호출된 시점 이후 혹은 인터프리터가 끝날 때까지 유지
- local scope
함수가 호출될 때 생성되고, 함수가 종료될 때까지 유지
이름 검색 규칙(Name Resolution, LEGB로 흔히 말함)

enclosed Scope : 함수 중첩될때
함수 내에서 바깥 Scope의 변수에 접근 가능하나 수정은 할 수 없음 (L → E → G → B)
파이썬에서 사용되는 이름(식별자)들은 특정한 이름공간(namespace)에 저장되어 있음
- 아래와 같은 순서로 이름을 찾아 나가며, LEGB Rule이라고 부름
- Local scope : 지역 범위(현재 작업 중인 범위)
- Enclosed scope : 지역 범위 한 단계 위 범위
- Global scope : 최상단에 위치한 범위
- Built-in scope : 모든 것을 담고 있는 범위(정의하지 않고 사용 할 수 있는 모든 것)
REGB Rule 예시
- sum이라는 이름을 global scope에서 사용하게 되면서
기존에 built-in scope에 있던 내장함수 sum을 사용하지 못하게 됨 - sum을 참조 시 LEGB Rule에 따라 global에서 먼저 찾기 때문
print(sum) #
print(sum(range(3))) # 3
sum = 5
print(sum) # 5
print(sum(range(3))) # TypeError: 'int' object is not callable
a = 1
b = 2
def enclosed(): a = 10 c = 3 def local(c): print(a, b, c) # 10 2 500 local(500) print(a, b, c) # 10 2 3
enclosed()
print(a, b) # 1 2
‘global’ 키워드
- 변수의 스코프를 전역 범위로 지정하기 위해 사용
- 일반적으로 함수 내에서 전역 변수를 수정하려는 경우에 사용
- 일반적으로 권장되지 않음
⇒ 함수로 값을 바꾸고자 한다면 항상 인자로 넘기고 함수의 반환 값을 사용하는 것을 권장
num = 0 # 전역 변수 def increment(): global num # num를 전역 변수로 선언 num += 1 print(num) # 0 increment() print(num) # 1
global 주의사항
global 선언 후 사용가능함
def increment(): # SyntaxError: name 'num' is used prior to global declaration print(num) global num num += 1
매개변수에 global 사용 불가
num = 0
def increment(num): # "num" is assigned before global declaration global num num += 1
재귀 함수
- 함수 내부에서 자기 자신을 호출하는 함수
- 어느 시점에서 무조건 종료가 된다
특징
- 특정 알고리즘 식을 표현할 때 변수의 사용이 줄어들며, 코드의
가독성
이 높아짐 - 1개 이상의 base case(종료되는 상황)가 존재하고, 수렴하도록 작성
⇒ 큰 문제를 작은 문제로 쪼개서 해결하기 위해 사용됨
⇒ 큰 문제를 잘못 짜면 터지기 때문
- 반복과 관련 있음, 반복을 재귀로 변경할 수 있지만, 재귀를 반복으로 못 바꾸는 경우가 있다. = 재귀를 써야만 하는 상황이 발생한다.
예시 - 팩토리얼
def factorial(n):
#종료 조건: n이 0이면 1을 반환 / basis 기저조건
if n ==0:
return 1
#재귀 호출 : n과 n-1의 팩토리얼을 곱한 결과를 반환 / Inductive 유도파트
return n * factorial(n-1)
#팩토리얼 계산 예시
result = factorial(5)
print(result) #120
재귀 함수는 반드시
- 종료 조건을 명확히
- 반복되는 호출이 종료 조건을 향하도록
유용한 내장 함수
1. map (function, iterable)
- 순회 가능한 데이터구조(iterable)의 모든 요소에 함수를 적용하고, 그 결과를 map object로 반환
⇒ iterable : 반복 가능한 친구(시퀀스)
str, list, dict, set, range()
⇒ 해당 iterable 에 하나하나 적용 하는 것
⇒ map object로 반환하기 때문에,
list(), set(), tuple()
등으로 다시 처리해야 함(주로 list())
numbers = input().split() # 1 2 3 4 5
print(numbers) # ['1','2','3','4','5']
# int() 하고싶다
result = map(int, numbers)
print(result) #
- 사용자 함수도 ( )을 떼고 사용함
2. zip(*iterables)
*
가포함된다 ⇒ 가변인자를 받는다- 임의의 iterable을 모아 튜플을 원소로 하는 zip object를 반환
- 매개변수의 수를 다르게 해서 테스트 하는 연습하기
girls = ['jane','ashley','jasmine']
boys = ['peter', 'jay']
pair = zip(girls, boys)
print(pair) #
print(list(pair)) # [('jane', 'peter'), ('ashley', 'jay')]
# jasmine과 매칭되는 boy는 없기 때문에 zip되지 않았음
arr = [
[1,2,3],
[4,5,6],
[7,8,9]
]
list(zip(*arr)) # list(zip([1,2,3],[4,5,6],[7,8,9])
list(zip(arr)) # [([1, 2, 3],), ([4, 5, 6],), ([7, 8, 9],)] # 튜플이니까 (인수,) 형태
print(list(zip(*arr))) # [(1,4,7),(2,5,8),(3,6,9)] #전치행렬 된것(colums 끼리 묶은것)
3. lambda 매개변수: 표현식
이름 없이 정의되고 사용되는 익명 함수
def addition(x, y):
return x + y
addition = lambda x,y: x + y
일회성으로 함수를 사용할 때 많이 사용됨, map이랑 같이 많이 쓰임
#map과 연결해서 사용할 때
def func(x,y):
return x ** 2
result1 = list(map(func, numbers))
result2 = list(map(lambda x: x**2, numbers))
# 결과는 같다
매개변수가 두개인 map 람다식 : Collection을 두개 입력해야 함
l = list(map(lambda x, y: x * y, [0, 1, 2, 3], [4, 5, 6, 7]))
print(l) #[0, 5, 12, 21]
# 2차원 정렬 하기
arr = [
[1,2,3],
[4,5,6],
[7,8,9]
]
arr = sorted(arr) #변화없음
arr = [
[9,7,3],
[9,5,6],
[7,8,9]
]
arr = sorted(arr) #[[7,8,9],[9,5,6],[9,7,3]]
arr = sorted(arr, key=lambda x: x[1]) # 몇번쨰 열을 기존으로 정렬을 할 건지? 람다가 필수같다
Packing & Unpacking
변수에 담긴 값들은 튜플
(tuple) 형태로 묶임
여러 개의 인자를 튜플로 묶는것
packed_values = 1, 2, 3, 4, 5
print(packed_values) # (1, 2, 3, 4, 5)
*
을 활용한 패킹- *b는 남은 요소들을
리스트
로 패킹하여 할당
numbers = [1,2,3,4,5] a, *b , c = numbers print(b) # [2,3,4]
- print함수에서 임의의 가변인자를 작성할 수 있었던 이유
⇒ 인자 개수와 상관 없이
튜플
하나로 패킹 되어서 내부에서 처리print(*objects, sep=' ',end='\n',file=sys.stdout,flush=False) '가변인자' '기본 인자' 들로 이루어짐 기본적으로 sep, end, file 에 기본적으로 이 값들이 들어가고 있었다.
print(1,2,3, end=" ") # end 을 커스텀 print(4,5,6) # 1 2 3 4 5 6 출력
- *b는 남은 요소들을
언패킹
- 패킹된 변수의 값을 개별적인 변수로 분리하여 할당하는 것
- tuple, 리스트 등의 객체의 요소들을 개별 변수에 할당
packed_values = 1,2,3,4,5 # 오른쪽 값이 왼쪽에 적용, 패킹된 값
a,b,c,d,e = packed_values # 오른쪽 값이, 각 요소에 적용, 언패킹된 값
print(a,b,c,d,e) #1 2 3 4 5
패킹된 값의 갯수, 언패킹된 값의 갯수가 동일해야 에러가 나오지 않음
패킹된 값의 갯수 > 언패킹된 값의 갯수 = too many value to unpack
패킹된 값의 갯수 < 언패킹된 값의 갯수 = not enough value to unpack
*
는 리스트의 요소를 언패킹names = ['alice', 'jane', 'peter'] print(*names) #alice jane peter
**
는 딕셔너리의키-값
쌍을 함수의 키워드 인자로 언패킹def my_function(x,y,z): print(x,y,z) my_dict = {'x':1,'y':2,'z':3} my_function(**my_dict) # 1 2 3
매개변수 이름과 인자의 이름이 동일해야지 언패킹 가능하다
*
, **
패킹/ 언패킹 연산자 정리
*
- 패킹 : 여러 개의 인자를 하나의 튜플로 묶음
- 언 패킹 : 시퀀스나 반복 가능한 객체를 각각의 요소로 언패킹하여 함수의 인자로 전달
**
- 패킹 : 사용안함
- 언 패킹 : 딕셔너리의
키-값
쌍을 키워드 인자로 언패킹하여 함수의 인자로 전달def my_function(x, y, z): print(x,y,z) my_dict = {'x':1,'y':2,'z':3} my_function(**my_dict) # 1 2 3
이 때 dictionary의
키의 이름
및개수
와 함수의 매개변수의이름
과개수
가 동일해야 한다그래야
키
의값
이 입력 된다.
추가로 배운 것
1. 상수 전달하는 방식
기본 자료형(str 등) : call by value, 값을
복사
한다 = 원본값은 변하지 않음리스트,객체 : call by refrence 값을 참조한다 = 원본이 변한다
자료형이 큰 애들은 크기 때문에 직접 찾아가라(참조시키는것이) 메모리 관리가 좋지만, 기본 자료형 들은 작기 때문에 복사하는 것으로 충분하다.
얕은 복사 (임시방편, 중첩문에서는 작동하지 않음, 1차원용) 리스트, 딕셔너리 등 '참조'하는 데이터들은 복사하기 위해서는 1. .copy 메소드를 사용해야된다 2. list_B = list_A[:] a = [ ['시', '반', '경계'], ['연', '고백', '그림자'], ['칼날', '간신', '영웅'], ['열쇠', '변화', '달성'], ] b = a (x) # 각 각의 리스트를 가지고 있는것 같지만, 하나의 리스트를 두개의 변수가 쓰는 꼴 # => 주소값이 같다는 말 print(a is b) # True c = a.copy() #.copy를 하면 같은 리스트를 공유하는 것이 아님 print(a is c) # False
해야 하는것
- 재귀함수
callback 이란?
다른 코드의 인자로 넘겨주는 함수
콜백 함수를 넘겨받은 코드는 이 콜백 함수를 필요에 따라 적절한 시점에 실행됨
⇒ 다른 코드에 인자로 넘겨줌 + 제어권 위임
map(int,list()))에서 int()가 아니라 int로 쓰는 이유
사용자 함수도 ( )을 떼고 사용함
callback 함수란?
함수내에서 함수를 인자로써 사용되는 함수
ex)
map()
zip()
등map 함수를 사용할 때
map(int,시퀀스)
처럼 int()를 쓰는 대신에 int만 쓰는 이유⇒ map함수는 함수
int
를참조
하기 때문이다⇒ int() 라고 적는다면 함수
int()
를호출
하려고 하기 때문에 에러가 발생한다. ⇒ 함수이름
만 전달하여 참조한다.얕은복사 깊은 복사 : 객체를 복사하는 두 가지 다른 방법
https://black-hair.tistory.com/49
https://dojang.io/mod/page/view.php?id=2477
- 얕은 복사 Shallow Copy
새로운 객체 생성 but 원본 객체 안에 있는 객체들은 동일한 객체를
참조
copy 모듈의
copy() 함수
나 객체의 메서드 중 하나인copy()
를 사용함참조
: 원본과 복사본이 같은 내부 리스트를 참조하기 때문에수정
할 시 원본도 같이 변경됨 - 깊은 복사 Deep Copy
새로운 객체 생성
and
모든 객체들에 대해 새로운 복사본을 생성 ⇒ 완전히 새로움copy 모듈의
deepcopy()
함수 사용
'TIl' 카테고리의 다른 글
관통 PJT 1 (1) | 2024.01.20 |
---|---|
모듈, 제어문 (0) | 2024.01.18 |
01.16 Collection, 형변환, 연산자 (0) | 2024.01.16 |
0115 파이썬 1 (1) | 2024.01.15 |
1월 14(토) (0) | 2024.01.13 |