5. First-Class Functions
Function in python are first-class objects (runtime / element / argument / return)
1. Treating a Function Like an Object
def test(n): """ return n*2 """ return n * 2 print test(5) # 10 # '__doc__' is used to generate the help text of an object # 'help(test)' do like this in Python Interactive Console print test.__doc__ # ' return n*2 ' # Function object is an instance of the function class. print type(test) # <type 'function'> # assign it, call it, use it as an argument tmp = test print tmp(5) # 10 print list(map(tmp, range(5))) # [0, 2, 4, 6, 8]
2. Higher-Order Functions
- A function that takes a function as argument or returns a function
fruits = ['fig', 'apple', 'cherry'] print sorted(fruits, key=len) # ['fig', 'apple', 'cherry'] # Any one-argument function can be used as the key. print sorted(fruits, key=lambda word: word[::-1]) # ['apple', 'fig', 'cherry']
2.1 Modern Replacements for map, filter, and reduce
def test(n): return n * 2 print list(map(test, range(5))) # [0, 2, 4, 6, 8] print [test(n) for n in range(5)] # [0, 2, 4, 6, 8] print list(map(test, filter(lambda n: n % 2, range(5)))) # [2, 6] print [test(n) for n in range(5) if n % 2] # [2, 6] print reduce(lambda x,y: x+y, range(100)) # 4950 print sum(range(100)) # 4950
3. The Seven Flavors of Callable Objects
- User-defined functions: def or lambda.
- Built-in functions: like len or time.strftime.
- Built-in methods: like dict.get.
- Methods: functions in class.
- Classes: a class runs its __new__ method to create an instance, then __init__ to initialize it, and finally the instance is returned to the caller.
- Class instances: if a class defines a __call__ method, then its instances may be invoked as functions.
- Generator functions: functions or methods that use the yield keyword.
[notes]: To determine whether an object is callable, use the callable() built-in function.
4. User-Defined Callable Types
- Python objects may also be made to behave like functions
class Test: def __init__(self, items): self._items = list(items) def pick(self): return self._items.pop() def __call__(self): return self.pick() t = Test(range(5)) print t.pick() # 4 print t() # 3 print callable(t) # True
5. Keyword-Only Parameters
- Can only be given as a keyword argument.
- Python 3 only.
def tag(name, *content, cls=None, **attrs): # cls: Keyword-Only """ Generate one or more HTML tags """ if cls is not None: attrs['class'] = cls if attrs: attr_str = ''.join(' %s="%s"' % (attr, value) for attr, value in sorted(attrs.items())) else: attr_str = '' if content: return '\n'.join('<%s%s>%s</%s>' % (name, attr_str, c, name) for c in content) else: return '<%s%s />' % (name, attr_str) print(tag('br')) # <br /> print(tag('a', 'hello', 'world', href='#')) # <a href="#">hello</a>\n<a href="#">world</a> print(tag('div', 'im div~', cls='f-left')) # <div class="f-left">im div~</div> print(tag(**{'name': 'img', 'id': 'my_img'})) # <img id="my_img" /> def f(a, *, b): # b: Keyword-Only return a, b print(f(1, b=2)) # (1, 2)
6. Function Introspection
def test(n): return n * 2 test.name = 'double it!' # a function uses the __dict__ attribute to store user attributes assigned to it print test.__dict__ # {'name': 'double it!'}
- Attributes of functions that don’t exist in plain instances
7. Retrieving Information About Parameters
def test(a, b=2, c=3, **x): d = a * b * c return d print test.__defaults__ # (2, 3) # defaults for keyword-only arguments # print(test.__kwdefaults__) # {'e': 3} print test.__code__ # <code object test at 0x...> # does't include any variable arguments prefixed with * or ** print test.__code__.co_varnames # ('a', 'b', 'c', 'd') print test.__code__.co_argcount # 3
def test(a, b=2, c=3, **x): d = a * b * c return d from inspect import signature # python 3 tmp = signature(test) print(str(tmp)) # (a, b=2, c=3, **x) for name, param in tmp.parameters.items(): # 'kind' can also be: VAR_POSITIONAL / VAR_KEYWORD / KEYWORD_ONLY / POSITIONAL_ONLY print(param.kind) # POSITIONAL_OR_KEYWORD print(name) # a print(param.default) # <class 'inspect._empty'> bind_tmp = tmp.bind(**{'a': 11, 'b': 22}) for name, value in bind_tmp.arguments.items(): print(name, '=', value) # a = 11 \n b = 22
8. Function Annotations
- Just annotation, noting else!
- Python 3 only.
def test(a:str, b:'in>0'=1) -> str: return a print(test('qqq', 2)) # qqq print(test.__annotations__) # {'a': <class 'str'>, 'b': 'in>0', 'return': <class 'str'>} from inspect import signature # python 3 tmp = signature(test) for name, param in tmp.parameters.items(): print(param.annotation) # <class 'str'> print(param.name) # a print(param.default) # <class 'inspect._empty'>
9. Packages for Functional Programming
9.1 operator
from operator import mul print mul(4, 5) # 20 from operator import itemgetter # pick items from sequences test_data = [('b', 2), ('a', 1), ('c', 3)] for item in sorted(test_data, key=itemgetter(1)): # lambda i: i[1] print item # ('a', 1) func = itemgetter(1, 0) # lambda i: (i[1], i[0]) for item in test_data: print func(item) # (2, 'b') from operator import attrgetter # read attributes from objects class A: tmp = 1 class B: a = A() class C: b = B() tmp = 2 c = C() func = attrgetter('b.a.tmp', 'tmp') print func(c) # (1, 2) from operator import methodcaller s = 'The time has come' my_upper = methodcaller('upper') print my_upper(s) # s.upper() my_replace = methodcaller('replace', ' ', '-') print my_replace(s) # s.replace(' ', '-') #['abs', 'add', 'and_', 'attrgetter', 'concat', 'contains', 'countOf', # 'delitem', 'eq', 'floordiv', 'ge', 'getitem', 'gt', 'iadd', 'iand', # 'iconcat', 'ifloordiv', 'ilshift', 'imod', 'imul', 'index', 'indexOf', # 'inv', 'invert', 'ior', 'ipow', 'irshift', 'is_', 'is_not', 'isub', # 'itemgetter', 'itruediv', 'ixor', 'le', 'length_hint', 'lshift', # 'lt', 'methodcaller', 'mod', 'mul', 'ne', 'neg', 'not_', 'or_', 'pos', # 'pow', 'rshift', 'setitem', 'sub', 'truediv', 'truth', 'xor']
9.2 functools
def test(a, b, c): return a * b * c from functools import partial tmp = partial(test, 1, c=2) # can't 'b=2' because 'c' print list(map(tmp, range(5))) # [0, 2, 4, 6, 8] print test # <function test at 0x10dca0e60> print tmp.func # <function test at 0x10dca0e60> print tmp.args # (1,) print tmp.keywords # {'c': 2}