본문 바로가기
AI 공부/파이썬

파이썬 (함수 - 1) Argument 와 parameter, 깊은 복사, 얕은 복사

by AI Sonny 2022. 8. 1.
728x90

함수란?

  • 코드의 반복을 줄이거나 어떠한 용도를 위해 코드를 모아둔 것
  • 어떠한 결과를 만들어내는 코드의 집합
  • (재사용할) 코드 묶음

 

함수 정의 방법

def <funtion_name>(parameter1, parameter2,...):
    code context

 

예시

 

def get_std(num_list): # => 파라미타 (주소를 반환)
    avg = sum(num_list) / len(num_list)

    deviation_list = [] # 편차를 담을 리스트

    for num in num_list:
        deviation = avg - num  
        deviation_list.append(deviation**2)

    var = sum(deviation_list) / len(deviation_list)
    return var**0.5    # 표준편차

 

함수 사용

 

num_list = [29,34,435,23] # 밑에 num_list와 다른 변수이다.
get_std(num_list)   # 값이 들어감

 


함수를 정의하는 4가지 형태

 

- 피라미터 X , 리턴 X

 

def do_func_type1():
    print("파라미터 X, 리턴 X")
do_func_type1()

=> 파라미터 X, 리턴 X

 

 

- 파라미터 O , 리턴 X

 

def do_func_type2(num1,num2):
    if num1 > num2:
        print("BIG")
    else:
        print("SMALL")
do_func_type2(2,3)

=> SMALL

 

 

- 파라미터 X , 리턴 O

 

def do_func_type3():
    lst = list(range(1,101,2))
    return sum(lst)
do_func_type3()

=> 2500

 

 

- 파라미터 O , 리턴 O

 

def do_func_type4(num1, num2):
    return num1 + num2
do_func_type4(10,20)

=> 30

 


함수를 정의할 때 규칙에 대한 관례

  • 함수이름은 동사로 시작하며, 어떠한 기능을 추측할 수 있게 된다.
  • 함수이름은 소문자로 작성하며, snake case 따른다. (ex.add_number)

예시

 

리스트를 아규먼트(인자)입력 받아서 짝수만 출력하는 함수를 만들어 보세요.

def print_even(lst):
    for i in lst:
        if i % 2 == 0:
            print(i)
-----------------------------------------------------------------------
print_even(list(range(1,10)))

=>	2
    4
    6
    8

 


문제 1)

 

두개의 숫자를 인자로 입력받고, 세번째 인자로 plus, minus,multiply, divide 중에 입력을 받아

 

덧셈, 뺄셈, 곱하기, 나누기 중에 결과를 반환하는 함수를 만들어 보시오.

 

풀이) 처음에는 def로 시작하여 함수가 잘 안만들어졌다. 강사님의 풀이를 보고 쉽게 접근하는 법을 알게 되었다.

 

if문을 먼저 완성시키고, 실행이 잘되고나면 def를 이용해서 함수를 만들어도 된다.

 

이 방식을 이용하니 함수에 대해 이해가 잘되었다. 

 

def calculate(n1,n2,act):

    if act == "plus":
        result = n1 + n2
    elif act == "minus":
        result = n1 - n2
    elif act == "multiply":
        result = n1 * n2
    else:
        result = n1 / n2 if n2 else 0   # 나누기 (에러 방지)
    return result
calculate(10,0,"divide")

=> 0

 

문제 2)

 

이메일이 담긴 리스트를 인자로 받아 아이디만 추출해서 다시 새로운 리스트에 담아

 

해당 리스트를 반환하는 함수를 만드시오.

 

풀이) 이번 문제는 split을 썻는데도 출력해보면 분리가 안되었다. 원인을 찾아보니 변수를 선언하지 않았다.

 

변수를 선언하지않으면 분리가 안된다는 값이 나온다는 것을 배웠다. 

email_list = ["user1004@gmail.com","user22@naver.com","user30@gmail.com","user100@hanmail.net"]
def email2id(email_list):   # 다시보기
    tmp = []
    for email in email_list:
        email = email.split("@")[0] # 변수를 선언! (변수를 선언안하면 분리가 안된 값이 나온다.)
        tmp.append(email)
    return tmp

email2id(email_list)

 

문제 3)

 

리스트를 인자로 받아 평균을 구하는 함수와 표준편차를 구하는 함수를 각각 구현하세요.

 

풀이) 처음에 나는 평균과 표준편차를 동시에 나오게 하고 싶어서 평균과 표준편차를 먼저 구하였다.

 

그 후 값을 리턴하면 될 줄 알았는데 오류가 나왔다. 이 후 나의 오류는 강사님의 풀이를 보고 이해가 되었다.

 

def get_avg(score): # 내 생각

    avg = sum(lst) / len(lst)
    dev_list = []

    for i in score:
        dev = avg - i
        dev_list.append(dev**2)

    var = sum(dev_list) / len(dev_list)
    return avg, var

 

강사님의 풀이를 보면 평균과 표준편차를 만들고, return을 하여 2개의 함수를 만들었다.

 

그리고 표준편차 안에 평균함수가 들어가있는 것을 볼 수 있다.

 

score1 = [100,80,62]    # 강사님 답
score2 = [90,81,70]     # 전역변수

def get_avg(lst):
    avg = sum(lst) / len(lst)   # 지역변수
    return avg

def get_std(lst):
    avg = get_avg(lst)
    diff_list = [ (avg - i) ** 2 for i in lst]
    var = sum(diff_list) / len(diff_list)
    return var ** 0.5

 


변수의 사용범위 (Scope)

  • 지역변수(local): 함수 내부에서 만들어진 지역변수는 함수 내에서만 사용가능 (파라미터 포함)
  • 전역변수(global): 함수 밖에서 만들어진 변수 (어디서든 사용 가능)
  • 파이썬의 제어문은 해당이 안된다. 제어문의 변수는 전역변수이다.

변수의 안좋은 예시에 대해 정리하였다.

 

예시 1)

 

gv = 10         # 안좋은 예시 (외부변수에 의존하게 됨)

def do_func(n):
    # gv = 10 (지역변수 내에 있는 것이 속도가 더 빠르다.)
    return n + gv

do_func(10)

 

다음 예시는 외부변수가 선언 되었기 때문에 , 지역변수는 외부변수를 참조하게 된다.

 

이렇게 되면 나중에 긴 코드를 짤 때 외부변수가 어디에 있는지 찾아야하는 수고가 생기게 된다.

 

또한 제 3자가 볼 때도 찾기 힘든 코드이다.

 

예시 2) 

 

def do_func():  # 안좋은 예시
    global loc  # 전역변수의 의미!
    loc = 100
do_func()

 

이 예시는 global을 이용하면 지역변수내에 있지만 전역변수를 의미하게 된다.

 

이러한 코드도 위와 같은 이유로 좋지 못한 코드라 할 수 있다.

 


얕은 복사 vs 깊은 복사

 

- 얕은 복사는 변수를 복사했지만 참조한 곳이 같아 같은변수를 가르키고 있는 것입니다.

 

그래서 얕은 복사는 mutable한 조건에서 사용하게 되면 원본 데이터가 변경될 위험이 있습니다.

 

 

 

 

- 깊은 복사는 원본을 건들이지 않습니다. 모든 것을 새롭게 복사를 하기 때문에 독립적으로 변하게 됩니다.

 

깊은 복사는 import copy를 이용하여 복사를 할 수 있다.

 

 


키워드 아규먼트

 

def do_func(a,b,c):
    print(a,b,c)

do_func(1,2,3)  # positional argument

=> 1 2 3

 

함수를 선언하면 그 함수에 대한 위치의 값이 나오게 되는 것이다.

 

do_func(b=2,a=1,c=3)

=> 1 2 3

 

순서를 바꿔도 동일하게 출력된다.

 


파라미터를 정의하는 방식

- 디폴트(default) 파라미터

  • 아규먼트를 넣어주지 않을때 파라미터에 지정된 초기값을 사용한다.

예시

 

def do_func(a,b=2,c=1): # 디폴트값으로 설정되고 나온다!
    print(a,b,c)
do_func("hellow",30)

 

그리고 디폴트 파라미터의 주의사항으로 디폴트 파라미터는 항상 일반 파라미터 뒤에 넣어줘야한다.

 

그렇지 않으면 오류가 발생한다.

 

def do_func(a,b=1,c):   # 일반 파라미터 앞에 디폴트 파라미터를 넣으면 에러가 난다.
print(a,b,c)

=>  File "<ipython-input-212-f8887c8b9af6>", line 2
    print(a,b,c)
        ^
IndentationError: expected an indented block

 


가변 파라미터

  • 함수를 정의하면서 아규먼트가 n개 이상이 들어갈 수 있다면 가변파라미터를 정의해주면 된다. (0개 포함)
  • *를 이용해서 파라미터명을 정의해주면 된다.
  • 일반적으로 *args으로 표현
  • 함수 내부에서는 튜플로 묶인다.

 

예시

 

def do_func(*args): # packing
    print(args)
    print(sum(args))
    print(list(args))

do_func(1,2,3,4,4,5)
do_func()

=>	(1, 2, 3, 4, 4, 5)
	19
	[1, 2, 3, 4, 4, 5]
	()
	0
	[]
-------------------------------------
for item in zip(list1,list2):   # unpacking
    print(item)
    
=>	(1, 4)
	(2, 5)
	(3, 6)

 

 

  • packing: 하나의 변수에 여러 객체를 묶어서 담는 방식
  • unpacking: 묶여 있는 여러 객체를 여러변수에 풀어서 담는 방식

 

def do_func(a,*arg,b=100):  # *arg가 1이후로 튜플로 묶였기 때문에 디폴트 값이 나온다.
    print(a)
    print(arg)
    print(b)

do_func(1,2,3,4,5,6,7,200)

=>	1
	(2, 3, 4, 5, 6, 7, 200)
	100

 

*arg를 사용할 때는 주의 사항이 있다.

 

*arg는 튜플로 묶어버리는 성질있어서 *arg 이후를 나타내고 싶으면 따로 선언을 해줘야한다.

 


키워드 가변 파라미터

  • 함수를 정의하면서 키워드 아규먼트가 n개 이상 들어올 수 있다. (0개 포함)
  • 일반적으로 **Kwargs 로 표현
  • 함수내부에 딕셔너리 형태로 묶인다.
  • 키워드 아규먼트에서 키워드가 key 값이 되고, 아규먼트가 value가 된다.

 

예시

 

kwargs_dict = {			# packing
    "c" : 30,
    "name" : "용석",
    "age" : 25
}

do_func(20,30,**kwargs_dict)    # c = 30, name = "용석", age = 25

=>	20
	30
	{'c': 30, 'name': '용석', 'age': 25}
    
-------------------------------------------------------------------
dict_args = {			# unpacking
    "loss_fn" : "mse",
    "learning_rate" : 0.001,
    "num_iterations" : 1000
}
train_model(**dict_args)    # 딕셔너리를 언패킹하면 키워드 아규먼트 형식으로 풀린다.

=>	mse
	0.001
	1000

문제가 추가적으로 있지만 여기에는 최대한 안넣으려한다.

 

여기는 개념만 집어놓고, 예제 문제는 코랩에서 따로 풀예정이다.

 

그리고 문제가 좀.... 어렵다....  많이....

728x90

댓글