
def fun(a, b):
return a + b
def repeat_print(message, count):
"""message를 count번 출력"""
for _ in range(count):
print(message)
repeat_print("안녕?", 3) # 위치 인자(positional arguments)
repeat_print(count = 3, message = "안녕") # 키워드 인자(keyword arguments)
기본적으로 정의된 매개변수 순서대로 전달해야 한다.
다만, 키워드 인자(keyword arguments)를 사용하게 되면 순서가 필요없다.
# 에러발생 → 기본값이 있는 매개변수가 더 앞에 있음.
def repeat_print_2(message = "test", count):
for _ in range(count):
print(message)
# 정상 → 기본값이 없는 매개변수가 왼쪽에 와야 함.
def repeat_print_2(message, count = 1):
for _ in range(count):
print(message)
인수(Parameter)의 자료형을 고려하지 않고, 일단 넣어서 작동.
("오리처럼 걷고, 오리처럼 꽥꽥거리면, 그 새를 오리라고 하겠다."는 말에서 유래)
def test_print_1(message):
for i in range(len(message) + 1):
print(message)
test_print_1("안녕??")
test_print_1(["딸기", "바나나", "포도"])
test_print_2(123) # 에러 -> int형은 len()을 사용할 수 없음.
def repeat_print_3(message: str, count: int) -> None:
for _ in range(count):
print(message)
repeat_print_3("3", 3)
# 시퀀스도 타입을 선언 가능
# mypy 같은 타입체커가 검사
arr: list[str] = ["a", "bc", "dcf"] # List
names: tuple[int, ...] = (1, 2, 3) # 개수 상관없는 Tuple
names: tuple[int, int, int] = (1, 2, 3) # 개수를 지정한 Tuple
nums: set[int] = {1, 2, 3} # Set
scores: dict[str, int] = {"kim": 90, "lee": 85} # Dictionary
def print_many(my_tuple):
print(my_tuple)
print_many(1, 2, 3, 4, 5) # 에러발생! -> 인수가 여러개...
print_many((1, 2, 3, 4, 5)) # 정상
# 단, 이렇게 하면 무조건 튜플로 받아와짐
def print_many_tuple(*my_tuple):
print(my_tuple)
print_many_tuple(1, 2, 3, 4, 5) # 출력: (1, 2, 3, 4, 5)
# *을 두 개 붙일 경우, dict로 받아와짐
def print_many_dict(**keywords):
for k in keywords:
print(f"{k} : {keywords[k]}")
print_many_dict(a = 1, b = 2, c = 3) # 출력: a b c (가로로)
여러 값을 담고 있는 객체를 개별 변수로 '풀어주는(Unpacking)' 역할
my_list = [1, 2, 3, 4, 5]
print(*my_list)
my_dict = {"a": 1, "b": 2, "c": 3}
print(*my_dict) # *가 한개면, key만 풀어버림
# *가 두개면, key와 value 둘 다 풀어버림
def greet(name, age):
print(f"{name}({age})")
params = {
"name": "철수",
"age": 20
}
greet(**params) # greet(name = "철수", age = 20)와 같음
def two_random_numbers():
return random.random(), random.random() # 기본적으로 튜플로 반환
# return (random.random(), random.random()) # 튜플로 반환
print(type(two_random_numbers())) # <class 'tuple'>
======================================================================
def two_random_numbers():
return [random.random(), random.random()] # 리스트로 반환
print(type(two_random_numbers()))# <class 'list'>
매개변수가 불변 객체일 경우, 내부에서 변경을 하면 객체를 새로 만듦. (인수로 넣어줬던 객체 영향 X)
def my_fun(a):
# 내부적으로 a = x
a += 100
return a
x = 1
print(x) # 1
print(my_fun(x)) # 101
가변 객체일 경우, 넣어줬던 객체도 영향 O
def append_hello(func_list):
print("In: ", id(func_list))
func_list.append("hello")
my_list = ['a']
print("Before: ", id(my_list))
append_hello(my_list) # my_list.copy()하면, 얕은 복사가 됨.
print("After: ", id(my_list))
print(my_list)
함수를 변수로 저장했다가, 사용하는 기능.
def test_fun():
print("아아 마이크 테스트")
test_fun_2 = test_fun
test_fun_2()
# 예제
from datetime import datetime
def print_date():
today = datetime.today()
print(f"오늘은 {today.year}년 ~~~")
def print_time():
current_time = datetime.now()
print(f"지금은 {current_time}시")
command_callback = {"날짜" : print_date, "시간" : print_time}
func = command_callback.get("날짜인지, 시간인지 입력")
func()
이름 없는 함수(익명 함수)
# 함수 객체처럼 변수를 통해서 사용
my_func = lambda x, y: x + y
my_func(1, 2)
map(), filter() 등의 내장함수들과 함께 사용하기에 편리하다.
# map(): 주어진 함수에 이터러블의 각 아이템을 적용함
print(list(map(lambda x: x + 100, [1, 2, 3])))
print(list(map(lambda x, y: x + y, [1, 2, 3], [10, 20, 30])))
# filter(): 주어진 조건에 따라, 요소들을 걸러줌
print(list(filter(lambda x: x % 2 == 0, [1, 2, 3, 4, 5])))
# reduce(): 주어진 함수를 시퀀스에 반복적으로 적용 -> 하나의 값으로 만듦.
from functools import reduce
print(reduce(lambda x, y: x + y, [1, 2, 3, 4])) # (((1 + 2) + 3) + 4) == 10
# sorted(): 정렬 기준을 지정해줄 때도, 람다를 사용할 수 있음.
print(dict(sorted(my_dict.items(), key=lambda item: item[1], reverse=True)))
# 실습 - 자리수 합치기
num = 1357
def add_digits(num: int) -> int:
print(num)
if num < 10:
return num
else:
#return add_digits(reduce(lambda x, y: int(x) + int(y), str(num)))
return add_digits(sum(map(int, str(num))))
# 1+3+5+7 = 16
# 1+6 = 7
print(add_digits(num))
old = [1,2,3,4,5]
new = old.copy()
print(id(old) == id(new)) # False (껍데기는 다름)
print(id(old[0]) == id(new[0])) # True (0번째 원소는 같은 객체)
얕은 복사 (Shallow Copy)는 객체들의 주소만 복사하여 새로운 객체로 생성한다.
주소를 복사하기 때문에, 원본과 복사본의 객체가 동일한 데이터를 참조한다.
old[0] = 3
print(id(old[0]) == id(new[0])) # False
int는 파이썬에서 불변 객체다.
불변 객체는 값이 바뀌게 되면, 새로운 객체를 생성하기 때문에 서로 다른 주소가 나오게 된다.
old = [[1], [2]]
new = old.copy()
old[0][0] = 999
print(new) # [[999], [2]] <- 내부 리스트를 공유해서 같이 바뀜
print(id(old[0]) == id(new[0])) # True
이처럼 가변 객체를 사용하고 변경할 경우, 주소는 동일하다.
즉, 주소만 그대로 가져온 것을 확인할 수 있다.
얕은 복사 (Shallow Copy) 메모리를 적게 사용하고, 복사 과정이 간단하기 때문에 성능이 좋다.
하지만, 객체의 상태를 완전히 복제할 수 없기 때문에 안전하지 않을 수 있다.
import copy
xs: list[int] = [1, 2, 3]
xa = xs # 복사가 아닌, 그냥 같은 객체
xc = xs.copy() # 얕은 복사
xdc = copy.deepcopy(xs) # 깊은 복사
깊은 복사 (Deep Copy)는 모든 필드를 복사하여, 새로운 객체를 생성하는 방식이다.
객체의 모든 데이터를 복사하기 때문에, 원본과 복사본이 독립적으로 존재한다.
즉 불변 객체든 가변 객체든 상관없이, 주소가 다 달라 다른 객체로 인식된다.
당연히 메모리도 많이 사용하고, 복사 과정은 얕은 복사보다 복잡하다.