You are on page 1of 38

Functions in Python

arguments, lambdas, decorators, generators


Defining simple functions:
>>> def my_func():
... print "Inside my function"
...
>>> my_func()
Inside my function
Function with arguments - basics
>>> def func_with_args(a, b):
... print 'a: ', a
... print 'b: ', b
...
>>> func_with_args(5, 'c')
a: 5
b: c
Two types of arguments
● positional - arguments order matters
>>> func_with_args(5, 'c')
a: 5
b: c
● keyword - argument name matters
func_with_args(b=5, a='c')
a: c
b: 5
Mixing position and keyword
arguments
>>> def func_with_args(a, b, c):
... print a, b, c
...

>>> func_with_args(1, c=3, b=2)


123

>>> func_with_args(a=1, 3, b=2)


File "<stdin>", line 1
SyntaxError: non-keyword arg after keyword arg
Passing attributes - old style
>>> func_with_args(1, 2, 3) # standart call
123

>>> apply(func_with_args, [1, 2, 3]) # positional


123

>>> apply(func_with_args, [], {'a': 1, 'c': 3, 'b': 2}) # keywords


123

>>> apply(func_with_args, [1], {'c': 3, 'b': 2}) # mixed


123
Passing attributes - new style
>>> func_with_args(*[1, 2, 3]) # positional
123

>>> func_with_args(**{'b': 2, 'a': '1', 'c': 3}) # keywords


123

>>> func_with_args(*[1], **{'b': 2, 'c': 3}) # mixed


123

>>> func_with_args(*[1], **{'b': 2, 'a': '1', 'c': 3})


TypeError: func_with_args() got multiple values for keyword
argument 'a'
Function arguments revisited
>>> def f(*args, **kwargs):
... print args
... print kwargs

>>> f()
()
{}
>>> f(1,2,3)
(1, 2, 3)
{}
>>> f(a=1, c=3, b=2)
()
{'a': 1, 'c': 3, 'b': 2}
>>> f(1, b=2, c=3)
(1,)
{'c': 3, 'b': 2}
Default arguments
>>> def func(a=5):
... print a

>>> func()
5

>>> func(6)
6

>>> func()
5
Default arguments - part II
>>> def func(a=[]):
... print a

>>> func()
[]

>>> func(['a'])
['a']

>>> func()
[]
Default arguments - part III
>>> def func(a=[]):
... a = a * 5
... print a
>>> func()
[]
>>> func()
[]
>>> func([1])
[1, 1, 1, 1, 1]
>>> func()
[]
Default arguments - part IV - problem
>>> def func(a=[]):
... a.append(2)
... print a

>>> func()
[2]
>>> func()
[2, 2]
>>> func([1])
[1, 2]
>>> func()
[2, 2, 2]
Default arguments - part V - solution
>>> def func(a=None):
... a = a or []
... a.append(2)
... print a
...

>>> func()
[2]

>>> func()
[2]
Preloading arguments
>>> def func(a, b, c):
... print a, b, c

>>> import functools


>>> func_preloader = functools.partial(func, a=5, c=3)
>>> func_preloader()
TypeError: func() takes exactly 3 arguments (2 given)

>>> func_preloader(b=2)
523
Preloading arguments - mixing
>>> def func(a, b, c):
... print a, b, c

>>> import functools


>>> func_preloader = functools.partial(func, a=5, c=3)

>>> func_preloader(2)
TypeError: func() got multiple values for keyword argument 'a'
Python introspection

""" In computing, type introspection is the ability of a


program to examine the type or properties of an object
at runtime. """

Wikipedia - Type Introspection


Introspection basics
>>> def func(a, b, c):
... """ Just an example how introspection works """
... pass

>>> help(func)
Help on function func in module __main__:
func(a, b, c)
Just an example how introspection works
Methods related to introspection
● type(var) # show the type of the object
● dir(var) # lists object's methods and attributes
● id(var) # return object specific identificator
● getattr(obj, <attribute name>, <default value>)
● hasattr(obj, <attribute name>)
● globals()
● locals()
● callable(obj) # returns True if the object is callable
Lambda functions
>>> f = lambda x,y: x+y
>>> f(1,2)
3

>>> f = lambda x,y=[]: x+y


>>> f([1])
[1]

>>> f([1], [2])


[1, 2]
Lambdas and print
# Python 2.7.3
>>> a = lambda x: print x # print is a statement
File "<stdin>", line 1
a = lambda x: print x

Lambdas can not contain statements

# Python 3.2.3
>>> f = lambda x: print(x) # print is a function
>>> f(5)
5
Lambdas usage
>>> map(lambda x: x**2, range(5))
[0, 1, 4, 9, 16]

>>> [x**2 for x in range(5)]


[0, 1, 4, 9, 16]

>>> reduce(lambda a, x: a+x, range(5))


10

Avoid using lambdas and check Guido van Rossum's vision


for lambdas http://www.artima.com/weblogs/viewpost.jsp?thread=98196
Generators
yield x # Generator function send protocol
>>> def a():
... yield 5
... yield 6
... yield 7

>>> a()
<generator object a at 0x158caa0>

>>> a()
<generator object a at 0x158caa0>
Generators - range vs xrange
>>> range(10**10)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
MemoryError

>>> xrange(10**10)
xrange(10000000000)

# range has been replaced by xrange in Python 3.x


Creating generators
>>> def fib():
... a, b = 0, 1
... while True:
... yield a, b
... a, b = b, a+b
...
>>> g = fib()
>>> g
<generator object fib at 0x19b4be0>
Creating generators
>>> g.next()
(0, 1)
>>> g.next()
(1, 1)
>>> g.next()
(1, 2)
>>> g.next()
(2, 3)
Exhausting generators
>>> def cubes(n):
... for i in range(n):
... yield i**3
>>> c = cubes(3)
>>> c.next() # 0
>>> c.next() # 1
>>> c.next() # 8
>>> c.next()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
Generators and comprehension lists
>>> def cubes(n):
... for i in range(n):
... yield i**3
...
>>> [a for a in cubes(3)]
[0, 1, 8]
Generators and the send method
>>> def waiting_for_input():
... i = 0
... while True:
... i = yield i**3
...
>>> f = waiting_for_input()
>>> f.next() # 0
>>> f.send(8) # 512
>>> f.send(10) # 1000
>>> f.send(12) # 1728
Decorators
>>> def decorator(func):
... print 'Entering decorator'
... return func
...
>>> @decorator
... def f():
... print 'Executing F'
...
Entering decorator
>>> f()
Executing F
Decorators simplified
>>> def decorator(func):
... print 'Entering decorator'
... return func
...
>>> @decorator
... def f():
... print 'Executing F'

# Using the decorator above is the same as using:


>>> f = decorator(f)
Functions as Decorators
>>> def decorator(func):
... def wrap():
... print 'Calling function %s' % func.__name__
... func()
... print 'Exiting decorator'
... return wrap
...
>>> @decorator
... def f():
... print 'Executing F'
...
>>> f()
Calling function f
Executing F
Exiting decorator
Functions as decorators - part II
>>> def decorator(func):
... def wrap(a, b, c):
... print 'Function f called with %s, %s, %s' % (a, b, c)
... return func(a, b, c)
... return wrap
...
>>> @decorator
... def f(a, b, c):
... return a + b + c
...
>>> f(1, 2, 3)
Function f called with 1, 2, 3
6
Classes as decorators
>>> class decorator(object):
... def __init__(self, func):
... print 'Initializing decorator'
... self.func = func
... def __call__(self, *args, **kwargs):
... print 'Calling method'
... return self.func(*args, **kwargs)
>>> @decorator
... def f(a, b, c):
... return a + b + c
...
Initializing decorator
>>> f(1, 2, 3)
Calling method
6
Decorators with arguments
>>> def decorator(debug=False):
... def wrap(func):
... def f_wrap(a, b, c):
... if debug:
... print 'Function f called with %s, %s, %s' % (a,
b, c)
... return func(a, b, c)
... return f_wrap
... return wrap
Decorators with arguments
>>> @decorator(debug=True)
... def f(a, b, c):
... return a + b + c
...
>>> f(1, 2, 3)
Function f called with 1, 2, 3
6

>>> @decorator(debug=False)
... def f(a, b, c):
... return a + b + c
...
>>> f(1, 2, 3)
6
Q&A
Problems to solve
1) Create a function that accept both positional and
keyword arguments and returns their sum

2) Create a generator that implements a dice that "brokes"


after the 10th roll

3) Create a decorator that converts letters to number


according to their position in the alphabet and combine it
with the solution of problem #1. When converting letters to
numbers make them lowercase and count from 0 i.e. a==0,
b==1.
About Me
eng. Ilian Iliev

● Web Developer for 9+ years


● Python/Django fan for 3+ years
● ilian@i-n-i.org
● http://ilian.i-n-i.org
● https://github.com/IlianIliev/

You might also like