def
return
In [ ]:
def foo():
return 1
foo()
Q. What happens if there is no return
?
locals()
and globals()
to see what happens:
In [ ]:
aString = 'Global var'
def foo():
a = 'Local var'
print locals()
foo()
print globals()
Variables within functions exist only withing their namespaces. Once the function stops, all the variables inside it gets destroyed. For instance, the following won't work.
In [ ]:
def foo():
x = 10
foo()
print x
In [ ]:
aString = 'Global var'
def foo():
print aString
foo()
In [ ]:
aString = 'Global var'
def foo():
aString = 'Local var'
print aString
foo()
Q. What would be the value of aString now? For instance, if I did this:
In [ ]:
global
. Like so:
In [ ]:
aString = 'Global var'
def foo():
global aString # <------ Declared here
aString = 'Local var'
print aString
def bar():
print aString
foo()
bar()
In [ ]:
def foo(x):
print locals()
foo(1)
In [ ]:
"Args"
def foo(x,y):
print x+y
"kwargs"
def bar(x=5, y=8):
print x-y
"Both"
def foobar(x,y=100):
print x*y
"Calling with args"
foo(5,12)
"Calling with kwargs"
bar()
"Calling both"
foobar(10)
In [ ]:
"Args"
def foo(x,y):
print x+y
"kwargs"
def bar(x=5, y=8):
print x-y
"Both"
def foobar(x,y=100):
print x*y
"kwargs"
bar(5,8) # kwargs as args (default: x=5, y=8)
bar(5,y=8) # x=5, y=8
"Change the order of kwargs if you want"
bar(y=8, x=5)
"args as kwargs will also work"
foo(x=5, y=12)
Q. will these two work?
In [ ]:
"Args"
def foo(x,y):
print x+y
"kwargs"
def bar(x=5, y=8):
print x-y
"Both"
def foobar(x,y=100):
print x*y
bar(x=9, 7) #1
foo(x=5, 6) #2
In [ ]:
def outer():
x=1
def inner():
print x
inner()
outer()
In [ ]:
def outer():
x = 1
def inner():
x = 2
print 'Inner x=%d'%(x)
inner()
return x
print 'Outer x=%d'%outer()
What about global variables?
In [ ]:
x = 4
def outer():
global x
x = 1
def inner():
global x
x = 2
print 'Inner x=%d'%(x)
inner()
return x
print 'Outer x=%d'%outer()
print 'Global x=%d'%x
class
keyword
In [ ]:
class foo():
def __init__(i, arg1): # self can br replaced by anything.
i.arg1 = arg1
def bar(i, arg2): # Always use self as the first argument
print i.arg1, arg2
FOO = foo(7)
FOO.bar(5)
print FOO.arg1
Lets try:
In [ ]:
class foo():
def __init__(i, num):
i.num = num
d = foo(2)
d()
__call__
raises an exception. Python lets you redefine it:
In [ ]:
class foo():
def __init__(i, num):
i.num = num
def __call__(i):
return i.num
d = foo(2)
d()
Would this work?
In [ ]:
class foo():
def __init__(i, num):
i.num = num
FOO = foo(5)
FOO += 1
Let's rewrite this:
In [ ]:
class foo():
def __init__(i, num):
i.num = num
def __add__(i, new):
i.num += new
return i
def __sub__(i, new):
i.num -= new
return i
FOO = foo(5)
FOO += 1
print FOO.num
FOO -= 4
print FOO.num
__repr__
, __call__
,__getitem__
,... are all awesome.
In [ ]:
class foo():
"Me is foo"
def __init__(i, num):
i.num = num
def __add__(i, new):
i.num += new
return i
def __sub__(i, new):
i.num -= new
return i
def __repr__(i):
return i.__doc__
def __getitem__(i, num):
print "Nothing @ %d"%(num)
FOO = foo(4)
FOO[2]
In [ ]:
issubclass(int, object)
a
here is a class.
In [ ]:
a = 9
dir(a)
In [ ]:
from pdb import set_trace # pdb is quite useful
def add(x,y): return x+y
def sub(x,y): return x-y
def foo(x,y,func=add):
set_trace()
return func(x,y)
foo(7,4,sub)
Remember this example?
In [ ]:
def foo():
x=1
foo()
print x
Obviously, this fails. Why? As per variable lifetime rules (see 2.1), foo()
has ceased execution, x
is destroyed.
So how about this?
In [ ]:
def foo():
x='Outer String'
def bar():
print x
return bar
test = foo()
test()
This works. But it shouldn't, because x
is local to foo()
, when foo()
has ceased execution, x
must be destroyed. Right?
Turns out, Python supports a feature called function closure. This enables nested inner functions to keep track of their namespaces.
lambda
keyword.
In [ ]:
def foo(x,y): return x**y
bar = lambda x,y: x**y # <--- Notice no return statements
print foo(4,2)
print bar(4,2)
lambda
is permitted (idk why you'd use them, still, worth a mention)
In [ ]:
foo = lambda x: lambda y: x+y
print foo(3)(5)
key
argument, key
is a lambda function that deterimes how the data is sorted.
In [ ]:
student_tuples = [ #(Name, height(cms), weight(kg))
('john', 180, 85),
('doe', 177, 99),
('jane', 169, 69),
]
# Sort based on height
print 'Weight: ', sorted(student_tuples, key=lambda stud: stud[1])
# Sort based on Name
print 'Name: ', sorted(student_tuples, key=lambda stud: stud[0])
# Sort based on BMI
print 'BMI: ', sorted(student_tuples, key=lambda stud: stud[2]*100/stud[1])
In [ ]:
def outer(func):
def inner(*args):
"Inner"
print 'Decorating...'
ret = func()
ret += 1
return ret
return inner
def foo():
"I'm foo"
return 1
print foo()
decorated_foo = outer(foo)
print decorated_foo()
In [ ]:
def outer(func):
def inner(*args):
"Inner"
print 'Decorating...'
ret = func()
ret += 1
return ret
print inner.__doc__, inner
return inner
def foo():
"I'm foo"
return 1
print foo.__name__, foo
decorated_foo = outer(foo)
print decorated_foo.__name__, decorated_foo
In [ ]:
def outer(func):
def inner():
"Inner"
print 'Decorating...'
ret = func()
ret += 1
return ret
return inner
def foo():
"I'm foo"
return 1
print foo()
foo = outer(foo)
print foo()
@
to represent foo = outer(foo)
. The above code can be retwritten as follows:
In [ ]:
def outer(func):
def inner():
"Inner"
print 'Decorating...'
ret = func()
ret += 1
return ret
return inner
@outer
def foo():
"I'm foo"
return 1
print foo()
In [43]:
import time
from pdb import set_trace
def logger(func):
def inner(*args, **kwargs):
print "Arguments were: %s, %s"%(args, kwargs)
return func(*args, **kwargs)
return inner
def timer(func):
def inner(*args, **kwargs):
t=time.clock()
set_trace()
func(*args, **kwargs)
print "Time taken: %0.2f sec"%(time.clock()-t)
return func(*args, **kwargs)
return inner
@logger
@timer
def foo(a=5, b=2):
time.sleep(2)
return a+b
@logger
@timer
def bar(a=10, b=1):
time.sleep(1)
return a-b
foo(2,3)
bar(5,7)