2026. 5. 28. 17:40ㆍKDT/1. Python
# 중첩 함수
# 중첩 함수
def outer_function():
enclosing_var = "둘러싼 범위 변수"
def inner_function():
print(enclosing_var)
return inner_function # 함수 자체를 return
f = outer_function()
f() # 둘러싼 범위 변수
- 클로저 변수 : 내부 함수가 외부 함수의 지역 변수를 사용하면, 파이썬은 그 값을 바로 삭제하지 않고 클로저 공간에 보관
# 클로저 공간과 빌트인 함수
1. 클로저 공간
- 내부 함수가 외부 함수의 변수를 계속 사용할 수 있도록 저장해두는 메모리 영역(정확히는 객체 구조)
- 내부 함수가 외부 함수의 지역 변수를 참조하고 있으면 파이썬은 해당 값을 지우지 않고 __closure__라는 형태로 보관
- 이때 저장되는 값은 보통 cell 객체로 감싸져 유지
- 내부 함수 실행 시 이 클로저 공간을 통해 외부 변수로 접근
- heap 영역에 클로저 객체 저장
2. 빌트인 함수
- import 없이 바로 사용 가능한 기본적인 함수
ex. print(), len(), type(), range() 같은 함수들 - 변수 이름을 찾을 때 적용되는 LEGB 규칙 (Local -> Enclosing -> Global -> Built-in)에서 마지막 단계이므로
만약 같은 이름의 변수를 직접 정의하면 Built-in 함수는 가려져 사용이 불가능하게 됨
(Enclosing : 중첩된 함수의 외부 함수 영역에 있는 변수)
# 함수 메모리 제거
- 함수도 파이썬 객체이므로 참조 카운팅과 가비지 컬렉션의 원칙에 따라 동작
(함수를 참조하는 변수나 요소가 없으면 메모리에서 제거될 수 있음) - del 명령어로 제거 가능하지만 즉시 메모리에서 제거된다는 것은 보장하지 않음
([2일차] 가비지 컬렉션 참고) - del로 제거 후 함수 호출하면 "NameError: name 'func1' is not defined" 에러 발생
# 콜백함수 callback function⭐
- 다른 함수에 인자로 전달되어, 특정 시점이나 조건이 되었을 때 나중에 호출되는 함수
(= 함수가 다른 함수를 호출) - 함수도 객체기 때문에 변수처럼 전달이 가능
- 공통 작업은 그대로 두고, 세부 동작만 바꾸고 싶을 때 유용
def say_hello(name):
print(f"{name}님 안녕하세요!")
def execute(callback): # 여기 callback은 변수명 (a등 다른 이름 사용해도 됨)
print("작업을 시작합니다.")
callback("김사과") # 위의 callback 변수명 사용. "김사과"는 매개변수.
print("작업을 종료합니다.")
execute(say_hello) # 👉여기서 say_hello가 콜백 함수
# say_hello() : 지금 당장 함수를 실행해라
# say_hello : 함수의 주소
# -> execute의 매개변수로 say_hello의 함수 주소를 넘기는 것
def print_score(score):
print(f"점수: {score}")
def print_pass_fail(score):
if score >= 80:
print(f"{score}점: 합격")
else:
print(f"{score}점: 불합격")
def print_bonus_score(score):
print(f"보너스 적용 점수: {score + 5}")
def process_scores(scores, callback):
for score in scores:
callback(score)
scores = [80, 95, 70, 100, 60]
print("원점수 출력")
process_scores(scores, print_score)
print("합격 여부 출력")
process_scores(scores, print_pass_fail)
print("보너스 점수 출력")
process_scores(scores, print_bonus_score)
👉콜백함수는 공통 작업은 그대로 두고, 세부 동작만 바꾸고 싶을 때 유용
students = [
{"name": "김사과", "score": 90},
{"name": "반하나", "score": 75},
{"name": "오렌지", "score": 100}
]
def get_score(student):
# print(student["score"], end = " ") # 90 75 100
return student["score"]
result = sorted(students, key=get_score)
print(result)
# [{'name': '반하나', 'score': 75}, {'name': '김사과', 'score': 90}, {'name': '오렌지', 'score': 100}]
# key=get_score : key 뒤에 있는 것(get_score)으로 sorted할거야 (get_score = 콜백함수)
👉 sorted()는 학생 하나하나 비교할 때마다 get_score()를 호출해서 정렬 기준값을 얻음
# 람다 함수 lambda function
- 이름 없이 한 줄로 정의 가능한 익명 함수
- 일반함수(def)에 비해 구조는 제한적이지만 코드가 간결해짐
- sorted, map, filter 같은 함수에서 콜백 함수로 자주 사용 (간단한 콜백 함수는 람다 함수 사용하면 굳)
- 무조건 return 형
- 이름 없는 함수기 때문에 함수명 호출로 사용 불가능
lambda 매개변수: 반환할_표현식
scores = [45, 70, 88, 52, 100]
result = list(map(lambda score: score + 10, scores))
print(result) # [55, 80, 98, 62, 110]
passed = list(filter(lambda score: score >= 60, scores))
print(passed) # [70, 88, 100]
👉 map() : 리스트의 각 요소에 같은 작업을 적용할 때 사용
👉 filter() : 조건을 만족하는 값만 골라낼 때 사용
# 프로그래밍 방법론
- 프로그램을 개발하는 다양한 접근 방식이나 철학
- 프로젝트 규모, 요구 사항, 개발 팀의 특성에 따라 적합한 방법론이 달라지기 때문에 각 방법론의 특징과 장단점 이해가 중요
1. 절차적 프로그래밍 (Procedural Programming)
- 절차를 중시하는 방식 (= 작업을 순서대로 실행하도록 프로그램 구성)
- 코드가 명령문으로 이루어져 있고 작업 순서가 중요
- 함수를 통해 프로그램의 기능을 분리하고, 코드 중복을 줄임
2. 객체지향 프로그래밍 (Object-Oriented Programming, OOP)
- 객체라는 개념을 사용해 프로그램을 구성
- 객체 = 데이터(속성) + 함수(메서드)
- 클래스라는 틀을 이용해 객체를 정의하고, 이를 통해 여러 객체 생성
- 캡슐화, 상속, 다형성 등의 특징을 활용해 코드를 재사용하고 확장 가능
3. 함수형 프로그래밍 (Functional Programming)
- 함수 중심 프로그램 구성
- 수학적 함수 개념에 기초
- 코드 간결, 직관적
ex. map, filter, reduce 등의 함수나 재귀를 주로 사용하여 데이터 처리 간결하게 수행
# 클래스

- 객체지향 프로그래밍에서 데이터(속성)와 기능(메서드)을 하나로 묶어 객체를 만들기 위한 설계도
- 클래스를 정의하면 여러 개의 객체(인스턴스) 생성
- 각 객체는 동일한 구조면서 서로 다른 값을 가질 수 있음
class 클래스이름:
# 클래스 속성(멤버 변수) 정의
속성1 = 초기값1
속성2 = 초기값2
# 생성자 메서드 (생략 가능)
def __init__(self, 매개변수1, 매개변수2, ...):
# 인스턴스 속성 초기화
self.속성1 = 매개변수1
self.속성2 = 매개변수2
# 메서드(멤버 함수) 정의
def 메서드1(self, 매개변수1, 매개변수2, ...):
# 메서드 동작 정의
pass
def 메서드2(self, 매개변수1, 매개변수2, ...):
# 메서드 동작 정의
pass
# 객체와 인스턴스
- 객체(Object)와 인스턴스(instance)는 거의 같은 의미지만 관점에 따라 구분
- 객체 : 메모리에 생성된 모든 실체를 의미하는 포괄적 의미
- 인스턴스 : 특정 클래스에 의해 생성된 객체를 가리키는 관계 중심의 용어
(어떤 클래스의 인스턴스이다) - 즉, 모든 인스턴스는 객체이지만 / 모든 객체가 특정 클래스의 인스턴스라고 강조해서 부르는 것은 아니며 / 인스턴스 라는 용여는 주로 클래스와의 관계를 설명할 때 사용
# 메서드
1. 생성자 Constructor
- 클래스의 인스턴스가 생성될 때 자동으로 호출되는 특별한 메서드
- 객체의 초기화
- __init__ : 파이썬 생성자 메서드 이름 (정해져있음)
- 속성은 self를 사용하여 접근하고 설정
class 클래스이름:
# 생성자
def __init__(self, 매개변수1, 매개변수2): # 매개변수 유무와 관계없이 self는 무조건 필요
self.속성1 = 매개변수1
self.속성2 = 매개변수2
2. 인스턴스 변수
- 각 인스턴스마다 별도로 생성되는 변수
- 객체를 만들 때마다 self 키워드를 통해 생성자(init)에서 정의됨
- 각 객체가 고유한 값을 따로 저장
- 인스턴스명으로만 접근 가능
- 다른 인스턴스에 영향을 주지 않고, 자신만을 데이터를 가질 수 있음
- 생성자 매개변수 개수 맞추지 않으면 TypeError 발생
# name과 quantity는 인스턴스 변수
class Fruit:
def __init__(self):
self.name = ''
self.quantity = 0
apple = Fruit()
print(apple) # <__main__.Fruit object at 0x0000022CF53338F0>
print(apple.name) #
print(apple.quantity) # 0
banana = Fruit()
print(banana)
print(banana.name)
print(banana.quantity)

class Fruit:
def __init__(self, name, quantity, origin='원산지 미상'):
self.name = name
self.quantity = quantity
self.origin = origin
apple = Fruit('사과', 10, '한국')
banana = Fruit('바나나', 5, '필리핀')
apple = Fruit() # 오류 발생
# TypeError: __init__() missing 2 required positional arguments
# -> 기본값 지정이 안되어있는 name, quantity 매개변수를 받아오지 못해서 에러!
3. 메서드
- 클래스 내부에 정의된 함수
- 객체(인스턴스)나 클래스와 관련된 동작(행동)을 수행
- 종류
- 인스턴스 메서드 : self 사용
self : 어떤 객체에서 호출했는지 찾기 위한 변수(객체 주소를 가지고 있음) -> 메모리를 아껴쓰기 위함 - 클래스 메서드 : cls 사용. @classmethod
cls : 클래스 주소를 가지고 있음 - 정적 메서드 : 별도의 참조 없이 독립적으로 동작. @staticmethod
- 인스턴스 메서드 : self 사용
class Fruit:
def __init__(self, name, quantity):
self.name = name
self.quantity = quantity
# 인스턴스 메서드1
def print_info(self):
print(f"과일 이름: {self.name}")
print(f"수량: {self.quantity}")
# 인스턴스 메서드2
def add_quantity(self, amount):
self.quantity += amount
print(f"{amount}개 추가되었습니다.")
apple = Fruit("사과", 10)
apple.print_info()
apple.add_quantity(5)
apple.print_info()
banana = Fruit("바나나", 3)
banana.print_info()
banana.add_quantity(2)
banana.print_info()

class Order:
tax_rate = 0.1 # 클래스 변수 (세율: 모든 주문 공통)
def __init__(self, price, quantity):
# 인스턴스 변수 = 생성자 안의 변수 (price, quantity)
self.price = price # 상품 가격
self.quantity = quantity # 수량
# 인스턴스 메서드
def total_price(self):
return self.price * self.quantity
# 클래스 메서드
@classmethod # 데코레이터
def set_tax_rate(cls, rate):
cls.tax_rate = rate
# 클래스 주소를 가지고 있으므로 클래스 변수 사용 가능
# 인스턴스가 아닌 (=객체를 생성하지 않고) 클래스로 호출 가능
# Order.set_tax_rate()로 사용 가능
# 정적 메서드
@staticmethod
def calculate_discount(price, discount_rate):
return price * (1 - discount_rate)
# 클래스 이름으로 접근 가능
# Order.calculate_discount()로 사용 가능
# 클래스 안에 속해만 있는 것 뿐. self, cls 사용 불가(= 인스턴스 변수, 인스턴스 메서드)
# 인스턴스 생성
order1 = Order(10000, 2) # 10000원 상품 2개
# 인스턴스 메서드 호출
print("총 가격:", order1.total_price()) # 20000
# 정적 메서드 사용 (할인 계산)
discounted = Order.calculate_discount(order1.total_price(), 0.2)
print("할인 적용 가격:", discounted) # 16000
# 클래스 메서드 사용 (세율 변경)
Order.set_tax_rate(0.2)
print("변경된 세율:", Order.tax_rate) # 0.2
4. 데코레이터 decorator
- 기존 함수나 메서드를 수정하지 않고 그 기능을 감싸서 추가 동작을 붙여주는 기능
- 실행 전후에 로그를 출력하거나 권한 체크, 시간 측정 등의 공통 기능을 쉽게 재사용할 수 있게 해줌
- "내부적으로는 함수를 인자로 받아 새로운 함수를 반환"하는 구조로 동작
(-> 값을 리턴하는 것이 아닌 함수를 리턴. 클로저로 데코레이터 생성 가능) - @데코레이터이름 형태로 사용하면 해당 함수가 자동으로 데코레이터에 전달되어 기능 확장
5. 클래스 변수
- 클래스 생성 시 정의
- 해당 클래스로 생성된 모든 인스턴스(객체)들이 공유하는 변수
- 클래스 변수는 클래스명으로 직접 접근하거나 인스턴스명으로 접근 가능
- 모든 인스턴스가 같은 메모리 공간의 값을 참조하기 때문에 한 인스턴스에서 값을 변경하면 다른 인스턴스에서도 변경된 값을 볼 수 있음
class Fruit:
origin = "한국" # 클래스 변수 (공통 속성)
def __init__(self, name, quantity):
# 인스턴스 변수 (각 객체마다 다름)
self.name = name
self.quantity = quantity
def print_info(self):
print(f'원산지: {Fruit.origin}') # 클래스 변수
print(f'이름: {self.name}') # 인스턴스 변수
print(f'수량: {self.quantity}') # 인스턴스 변수
print("----- 클래스 변수 변경 -----")
Fruit.origin = '필리핀'
apple.print_info()
banana.print_info()
print("----- 인스턴스 변수로 덮어쓰기 -----")
apple.origin = '미국' # apple만 따로 가짐
apple.print_info()
print(apple.origin) # 미국 (없던 apple.origin을 만들었기 때문에)
# -> 1. 인스턴스 변수를 뒤지고 2. 클래스 변수 뒤지고 없으면 새로 만듦
print(banana.origin) # 필리핀
# 객체지향 프로그래밍 4대 패러다임
- 캡슐화, 상속, 다형성, 추상화
- 네 가지를 통해 재사용성, 확장성, 유지보수성 향상 가능
# 1. 캡슐화 encapsulation
- 데이터(속성)와 메서드를 하나로 묶고, 외부에서 내부 구현에 직접 접근하지 못하도록 제한하여 객체를 보호
- 접근 수준 구분 : public, _protected, __private
- getter/setter, @property : 안전하게 값을 읽고 수정 가능
class Fruit:
def __init__(self, name, price):
self.name = name # public
# 클래스 밖에서 fruit.name = "사과" 이렇게 변경 가능
self.__price = price # private (캡슐화) -> 클래스 내에서만 접근 가능 (외부에서 접근 불가)
# 클래스 밖에서 fruit.__price = 5000 이렇게 변경 불가능
# getter
def get_price(self):
return self.__price
# setter
def set_price(self, price):
if price > 0:
self.__price = price
else:
print("가격은 0보다 커야 합니다.")
def print_info(self):
print(f"과일: {self.name}")
print(f"가격: {self.__price}")
class Apple(Fruit): # Apple 클래스는 Fruit 클래스를 상속
def __init__(self, name, price, origin):
super().__init__(name, price) # 굳이 여기서 다시 self.name = name을 할 필요가 없으니까 super().__init__ 사용
self.origin = origin
def print_origin(self):
print(f"{self.name}의 원산지: {self.origin}")
# ========================================================
apple = Apple("사과", 3000, "한국")
apple.print_info()
apple.print_origin()
print("가격 조회:", apple.get_price())
print("----- 가격 수정 -----")
apple.set_price(3500)
print("수정된 가격:", apple.get_price())
print("----- 잘못된 값 입력 -----")
apple.set_price(-1000) # 검증 실패
print("----- 외부 접근 시도 -----")
apple.__price = 10000 # 새로운 변수 생성 (실제 private 변경 아님)
print("getter로 확인:", apple.get_price()) # 여전히 3500
print("직접 접근:", apple.__price) # 10000 (다른 변수)
👉 self.__price는 실제로 private 되는 것이 아니라 내부적으로 _Fruit__price로 바뀜
class Fruit:
def __init__(self, name, price):
self.name = name
self.__price = price # private
# getter → 속성처럼 접근 가능
# getter만 만들기는 가능
@property
def price(self):
return self.__price
# setter → 값 할당 시 자동 호출
# getter를 만들지 않으면 setter를 만들 수 없음 (price.setter에서 price 때문에)
@price.setter
def price(self, value):
if value > 0:
self.__price = value
else:
print("가격은 0보다 커야 합니다.")
def print_info(self):
print(f"과일: {self.name}")
print(f"가격: {self.price}") # 함수가 아니라 속성처럼 사용
class Apple(Fruit):
def __init__(self, name, price, origin):
super().__init__(name, price)
self.origin = origin
def print_origin(self):
print(f"{self.name}의 원산지: {self.origin}")
# ================================================================
apple = Apple("사과", 3000, "한국")
apple.print_info()
apple.print_origin()
print("가격 조회:", apple.price) # getter 호출
print("----- 가격 수정 -----")
apple.price = 3500 # setter 호출
print("수정된 가격:", apple.price)
print("----- 잘못된 값 입력 -----")
apple.price = -1000 # 검증 실패
print("----- 외부 접근 시도 -----")
apple.__price = 10000 # 새로운 변수 생성
print("실제 가격:", apple.price) # 여전히 3500
print("외부에서 만든 값:", apple.__price)
👉 @property는 getter/setter를 함수가 아닌 "속성처럼" 사용하게 해주는 Pythonic한 캡슐화 방식
# 2. 상속 Inheritance
- 부모 클래스의 속성과 메서드를 자식 클래스가 물려 받아 재사용하고 확장
- 공통 기능 중복 작성을 피할 수 있고 재사용성 높일 수 있음
- 자식은 부모의 기능 그대로 사용하거나 필요한 부분을 추가 또는 오버라이딩(재정의) 가능
- super()를 사용해 부모 클래스의 메서드 호출 가능
class Parent:
pass
class Child(Parent):
pass
1. 상속 구조
class Fruit:
def __init__(self, name, quantity):
self.name = name
self.quantity = quantity
def store(self, place):
print(f'{self.name}를 {place}에 보관합니다')
def sell(self, amount):
print(f'{self.name}를 {amount}개 판매합니다')
# 자식(Apple)의 생성자가 없으면 부모(Fruit) 생성자
# Fruit 생성자의 매개변수를 전달해야 함 (안 넣으면 에러)
class Apple(Fruit):
pass
apple = Apple('사과', 50)
apple.store('냉장고')
apple.sell(5)
2. 생성자 호출
# 👉 자식 클래스에 __init__이 없을 때 자동 호출
class Fruit:
def __init__(self, name):
print("Fruit 생성자 호출")
self.name = name
class Apple(Fruit):
pass
apple = Apple("사과")
# 👉 자식 클래스에 __init__을 직접 정의하면 부모 생성자 호출 안됨 (오버라이딩)
class Fruit:
def __init__(self, name):
print("Fruit 생성자 호출")
self.name = name
class Apple(Fruit):
def __init__(self):
print("Apple 생성자 호출")
# self.name = name # 부모를 호출한 적이 없는데 self.name = name 코드가 살아있으면 자식에서 덮어쓰게 된다는 것이 말이 안됨
apple = Apple()
# Apple 생성자 호출
# print(apple.name) # AttributeError: 'Apple' object has no attribute 'name'
# Apple() 은 자식 생성자 사용 중이므로 매개변수를 전하면 에러 발생
# 👉 super() 함수는 현재 클래스의 부모 클래스를 참조하며, 부모 클래스의 생성자를 호출할 수 있습니다.
class Fruit:
def __init__(self, name):
print("Fruit 생성자 호출")
self.name = name
class Apple(Fruit):
def __init__(self, name):
super().__init__(name) # 부모 생성자 호출
print("Apple 생성자 호출")
apple = Apple("사과")
print(apple.name)
# Fruit 생성자 호출
# Apple 생성자 호출
# 사과
3. 오버라이딩 overriding
- 상속 관계에서 부모에 이미 정의된 메서드를 자식 클래스에서 동일한 이름으로 재정의하여 동작을 변경
- 이를 통해 부모 기본 기능을 그대로 사용할수도 or 일부 또는 전체 수정하여 사용 가능
- 별도의 키워드 없이 같은 메서드 이름 정의하면 자동 오버라이딩
- 필요할 경우 super()로 부모의 메서드 함께 호출 가능
class Fruit:
def __init__(self, name, quantity):
self.name = name
self.quantity = quantity
def eat(self):
print(f'{self.name}를 먹습니다')
def store(self, place):
print(f'{self.name}를 {place}에 보관합니다')
class Apple(Fruit):
# 부모 클래스에 없는 확장된 메소드
def wash(self):
print(f'{self.name}를 깨끗이 씻습니다')
# 오버라이딩 (부모 꺼 안쓰고 덮어씀)
def eat(self):
print(f'{self.name}를 아주 맛있게 먹습니다')
# 부모 메서드 호출
def superEat(self):
super().eat()
# ============================================
apple = Apple('사과', 10)
apple.eat() # 오버라이딩된 메서드
apple.store('냉장고') # 부모 메서드
apple.wash() # 자식 메서드
print("----- 부모 메서드 호출 -----")
apple.superEat() # 부모 eat 호출
fruit = Fruit('과일', 20)
fruit.eat()
fruit.store('창고')
# fruit.wash() (Fruit에는 없음)
4. 다중 상속
- 클래스가 둘 이상의 부모로부터 상속 받는 기능
- 다른 객체 지향 언어와 달리 다중 상속 지원
- 다중 상속 사용 시 재사용성 향상 가능하지만 동시에 복잡성이 높아지기 때문에 주의
class Parent1:
pass
class Parent2:
pass
class Child(Parent1, Parent2):
pass
class Fruit:
def __init__(self, name, quantity):
self.name = name
self.quantity = quantity
def eat(self):
print(f'{self.name}를 먹습니다')
def sleep(self, hour):
print(f'{self.name}를 {hour}시간 동안 보관합니다')
def superSleep(self, hour):
super().sleep(hour)
class Storage:
def __init__(self, name, quantity):
self.name = name
self.quantity = quantity
def process(self, hour):
print(f'{self.name}를 {hour}시간 동안 가공합니다')
def sleep(self, hour):
print(f'{self.name}를 {hour}시간 동안 저온 숙성합니다')
# 다중 상속
class ProcessedFruit(Fruit, Storage):
pass
# =========================================================
fruit = ProcessedFruit('사과', 10)
fruit.eat() # Fruit
fruit.process(2) # Storage
fruit.sleep(8) # MRO에 따라 Fruit의 sleep 실행 (쉽게 말하면 Fruit, Storage 순서)
fruit.superSleep(5) # 맨 아래줄 설명에 의해 Storage가 Fruit의 부모 클래스가 됨
print(ProcessedFruit.mro())
# ProcessedFruit.mro() 실행 시 나오는 순서대로 상속받음 (최상위 부모클래스 object)
# ProcessedFruit -> Fruit -> Storage -> object
5. object 클래스
- 모든 클래스의 최상위 부모 클래스(루트 클래스)
- 사용자가 명시적으로 상속하지 않아도 자동 상속되는 기본 클래스
- 객체가 가져야 할 최소한의 공통 기능 제공
(__str__, __repr__, __eq__ 등 기본 매직 메서드의 기본 구현) - 즉, 모든 파이썬 객체는 내부적으로 object를 기반으로 동작
새로운 클래스 정의 시에도 이 기본 기능들 사용 가능
* "LEGB 규칙"이라는게 있구나
(지역변수가 전역변수보다 우선순위가 높다. 빌트인 함수는 사용하게 되면 덮어씌워진다. 두 가지 내용만 알고 있었음)
* 함수 정의 시 함수 객체가 heap에 올라가고 호출(call)되면 stack에 복사돼서 사용
* 후에 굉장히 많은 데이터 처리를 위해 콜백함수를 많이 사용하게 될 것
* 람다 함수는 메모리에 올라가지 않는건가?
-> False. 람다 함수 실행 시 heap에 생성되고 참조가 사라지면 GC에 의해 즉시 메모리가 해제되어 효율적으로 사용이 가능하다는 의미
* PP의 단점을 보완한 것이 OOP
* 클래스 : 객체를 만들기 위한 설계도(빵틀)
- 구성 요소 : 1. 속성 2. 메서드
- 인스턴스 생성 시 메서드는 공용으로 사용
- 인스턴스 메서드는 heap에 올라감 (속성은 따로 사용 -> 인스턴스 변수 부분 참고)
* @staticmethod 필요없지 않냐? -> 원래 함수 자체는 독립적으로 사용하는게 맞음 -> 근데 객체 생성 없이 그냥 필요한 함수가 있을수도 있다 (관점의 차이?)
* 캡슐화 : 메서드를 통해 속성을 변경하도록 하여 클래스 속성을 보호하고자 사용하는 것
- getter : 값을 return할 때 사용
- setter : 값을 변경할 때 사용
* 다중 상속 : C++에서는 개발 시 교통 정리를 잘 해야 하지만 Python에서는 교통 정리가 잘 되어 있음
[수업후기]
- 내용은 알지만 구체적인 용어를 몰랐던 것들에 대해 공부할 수 있었다. 예를 들어 LEGB규칙이나 클로저 공간 같은 것.
- 대학교 수업 중 (과목이름 기억이안남) 콜백 함수가 시험에 나왔는데 틀렸던 기억이 떠올랐다
- 람다 함수도 코테 공부할 때 자주 사용했던 아이라 반가웠다.
- 프로그래밍 방법론도 귀에 피나게 들었던 내용이다. 그만큼 중요하다는 거겠지?
- 클래스를 설명하는 건 어딜가도 똑같다. 빵틀을 그렇게나 좋아하셨던 교수님이 생각난다. 최초로 클래스를 공부했을 때를 떠올려보면 클래스와 객체, 인스턴스, 인스턴스 변수 등 한 번에 몰아치는 용어들을 이해하는 게 힘들었던 기억이 있다. 이 수업에서는 용어들에 대해 정확하게 짚고 설명해주셔서 좋다. (내가 비전공자였다면 정말 따라가기 힘들었을 것 같아ㅠ,ㅠ)
- Spring의 Annotation과 Python의 Decorator가 비슷하게 생겨서 찾아보니 전혀 비슷하지 않은 내용이었다. 그냥 기호만 같은거였어..!
| Spring의 Annotation | Python의 Decorator | |
| 핵심 개념 | 코드에 대한 메타 데이터 제공 및 설정 | 기본 함수나 클래스를 감싸는 동작 변경/확장 |
| 동작 시점 | 컴파일 타임 또는 런타임 | 런타임(호출시점에 동적으로 실행) |
| 내부 구현 | 리플렉션을 통해 메타데이터 읽어 프록시/빈 생성 | 함수 안에 함수를 넣는 클로저 형태 |
| 주요 목적 | DI/IoC 설정, 트랜잭션 등 프레임워크 수준의 처리 | 로깅, 권한 검사, 실행 시간 측정 등 함수 단위의 공통 로직 |
- 4대 패러다임도 Java 수강할 때 귀에 딱지 앉도록 들었던 내용이다.
- 어제보다 확실히 복잡한 내용을 해서 수강생들의 질문이 늘었다. 내일부터 블로그 작성이 아닌 과제를 내주신다는데 굉장히 걱정이 된다. 벌써부터 울고 싶어..!!!
'KDT > 1. Python' 카테고리의 다른 글
| [9일차-1] 모듈 (0) | 2026.06.01 |
|---|---|
| [8일차] 객체지향 프로그래밍(다형성), 예외, 매직메서드 (0) | 2026.05.29 |
| [6일차] 제어문(반복문), 사용자 정의 함수(함수 생성, 전역변수, 지역변수) (0) | 2026.05.27 |
| [5일차] 컬렉션 타입(튜플, 세트, 딕셔너리), 제어문(조건문, 반복문) (0) | 2026.05.26 |
| [4일차] 입출력, 연산자, 컬렉션타입(리스트) (0) | 2026.05.26 |