def hello():
print("hello")
id(hello) # 함수 객체가 할당한 주소를 확인 할 수 있음
-> 파이썬에서는 함수도 객체이기 때문에, 나중에 같은 이름에 다른 값을 대입하면 그 이름은 새 객체를 가리킴
def f():
print("function executed!")
f = "hello"
f()
TypeError: 'str' object is not callable
callable(f)
# or
import inspect
inspect.isfunction(f)
call 메소드가 init 이 자동 호출되는 것과 비슷하게 호출되기 때문
class Func:
def call(self):
print("function executed!")
f = Func()
f()
-> 실행하면 파이썬 인터프리터는 call 메소드를 객체 공간에서 먼저 찾지만 없다면 클래스 공간으로 가서 찾는데 클래스 공간에 call 메소드가 존재하므로 이를 호출
Positional variable argument(*args)
위치 인자(인수)란 함수를 호출할 때 전달되는 값이 매개변수에 순서대로 전달되는 인자
(함수 호출 시, 인자의 순서(위치)로 값이 전달되는 인자, 이름은 안 중요하고 위치만 중요함)
f(1, 2) # 위치 인자
f(a=1, b=2) # 키워드 인자
f(1, b=2) # 혼합 (가능)
def print_number(a, b, c):
print(a, b, c)
print_number(7, 8, 9)
--> 인자 7은 파라미터 'a'에 인자 8은 파라미터 'b'에 9 인자는 파라미터 'c'에 연결
방법1
print_number(*[7, 8, 9])
방법2
print_number(*(7, 8, 9))
방법3
x = [7, 8, 9]
print_number(*x)
def foo(*args):
print(args)
foo(1,2,3)
foo(1,2,3,4)
함수 호출 시 args라는 변수는 여러개의 입력에 대해 튜플로 저장한 후, 이 튜플 객체를 바인딩
def foo(**kwargs):
print(kwargs)
foo(a=1, b=2, c=3)
{'a': 1, 'b': 2, 'c': 3}
파이썬 함수 정의에서 인자의 순서는 고정
일반 위치 인자 → 위치 가변 인자 (*args) → 키워드 전용 인자 → 키워드 가변 인자 (**kwargs)
def f(a, b, *args, **kwargs):
print(a, b)
print(args)
print(kwargs)
f(1, 2, 3, 4, x=10, y=20)
1 2
(3, 4)
{'x': 10, 'y': 20}
왜?
파이썬의 해석 순서 때문:
위치 인자 → 왼쪽부터
남은 위치 인자 → *args
키워드 인자 → 이름 매칭
남은 키워드 인자 → **kwargs
**kwargs 는 "남은 모든 키워드 인자"를 이미 다 가져가버리기 때문에 그 뒤에 뭘 둘 수가 없음
def f(a, b, *, c, d):
print(a, b, c, d)
f(1, 2, c=3, d=4) # OK
f(1, 2, 3, 4) # x
전체 예제:
def f(a, b, *args, c=0, d=0, **kwargs):
pass
위치 + 키워드 섞어 쓰기 예제:
def f(a, b, c):
print(a, b, c)
f(1, c=3, b=2)
잘못된 위치 인자 사용 예제:
def f(a, *, b):
pass
f(x=1, b=2) #에러
f(1, b=2)
잘못된 생각
-> * 뒤에만 키워드 강제니까 앞에 있는 a 는 아무 키워드나 받아도 되는 거 아닌가?
-> 인덱싱 수행후 전달 가능
def foo(a,b,c):
print(a, b, c)
data = [1, 2, 3]
foo(data[0], data[1], data[2])
-> 함수 인자의 개수가 많은 경우에는 * 사용
foo(*data)
def mul15(x):
return 5 * x
a = lambda x : 5 * x
-> a 가 함수 객체를 바인딩 하므로 a()를 통해 함수 호출 가능
a(2)
10
http://docs.python.org/ko/3/library/functions.html
def outer():
def inner():
print("inner")
return inner
d = outer()
d()
def outer():
inner = 3
return inner
f = outer()
print(f)
3
만약 위의 코드에서 f() 하면: 에러 발생. 3은 int지 함수가 아니기 때문
-> 어떤 변수가 바인딩 하는 값을 참조하는 경우 L, E, G, B 순으로 탐색함
변수 의미
L - Local의 약자로 함수 안을 의미
E - Enclosed funcition locals 의 약자로 내부함수에서 자신의 외부 함수의 범위를 의미
G - Global의 약자로 함수 바깥 즉, 모듈 범위
B - Built-in의 약자로 open, range와 같은 파이썬 내장함수를 의미
예제1)
a = 10
def test():
a = 20
print(a)
test()
20
Local 안의 20이 출력
예제2)
a = 10
def test():
print(a)
test()
10
Local 안에 a 가 존재하지 않고 Enclosed function locals 영역은 존재하지 않으므로 Global 영역의 a를 출력
예제 3)
a = 10
def test():
a = 20
print(a)
test()
print(a)
20
10
Local 영역에서 Global의 변수 값을 수정할 수 없음 (로컬 변수는 함수 호출 끝날때 소멸)
예제4)
로컬 내에서 글로벌 변수 수정을 위해서는 Global 영역의 변수를 선언하는 것이 필요
a = 10
def test():
Global a
a = 20
test()
print(a)
20
def outer():
num = 3 # Enclosed Function Locals 영역의 변수
def inner():
print(num)
return inner
f = outer()
f()
print(f.__closure__[0].cell_contents)
3
3
내부 함수 객체는 함수 객체가 생성될 때 자신이 참조하는 Enclosed Function locals 영역의 변수를 자신의 객체 안에 저장해 두기 때문
__closure__ 라는 속성을 갖고 있는데, print(type(f.__closure__))print(type(f.__closure__[0]))function object
├─ code → code object (바이트코드)
├─ globals → 전역 네임스페이스
├─ defaults → 기본 인자
├─ kwdefaults → 키워드 전용 기본값
├─ closure → cell 객체들
├─ annotations→ 타입 정보
├─ dict → 사용자 정의 속성
└─ metadata → name, module, doc
파이썬의 function 객체는
실행 코드(code) + 스코프(globals, closure) + 메타데이터를 담은 1급 객체이다.
외부 함수에서 내부 함수를 정의
내부 함수에서 참조하는 변수는 내부 함수 객체에 같이 저장
외부 함수는 내부 함수 객체를 리턴
클래스는 데이터와 이를 처리하는 메서드(함수)로 구성
class Person:
pass
p = Person()
p.data = 3
파이썬 객체는 기본적으로 dict 를 가지고 있고, 인스턴스 속성은 여기에 동적으로 저장되기 때문
Person 클래스는 인스턴스 구조를 제한하지 않음
인스턴스는 빈 dict 를 가진 상태로 생성
p.data =3 # p.__dict__['data'] = 3
파이썬은 아래 순서로 찾습니다 (MRO 포함):
인스턴스 p.__dict__ 클래스 Person.__dict__ 부모 클래스 (object.__dict__) 없으면 AttributeError
C++ / Java
클래스 구조 고정
컴파일 시 멤버 결정
런타임 동적 추가 X
Python
런타임 동적 모델
객체 = 해시 테이블 기반 속성 저장
동적 언어의 특징
class Person:
__slots__ = ("name",)
p = Person()
p.name = "Tom"
p.data = 3 # ❌ AttributeError
객체를 초기화하거나 초깃값 세팅을 위해 필요
class Person:
def __init__(self):
print("born...")
p = Person()
생성된 인스턴스의 개수는 각 인스턴스가 갖고 있기 보다는 모든 인스턴스가 참조할 수 있는 공간인 클래스에 저장되는 것이 좋음
class MyClass:
count = 0
def __init__(self):
MyClass.count += 1
def get_count(self):
return MyClass.count
a = MyClass()
b = MyClass()
c = MyClass()
print(a.get_count())
print(MyClass.count)
https://docs.python.org/3.14/reference/datamodel.html
메소드 중에 __로 시작해서 __로 끝나는 메소드를 매직 메소드 혹은 특별 메소드라 부름
예를 들어 __init__
class Car:
def __init__(self):
print("자동차 제작 완료")
a = Car()
리스트, 튜플, 딕셔너리, 정수, 실수, 문자열 등과 같은 타입 역시 클래스를 통해 만들어진 기본 데이터 타입
class Stock:
pass
a = Stock()
b = Stock()
print(a + b)
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
/tmp/ipython-input-298669745.py in <cell line: 0>()
5 b = Stock()
6
----> 7 print(list(a + b))
TypeError: unsupported operand type(s) for +: 'Stock' and 'Stock'
Stock 클래스에 + 연산이 정의되어 있지 않아서 에러
(파이썬에서 a + b는 내부적으로 a.__add__(b)를 호출, 그런데 __add__ 메서드가 Stock 클래스에 없기 때문에)
1) __add__ 연산자를 오버로딩
class Stock:
def __init__(self, items=None):
self.items = items or []
def __add__(self, other):
return self.items + other.items
a = Stock([1, 2, 3])
b = Stock([4, 5])
print(list(a + b))
c = a + b
type(c)
[1, 2, 3, 4, 5]
list
a, b -> Stock
a+b = list
Sotck을 더했지만 add의 반환값이 객체가 아니기때문에 c 는 list 가 됨
Stock 객체에 문자열 외 값(예: int)을 넣어 만드는 경우 에러 발생.
2) __add__가 Stock 객체 반환하게 하기
class Stock:
def __init__(self, items=None):
self.items = items or []
def __add__(self, other):
return Stock(self.items + other.items)
a = Stock([1, 2])
b = Stock([3, 4])
c = a + b
print(list(c.items))
type(c)
list(c)
[1, 2, 3, 4]
Stock
TypeError: 'Stock' object is not iterable
Stock 객체가 iterable 가능한 객체라고 선언된 적이 없으므로
3) __iter__ 추가
class Stock:
def __init__(self, items=None):
self.items = items or []
def __add__(self, other):
return Stock(self.items + other.items)
def __iter__(self):
return iter(self.items)
a = Stock([1, 2])
b = Stock([3, 4])
print(list(a + b))
어떤 클래스 타입에 ()를 붙여주면 __call__ 이 호출.
class MyFunc:
def __call__(self, *args, **kwargs):
print("호출됨")
f = MyFunc()
f()
파이썬 함수는 'function' 클래스의 객체
변수가 어떤 객체를 바인딩 하고 있을 때 점(.)을 찍으면 클래스의 __getattribute__ 라는 매직 메소드를 호출
class Stock:
def __getattribute__(self, item):
print(item, "객체에 접근하셨습니다.")
s = Stock()
s.data
내장 함수의 인자로 넘겨주는 문자열 타입의 이름이 존재하면 True 아니면 False
class Car:
def __init__(self):
self.wheels = 4
def drive(self):
print("drive")
mycar = Car()
print(hasattr(mycar, "wheels"))
print(hasattr(mycar, "drive"))
True
True
class Car:
def __init__(self):
self.wheels = 4
def drive(self):
print("drive")
mycar = Car()
getattr(mycar, "wheels")
method = getattr(mycar, "drive")
method()
drive
추상 클래스(abstract class)란 메서드의 이름만 존재하는(메서드 구현은 없이) 클래스로 보통 객체 지향 설계에서 부모 클래스에 메서드만을 정의하고 이를 상속받은 클래스가 해당 메서드를 반드시 구현하도록 강제하기 위해 사용
from abc import *
class Car(metaclass=ABCMeta):
@abstractmethod
def drive(self):
pass
class K5(Car):
pass
K5 = K5()
TypeError: Can't instantiate abstract class K5 without an implementation for abstract method 'drive'
drive 메서드를 추가해 주어야 함
from abc import *
class Car(metaclass=ABCMeta):
@abstractmethod
def drive(self):
pass
class K5(Car):
def drive(self):
print("K5 drive")
K5 = K5()
K5.drive()
K5 drive
이 클래스는 반드시 구현되어야 하는 규약(인터페이스)을 가진다”는 것을 파이썬이 강제로 검사하게 만들기 위해서입니다
ABCMeta
이 클래스를 추상 클래스로 만들어주는 메타클래스
“아직 완성되지 않은 클래스”라는 딱지를 붙임
@abstractmethod
“이 메서드는 반드시 자식 클래스에서 구현해야 한다”
구현 안 하면 인스턴스 생성 자체를 막음