def foo():
return 1
foo()
1
a_string = 'This is a global variable'
def foo():
print locals()
# print globals()
foo()
{}
a_string = 'This is a global variable'
def foo():
print a_string
foo()
This is a global variable
def foo():
a_string = 'test'
print locals()
foo()
{'a_string': 'test'}
a_string
'This is a global variable'
def foo():
x = 1
foo()
print x
--------------------------------------------------------------------------- NameError Traceback (most recent call last) <ipython-input-15-2d264e11d975> in <module>() ----> 1 print x NameError: name 'x' is not defined
def foo(x):
print locals()
foo(1)
{'x': 1}
def foo(x, y=0): # 1
return x - y
# 위치에 따른 인자 넘김
foo(3, 1) # 2
2
# x의 인자만 넘김. y는 default값
foo(3) # 3
3
# 최소한 x의 인자는 넘겨야 되는데 없어서 Error
foo() # 4
--------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-21-2af411ea9e65> in <module>() 1 # 최소한 x의 인자는 넘겨야 되는데 없어서 Error ----> 2 foo() # 4 TypeError: foo() takes at least 1 argument (0 given)
# keyword 형태로 인자 넘김.
foo(y=1, x=3) # 5
2
# keyword 뒤에 keyword 없는걸 넘기면 안됨.
# 즉, 내 의도는 y=1, x=2 라고 생각하고 넘겼지만
# 인터프리터는 그렇게 생각하지 않는다는 이야기
foo(y=1, 2)
File "<ipython-input-23-9aeb27cfd8e9>", line 4 foo(y=1, 2) SyntaxError: non-keyword arg after keyword arg
def outer():
x = 1
def inner():
print x # 1
inner() # 2
outer()
1
def outer():
x = 1
def inner():
print x # 1
print locals()
print inner
inner() # 2
# locals에 x와 inner 가 같이 있네. inner도 함수를 담고 있는 변수로 인식
outer()
{'x': 1, 'inner': <function inner at 0x107f4d0c8>} <function inner at 0x107f4d0c8> 1
# all objects in Python inherit from a common baseclass
issubclass(int, object)
True
def foo():
pass
foo.__class__
function
issubclass(foo.__class__, object)
True
def add(x, y):
return x + y
def sub(x, y):
return x - y
# 아 여기에서 apply 함수가 나오네
# 요즘 파이썬 라이브러리를 활용한 데이터 분석 하는데 apply 함수가 자주 등장하던데..
# 내가 설렁 배워서 그랬구나..ㅠㅠ
def apply(func, x, y): # 1
return func(x, y) # 2
# 함수 이름은 다른 변수들처럼 변수의 이름일 뿐
apply(add, 2, 1) # 3
3
apply(sub, 2, 1)
1
def outer():
def inner():
print 'Inside inner'
return inner # 1
foo = outer() # 2
foo # doctest:+ELLIPSIS
<function __main__.inner>
foo()
Inside inner
def outer():
x = 1
def inner():
print x # 1
print locals()
return inner
foo = outer()
{'x': 1, 'inner': <function inner at 0x107f4d398>}
# inner만 리턴되었기 때문에 print locals는 실행 안됨
# x는 outer의 로컬변수이고 inner 함수만 리턴됐는데 어떻게 x가 살아남을 수가 있지?
# function closures 라는 기능 때문에 에러 발생 안함
foo()
1
foo.func_closure # doctest: + ELLIPSIS
(<cell at 0x107f1e948: int object at 0x100310e58>,)
def outer(x):
def inner():
print x # 1
return inner
print1 = outer(1)
print2 = outer(2)
print1()
1
print2()
2
이 예제를 보면 closure - 자신을 감싸고 있는 scope를 기억하는 함수 -를 이용해, 인자를 hard-coding한 함수들을 만들 수 있다는 것을 알 수 있다.
숫자 1, 2를 inner 함수에게 전달하는 것이 아니라, 1, 2를 기억하고 있는 함수를 몇 개 만든 것이다.
이것은 굉장히 강력한 기술이다. - 객체 지향 프로그래밍을 떠올릴 수도 있다.
outer는 멤버 변수 x를 가진 inner 함수의 생성자라고 볼 수 있다. 이용할 수 있는 영역은 많다.
sorted 함수의 key 파라미터를 본 적이 있다면, 아마도 리스트의 첫번째 원소가 아닌 두번째 원소로 리스트의 리스트를 정렬하는 lambda 함수를 작성해 본 적이 있을 것이다. 이제 사용할 index를 받아서 key parameter에 사용할 함수를 리턴하는 itemgetter 함수를 만들 수도 있다.
하지만 지루한 closure는 이제 그만 보도록 하자! decorator로 넘어가자!
def outer(some_func):
def inner():
print 'before some_func'
ret = some_func() # 1
return ret + 1
return inner
def foo():
return 1
decorated = outer(foo) # 2
decorated()
before some_func
2
foo = outer(foo)
foo
<function __main__.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)
add(one, two)
Coord: {'y': 400, 'x': 400}
one = Coordinate(100, 200)
two = Coordinate(300, 200)
three = Coordinate(-100, -100)
sub(one, two)
Coord: {'y': 0, 'x': -200}
add(one, three)
Coord: {'y': 100, 'x': 0}
def wrapper(func):
def checker(a, b): # 1
# 입력값이 0보다 작다면 모두 0으로 설정
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)
# return 값에서 x나 y가 하나라도 0보다 작다면
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)
# checker 함수겠지
add
<function __main__.checker>
sub = wrapper(sub)
# checker 함수가 a, b를 기억하는 것은 closures 때문
sub(one, two)
Coord: {'y': 0, 'x': 0}
add(one, three)
Coord: {'y': 200, 'x': 100}
# decorator로 변형한 함수를 변수에 저장하여 사용.. 귀찮
add = wrapper(add)
@wrapper
def add(a, b):
return Coordinate(a.x + b.x, a.y + b.y)
def one(*args):
print args # 1
one()
()
one(1, 2, 3)
(1, 2, 3)
def two(x, y, *args): # 2
print x, y, args
two('a', 'b', 'c')
a b ('c',)
two('a', 'b', 'c', 'd')
a b ('c', 'd')
첫번째 함수 one은 위치기반 인자들을 받아서 출력
arg 라는 변수를 사용. *args는 함수 정의에서만 사용되고 위치기반 인자들이 args 변수에 저장되어야 한다는 뜻
#2에서 보는 것처럼 몇 개의 변수를 받은 후, 그 외의 변수들을 args로 받는 것도 가능하다.
* 오퍼레이터는 함수를 호출할 때 사용할 수도 있다. 이 때에도 비슷한 뜻으로 쓰인다. 함수를 호출할때 #가 앞에 붙은 변수는, 이 변수의 내용이 위치기반 인자로 사용되어야 한다는 뜻
def add(x, y):
return x + y
lst = [1, 2]
add(lst[0], lst[1]) # 1
3
add(*lst)
3
def foo(**kwargs):
print kwargs
foo()
{}
foo(x=1, y=2)
{'y': 2, 'x': 1}
dct = {'x': 1, 'y':2}
def bar(x, y):
return x + y
bar(**dct)
3
bar(*lst)
3
dct2 = {'a':2, 'b':3}
# bar 함수에 'a'라는 keyword 인자가 없음
bar(**dct2)
--------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-93-52ef253a1c86> in <module>() 1 # bar 함수에 'a'라는 keyword 인자가 없음 ----> 2 bar(**dct2) TypeError: bar() got an unexpected keyword argument 'a'
bar(x=2, y=3)
5
# 이거랑 같은 거다.
# 당연히 bar 함수에는 a keyword 인자가 없음.
bar(a=2, b=3)
--------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-95-4a9b57f2bd8d> in <module>() 1 # 이거랑 같은 거다. 2 # 당연히 bar 함수에는 a keyword 인자가 없음. ----> 3 bar(a=2, b=3) TypeError: bar() got an unexpected keyword argument 'a'
def logger(func):
def inner(*args, **kwargs): #1
print 'Arguments were: %s, %s' % (args, kwargs)
return func(*args, **kwargs) #2
return inner
@logger
def foo1(x, y=1):
return x * y
@logger
def foo2():
return 2
foo1(5, 4)
Arguments were: (5, 4), {}
20
foo1(1)
Arguments were: (1,), {}
1
foo2()
Arguments were: (), {}
2