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 '
'.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>
<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 
 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}
原文地址:https://www.cnblogs.com/lb477/p/10902762.html