outline 
函数与函数式编程: 
一等函数 
把函数视为对象 
高阶函数 
列表推导 vs (map/reduce) 
函数的参数与返回值 
 
方法和装饰器: 
 
一等函数 
特点: 
可以运行的时候创建; 
可以作为变量,传给函数; 
可以作为函数的返回结果。 
 
1 2 3 4 5 6 7 def  factorial (n ):    """      return n!     """     return  1  if  n < 2  else  factorial(n - 1 ) * n print (factorial(5 ))
 
既然是对象,就有相应的属性,且可以赋值:
1 2 3 4 5 6 7 print (factorial.__doc__)print (type (factorial))fact = factorial print (fact)print (fact(5 ))
 
输出结果:
1 2 3 4 5 6     return  n!      <class  'function '> <function  factorial  at  0x00000184B923F288 > 120 
 
为了更好的理解函数作为对象的特征,可以看看下面这个例子:
1 2 3 4 5 6 7 8 9 def  make_averager ():    nums = []     def  averager (new_value ):         nums.append(new_value)         total = sum (nums)                  return  total / len (nums)     return  averager 
 
外层函数返回的是函数的对象,这样一来经过下面的测试可以得到预期结果:
1 2 3 avg = make_averager() print (avg)print (avg(3 ))
 
输出结果如下:
1 2 <function make_averager.<locals >.averager at 0x00000184B923FDC8 > 3.0 
 
高阶函数 
**特点:**接受函数作为参数或者函数作为返回结果。eg:map/reduce,sorted。
map/reduce/filter就是典型的高阶函数:
1 2 3 num_list = [1 , 2 , 3 , 4 , 5 , 7 , 9 ] print (list (map (factorial, num_list)))
 
输出:[1, 2, 6, 24, 120, 5040, 362880]
reduce和map的区别是,map只允许传入的函数只能接受1个参数,而reduce可以接受两个参数,如:
1 2 3 4 5 from  functools import  reducelyst = [2 , 3 , 4 , 5 , 6 ] print (list (map (lambda  x: x**2 , lyst)))print (reduce(lambda  x, y: x + y, lyst))
 
注意reduce返回的是一个值,而不是一个可迭代对象。 
对于常见的加减乘除,还可以使用这种方式来实现:
1 2 3 4 from  functools import  partialfrom  operator import  add, mulop_mul = partial(mul, 3 ) op_mul(9 ) 
 
这个op_mul就相当于一个乘法运算对象,而且其中有一个值固定为3,现在就传入另外一个值即可。从这里就可以看出来partial的作用:
对于一个函数,如果有几个参数是确定的,但是还有一部分不确定,可以将确定的参数传入,然后借助partial函数,将这个传入了几个确定参数之后的函数作为一个新的函数,接下来只用将剩余的参数传入这个新的函数即可。 
字典实现列表推导式:
1 2 3 4 usernames = ['sophia' , 'emma' , 'olivia' , 'ava' ] name_dict = {name: len (name) for  name in  usernames} print (name_dict)
 
输出结果如下:
{'sophia': 6, 'emma': 4, 'olivia': 6, 'ava': 3}
嵌套高阶函数:
1 2 3 4 5 6 7 print (list (map (factorial, filter (lambda  n: n % 2 , num_list))))result = [factorial(num) for  num in  num_list if  num % 2 ] print (result)
 
输出:
[1, 6, 120, 5040, 362880]
[1, 6, 120, 5040, 362880]
python单下划线和双下划的区别 
python下划线有5种:
模式 
样例 
含义 
 
 
单前导下划线 
_xxx 
约定俗成,仅供类内使用,外部不调用,但是强制调用还是可以的 
 
双前导下划线 
__xxx 
强制不可外部调用,如果是函数,那么函数所在的类的子类无法轻易重写该函数 
 
单末尾下划线 
xxx_ 
防止和python内置关键字冲突 
 
双前导和末尾下划线 
__xxx__ 
python语义的特殊方法,尽量避免自己使用 
 
单下划线 
_ 
较多的用法是,该变量在下面的作用域内不会使用到 
 
 
下面就这几种模式来举一下例子:
单前导下划线: 
1 2 3 4 5 6 7 class  A ():    def  __init__ (self ):         self._name = "julia"          pass  a = A() print (a._name)
 
输出:julia
双前导下划线: 
1 2 3 4 5 6 7 class  A ():    def  __init__ (self ):         self.__name = "julia"          pass  a = A() print (a.__name)
 
报错:
1 2 3 4 5 6 7 8 --------------------------------------------------------------------------- AttributeError                            Traceback (most recent call last) <ipython-input -24 -a75c7bdbf94f> in  <module>       4          pass        5  a = A() ----> 6  print (a.__name) AttributeError: 'A'  object  has no attribute '__name'  
 
如果是带双前导下划线的函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 class  A ():    def  __init__ (self ):         self.__name = "julia"           def  __getname (self ):         return  self.__name class  B (A ):    def  __init__ (self ):         pass           def  __getname (self ):         pass  print (dir (A))print (dir (B))
 
运行结果:
1 2 ['_A__getname' , '__class__' , '__delattr__' , '__dict__' , '__dir__' , '__doc__' , '__eq__' , '__format__' , '__ge__' , '__getattribute__' , '__gt__' , '__hash__' , '__init__' , '__init_subclass__' , '__le__' , '__lt__' , '__module__' , '__ne__' , '__new__' , '__reduce__' , '__reduce_ex__' , '__repr__' , '__setattr__' , '__sizeof__' , '__str__' , '__subclasshook__' , '__weakref__' ] ['_A__getname' , '_B__getname' , '__class__' , '__delattr__' , '__dict__' , '__dir__' , '__doc__' , '__eq__' , '__format__' , '__ge__' , '__getattribute__' , '__gt__' , '__hash__' , '__init__' , '__init_subclass__' , '__le__' , '__lt__' , '__module__' , '__ne__' , '__new__' , '__reduce__' , '__reduce_ex__' , '__repr__' , '__setattr__' , '__sizeof__' , '__str__' , '__subclasshook__' , '__weakref__' ] 
 
这里的__getname函数是带前导双下划线的函数,第一个输出的时候,发现__getname的名称前面添加了_A,B类也有一个自己的函数_B__getname,在python中这种用法是为了避免和子类定义的函数名称冲突。
单末尾下划线: 
这种用法是为了避免定义的变量和python的关键字冲突,比如class,这个时候可以使用class_来代替。
单下划线: 
1 2 for  _ in  range (10 ):    print ("000" ) 
 
比如这种情况就没有使用到循环变量,可以使用_来代替。
参考:https://blog.csdn.net/sinat_38682860/article/details/96902828 
函数参数的特殊情况 
下面需要实现一个html标签,如<p>hello</p>,<item class='' size='small'>a</item>。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 def  make_element (name, *contents, cls=None , **kwargs ):    if  cls:         kwargs['class' ] = cls          pairs = [f"{attr} ={value} "  for  attr, value in  kwargs.items()]     attr_str = ' ' .join(pairs)          if  not  contents:         return  f"<{name}  {attr_str} />"           elements = [f"<{name}  {attr_str} >{content} <{name} />"  for  content in  contents]          return  '\n' .join(elements) print (make_element("p" , "hello" , "world" , cls="sidebar" , size="small" ))
 
输出:
1 2 <p size=small class =sidebar >hello <p /> <p  size =small  class =sidebar >world <p /> 
 
因为class是python中的关键字,传入函数的话会冲突,所以将cls特意拿出来接受class参数。现在想设计一个专门生成图片标签的函数,可以利用前面说到的partial函数,代码如下:
1 2 3 4 5 6 7 from  functools import  partialimage_make = partial(make_element, "img" , cls="pic-frame" ) print (image_make(src="car.png" ))item_make = partial(make_element, "item" , size="small" ) print (item_make("A" , "B" , "C" ))
 
输出:
<img src=car.png class=pic-frame/> 
<item size=small>A<item/> 
<item size=small>B<item/> 
<item size=small>C<item/>
这种涉及到的特殊参数是*contents和**kwargs,传入函数之后,contents是一个元组,kwargs是一个字典,如果需要对元组和字典进行解包,可以使用如下的方式:
1 2 3 4 5 6 7 8 a = (2 , 3 , 4 , 5 ) print (*a)dict_a = {"a" : 1 , "b" : 2 } dict_b = {"c" : 3 , "d" : 4 } new_dict = {**dict_a, **dict_b} print (new_dict)
 
输出:
2 3 4 5 
{'a': 1, 'b': 2, 'c': 3, 'd': 4}
装饰器 
装饰器的本质:在不改变函数签名、返回值的情况下,增加额外的功能。
带运行时间的装饰器 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 import  timedef  build_func_with_time_record (func ):         def  wrapper (*args, **kwargs ):         start = time.time()         result = func(*args, **kwargs)         end = time.time()         print (f"function {func.__name__}  cost {end - start} " )     return  wrapper def  func_foo (a, b ):    time.sleep(2 )     print (a + b) new_func = build_func_with_time_record(func_foo) new_func(1 , 3 ) 
 
这段代码实现的就是在func_foo函数的基础上,再“装饰”一下,实现装饰功能的函数是build_func_with_time_record。
python提供了@语法糖来简化上述的“装饰”过程,使用@关键字可以将上述代码简化为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 import  timedef  build_func_with_time_record (func ):         def  wrapper (*args, **kwargs ):         start = time.time()         result = func(*args, **kwargs)         end = time.time()         print (f"function {func.__name__}  cost {end - start} " )         return  result          return  wrapper @build_func_with_time_record def  func_foo (a, b ):    time.sleep(2 )     print (a + b) func_foo(1 , 3 ) 
 
通过这种方式可以省去显示的将函数传入、赋值等操作。注意,上述的函数有参数a,b。参数不是通过build_func_with_time_record传入的,而是通过里面的wrapper函数传入的。
这样,可以将这个装饰器加在上述的make_element函数上:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 @build_func_with_time_record def  make_element (name, *contents, cls=None , **kwargs ):    """make HTML element      Args:         name (label name): such as p         cls (str, optional): make a distinction with class. Defaults to None.     Returns:         str: the results     """     if  cls:         kwargs['class' ] = cls          pairs = [f"{attr} ={value} "  for  attr, value in  kwargs.items()]     attr_str = ' ' .join(pairs)          if  not  contents:         return  f"<{name}  {attr_str} />"           elements = [f"<{name}  {attr_str} >{content} <{name} />"  for  content in  contents]          return  '\n' .join(elements) print (make_element("p" , "hello" , "world" , cls="sidebar" , size="small" ))
 
输出如下:
1 2 3 function make_element cost 0.0  <p size=small class =sidebar >hello <p /> <p  size =small  class =sidebar >world <p /> 
 
但是,此时我们再输出函数属性:
1 2 3 4 5 print (make_element.__doc__)print (make_element.__name__)from  inspect import  signaturesignature(make_element) 
 
1 2 3 None wrapper <Signature (*args, **kwargs)> 
 
发现函数的属性 和函数签名 都发生了变化,我们回到不使用@关键字实现装饰器功能的代码上,发现build_func_with_time_record函数返回值就是wrapper函数对象,所以最后我们得到的不再是一开始的make_element函数了,那么有什么方法能解决这个问题呢?
需要将一开始的build_func_with_time_record做一下修改:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 import  timefrom  functools import  wrapsdef  build_func_with_time_record (func ):         """      wraps在装饰器函数返回之前     通过获取func的属性     并存储在返回的最终函数中设置上述属性。     """     @wraps(func )     def  wrapper (*args, **kwargs ):         start = time.time()         result = func(*args, **kwargs)         end = time.time()         print (f"function {func.__name__}  cost {end - start} " )         return  result          return  wrapper 
 
然后重新尝试一下:
1 2 3 4 5 print (make_element.__doc__)print (make_element.__name__)from  inspect import  signaturesignature(make_element) 
 
1 2 3 4 5 6 7 8 9 10 11 make HTML element     Args:         name (label name): such as  p         cls (str , optional): make a distinction with  class . Defaults to None .     Returns:         str : the results      make_element <Signature (name, *contents, cls=None , **kwargs)>