1. 개요
1.1. 프로그램 작성 유형
- 절차적 프로그래밍
- 고전적, C언어 스타일
- 흐름 제어를 잘 사용하여 구성
- 직관적
- 코드가 복잡
- 유지 보수 어려움
- 함수 지향적 프로그래밍
- 절차적 프로그래밍의 문제점을 대부분 해결
- 복잡한/반복되는 단위는 함수 단위로 묶어서 간단하게 구성
- 간결한 코드
- 재활용성이 높아짐
- 유지보수가 향상됨
- ex) WEB
- 객체 지향적 프로그래밍
- ex) GUI
트렌드 | 절차적 → 함수 지향적 → 객체 지향적 → 함수 지향적
1.2. 기본적 본질
- 함수의 워크 플로우
- 데이터 입력 → 입력한 데이터를 처리 → 처리된 결과 출력
- 함수의 내부에서 처리
- 기존 개념
- 프로그래머가 요구 사항에 맞춰서 로직, 알고리즘을 동원하여 특정 목적을 달성하는 행위
- 사람이 직접 구현
- # 더하기 함수 - 입력 : 1, 3 - 처리 : 1 + 3 <- 구현 (함수 형태로) - 출력 : 4
- 기존 개념
- AI, 머신러닝에서의 처리
- 입력, 출력을 제시 → 학습 → 처리(모델) 를 획득
- 사람의 인지 능력으로 해결 못함 (로직 구성 불가) → 기계 학습으로 처리
- - 대량의 데이터 준비 - 입력(특징) -> 출력 (정답) 1, 2 -> 3 2, 3 -> 5 5, 1 -> 6 - 학습 -> 모델 획득 -> (정확도, 오차율(에러))
1.2. 용어
- 입력
- 함수를 정의할 때 : 매개변수, parameter
- 함수를 호출할 때 : 인자, argument
- 개수는 0개(생략 가능) ~ n개
- 출력
- 반환값, 리턴값 (return value)
- 개수는 0개(생략 가능) ~ n개(tuple)
2. 기초 문법 (커스텀 함수)
- [ ] : 생략 가능함
# 함수 정의
[데코레이터] # 웹 서비스 구성 시 볼 수 있음
def 함수명( [입력, ...] ): # 함수 선언문
# 처리 : 코드블럭, 들여쓰기 사용(=인텐트를 사용하여 표현)
# 수행 코드는 여러 줄 나올 수 있다
statements
# 출력값 리턴
[ return [출력값... ] ]
# 함수 호출 -> 실제 코드가 작동
함수명([입력값, ...])
- 함수명 네이밍 컨벤션
- 스네이크 스타일 (xx_xx_)
유형
1. [기본 유형] 리턴값 1개
def add1(x, y):
result = x + y
return result
add1(1, 2)
2. [기본 유형] 리턴값 생략
def add2(x, y):
print(x + y)
def add3(x, y):
print(x + y)
return
add2(1, 2), add3(1, 2) # (None, None) : 함수가 아무것도 반환하지 않는다
3. [기본 유형] 리턴값 여러개
def multi_oper(x, y):
sum = x + y
dec = x - y
mul = x * y
div = x / y
return sum, dec, mul, div
print(type(multi_oper(6, 3))) # <class 'tuple'>
_, _, result, _ = multi_oper(5, 6) # 필요하지 않은 값 선택적으로 버릴 때 언더바 (_) 사용
print(result)
4. [가변 인자]
4.1. 튜플로 처리
# x로 전달되는 모든 인자의 총합
def add4( *x ):
sum = 0
for num in x:
sum += num
print(x, type(x), sum)
add4(1) # (1,) <class 'tuple'> 1
add4(1, 2) # (1, 2) <class 'tuple'> 3
add4(1, 2, 3) # (1, 2, 3) <class 'tuple'> 6
4.2. 일반 인자 → 가변 인자 혼합
- 첫번째 인자는 무조건 일반 인자로 대입, 나머지는 모두 가변 인자로 대입
- 항상 일반 → 가변 순으로 배치해야 함
def add5( x, *y):
print(x, y)
add5(1) # 1 ()
add5(1, 2) # 1 (2,)
add5(1, 2, 3) # 1 (2, 3)
5. [매개변수 기본값] 매개변수 기본값 부여
- 타입을 제시하는 느낌
- 함수 호출 시 인자 생략 가능해짐 → 기본값을 따라감
- 기본값 : 기준/표준/중요값
- 규칙
- 인자명=값 형식으로 명확하게 제시하는 것이 중요하다
- 인자명을 생략한다면, 매개변수가 정의된 순서대로 인자값이 전달된다
- 인자명을 생략한다면, 매개변수가 정의된 순서대로 인자값이 전달된다
def add7( x=1, y=2, shape=None ):
if not shape: # 부정 상황을 잡는 조건문 (shape == None)
shape = [x, y]
print(x, y, shape)
add7() # 1 2 [1, 2]
add7(2, 3) # 2 3 [2, 3]
add7(2, 3, (5, 6)) # 2 3 (5, 6)
add7(50) # 50 2 [50, 2]
add7( x = 100 ) # 100 2 [100, 2]
# 두번째 인자만 세팅하고 호출한다면?
add7( y = 200) # 1 200 [1, 200]
# 인자의 순서를 다르게 세팅하고 호출한다면?
add7( y = 6, x = 8 ) # 8 6 [8, 6]
6. keyword arguments
- 형식 : **매개변수명
- 함수 내부에서 딕셔너리로 해석됨
- 위치
- 매개변수의 가장 마지막 자리로 배치
- 함수의 확장(업데이트)를 위한 장치
def add8(**kwargs): # 딕셔너리로 해석됨 (매개변수 순서 X)
print(kwargs)
add8() # {}
add8( language='파이썬', age=35 ) # {'language': '파이썬', 'age': 35}
7. [종합] 인자 유형 모두 혼합
- 일반 인자 …. , 가변 인자 … , kwargs 순으로 배치
def add9( a, b, *c, **kwargs):
print( a, b, c, kwargs )
pass
add9(1, 2, 3, 4) # 1 2 (3, 4) {}
add9(1, 2, 3, 4, name='k') # 1 2 (3, 4) {'name': 'k'}
def add10( a=10, b=11, *c, **kwargs):
print( a, b, c, kwargs )
pass
# add10() missing 2 required positional arguments: 'a' and 'b'
# 기본값이 없는 일반인자는 함수 호출시 반드시 인자값을 전달해야 한다
# def add10( a, b, *c, **kwargs): <- 에러
# 첫번째 일반인자에 기본값을 부여했다면 나머지 일반 인자도 기본값을 부여해라
# a에 값 부여, b는 기본값이 없음
# def add10( a=10, b, *c, **kwargs): <- 에러
# 기본값 모두 부여후 호출 <- 정상 동작
add10()
3. 함수의 종류
3.1. 커스텀 함수
3.2. 내장 함수
- 파이썬 설치후 사용 가능하다
- ex) print(), map(), filter(), ..
- open()
- 파일(외부 리소스)을 엑세스(열기, 읽기, 쓰기, 닫기)를 처리하기 위한 함수
- 외부 리소스를 엑세스 (I/O)를 하는 경우 반드시 예외 상황(오류)을 동반할 수 있다
- 외부 리소스를 오픈 했으면, 반드시 닫는다
- 닫는 부분의 누락 실수 종종 발생 => 방지 with문
- 파이썬 : open() ↔ 파이썬 ↔> C/C++ 작동 ↔ OS단의 파일과 작업
# 기본형 # 1. 파일 생성 및 오픈(읽기?|쓰기? and 텍스트|바이너리(b))), 반환값 : 파일을 엑세스할수 있는 객체 # 'a.txt'를 오픈해라 -> 없다면 -> 생성하고 오픈해라 -> 단 텍스트로 쓰기모드로 오픈한다 f = open('a.txt', 'w', ) # (파일명, 쓰기모드(w)<->읽기모드(r) ) # 2. 작업 # 3. 닫기 f.close()
# 1. 파일 생성 및 오픈 f = open('a.txt', 'w', ) # 2. 작업 # a.txt 파일에 다음과 같은 내용 쓰기 "가나다 abc ABC 123 !@#" # 한글이 깨지지 않고 잘 기록된다(체크), 인코딩 처리 x f.write("가나다 abc ABC 123 !@#") # 3. 닫기 f.close()
# with 문 사용 -> 코드를 간결하게, 자동으로 닫기 처리 # 별칭 => 원본 as 별칭 with open('a.txt', 'r') as f
- input()
- 터미널에서 사용자 입력을 받는 함수
- 사용자 입력 후 엔터를 치면 input() 함수가 반환. 그전까지는 무한 대기
- 문자열 리턴
- intput( “프롬프트 제시”)
def input_title(): while True: msg = '제목을 입력해 주세요. 최대 28자이내(<=28), 영문/숫자 조합으로 입력하세요\\n' err_msg = '정확하게 입력하세요' title = input( msg ).strip() if not title: print(err_msg) elif len(title) > 28: print(err_msg) else: print('-'*30) print(f'-{title:^28}-') print('-'*30) break input_title()
- map()
- 자료구조(주로 리스트)상 데이터를 하나씩 꺼내서 작업을 수행 -> 다시 리스트 등 추출할 때
# 리스트 데이터 제공 datas = [1, 2, 3, 4] # 요구사항 => datas 모든 맴버들을 2배로 값을 증가시켜서 리스트로 구하시오 => [2, 4, 6, 8] # datas * 2 => 2번 반복의 의미 %%time # Wall time: 13.6 µs # 리스트 컴프리핸션(내포) [ v*2 for v in datas ] %%time # Wall time: 44.1 µs # 위의 표현으로 수행시간 체크 # map() 활용 #map( 함수, 이터러블한데이터 ) def 더블함수( x ): # 함수는 한개의 인자를 받는다 (하나씩 꺼내서를 구현) return x*2 list( map( 더블함수, datas ) ) %%time # Wall time: 14.3 µs # 람다함수(아직 배우기전) 활용 # 람다함수는 함수 유형중 가장 빠른 함수, 1회성 list( map( lambda x:x*2, datas ) )
- filter()
- 특정 조건을 만족(True/False)하는 데이터만 추출
- 조건의 결과가 참인 데이터만 포함
- filter( 콜백함수(참/거짓 리턴), 데이터 )
# 리스트 컴프리핸션 -> datas중 짝수만 포함된 리스트를 출력하시오 [ v for v in datas if not (v % 2) ]
def check( x ): # 데이터를 하나씩 꺼내서 판단받겠다 -> 짝수면 : True, 홀수면 ; False return not (x % 2) list( filter( check, datas ) )
- ord()
ord(문자) → 아스키코드 리턴source_txt = ''' BTS (Korean: 방탄소년단; RR: Bangtan Sonyeondan; lit. Bulletproof Boy Scouts), also known as the Bangtan Boys, is a South Korean boy band formed in 2010. The band consists of Jin, Suga, J-Hope, RM, Jimin, V, and Jungkook, who co-write or co-produce much of their material. Originally a hip hop group, they expanded their musical style to incorporate a wide range of genres, while their lyrics have focused on subjects including mental health, the troubles of school-age youth and coming of age, loss, the journey towards self-love, individualism, and the consequences of fame and recognition. Their discography and adjacent work has also referenced literature, philosophy and psychology, and includes an alternate universe storyline. ''' # 요구사항 # source_txt에서 알파벳만 추출(정규식)하여, a-z까지 개별 빈도 획득하여, 리스트로 출력하시오 # 정규식 적용 (내일 이후 배울 내용) import re # 정규식 표현 # [] : 문자 클레스 # a-z : 소문자 # A-Z : 대문자 # [^문자] : 해당문자를 제외하고 # * : 0~ 무한대 반복 #re.compile('[a-zA-Z]') # 문자 1개인데 알파벳 #re.compile('[a-zA-Z]*') # 문자 1개인데 알파벳인 경우가 무한대로 나올수 있다 pattern = re.compile('[^a-zA-Z]*') # 알파벳을 제외한 모든 문자가 무한대로 나올수 있다 # 알파벳을 제외한 모든 문자를 만다면 ""로 대체해라 texts = pattern.sub("", source_txt) texts[:100]
# texts => 알파벳 빈도 카운트, 써드파트 라이브러리 x # 0. 알파벳 빈도를 저장할 리스트 구성 [0, 0, 0, .... , 0] : 26개의 맴버(빈도)수를 가진 리스트 counts = [0] * 26 #print( counts, len(counts) ) # 1. texts -> 알파벳 1개씩 추출 # 30초 실습 for text in texts:#[:10]: # 2. 알파벳은 소문자만 카운트하겠다, 대문자를 소문자로 변환 text = text.lower() # 3. 문자 하나에 대한 아스키값 확인 #print(text, ord(text) ) # 4. 문자 -> 몇번째 리스트의 맴버를 증가할지 판단하여 +1 증가 # ex) 문자가 'b' -> 2번째 맴버값(인데스로는 1)을 +1 # ex) 문자가 'z' -> 26번째 맴버값(인데스로는 25)을 +1 특정문자의인덱스 = ord(text) - ord('a') counts[ 특정문자의인덱스 ] += 1 print( counts)
- zip()
- 동수를 가진 데이터를 묶어서 세트로 데이터셋을 구성
- 같은 위치(인덱스)에 존재하는 데이터를 묶어서 세트 구성
a = [1,2,3] b = ['가','나','다'] for _ in zip(a, b): print( _ )
3.3. 외장 함수
- 내장함수를 구분하기 위한 용어
- 특정 패키지(라이브러리)에 속한 함수
- 패키지(모듈의 집합) <-> 모듈(*.py)
- 수학, 과학용 라이브러리(numpy)를 사용
- 해당 패키지 설치
- # pip install 패키지명 pip install numpy
- # 아나콘다 같은 툴 활용 conda install numpy
- 해당 패키지를 사용한다면
- 모듈 가져오기
- 모듈.함수|변수|클레스 사용
- 모듈가져오기
- import ~
- 도트 연산자(.)를 통해서 사용
- 소속을 표현함
- from ~ import ~
- 함수()
- import ~
- 모듈가져오기
# 현재 코랩에 설치된 패키지 확인 # !명령어 => 코랩에서 리눅스에게 명령어 전달시 사용하는 키워드 # !pip list # 수학 과학용 라이브러리 패키지인 numpy가 설치 되었는 필터링해서 출력 !pip list | grep numpy # 리눅스(윈도우, 맥) 포함해서 실제 명령 # !만 제외 # pip list | grep numpy
- 해당 패키지 설치
- pickle()
- 파이썬 레벨에서 사용
- 데이터의 타입 원형을 보전하여 저장(덤프), 로드할수 있는 모듈
- 바이너리로 저장됨
# 실제로는 설치되어 운영 가능, 노출이 안됨 (체크) #!pip list | grep pickle # 1. 모듈 가져오기(단, 설치되어 있다는 전제 하) import pickle as p # 2. 저장할 데이터 준비 # [1, 2, 3, 4], 리스트 datas # 3. 저장(파일) -> I/O # 'wb' : 쓰기모드 + 바이너리데이터 with open('test.pk', 'wb') as f: # 원 데이터의 구조, 형식, 데이터등을 모두 보존하여 저장 # 패키지명.함수() : p.dump() p.dump( datas, f, p.HIGHEST_PROTOCOL ) pass # 4. 로드 -> 바이너리 파일을 읽어서 -> 데이터 로드 -> 리스트 with open('test.pk', 'rb') as f: temp = p.load(f) print( temp, type(temp) )
3.4. 고급 함수
3.4.1. Annotation
- 특징
- 파이썬 : 타입 추론형 - 값이 할당될 때 타입이 결정된다
- 타입을 모른다 - Any
- 활용
- 변수, 함수의 구성원 등의 타입을 가이드할 수 있다 (강제성 X)
- 다른 타입의 값이 와도 오류 X (단, 내부적인 로직에서 오류 발생 가능성 존재)
- 3.6 버전부터 추가됨
- Tip
- 포트폴리오 관리 시 파이썬 코드 업로드할 때 유용
- 대부분 서드 파티 모듈을 적용함
- 변수 어노테이션
# Q. 변수에 다른 타입을 세팅한다면? # A. 에러 발생 X, 강제성 X score: int = 1000 score = 'hello'
- 함수 어노테이션
# 인자 => 변수: 타입 # 리턴값 => -> 리턴값의 타입 def test_an2 ( x: int = 1, name: str='k' ) -> str: return f'{x} {name}' test_an2( 10, 'k' ) # 타입에 강제성이 없다 -> 오류 x test_an2( 'hello', 100 )
3.4.2. 내부 함수 (inner function)
- 함수 안에 함수가 존재할 때 내부에 존재하는 함수가 내부 함수
- 목적
- 중요 기능을 은닉하는 전략
- 데코레이터 기능 구현을 위해
# 전역변수 def outer(): # 지역변수 print('외부함수 실행 영역') # 내부함수 def inner(): # outer()의 지역변수를 엑세스한다면 => 넌로컬변수 # 지역변수 print('내부함수 실행 영역') # 내부함수 호출 inner() outer()
3.4.3. 클로저, 순수 함수 (pure function)
- 순수 함수(pure function)
- 전역변수를 사용하지 않는 함수
- 단독적으로 특정 기능을 완료하는 함수
- # 순수함수 예시 def add( x, y) return x + y
- 클로저 조건
- outer() 함수는 순수함수이여야 한다
- 내부 함수가 존재해야 한다
- 내부 함수 내에서 outer 함수의 정의된 변수(지역변수)를 반드시 참조한다
- 넌로컬변수를 엑세스 해야 한다
- outer 함수의 반환값은 내부 함수이다
# 예시
# outer() 함수는 순수함수
def outer_pure():
cnt = 0
# 내부함수 존재
def inner():
# cnt는 넌로컬변수, outer_pure()에게는 지역변수, inner()에게는 지역 변수 x
# 넌로컬변수 엑세스
print( cnt )
# 내부함수 반환
return inner
# 함수호출
outer_pure()() # 0
t = outer_pure()
t() # 0
def outer_pure2():
cnt = 0
def inner():
nonlocal cnt # 넌로컬변수 선언
cnt += 1 # 넌로컬변수 엑세스
print( cnt )
return inner
# 함수호출때마다 값을 기억하여 최종값에서 계속 진행된다
t = outer_pure2()
t() # 1
t() # 2
t() # 3
# 매변 새로 호출한다 -> 매번 지역변수가 초기화됨
outer_pure2()() # 1
outer_pure2()() # 1
outer_pure2()() # 1
3.4.4. 데코레이터
- 클로저 조건을 만족하는 함수로 표현
- 웹 프로그래밍 할 때 계속 등장
- 특징
- 기본 함수에 특정 기능을 공통적으로 적용하고 싶다면!!
- 웹 프로그램에서 주로 사용
- django, flask, fastapi
- 형식
- @고차함수() def 일반함수(): statements [return ..]
- 예제
# 구현2 def outer_msg2( f ): # @outer_msg2를 표시한 모든 코드에 공통으로 들어갈 내용을 기술 def inner(): print('1. f가 실행되기전 메세지') f() print('3. f가 실행된 후 메세지') return inner # 호출할때 데코레이터 적용 @outer_msg2 def args_func2(): print('2. 인자로 전달될 함수') args_func2()
# 구현3 + 인자전달 # f 함수의 매개변수를 내부함수에 전달하는 케이스 def outer_msg3( f ): def inner( *args, **kwargs ): print('1. f가 실행되기전 메세지', args, kwargs) res = f( *args, **kwargs ) print('3. f가 실행된 후 메세지', res) return res return inner @outer_msg3 def args_func3( *args, **kwargs ): print('2. 인자로 전달될 함수', args, kwargs) return args args_func3(1, 2, 3) ''' 결과 1. f가 실행되기전 메세지 (1, 2, 3) {} 2. 인자로 전달될 함수 (1, 2, 3) {} 3. f가 실행된 후 메세지 (1, 2, 3) (1, 2, 3) '''
3.4.5. 이터레이터
- Iterable
- 반복 가능한 객체
- for ~ in (Iterable한 객체):
- 대표적 타입
- list, dict, tuple, set, str
- range()
- bytes
- 반복 가능한 객체
- Iterator
4. 함수의 주석
- 성실하게 주석 처리
- 함수명, 변수명, 클레스명등 아주 길게 작성(설명 가능한 형태) 주석 대체
- 내용
- 함수 목적
- 매개변수 사용법
- 리턴값
- 형식
- 한 줄 주석 #
- 여러 줄 주석 ''' ... ''', """ ... """
- 어노테이션 활용 : 매개변수, 반환값 등 제공 → 타입을 표현
# 함수 주석 # 함수 내부 첫번째 라인에서 기술 # 결론: 사내 문화를 따른다 #def test_comment( name, age ): # 애너테이션 적용전 def test_comment( name:str, age:int )->None: # 애너테이션 적용후 """함수 주석입니다. 기능 : 함수의 설명 Args: name(str) : 이름, 문자열 age(int) : 나이, 정수형 Returns: None """ pass # 함수 주석 출력 print( test_comment.__doc__ ) # 실제 함수 작성시 자동완성으로 제공 test_comment()
5. Scope (범위)
변수가 함수 내외에 존재할 때 사용 범위 체크
지역 변수 | 전역 변수 | 넌지역변수 |
|
|
|
'SK 쉴더스 루키즈 > 인프라 활용을 위한 파이썬' 카테고리의 다른 글
[인프라 활용을 위한 파이썬] 제어문과 컴프리헨션 (0) | 2024.11.22 |
---|---|
[인프라 활용을 위한 파이썬] 리스트 (0) | 2024.11.13 |
[인프라 활용을 위한 파이썬] 가상 환경, 데이터 타입, 연산자 (0) | 2024.10.24 |