📚 Variable Argument

함수 호출 시 필요한 인자의 개수가 정해져있지 않음

Positional variable argument(위치 가변 인자) (*args)

def func1(*args)
	print(args)

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

결과:

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

Keyword variable argument(키워드 가변 인자) (**kwargs)

def func2(**kwargs)
	print(kwargs)
    
func2(a=1,b=2,c=3)
func2('book'=10, 'iPhone'=2000, 'coffee'=4)

결과:

{'a':1, 'b':2, 'c':3}
{'book':10, 'iPhone':2000, 'coffee':4}

🧪 *args + **kwargs

def func3(*args, **kwargs)
	print(args)
    print(kwargs)

func3(1,2,4=8,5=10)

(1, 2)
{4:8, 5:10}

📚 Lambda Function

def 키워드가 아닌 lambda 키워드로 정의된 익명의 함수


1. 기본 문법

	lambda arguments: expression
  • arguments: 0개 이상의 매개변수
  • expression: 반드시 한 줄짜리 식(expression)만 올 수 있으며, 이 식의 평가값이 그 함수의 반환값이 됩니다.
  • 예)
  # 두 수의 합을 계산하는 lambda 함수
  add = lambda x, y: x + y
  print(add(3, 5))   # → 8

2. 주용도

  1. 함수를 즉석에서 만들어야 할 때
  # 리스트를 제곱값으로 변환
  squares = list(map(lambda x: x**2, [1, 2, 3, 4]))
  # → [1, 4, 9, 16]
  1. 정렬 기준(key)으로 사용할 때
  data = [('apple', 2), ('banana', 5), ('cherry', 1)]
  # 두 번째 요소로 오름차순 정렬
  sorted_data = sorted(data, key=lambda item: item[1])
  # → [('cherry', 1), ('apple', 2), ('banana', 5)]
  1. 필터링(filter)이나 축소(reduce)와 조합할 때
  # 짝수만 걸러내기
  evens = list(filter(lambda x: x % 2 == 0, range(10)))
  # → [0, 2, 4, 6, 8]

3. def 함수와의 차이

항목def 함수lambda 함수
이름(Name)반드시 이름을 가짐이름이 없거나(익명), 변수에 할당 가능
본문(Body)여러 줄 작성 가능한 줄의 표현식만 가능
가독성복잡한 로직에 적합간단한 로직에 적합
반환(return)return 키워드 사용표현식 결과 자동 반환

4. 장·단점

  • 장점

    • 코드가 간결해짐
    • 일회성(temporary) 용도로 함수 객체를 바로 만들 때 편리
  • 단점

    • 복잡한 로직을 담기 어렵고, 지나치게 남용하면 오히려 가독성을 해칠 수 있음
    • 디버깅 시 스택 트레이스에 이름 대신 <lambda>가 뜨므로 위치 파악이 다소 힘들 수 있음

요약

  • lambda 함수는 “한 줄짜리, 익명 함수”
  • 주로 map, filter, sorted 등의 인자로 쓰일 때 유용
  • 복잡한 로직에는 일반 def 함수를 사용해 가독성을 유지하는 것이 좋다.

📚 LEGB 규칙

파이썬에서의 변수 참조 규칙
L : Local
<
E : Enclosed function locals
<
G : Global
<
B : Built-in

global 키워드

Local 영역 내에서 Global 변수를 사용하려면
global 키워드를 이용해 해당 변수가 Global 변수임을 선언한다.

a = 10

def test():
	global a
    a = 20

test()
print(a)

결과:

20

📚 Closure

def outer():
    num = 3
    def inner():
        print(num)
    return inner

f = outer()
f()

결과:

3

✅ 왜 inner()num을 참조할 수 있을까?

파이썬은 클로저(Closure)를 지원하는 언어이기 때문

🔑 클로저(Clouser)란?

함수가 자신이 정의될 때의 "외부 스코프 변수들"을 기억하고 참조할 수 있는 함수
즉, 함수가 return 되어도 그 함수가 정의될 당시의 환경(변수 상태)을 함께 묶어서 __closure__ 속성에 튜플 형태로 저장해둔 것

print(f.__closure__[0].cell_contents)

결과:

3

✅ 이 코드에서 무슨 일이 일어나는가?

  1. outer()가 호출되면 로컬 변수 num = 3이 생성됨
  2. inner() 함수는 num이라는 이름을 자신 내부가 아니라 바깥(outer)의 스코프에서 찾음
  3. outer()inner() 함수를 반환하면서 그 때의 num = 3 상태도 기억된 함수 객체가 f에 저장됨
  4. f()를 호출하면 inner()가 실행되면서 여전히 num = 3을 알고 있어서 출력 가능함

✅ 메모리 구조 시각화

outer() 호출
├── num = 3
└── inner() 정의
	└── 내부에서 num을 사용 ← 이 num은 outer 스코프의 num을 참조함

f = outer() 호출 결과인 inner()
f()inner() 실행, 이때 num = 3을 기억하고 있음

✅ 한 줄 요약

inner 함수는 정의될 당시의 환경(outer의 지역 변수들)을 기억하고 있으므로 num을 참조할 수 있다.

📦 보너스: 이게 왜 유용할까?

클로저는 다음과 같은 상황에 유용:
✔️ 상태를 기억하는 함수 만들기
✔️ 데코레이터 구현
✔️ 캡슐화된 함수형 프로그래밍

📚 __getattribute__ & hasattr() & getattr()

1️⃣ __getattribute__ : magic method

class Car:
	def __getattribute__(self, item):
    	print(item, '객체에 접근하였습니다.')
        
	def __init__(self):
    	self.wheel = 4
        
    def drive(self):
    	print("drive~")
        
myCar = Car()

myCar.nothing

결과:

nothing객체에 접근하였습니다.

2️⃣ hasattr(object, name)

hasattr(myCar, 'nothing')
hasattr(myCar, 'wheel')

결과:

False
True

3️⃣ getattr(object, name[, default])

newDrive = getattr(myCar, 'drive')
newDrive()

결과:

drive~

📚Abstract Class(추상 클래스)

✅ 한 줄 정의

추상 클래스(Abstract Class)는 상속을 통해서만 사용 가능한 클래스이며,
자식 클래스가 반드시 구현해야 할 메서드(추상 메서드)를 정의하는 데 사용됩니다.

🧱 Python에서 추상 클래스 만드는 법

Python에서는 abc(Abstract Base Class) 모듈을 사용합니다.

from abc import ABC, abstractmethod

class Animal(ABC):  # ABC를 상속받아야 추상 클래스가 됨
    @abstractmethod
    def speak(self):
        pass

❌ 인스턴스화 불가 예

a = Animal()  # TypeError: Can't instantiate abstract class Animal with abstract methods speak

✅ 자식 클래스에서 구현

class Dog(Animal):
    def speak(self):
        return "Woof!"

d = Dog()
print(d.speak())

결과:

Woof!

✅ 정리: 추상 클래스의 특징

특징설명
직접 인스턴스화 불가추상 메서드가 하나라도 있으면 Animal()처럼 직접 못 씀
자식 클래스가 구현추상 메서드를 반드시 자식 클래스에서 구현해야 함
인터페이스 제공"이런 기능이 필요하다"는 계약(Contract) 역할
공통 로직 포함 가능추상 클래스 안에 실제 구현된 메서드도 포함 가능

🎯 언제 쓰는가?

  1. 여러 클래스가 같은 인터페이스(메서드 이름 등)를 가져야 할 때
  2. 팀 단위 개발 시 "이 메서드는 무조건 만들어야 해" 라고 명시하고 싶을 때
  3. 일부는 공통 구현하고, 일부는 자식 클래스에 맡기고 싶을 때
profile
개발자

0개의 댓글