본문 바로가기
SK 쉴더스 루키즈/인프라 활용을 위한 파이썬

[인프라 활용을 위한 파이썬] 함수

by seoyamin 2025. 1. 9.

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(), ..

 

  1. 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
    
  2. 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()
    
  3. 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 ) )
    
  4. 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 ) )
    
  5. 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)
    
     
  6.  
  7. zip()
    • 동수를 가진 데이터를 묶어서 세트로 데이터셋을 구성
    • 같은 위치(인덱스)에 존재하는 데이터를 묶어서 세트 구성
    a = [1,2,3]
    b = ['가','나','다']
    
    for _ in zip(a, b):
      print( _ )
    

 

3.3. 외장 함수

  • 내장함수를 구분하기 위한 용어
  • 특정 패키지(라이브러리)에 속한 함수
    • 패키지(모듈의 집합) <-> 모듈(*.py)
  • 수학, 과학용 라이브러리(numpy)를 사용
    • 해당 패키지 설치
      • # pip install 패키지명 pip install numpy
      • # 아나콘다 같은 툴 활용 conda install numpy
    • 해당 패키지를 사용한다면
      1. 모듈 가져오기
      2. 모듈.함수|변수|클레스 사용
        • 모듈가져오기
          • import ~
            • 도트 연산자(.)를 통해서 사용
            • 소속을 표현함
          • from ~ 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 (범위)

변수가 함수 내외에 존재할 때 사용 범위 체크

지역 변수 전역 변수 넌지역변수
  • local
  • 함수 내에서 정의된 변수
  • global
  • 함수 밖에서 정의된 변수
  • 모든 영역에서 사용 가능함 (단, 추가 표현이 필요함)
  • nonlocal
  • 고급 함수에서 등장함, 내부 함수에서 등장함