1. 함수
def foo():
return 1
foo()
- Python에 있어서 함수는 def 키워드로 함수명과 파라미터의 리스트(임의)를 이용해 정의한다.
2. 스코프
def foo(arg):
x = 10
print(locals())
foo(20)
y=30
print(globals())
- Python에서는 함수를 만들면 새로운 스코프가 만들어진다.
- 각각의 함수가 각각의 이름 공간을 가지고 있다는 의미이다.
3. 변수의 해결 규칙
- 작성할 때는 항상 새로운 변수가 그 이름공간 안에 만들어진다.
- 참고는 먼저 이름공간 내부터 검색하고 없으면 외부로 검색 영역을 넓혀간다.
text = "I am global!"
def foo():
print(text)
foo()
text = "I am global!"
def foo():
text = "I am local!"
print(locals())
foo()
print(text)
- 함수의 안쪽에서는 글로벌 변수에 참고할 수 있는 것을 대입할 수 없다.
4. 변수의 라이프사이클
def foo():
x=1
foo()
print(x)
- 스코프만으로는 에러가 발생한 이유를 설명할 수 없다.
- 네임스페이스는 함수 foo가 호출될 때마다 생성되며 처리가 끝나면 사라져버린다.
5. 함수의 인수와 파라미터
def foo(x):
print(locals())
foo(1)
def foo(x, y=0):
return x-y
print(foo(3, 1))
print(foo(3))
print(foo())
print(foo(y=1, x=3))
6. 함수의 중첩
- Python에서는 함수 내에 다시 함수를 정의, 즉 충첩할 수 있다.
def outer():
x = 1
def inner():
print(x)
inner()
outer()
- print(x)에서 로컬 변수 x를 찾아보지만 없으므로 네임스페이스를 찾아서 outer내에 정의되어 있는 x를 참고한다.
- inner()를 호출하고 있지만, 여기서 중요한 것은 inner이라는 것도 하나의 변수명에 지나지 않고, 해결 규칙 내용을 바탕으로 outer내에 네임스페이스의 정의를 찾아서 호출한다.
7. 함수는 Python에 있어서 퍼스트 클래스 오브젝트이다.
print(issubclass(int, object))
def foo():
pass
print(foo.__class__)
print(issubclass(foo.__class__, object))
- 즉, 함수를 일반적으로 다른 변수와 동일하게 취급한다.
-> 다른 함수의 인수로 전달하거나 함수의 리턴 값으로써 사용할 수 있다.
def add(x, y):
return x+y
def sub(x, y):
return x-y
def apply(func, x, y):
return func(x, y)
print(apply(add, 2, 1))
print(apply(sub, 2, 1))
def outer():
def inner():
print("Inside inner")
return inner
foo = outer()
print(foo)
foo()
- return inner에서 리턴 값으로써 실행 결과를 보여주는 것이 아닌 함수 그 자체를 지정하고 있다. (inner() x)
- 이것은 보통 대입가능하므로 foo에 함수를 넣어 실행하는 것이 가능하다.
8. 클로저
def outer():
x = 1
def inner():
print(x)
return inner
foo = outer()
foo()
print(foo.__closure__)
- inner는 outer에 의해 반환되는 함수로, foo에 저장되어 foo()에 의해 실행된다.
- 변수 x는 outer함수가 실행되는 동안에만 존재한다. 여기서 outer함수의 처리가 종료된 후에 inner함수가 foo에 대입하고 있으므로 foo()는 실행할 수 없지 않을까?
-> Python은 Function closure의 기능을 가지고 있기 때문에 가능하다.
- 클로저는 글로벌 스코프 이외에 정의된 함수(inner)가
정의했을 때
의 자신을 포함한 스코프 정보를 기억하고 있는 것이다.
정의했을 때
-> inner함수는 outer함수가 호출될 때마다 새롭게 정의된다.
def outer(x):
def inner():
print(x)
return inner
foo1 = outer(1)
foo2 = outer(2)
foo1()
foo2()
- foo1, foo2에 직접 값을 인수로써 넣지 않아도 각각의 내부의 inner함수가 어떤 값을 출력해야하나를 기억하고 있다.
-> 이것을 이용해서 고정 인수를 얻도록 커스터마이즈한 함수를 생성하는 것도 가능하다.
9. 데코레이터
- 데코레이터란 함수를 인수로 받아 명령을 추가한 뒤에 다시 함수의 형태로 반환하는 함수이다.
def outer(some_func):
def inner():
print("before some_func")
ret = some_func()
print(ret + 1)
return inner
def foo():
return 1
decorated = outer(foo)
decorated()
- 파라미터로 some_func를 받는 outer함수를 정의했다.
- outer안에 inner라는 내부 함수가 정의되어 있다.
- inner는 문자열을 print한 후에 반환하는 값을 받았다.
- some_func는 outer를 호출하는 것으로 다른 값을 얻을 수 있지만 여기서는 무엇을 받던 간에 그 결과에 1을 더한 값을 반환한다.
- outer함수는 inner함수의 것을 반환한다.
class Coordinate(object):
def __init__(self, x, y):
self.x = x
self.y = y
def __repr__(self):
return "Coord: " + str(self.__dict__)
def add(a, b):
return Coordinate(a.x + b.x, a.y + b.y)
def sub(a, b):
return Coordinate(a.x - b.x, a.y - b.y)
one = Coordinate(100, 200)
two = Coordinate(300, 200)
print(add(one, two))
one = Coordinate(100, 200)
two = Coordinate(300, 200)
three = Coordinate(-100, -100)
print(sub(one, two))
print(add(one, three))
- 취급할 좌표계가 0이하일때 체크 처리가 필요하다면 어떻게 할 수 있을까?
def wrapper(func):
def checker(a, b):
if a.x < 0 or a.y < 0:
a = Coordinate(a.x if a.x > 0 else 0, a.y if a.y > 0 else 0)
if b.x < 0 or b.y < 0:
b = Coordinate(b.x if b.x > 0 else 0, b.y if b.y > 0 else 0)
ret = func(a, b)
if ret.x < 0 or ret.y < 0:
ret = Coordinate(ret.x if ret.x > 0 else 0, ret.y if ret.y > 0 else 0)
return ret
return checker
add = wrapper(add)
sub = wrapper(sub)
one = Coordinate(100, 200)
two = Coordinate(300, 200)
three = Coordinate(-100, -100)
print(sub(one, two))
print(add(one, three))
10. @심볼의 적용
add = wrapper(add)
@wrapper
def add(a, b):
return Coordinate(a.x + b.x, a.y + b.y)
11. *
args, *
kwargs
- 위의 데코레이터 wrapper은 유용하지만 파라미터가 2개뿐인 함수에만 적용할 수 있다.
- 함수를 정의할 때는 파라미터에
*
를 붙이면 임의의 수의 필수 파라미터를 수용하도록 할 수 있다.
- 정의할 때뿐만 아니라 호출할 때에도 인수에
*
를 붙이면 기존 리스트나 튜플 형식의 인수를 언패키징해서 고정 인수에 적용해준다.
def one(*args):
print(args)
one()
def two(x, y, *args):
print(x, y, args)
two('a', 'b', 'c')
def add(x, y):
return x + y
lst = [1,2]
print(add(lst[0], lst[1]))
print(add(*lst))
**
에서는 사전형이 된다.
**kwargs
은 명시적으로 지정하지 않은 파라미터는 kwargs이라는 이름의 사전으로 저장된다는 의미이다.
*args
와 동일하게 함수를 호출할 때의 언패키징에도 대응한다.
dct = {'x':1, 'y':2}
def bar(x, y):
return x + y
print(bar(**dct))
12. 제네릭한 데코레이터
- 위의 기능을 이용하여 함수의 인수를 로그에 출력해주는 데코레이터를 작성해보자
def logger(func):
def inner(*args, **kwargs):
print('Arguments were: %s, %s' % (args, kwargs))
return func(*args, **kwargs)
return inner
@logger
def foo1(x, y=1):
return x*y
@logger
def foo2():
return 2
print(foo1(5, 4))
print(foo1(1))
print(foo2())