Python 기초 - 12.기초마무리&복습
변수의 유효범위와 메모리
-
지역변수: 함수 영역 안에서만 동작하는 변수 - local variable
- 함수가 호출될 때 임시 저장 공간(메모리)에 할당되고 함수 실행이 끝나면 사라진다.
- 따라서 다른 함수에서 같은 이름으로 변수를 사용해도 각각 다른 임시 저장 공간에 할당되고 독립적으로 동작한다.
-
전역변수: 함수 영역 밖에서 생성한 변수로 코드 내 어디서나 사용 가능 - global variable
-
저장공간에 따른
유효범위(scope)
- 이름 공간: 변수를 정의할 때 변수가 저장되는 공간
- 유효범위(scope) : 코드 내에서 영향을 미치는 범위
- 지역변수를 저장하는 이름 공간 = 지역 영역 (local scope)
- 전역변수를 저장하는 이름 공간 = 전역 영역 (global scope)
- 파이썬 자체에서 정의한 이름 공간 = 내장 영역 (built-in scope)
- 변수 확인 순서 (scoping rule 또는 LGB rule) : 지역 → 전역 → 내장
- 동일한 변수명을 사용할 경우 스코핑 룰에 따라 변수를 선택
단순 복제 vs. 얕은복사 (shallow copy) vs. 깊은복사 (deep copy)
-
단순복제
a = [1, 2, 3, 4] a = b print(b) # OUT: [1, 2, 3, 4] b[0] = 100 print(a) # OUT: [100, 2, 3, 4]
-
변수만 복제를 했기 때문에 a와 b가 바라보는 객체는 [1, 2, 3, 4]로 동일
-
a의 값을 바꾸면 b의 값도 바뀌고, b의 값을 바꿔도 a의 값도 바뀐다
-
단, list 처럼 mutable 한 객체의 참조값을 바꾸는 경우에만 해당되고 숫자나 문자열 같은 immutable 객체인 경우에는 해당되지 않는다. (새로운 값을 할당하는 것이라고 보면 됨)
a = 10 b = a print(b) # OUT: 10 b = "abc" print(b) # OUT: "abc" print(a) # OUT: 10
복잡하게 설명했지만, 그냥 문법상 당연히 b의 값을 새로 정의하는 것처럼 보인다.
-
-
얕은복사
import copy a = [1, [1, 2, 3]] b = copy.copy(a) # shallow copy 발생 print(b) # OUT: [1, [1, 2, 3]] b[0] = 100 print(b) # OUT: [100, [1, 2, 3]] print(a) # OUT: [1, [1, 2, 3]] - shallow copy 가 발생해 복사된 리스트는 별도의 객체이므로 item을 수정하면 복사본만 수정된다. (immutable 객체의 경우) c = copy.copy(a) c[1].append(4) # 리스트의 두번째 item(내부리스트)에 4를 추가 print(c) # [1, [1, 2, 3, 4]] 출력 print(a) # [1, [1, 2, 3, 4]] 출력, a가 c와 똑같이 수정된 이유는 리스트의 item 내부의 객체는 동일한 객체이므로 mutable한 리스트를 수정할때는 둘다 값이 변경됨
- a를 복사해서 객체 b와 객체 c를 별도로 만들었지만, 리스트 속의 리스트에 대해서는 새로운 리스트를 만든 것이 아니라 참조값을 복사해왔기 때문에 영향을 받음
-
딥카피
import copy a = [1, [1, 2, 3]] b = copy.deepcopy(a) # deep copy 실행 print(b) # OUT: [1, [1, 2, 3]] b[0] = 100 b[1].append(4) print(b) # OUT: [100, [1, 2, 3, 4]] print(a) # OUT: [1, [1, 2, 3]]
- 리스트 속의 리스트까지 참조값을 복사하는 것이 아니라 복제된 값을 새로 할당시켰기 때문에 서로 영향을 받지 않는다.
람다(lambda) 함수
-
lambda : 함수를 생성할 때 사용하는 예약어로 def와 동일한 역할. 간단한 연산 함수를 한 줄로 표현할 때 사용
-
표현방법
lambda 파라미터1, 파라미터2, ... : 표현식 예) add = lambda a, b : a+b print(add(3,4)) # OUT: 7 power = lambda x : x**2 print(power(3)) # OUT: 9 myFunc = lambda x, y, z : 2*x + 3*y + z print(myFunc(1,2,3)) # OUT: 11
인자 활용법
용어정리
- 파라미터(parameter) = 매개변수. 함수를 정의할 때 나열되는 변수(variable)
- 인자(argument) = 함수 호출시 전달되는 실제 값(value)
함수의 매개변수를 정의하고 인자를 요구할 때 다양한 방법으로 인자를 요구하여 전달받을 수 있다.
-
위치인자 : 매개변수와 인자의 위치를 일치시켜 매칭하는 방법
def func(a, b, c): print(a, b, c) func(3, 2, 1) # a=3, b=2, c=1. OUT: 3 2 1
-
키워드인자: 매개변수와 인자의 이름을 일치시켜서 매칭하는 방법. 인자 입력 순서가 바뀌어도 상관없음
def func(a, b, c): print(a, b, c) func(c=30, a=10, b=20) # OUT: 10 20 30
-
위치인자와 키워드 인자를 섞어서 사용해도 된다. 다만, 위치인자를 키워드인자보다 먼저 작성해야한다.
def func(a, b, c, d, e): print(a, b, c, d, e) func(5, 4, e=1, d=2, c=3) # 가능. OUT: 5 4 3 2 1 func(d=2, e=1, 3, 4, a=5) # 불가능
-
가변인자: 입력받을 **인자의 수를 제한하지 않고 튜플로 받아서 쓸 수 있다
def func(*nums): for i in nums: print(i) func(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) # OUT: 1 2 3 4 5 6 7 8 9 10 def add(*x): s = 0 for i in x: s += i return s print(add(5, 10 ,15, 20, 25)) # OUT: 75
-
매개변수 기본값 지정: 매개변수의 기본값을 지정해놓으면 인자를 보내지 않아도 기본값을 사용하여 함수를 실행한다.
def func(a, b=5): return a+b print(func(3)) # OUT: 8 print(func(3, 7)) # OUT: 10
재귀함수
-
자신을 호출하여 사용하는 함수
-
반복동작을 짧게 구현할 수 있는 장점, 그러나 메모리를 많이 차지하고 스택오버플로우가 발생할 수 있다.
- 함수를 호출 시 함수의 매개변수, 지역변수, 리턴 값, 그리고 함수 종료 후 돌아가는 위치를 스택 메모리에 저장한다.
- 재귀함수를 사용시 함수를 반복하여 호출. 1번 과정을 되풀이한다.
- 호출하는 횟수가 많아질 수록 사용하는 스택 메모리가 커지고 결국 스택 오버플로우로 이어진다.
⇒ 대부분 재귀함수는 루프로 대체가 가능하기 때문에 루프로 대체하는 것이 좋다.
- 재귀함수의 가장 대표적인 사용 예인 팩토리얼 계산:
def factorial(num): if num==1: return 1 else: return num * factorial(num-1)
함수객체(functor)
변수에 함수를 담아서 활용, 함수 속의 함수. (클래스 예제에서 이미 많이 진행시켜 본 구조다)
- 핸들러: 이벤트가 발생했을 때 그 처리를 담당하는 실행 함수
def onEvent(f):
print('이벤트 등록')
f() # onEvent 함수가 호출되면 f() 함수를 실행한다
def handler(): # onEvent 함수 호출시 실질적으로 실행될 함수
print('3의 배수')
def main():
for i in range(1, 100):
print(i)
if i % 3 == 0: # 3으로 나눠서 나머지가 없을 때
onEvent(handler) #onEvent 이벤트 발생, handler 함수를 실행하여 '3의 배수'를 출력한다.
- 룩업테이블 (순람표, LUT - Look Up Table) : 원하는 값으로 쉽게 도달하게 해주는 좌표값
#룩업테이블(리스트)
def 밥먹기():
print('피카츄 밥먹음')
def 놀기():
print('피카츄 놀음')
def 잠자기():
print('피카츄 잠')
def 운동하기():
print('피카츄 운동함')
def main():
funcs = [밥먹기, 놀기, 잠자기, 운동하기]
menu = int(input('1.밥 2.놀 3.잠 4.운동'))
funcs[menu-1]()
클래스에서 사용하는 함수의 종류
인스턴스 메서드:
class Test:
def instanceMethod(self):
print('인스턴스 메서드')
x = Test()
x.instanceMethod() # OUT: '인스턴스 메서드'
각 객체에서 개별적으로 동작하는 함수를 만들 때 사용. 첫 인자로 self를 필요로 하는, 지금까지의 예제들에서 가장 보편적으로 정의하고 사용한 메서드.
정적 멤버 변수 & 정적 메서드:
class Test:
a = 0 # 클래스 변수/정적 멤버 변수
@staticmethod # 데코레이터, 정적 메서드임을 표시
def staticMethod():
if a < 0 :
print('음수')
elif a > 0 :
print('양수')
- 정적 멤버 변수: 정적메모리(static)에 저장. 프로그램이 시작할 때부터 종료할 때까지 메모리가 유지되며, 이 클래스로 만든 모든 객체가 공용으로 사용
- 정적 메서드: 클래스와 관련이 있어서 클래스 안에 두기는 하지만 클래스나 객체와 무관하게 독립적으로 동작하는 함수를 만들고 싶을 경우 이용.
- self를 사용하지 않는다. 바로 클래스명을 사용하여 호출 가능 (Test.statciMethod())
- 정적 메서드 안에서는 인스턴스 메서드나 인스턴스 변수에 접근할 수 없다
클래스 메서드
class Test:
@classmethod
def classMethod(cls, 인자, 인자, ...):
- 클래스 변수를 사용하기 위한 함수. 함수 정의 시 첫 번째 인자로 반드시 클래스를 넘겨받는 cls가 필요. self를 사용하지 않아 객체 생성없이 바로 클래스명을 사용하여 호출 가능. (Test.classMethod(cls))
댓글남기기