python随用随学20200118-函数的高级特性

2020-02-26

高阶函数

话说当年C语言和Java里好像都有这么个东西...忘了

一句话说就是函数名本身就是一个引用. 可以作为变量传递.

一个简单的例子:

  1. def power_demo(x): 
  2. return x*x 
  3.  
  4. def foo(num1,num2,fun): 
  5. return fun(num1) + fun(num2) 
  6.  
  7. print(foo(2,3,power_demo)) 
  8.  

map/reduce的用法

map()方法

map(function, iterable, ...)

Return an iterator that applies function to every item of iterable, yielding the results. If additional iterable arguments are passed, function must take that many arguments and is applied to the items from all iterables in parallel. With multiple iterables, the iterator stops when the shortest iterable is exhausted. For cases where the function inputs are already arranged into argument tuples, see itertools.starmap().

解读:
入参是一个函数和一个可迭代对象. 函数依次作用于可迭代对象上.
返回值是一个迭代器

  1. def f(x): 
  2. return x*x 
  3. L = map(f,[1,2,3,4,5,6]) 
  4. print(type(L)) 
  5. print(list(L)) 
  6.  

reduce()方法

functools.reduce(function, iterable[, initializer])
Apply function of two arguments cumulatively to the items of sequence, from left to right, so as to reduce the sequence to a single value.
For example, reduce(lambda x, y: x+y, [1, 2, 3, 4, 5]) calculates ((((1+2)+3)+4)+5).
The left argument, x, is the accumulated value and the right argument, y, is the update value from the sequence. If the optional initializer is present, it is placed before the items of the sequence in the calculation, and serves as a default when the sequence is empty. If initializer is not given and sequence contains only one item, the first item is returned.

文档中给出的大概的实现

  1. def reduce(function, iterable, initializer=None): 
  2. it = iter(iterable) 
  3. if initializer is None: 
  4. value = next(it) 
  5. else: 
  6. value = initializer 
  7. for element in it: 
  8. value = function(value, element) 
  9. return value 
  10.  

一个例子:

  1. from functools import reduce 
  2.  
  3. def foo(x,y): 
  4. return x*10+y 
  5. result = reduce(foo,[1,3,5,7,9]) 
  6.  
  7. print(type(result),result) 
  8.  

filter()方法

filter(function, iterable)
Construct an iterator from those elements of iterable for which function returns true. iterable may be either a sequence, a container which supports iteration, or an iterator. If function is None, the identity function is assumed, that is, all elements of iterable that are false are removed.

Note that filter(function, iterable) is equivalent to the generator expression (item for item in iterable if function(item)) if function is not None and (item for item in iterable if item) if function is None.

先来个简单的例子,筛选奇数

  1. def is_odd(x): 
  2. return x%2 ==1 
  3.  
  4. print(list(filter(is_odd,[1,2,3,4,5,6,7,8,9]))) 
  5.  

举个例子,比如使用传说中的埃拉托色尼筛选法筛选出所有素数

  1. def odd_iter(): 
  2. n = 1 
  3. while True: 
  4. n = n + 2 
  5. yield n 
  6.  
  7. def not_divisible(n): 
  8. return lambda x: x % n > 0 
  9.  
  10. def primes(): 
  11. yield 2 
  12. it = odd_iter() 
  13. while True: 
  14. n = next(it) 
  15. yield n 
  16. it = filter(not_divisible(n), it) 
  17.  

sorted()函数

自定义排序的一个函数. 举个简单的例子吧,感觉一是半会儿用不上.

  1.  
  2. sorted([36, 5, -12, 9, -21], key=abs,reverse=True) 
  3.  

python中的闭包

变量的作用域
不写了,回头发文章补齐

嵌套函数
函数里套函数,发文章的时候补齐

什么是闭包

看维基百科的学术定义

在计算机科学中,闭包(Closure)是词法闭包(Lexical Closure)的简称,是引用了自由变量的函数。这个被引用的自由变量将和这个函数一同存在,即使已经离开了创造它的环境也不例外。所以,有另一种说法认为闭包是由函数和与其相关的引用环境组合而成的实体。

说啥呢看不懂...

举个例子:

  1. def print_msg(): 
  2. msg = "hello world" 
  3. def printer(): 
  4. print(msg) 
  5. # 注意,这里没带括号,返回的是printer()这个函数的引用(指针) 
  6. return printer 
  7.  
  8. # 这里等于通过print_msg()拿到了内部函数printer()的引用,所以printer这个变量指向了一个叫printer的函数 
  9. printer = print_msg() 
  10. print(printer) 
  11. printer() 
  12.  


pic-1582723050329.png

不是所有嵌套函数都是闭包,就如同不是所有牛奶都是...跑题了

看箭头的位置,按照剧本,当函数执行完成后,其内部的局部变量应该已经销毁.
也就是说在print_msg()这函数执行完了之后,msg这个变量应该不存在了.
但是,BUT!!!

当我们通过print_msg()返回的引用去执行其内部的函数printer()的时候,msg这个变量又神奇的被打印出来了.

这里的printer变量就是一个闭包.

以下是我个人的理解,如果有错误,烦请指出. 因为python的GC使用的是引用计数机制.
所以当我们执行printer = print_msg()的时候,虽然print_msg()函数执行完了,但是在执行过程中,创建了一个字符串对象'hello world' (并将其引用赋值给了msg)和一个函数对象printer. 然后printer中又保留了到字符串对象msg的引用.所以在执行上述语句的时候发生了这么一个关系printer(外面的变量,这个名字起瞎了)-->printer--->msg
所以在函数print_msg执行完毕之后,变量msg的引用并没有变成0,这个变量依旧存在,没有被GC处理掉.

闭包有啥用

闭包避免了使用全局变量. 闭包允许函数将与其操作的某些数据(环境))关联起来.
在面向对象的编程中,对象允许我们将一些数据(属性)和一个或多个方法关联起来.
但是当对象中只有一个方法的时候,使用闭包或许是更好的选择.

  1. def foo(x): 
  2. def foo_inner(y): 
  3. return x+y 
  4. return foo_inner 
  5.  
  6. foo1 = foo(5) 
  7.  
  8. print(foo1(10)) 
  9. print(foo1(6)) 
  10.  


pic-1582723050329.png

匿名函数

就是lambda表达式. lambda 入参 : 返回值
冒号前面是入参,冒号后面是返回值.用来简写函数的. 比如

  1. L = list(map(lambda x:x*x,[1,2,3,4,5])) 
  2. print(L) 
  3.  

不好理解,新手别瞎JB用

装饰器

面向对象的语言中,万物都是对象,方法(函数)当然也是一个对象. 是对象就要有属性方法和引用. 函数对象有一个__name__的属性可以拿到函数的名称.

  1. def now():     
  2. print("2020-02-18") 
  3. foo = now 
  4.  
  5. print(foo.__name__) 
  6. print(now.__name__) 
  7.  

这俩输出肯定是一样的. 不懂的话C语言基础(非谭浩强版)从头学

那么如果现在我们想增强now()方法的功能,除了重写这个方法之外还有没有其他的办法?
想想嵌套函数貌似是可以的.

  1. def log(func): 
  2. def wrapper(*args, **kw): 
  3. #这里是我们想增强的代码 
  4. print('this is a log: function "%s" is called'%func.__name__) 
  5. return func(*args, **kw) 
  6. return wrapper 
  7.  
  8. @log 
  9. def now(): 
  10. print("2020-02-18") 
  11.  

这玩意儿就有点装饰器的样子了.

在now函数上面写个@log实际上就是在执行 log(now())

但是有个问题.

改造后的代码就是这个样子的.

  1. import functools 
  2. def log(func): 
  3. @functools.wraps(func) 
  4. def wrapper(*args, **kw): 
  5. #这里是我们想增强的代码 
  6. print('this is a log: function "%s" is called'%func.__name__) 
  7. return func(*args, **kw) 
  8. return wrapper 
  9.  
  10. @log 
  11. def now(): 
  12. print("2020-02-18") 
  13.  
  14. print(now.__name__) 
  15.  

这里我们又发现一个神器的东西. 就是装饰器functools.wraps带参数...这玩意儿咋实现的呢?

看个例子:

  1. def log(text): 
  2. def decoraotor(func): 
  3. @functools.wraps(func) 
  4. def wrapper(*args, **kwargs): 
  5. print("%s %s" % (text, func.__name__)) 
  6. return func(*args, **kwargs) 
  7. return wrapper 
  8. return decoraotor 
  9.  
  10. @log("Holy shit!") 
  11. def now(): 
  12. print("2020-02-18") 
  13.  

真特么复杂...用到的时候翻笔记吧. 记不住记不住

偏函数

python中的偏函数不是数学上的偏函数(话说数学上的偏函数是啥,我也不知道...),看文档定义:
functools.partial(func, /, *args, **keywords)
Return a new partial object which when called will behave like func called with the positional arguments args and keyword arguments keywords. If more arguments are supplied to the call, they are appended to args. If additional keyword arguments are supplied, they extend and override keywords. Roughly equivalent to:

  1. def partial(func, /, *args, **keywords): 
  2. def newfunc(*fargs, **fkeywords): 
  3. newkeywords = {**keywords, **fkeywords} 
  4. return func(*args, *fargs, **newkeywords) 
  5. newfunc.func = func 
  6. newfunc.args = args 
  7. newfunc.keywords = keywords 
  8. return newfunc 
  9.  

The partial() is used for partial function application which “freezes” some portion of a function’s arguments and/or keywords resulting in a new object with a simplified signature. For example, partial() can be used to create a callable that behaves like the int() function where the base argument defaults to two:

  1. from functools import partial 
  2. basetwo = partial(int, base=2) 
  3. basetwo.__doc__ = 'Convert base 2 string to an int.' 
  4. basetwo('10010') 
  5.  

partial Objects

partial objects are callable objects created by partial(). They have three read-only attributes:

partial.func
A callable object or function. Calls to the partial object will be forwarded to func with new arguments and keywords.

partial.args
The leftmost positional arguments that will be prepended to the positional arguments provided to a partial object call.

partial.keywords
The keyword arguments that will be supplied when the partial object is called.

partial objects are like function objects in that they are callable, weak referencable, and can have attributes. There are some important differences. For instance, the__name__ and __doc__attributes are not created automatically. Also, partial objects defined in classes behave like static methods and do not transform into bound methods during instance attribute look-up.

看一个例子就得了.高级玩法,新手别瞎JB玩.

  1. def int2(x,base=2): 
  2. return int(x,base) 
  3. print(int2('10000000')) 
  4.  
  5. int22 = functools.partial(int,base=2) 
  6. print(int22('10000000')) 
  7.