• python小记(4)


    在Python 3中,你可以使用nonlocal关键词,如下演示。

     1 def foo():
     2     a = 1
     3     def bar():
     4         nonlocal a
     5         a = a + 1
     6         print("bar()a=",a)
     7     bar()
     8     print("foo()a=",a)
     9 
    10 foo()
    11 #output
    12 #bar()a= 2
    13 #foo()a= 2

    lambda

    lambda函数,是一个只用一行就能解决问题的函数,听着是多么诱人呀。看下面的例子:

    >>> def add(x):
    ...     x += 3
    ...     return x
    ... 
    >>> numbers = range(10)
    >>> numbers
    [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
    
    >>> new_numbers = []
    >>> for i in numbers:
    ...     new_numbers.append(add(i))  
    ... 
    >>> new_numbers
    [3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
    

    在这个例子中,add()只是一个中间操作。当然,上面的例子完全可以用别的方式实现。比如:

    >>> new_numbers = [ i+3 for i in numbers ]
    >>> new_numbers
    [3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
    

    首先说明,这种列表解析的方式是非常非常好的。

    但是,我们偏偏要用lambda这个函数替代add(x)

    >>> lam = lambda x:x+3
    >>> n2 = []
    >>> for i in numbers:
    ...     n2.append(lam(i))
    ... 
    >>> n2
    [3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
    

    这里的lam就相当于add(x),这一行lambda x:x+3就完成add(x)函数体里面的两行。还可以写这样的例子:

    >>> g = lambda x, y: x + y  
    >>> g(3, 4)
    7
    >>> (lambda x : x ** 2)(4)  #返回4的平方
    16
    

    通过上面例子,总结一下lambda函数的使用方法:

    • 在lambda后面直接跟变量
    • 变量后面是冒号
    • 冒号后面是表达式,表达式计算结果就是本函数的返回值

    为了简明扼要,用一个式子表示是必要的:

    lambda arg1, arg2, ...argN : expression using arguments
    

    要特别提醒:虽然lambda 函数可以接收任意多个参数 (包括可选参数) 并且返回单个表达式的值,但是lambda 函数不能包含命令,包含的表达式不能超过一个。不要试图向 lambda 函数中塞入太多的东西;如果你需要更复杂的东西,应该定义一个普通函数,然后想让它多长就多长。

    就lambda而言,它并没有给程序带来性能上的提升,它带来的是代码的简洁。比如,要打印一列表,里面依次是某个数字的1次方,二次方,三次方,四次方。用lambda可以这样做:

    >>> lamb = [ lambda x:x, lambda x:x**2, lambda x:x**3, lambda x:x**4 ]
    >>> for i in lamb:
    ...     print i(3),
    ... 
    3 9 27 81
    

    lambda做为一个单行的函数,在编程实践中,可以选择使用。

    map

    先看一个例子,还是上面讲述lambda的时候第一个例子,用map也能够实现:

    >>> numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 
    
    >>> map(add, numbers) 
    [3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
    
    >>> map(lambda x: x+3, numbers)      #用lambda当然可以啦
    [3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
    

    map()是python的一个内置函数,它的基本样式是map(func,seq)

    func是一个函数,seq是一个序列对象。在执行的时候,序列对象中的每个元素,按照从左到右的顺序,依次被取出来,塞入到func那个函数里面,并将func的返回值依次存到一个列表中。

    在应用中,map的所能实现的,也可以用别的方式实现。比如:

    >>> items = [1,2,3,4,5]
    >>> squared = []
    >>> for i in items:
    ...     squared.append(i**2)
    ... 
    >>> squared
    [1, 4, 9, 16, 25]
    
    >>> def sqr(x): return x**2
    ... 
    >>> map(sqr,items)
    [1, 4, 9, 16, 25]
    
    >>> map(lambda x: x**2, items)
    [1, 4, 9, 16, 25]
    
    >>> [ x**2 for x in items ]     #这个我最喜欢了,一般情况下速度足够快,而且可读性强
    [1, 4, 9, 16, 25]
    

    条条大路通罗马,以上方法,在编程中,自己根据需要来选用啦。

    在以上感性认识的基础上,在来浏览有关map()的官方说明,能够更明白一些。

    map(function, iterable, ...)

    Apply function to every item of iterable and return a list of 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. If one iterable is shorter than another it is assumed to be extended with None items. If function is None, the identity function is assumed; if there are multiple arguments, map() returns a list consisting of tuples containing the corresponding items from all iterables (a kind of transpose operation). The iterable arguments may be a sequence or any iterable object; the result is always a list.

    理解要点:

    • 对可迭代对象中的每个元素,依次应用function的方法(函数)(这本质上就是一个for循环)。
    • 将所有结果返回一个列表。
    • 如果参数很多,则对那些参数并行执行function。

    例如:

    >>> lst1 = [1, 2, 3, 4, 5]
    >>> lst2 = [6, 7, 8, 9, 0]
    >>> map(lambda x, y: x + y, lst1, lst2)     #将两个列表中的对应项加起来,并返回一个结果列表
    [7, 9, 11, 13, 5]
    

    上面这个例子如果用for循环来写,还不是很难,如果扩展一下,下面的例子用for来改写,就要小心了:

    >>> lst1 = [1, 2, 3, 4, 5]
    >>> lst2 = [6, 7, 8, 9, 0]
    >>> lst3 = [7, 8, 9, 2, 1]
    >>> map(lambda x,y,z: x+y+z, lst1, lst2, lst3)
    [14, 17, 20, 15, 6]
    

    这才显示出map的简洁优雅。

    reduce

    首先声明:如果读者使用的是Python3,跟上面有点不一样,因为在Python3中,reduce()已经从全局命名空间中移除,放到了functools模块中,如果要是用,需要用from functools import reduce引入之。

    再看这个:

    >>> reduce(lambda x,y: x+y,[1, 2, 3, 4, 5])
    15
    

    请仔细观察,是否能够看出是如何运算的呢?画一个图:

    还记得map是怎么运算的吗?忘了?看代码:

    >>> list1 = [1,2,3,4,5,6,7,8,9]
    >>> list2 = [9,8,7,6,5,4,3,2,1]
    >>> map(lambda x,y: x+y, list1,list2)
    [10, 10, 10, 10, 10, 10, 10, 10, 10]
    

    对比一下,就知道两个的区别了。原来map是上下运算,reduce是横着逐个元素进行运算。

    权威的解释来自官网:

    reduce(function, iterable[, initializer])

    Apply function of two arguments cumulatively to the items of iterable, from left to right, so as to reduce the iterable 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 iterable. If the optional initializer is present, it is placed before the items of the iterable in the calculation, and serves as a default when the iterable is empty. If initializer is not given and iterable contains only one item, the first item is returned. Roughly equivalent to:

    def reduce(function, iterable, initializer=None):
        it = iter(iterable)
        if initializer is None:
            try:
                initializer = next(it)
            except StopIteration:    
                raise TypeError('reduce() of empty sequence with no initial value')    
        accum_value = initializer                                                                   
        for x in it:
            accum_value = function(accum_value, x)    
        return accum_value
    

    如果用我们熟悉的for循环来做上面reduce的事情,可以这样来做:

    >>> lst = range(1,6)
    >>> lst
    [1, 2, 3, 4, 5]
    >>> r = 0
    >>> for i in range(len(lst)):
    ...     r += lst[i]
    ... 
    >>> r
    15
    

    for普世的,reduce是简洁的。

    为了锻炼思维,看这么一个问题,有两个list,a = [3,9,8,5,2]b=[1,4,9,2,6],计算:a[0]b[0]+a[1]b[1]+...的结果。

    >>> a = [3, 9, 8, 5, 2]
    >>> b = [1, 4, 9, 2, 6]
    
    >>> zip(a,b)        #复习一下zip,下面的方法中要用到
    [(3, 1), (9, 4), (8, 9), (5, 2), (2, 6)]
    
    >>> sum(x*y for x,y in zip(a,b))    #解析后直接求和
    133
    
    >>> new_list = [x*y for x,y in zip(a,b)]
    
    >>> #这样也可以:new_tuple = (x*y for x,y in zip(a,b)),与上面的区别,后续会讲到
    >>> new_list
    [3, 36, 72, 10, 12]
    >>> sum(new_list)     #或者:sum(new_tuple)
    133
    
    >>> reduce(lambda sum,(x,y): sum+x*y,zip(a,b),0)    #这个方法是在耍酷呢吗?
    133
    
    >>> from operator import add, mul            #耍酷的方法也不止一个
    >>> reduce(add, map(mul, a, b))
    133
    
    >>> reduce(lambda x,y: x+y, map(lambda x,y: x*y, a,b))  #map,reduce,lambda都齐全了,更酷吗?
    133
    

    在Python 2中,如果使用map/reduce之类,可能或遇到性能不稳定的情况,如果是Python 3,就放心多了,不仅性能稳定,而且速度足够快。

    但是,我还是更推荐使用列表解析,因为它可读性更好,你无法保证队友都跟你一样。

    filter

    filter的中文含义是“过滤器”,在Python中,它就是起到了过滤器的作用。首先看官方说明:

    filter(function, iterable)

    Construct a list 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 iterable is a string or a tuple, the result also has that type; otherwise it is always a list. 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 [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.

    这次真的不翻译了(好像以往也没有怎么翻译呀),而且也不解释要点了。请列位务必自己阅读上面的文字,并且理解其含义。英语,无论怎么强调都是不过分的,哪怕是做乞丐,说两句英语,没准还可以讨到英镑美元呢。

    通过下面代码体会:

    >>> numbers = range(-5,5)
    >>> numbers
    [-5, -4, -3, -2, -1, 0, 1, 2, 3, 4]
    
    >>> filter(lambda x: x>0, numbers) 
    [1, 2, 3, 4]
    
    >>> [x for x in numbers if x>0]     #与上面那句等效
    [1, 2, 3, 4]
    
    >>> filter(lambda c: c!='i', 'qiwsir')  #能不能对应上面文档说明那句话呢?
    'qwsr'                                  #“If iterable is a string or a tuple, the result also has that type;”
    

    至此,介绍了几个函数,这些函数在对程序的性能提高上,并没有显著或者稳定预期,但是,在代码的简洁上,是有目共睹的。有时候是可以用来秀一秀,彰显python的优雅和自己耍酷。如何用、怎么用,看你自己的喜好了。

    不过,编程的时候,往往不能靠单纯自己的喜好,还得考虑队友。

  • 相关阅读:
    Indy10 系列(一)——获取最新的Indy源码
    Oracle9i中关于select into的执行过程 (转)
    Oracle创建删除用户、角色、表空间、导入导出数据库命令行方式总结(转)
    手动创建Oracle 11g数据库(转载)
    Indy10 系列(四)——编译Indy源码
    Indy10 系列(二)——获取最新的Indy源码
    Delphi 2010手动安装indy10.5.7
    Indy10 系列(三)——获取最新的Indy源码
    Indy10 系列(五)——安装Indy控件,重新编译Lazarus
    Unix文化RTFM
  • 原文地址:https://www.cnblogs.com/liqiantu/p/5787094.html
Copyright © 2020-2023  润新知