Skip to content

Latest commit

 

History

History
277 lines (219 loc) · 6.74 KB

Functions.md

File metadata and controls

277 lines (219 loc) · 6.74 KB

Function

A function can help programmers to wrap their logic into a task for avoiding duplicate code. In Python, the definition of a function is so versatile that we can use many features such as decorator, annotation, docstrings, default arguments and so on to define a function. In this cheat sheet, it collects many ways to define a function and demystifies some enigmatic syntax in functions.

.. contents:: Table of Contents :backlinks: none

Functions

Documentation provides programmers hints about how a function is supposed to be used. A docstring gives an expedient way to write a readable document of functions. PEP 257 <https://www.python.org/dev/peps/pep-0257>_ defines some conventions of docstrings. In order to avoid violating conventions, there are several tools such as doctest <https://docs.python.org/3/library/doctest.html>, or pydocstyle <https://github.com/PyCQA/pydocstyle> can help us check the format of docstrings.

.. code-block:: python

>>> def example():
...   """This is an example function."""
...   print("Example function")
...
>>> example.__doc__
'This is an example function.'
>>> help(example)

Option Arguments

.. code-block:: python

>>> def example(a, b=None, *args, **kwargs):
...     print(a, b)
...     print(args)
...     print(kwargs)
...
>>> example(1, "var", 2, 3, word="hello")
1 var
(2, 3)
{'word': 'hello'}

Unpack Arguments

.. code-block:: python

>>> def foo(a, b, c='BAZ'):
...     print(a, b, c)
...
>>> foo(*("FOO", "BAR"), **{"c": "baz"})
FOO BAR baz

Keyword-Only Arguments

New in Python 3.0

.. code-block:: python

>>> def f(a, b, *, kw):
...     print(a, b, kw)
...
>>> f(1, 2, kw=3)
1 2 3
>>> f(1, 2, 3)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: f() takes 2 positional arguments but 3 were 

Annotations

New in Python 3.0

Annotations can be a useful way to give programmers hints about types of arguments. The specification of this feature is on PEP 3107 <https://www.python.org/dev/peps/pep-3107/>. Python 3.5 introduced typing module to extend the concept of type hints. Moreover, from version 3.6, Python started to offer a general way to define a variable with an annotation. Further information can be found on PEP 483 <https://www.python.org/dev/peps/pep-0483>, PEP 484 <https://www.python.org/dev/peps/pep-0484>, and PEP 526 <https://www.python.org/dev/peps/pep-0526>.

.. code-block:: python

>>> def fib(n: int) -> int:
...     a, b = 0, 1
...     for _ in range(n):
...         b, a = a + b, b
...     return a
...
>>> fib(10)
55
>>> fib.__annotations__
{'n': <class 'int'>, 'return': <class 'int'>}

Callable

In some cases such as passing a callback function, we need to check whether an object is callable or not. The built-in function, callable, assist us to avoid raising a TypeError if the object is not callable.

.. code-block:: python

>>> a = 10
>>> def fun():
...   print("I am callable")
...
>>> callable(a)
False
>>> callable(fun)
True

Get Function Name

.. code-block:: python

>>> def example_function():
...   pass
...
>>> example_function.__name__
'example_function'

Lambda

Sometimes, we don't want to use the def statement to define a short callback function. We can use a lambda expression as a shortcut to define an anonymous or an inline function instead. However, only one single expression can be specified in lambda. That is, no other features such as multi-line statements, conditions, or exception handling can be contained.

.. code-block:: python

>>> fn = lambda x: x**2
>>> fn(3)
9
>>> (lambda x: x**2)(3)
9
>>> (lambda x: [x*_ for _ in range(5)])(2)
[0, 2, 4, 6, 8]
>>> (lambda x: x if x>3 else 3)(5)
5

Generator

.. code-block:: python

>>> def fib(n):
...     a, b = 0, 1
...     for _ in range(n):
...         yield a
...         b, a = a + b, b
...
>>> [f for f in fib(10)]
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34]

Decorator

New in Python 2.4

  • PEP 318 <https://www.python.org/dev/peps/pep-0318/>_ - Decorators for Functions and Methods

.. code-block:: python

>>> from functools import wraps
>>> def decorator(func):
...     @wraps(func)
...     def wrapper(*args, **kwargs):
...         print("Before calling {}.".format(func.__name__))
...         ret = func(*args, **kwargs)
...         print("After calling {}.".format(func.__name__))
...         return ret
...     return wrapper
...
>>> @decorator
... def example():
...     print("Inside example function.")
...
>>> example()
Before calling example.
Inside example function.
After calling example.

Equals to

.. code-block:: python

... def example():
...     print("Inside example function.")
...
>>> example = decorator(example)
>>> example()
Before calling example.
Inside example function.
After calling example.

Decorator with Arguments

.. code-block:: python

>>> from functools import wraps
>>> def decorator_with_argument(val):
...     def decorator(func):
...         @wraps(func)
...         def wrapper(*args, **kwargs):
...             print("Val is {0}".format(val))
...             return func(*args, **kwargs)
...         return wrapper
...     return decorator
...
>>> @decorator_with_argument(10)
... def example():
...     print("This is example function.")
...
>>> example()
Val is 10
This is example function.

Equals to

.. code-block:: python

>>> def example():
...     print("This is example function.")
...
>>> example = decorator_with_argument(10)(example)
>>> example()
Val is 10
This is example function.

Cache

New in Python 3.2

Without Cache

.. code-block:: python

>>> import time
>>> def fib(n):
...     if n < 2:
...         return n
...     return fib(n - 1) + fib(n - 2)
...
>>> s = time.time(); _ = fib(32); e = time.time(); e - s
1.1562161445617676

With Cache (dynamic programming)

.. code-block:: python

>>> from functools import lru_cache
>>> @lru_cache(maxsize=None)
... def fib(n):
...     if n < 2:
...         return n
...     return fib(n - 1) + fib(n - 2)
...
>>> s = time.time(); _ = fib(32); e = time.time(); e - s
2.9087066650390625e-05
>>> fib.cache_info()
CacheInfo(hits=30, misses=33, maxsize=None, currsize=33)