Python学习笔记整理(十二)Pyth

发布时间:2019-09-12 07:55:51编辑:auto阅读(1723)

    一、函数基础
    函数可以计算出一个返回值。作用:最大化代码重用,最小化代码冗余,流程的分解
    1、函数相关的语句和表达式
    语句        例子
    Calls        myfunc(‘diege','eggs',meat=lit) #使用函数
    def,return,yield      def adder(a,b=1,*c):
                              return a+b+c[0]
    global        changer():
                    global x;x='new'
    lambda        Funcs=[lambad x:x**2,lambad x:x*3]

    2、编写函数
    def是可执行的代码,实时执行的,Python中所有语句都是实时执行的,if,while,def可嵌套,可以出现在任何地方,但往往包含在模块文件中,
    并早模块导入时运行,函数还可以通过嵌套到if语句中去实现不同的函数定义。
    def创建了一个对象并将其赋值给某一个变量名。
    return将一个结果对象发送给调用者。
    函数是通过赋值(对象引用)传递的。

    参数通过赋值传递给函数。
    global声明了一个模块级的变量并被赋值。
    参数,返回值以及变量并不是声明

    def语句
    def语句将创建一个函数对象并将其赋值给一个变量名。一般格式如下:

    def <name>(arg1,age2,...,agrN):
        <statements>
         return <value>
    函数主体往往都包含了一个return语句(不是必须的),如果它没有出现,那么函数将会在控制流执行完函数主体时结束,技术上,没有返回值的函数自动返回了none对象。
    return可以在函数主体中的任何地方出现。它表示函数调用的结束,并将结果返回至函数调用处。

    函数通过嵌套到if语句中去实现不同的函数定义的格式:
    if test:
        def func():
            ...
    else:
        def func()
    func()    #call

    3、例子1
    定义
    >>> def Dtest(x,y):
    ...     return x*y
    使用(call)
    >>> Dtest(3,5)
    15
    >>> Dtest(3.14,3)
    9.42
    >>> Dtest('A',4)    
    'AAAA'
    这里又看到Python中的多态了
    Dtest函数中的表达式x*y的意义完全取决于x和y的对象类型。
    类似的多态操作还包括:print,index,*操作符
    >>> Dtest('A','A') #会报错,但不要尝试判断传入参数的对象类型,这样实质上破坏了函数的灵活性,把函数限制在特定的类型上。任何支持函数所预期的结果的对象都能用。(结果一词是指函数所执行的一组方法和表达式运算符)
    4、例子2
    寻找序列的交集
    定义
    >>> def Intersect(seq1,seq2):
    ...     res=[]
    ...     for x in seq1:
    ...             if x in seq2:
    ...                     res.append(x)
    ...     return res
    调用
    >>> Intersect([1,3,5,6,7,9],[2,4,5,7,8,10])
    [5, 7]
    >>> s1='SPAM'
    >>> s2='sCaM'
    >>> Intersect(s1,s2)                        
    ['M']
    重访多态,这些编写这个函数是多态,它可以支持多种类型
    >>> Intersect([1,3,5,6,7,9],(1,4))             
    [1]
    技术上讲,文件也可以做为该函数的第1个参数,但不能作为第2个参数。在一个文件中搜索2个参数
    >>> Intersect(open('/etc/rc.conf').read(),'apache')

    5、本地变量
    例子2中.res变量在python中称为本地变量,这个变量只是在def内的函数中是可见的,并且仅在函数运行时是存在的。所有函数内部进行赋值的变量名
    都默认为本地变量,。
    *req是明显被赋值过的,所以它是一个本地变量
    *参数也是通过赋值被传入的,所以seq1和seq2也是本地变量。
    *for循环将元素赋值给了一个变量,所以变量x也是本地

    二、作用域和参数
    (一)作用域
    python作用域:变量定义以及查找的地方
    参数传递:传递给函数作为其输入对象的方式
    1、作用域法则
    Python创建,改变或者查找变量名都是在所谓的命名空间(一个保存变量名的地方)中进行。作用域这个术语指的就是命名空间。
    也就说,在代码中变量名被赋值的位置决定了这个变量名能被访问到的范围
    一个函数所有变量名都与函数的命名空间相关联。
    *def内定义变量名def内使用
    *def之中的变量名与def之外的变量名不发生冲突,使用别处相同的变量名也没问题。
    x=99
    def func()
        x=88
    函数定义了本地作用域,而模块定义了全局作用域。两作用域关系。
    *内嵌的模块是全局作用域:对于外部的全局变量就成为了一个模块对象的属性。
    *全局作用域的作用范围仅限单个文件:不要被全局迷惑,这里的全局是一个文件的顶层的变量名,仅对这个文件内部的代码而言是全局。
    Python中,没有一个无所不包的情景文件作用域。替代的方法是,变量名由模块文件隔开,必须精准地导入一个模块文件才能偶使用这文件中
    定义的变量名,
    *每次对函数的调用都创建了一个新的本地作用域。
    *赋值的变量名废除声明为全局变量,否则均为本地变量。
    *所用的变量名都可以归纳为本地,全局,或者内置。(内置:ptyhon预定义的__builtin__模块提供的)
    2、变量名解析:LEGB原则
    对一个def语句
    *变量名引用分为三个作用域进行查找:首先查找本地,然后是函数内(如果有),之后全局,最后内置。
    *默认情况下,变量名赋值会创建或改变本地变量
    *全局声明将赋值变量名映射到模块文件内部的作用域。
    3、作用域实例
    #定义全局作用域
    >>> X=99
    >>> def func(Y):
    ...     Z=X+Y   #本地作用域
    ...     return Z
    ...
    >>> func(1)    
    100
    全局变量:X,func
    本地变量:Y,Z
    4、内置作用域
    需要导入__builtin__
    >>> import  __builtin__
    >>> dir(__builtin__)
    里面前一半是内置异常,后一半是内置函数
    5、global
    global语句包含关键字global
    *全局变量是位于模块文件内部顶层的变量名
    *全局变量如果是在函数内部被赋值的话,并需经过声明
    *全局变量名在函数的内部不经过声明也可以被引用
    >>> X=88     
    >>> def func():
    ...     global X
    ...     X=99
    ...
    >>> func()
    >>> print X
    99
    >>> y,z=1,2
    >>> def Ftest():
    ...     global x
    ...     x=y+z
    ...
    >>> Ftest()
    >>> x
    3
    6、最小化全局变量

    (二)传递参数
    *参数的传递是通过自动将对象赋值给本地变量来实现的。
    *在函数内部的参数名的赋值不会影响调用者。
    *改变函数的可变对象参数的值也许会对调用者有影响。
    换句话说,因为参数是简单的通过赋值进行对象的传递,函数能够改变传入的可变对象,因此其结果会影响调用者。
    *不可变参数是“通过值”进行传递。
    像整数和字符串这样的对象是通过对象引用而不是拷贝进行传递的,但是因为你无论如何都不可能在原处改变不可变对象,实际的效果就是很像创建了一份拷贝。
    可变对象是通过“指针”进行传递的。
    1、参数和共享引用
    >>> def changer(a,b):
    ...     a=2
    ...     b[0]='diege'
    ...
    >>> X=1
    >>> L=[1,2]
    >>> changer(X,L)
    >>> X,L
    (1, ['diege', 2])
    函数内部对参数或者参数的分片赋值了,X没改变,L改变了,X,Y都是全局变量。这说明了数字字符串不可变参数函数无法改变。
    而列表,字典等可改变参数可以在执行函数调用后改变。
    这里a是函数的本地变量名,第一个赋值对函数调用者没有影响。它仅简单的修改了本地变量名a,并没有改变参数a绑定的函数调用者的变量名X。
    b也是一个本地变量名,但是他被传递给了一个可变对象。因为第二个赋值是一个在原处发生的对象改变(如果是b='diege'就没有改变,因为这样只改变本地变量名),对函数中b[0]进行赋值的结果是在函数返回后影响L的值。事实上我们没有修改b,修改是是b当前所引用
    对象的一部分,并且这个改变将影响调用者。
    2、避免可变参数的修改
    在Python中,默认通过引用(也就是指针)进行函数的参数传递。如果不想在函数内部在原处的修改影响传递给它的对象。那么,能够简单的创建一个可变对象的拷贝。我们总是能够在调用时对列表进行拷贝L=[1,2]
    changer(X,L[:])
    如果不想改变传入的对象,无论函数是如何调用的,我们可以在函数内部进行拷贝,避免可变参数的修改
    >>> def changer(a,b):
    ...     a=2
    ...    b=b[:]
    ...     b[0]='diege'
    ...
    3、对参数输出进行模拟
    return能够返回任意种类的对象,它也能返回多个值,如这些值被封装进一个元组或者其他的集合类型
    >>> def mul(x,y):
    ...     x=2
    ...     y=[3,4]
    ...     return x,y
    ...
    >>> X=1
    >>> L=[1,2]
    >>> mul(X,L)
    (2, [3, 4])
    >>> X,L=mul(X,L) 
    >>> X,L
    (2, [3, 4])
    4、特定参数匹配模型
    参数在ptyhon中总是通过赋值进行传递,传入的对象赋值给了在def头部的变量名。尽管这样,在模型的上层,python提供了额外的工具,该工具改变了调用过中,赋值时参数对象匹配在头部的参数名的优先级。这些工具是可选的。
    默认情况下,参数是通过其位置进行匹配的,从左到右,而且必须精确地传递和函数头部参数名一样多的参数。还能够定义变量名进行匹配,默认参数值(arg2=10),以及对于额外参数的容器,必须要根据变量名匹配对象,匹配完成后在传递机制的底层依然是赋值。这些工具对于编写库文件的人来说,要比应用程序开放者更有用。
    规则
    *位置:从左到右进行匹配
    *关键字参数:通过参数名进行匹配。
    【调用者】可以定义那个函数接受这个值,通过在调用时使用参数的变量名,使用name=value这种语法。
    *默认参数:为没有传入值得参数定义参数值【定义函数时】
    如果调用时传入的值过于少的话,函数能够为参数定义接受的默认值,在函数定义中使用name=value
    *可变参数:收集任意多基于位置或关键字的参数
    以*开头,收集任意多的额外参数
    *可变参数:传递任意多的基于位置或关键字的参数。【调用时】
    调用者能够再使用*语法去将参数集合打散,分成参数。这个*与函数头部的*恰恰相反。在函数头部他意味着收集任意多的参数,而在调用者中意味着传递任意多的参数。

    总结与特定模式有关的语法:
    语法            位置    解释
    func(value)        调用者    常规参数,通过位置进行匹配
    func(name=value)    调用者    关键字参数,通过变量名匹配
    func(*name)        调用者    以name传递所有的对象,并作为独立的基于位置的参数
    func(**name)        调用者    以name成对的传递所有的关键字/值,并作为独立的关键字的参数

    def func(name)        函数    常规参数:通过位置或变量名进行匹配
    def func(name=value)    函数    默认参数值:如果没有在调用中传递的话,就是用默认值
    def func(*name)        函数    匹配并收集(在元组中)所有包含位置的参数
    def func(**name)    函数    匹配并收集(在字典中)所有包含位置的参数。

    5、关键字参数和默认参数的实例
    >>> def f(a,b,c):
    ...     print a,b,c
    ...
    >>> f(1,3,4)
    1 3 4
    关键词参数
    >>> f(c=5,a=8,b=9)      
    8 9 5
    >>> f(4,c=8,b=9)   
    4 9 8
    >>> def func(name,age,job):
    ...     print 'name','age','job'
    ...
    >>> func('diege',18,'op')
    name age job
    >>> func(age=18,job='op',name='diege')
    name age job
    关键字参数在调用中起到了数据标签的作用

    默认参数,如果调用时没有传入值得话,在函数运行前,参数就被赋了默认值。
    >>> def f(a,b=2,c=5):
    ...     print a,b,c
    ...
    >>> f(4)
    4 2 5
    >>> def f(a,b=2,c=5):
    ...     print a+b+c
    ...
    >>> f(4)
    11
    >>> f(6,5) 
    16
    >>> f(a=10)
    17
    >>> f(a=3,b=4)
    12
    >>> f(2,c=6)    
    10
    >>> f(6,7,8)
    21
    6、任意参数的实例
    *和** 让函数支持接收任意数目的参数。
    收集参数
    第一种用法:在函数定义中,在元组中收集不匹配的位置参数
    >>> def f(*args):print args
    ...
    >>> f()
    ()

    **特性类似,但是它只对关键字参数有效。将这些关键字参数传递给一个新的字典。
    >>> def f(**agrs):print args
    >>> f()
    >>> def f(**args):print args
    ...
    >>> f()
    {}
    min调用
    def min1(*args):
        res=args[0]
        for arg in args[1:]:
            if arg < res:
                res=arg
        return
    三、函数的高级话题
    (一)、匿名函数:lamdba
    lambad 创建了一个之后能够被调用的函数,它返回了一个函数而不是将这个函数赋值给一个变量名。
    1、lambda表达式
    lanbda arg1,arg2,...,argN:expression using arguments
    lambda 是一个表达式,而不是一个语句
    lambda的主体是一个单个的表达式,而不是代码块
    >>> def func(x,y,z):return x+y+z
    ...
    >>> func(2,3,4)
    9
    等同于
    >>> f=lambda x,y,z:x+y+z
    >>> f(2,3,4)
    9
    默认参数也能够再lambda参数中使用,就像在def中使用一样。
    >>> x=(lambda a='free',b='file',c='feel':a+b+c)  
    >>> x('well')
    'wellfilefeel

    为什么要使用lambda
    lambda 通常用来编写跳转表,也就是行为的列表或字典,能够按照需要执行相应的动作。
    L=[(lambda x:x**2),(lambda x:x**3),(lambda x:x**4)]
    >>> for f in L:
    ...     print f(2)
    ...
    4
    8
    16
    >>> print L[0](3)
    9

    2、嵌套lambda和作用域
    >>> def action(x):
    ...     return (lambda y:x+y)
    ...
    >>> act=action(99)
    >>> act
    <function <lambda> at 0x285066f4>
    >>> act(2)
    101
    (二) 作参数来应用函数
    1、内置函数apply
    当需要变得更加动态的话,可以通过将一个函数作为一个参数传递给apply来调用一个生成的函数,并且也将传给那个函数的参数作为一个元组传递给apply函数()
    2、传入关键字参数
    >>> echo(1,2,a=3,b=4)
    (1, 2) {'a': 3, 'b': 4}

    >>> arg1=(1, 2)
    >>> arg2={'a': 3, 'b': 4}
    >>> apply(echo,arg1,arg2)
    (1, 2) {'a': 3, 'b': 4}
    3、和apply类似的调用语法
    >>> apply(func,args)
    9
    >>> func(args)
    9
    >>> apply(echo,arg1,arg2)
    (1, 2) {'a': 3, 'b': 4}
    (三) 在序列中映射函数:map
    >>> L=[1,2,3,4,5]
    >>> updated=[]
    >>> for x in L:           
    ...     updated.append(x+10)
    ...
    >>> updated
    [11, 12, 13, 14, 15]
    使用内置工具map,map函数会对一个序列对象中的每一个元素应用被传入的函数,并且返回一个包含了所有函数调用结果的一个列表。
    >>> def inc(x):return x+10
    ...
    >>> L=[1,2,3,4,5]
    >>> map(inc,L)
    [11, 12, 13, 14, 15]
    map(函数,传入函数的序列对象)
    >>> map(None,S1,S2)
    >>> L=[1,2,3,4,5] 
    map嵌套lambda      
    >>> map((lambda x:x+3),L)
    [4, 5, 6, 7, 8]
    高级功能:提供了多个序列作为参数,它能够并行返回分别以每个序列的元素作为【函数对应参数】得到的结果的列表
    >>> pow(3,4)
    81
    >>> map(pow,[1,2,3],[2,3,4])    #1**2,2**3,3**4
    [1, 8, 81]
    (四) 函数式编程工具:filter和reduce
    函数式编程的意思就是对序列应用一些函数的工具。
    基于某一测试函数过滤出一些元素-filter
    对每对元素都应用函数并运行到最后结果-reduce
    >>> range(-5,5)
    [-5, -4, -3, -2, -1, 0, 1, 2, 3, 4]
    >>> filter((lambda x:x>0),range(-5,5))
    [1, 2, 3, 4]
    这个等效于for range:if语句
    reduce稍微复杂一点。这里两个reduce调用,计算在一个列表中所有元素加起来和以及乘起来的乘积
    >>> reduce((lambda x,y:x+y),[1,2,3,4])
    10
    >>> reduce((lambda x,y:x*y),[1,2,3,4])
    24

    (五)重访列表解析:映射
    1、列表解析基础
    >>> [x**2 for x in range(10)]
    [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
    >>> map((lambda x:x**2),range(10))
    [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
    2、增加测试和嵌套循环
    在for之后编写if分支,用来增加逻辑选择,if分支相当filter
    >>> [x for x in range(5) if x%2==0]
    [0, 2, 4]
    >>> filter((lambda x:x%2==0),range(5))
    [0, 2, 4]
    filter出来的列表可以作为map的第2个参数
    >>> map((lambda x:x**2),filter((lambda x:x%2==0),range(5)))
    [0, 4, 16]
    3、列表解析和矩阵
    4、理解列表解析
    对ptyhon初学者,通常使用简单的for循环,在其他大多数情况下,使用map调用(除非它们会变得过于复杂)
    列表解析比map快,map比for循环快2倍

    (六)重访迭代器:生成器
    编写的函数能够返回一个值,并且稍后还可以从它刚才离开的地方仍然返回值。这样的函数被认作是生成器,因为它们随时间生成一个序列的值。
    大多数方面生成器函数就像一般函数,在Python它们被自动用作实现迭代协议,因它只能够再迭代的语境中出现。

    生成器和一般的函数之间代码上最大的不同就是一个生成器yield一直,而不是return一个值。yield语句将会将函数关起,并向它的调用者返回一个值
    但是保存足够的状态信息为了让其能够在函数从它挂起的地方恢复。
    包含yield的语句的函数将会特地编译成为生成器。当调用它时,他们返回一个生成器对象,这个生成器对象支持迭代器对象接口。
    1、生成器实例的。
    >>> def Dtest(N):
    ...     for i in range(N):
    ...             yield i**2
    使用
    >>> for i in Dtest(5):
    ...     print i,':',
    ...
    0 : 1 : 4 : 9 : 16 :
    查看过程
    >>> x=Dtest(4)
    >>> x.next()
    0
    >>> x.next()
    1
    >>> x.next()
    4
    >>> x.next()
    9
    >>> x.next()
    for循环和生成器是一样的:通过重复调用next方法,知道捕获一个异常。
    2、扩展生成器协议send和next
    生成器函数协议中增加了一个send方法,send方法生成一系列结果的下一个元素,这一点就像next方法一样,但是它也提供了
    一种调用者与生成器之间的进行通信的方法,从而能偶影响它的操作。
    yiled是一个表达式,可以返回传入的元素来发送,而不是一个语句。值是通过调用本身send(value)方法传给生成器的。
    之后恢复生成器的代码,并且yield表达式返回了为了发送而传入的值。如果调用了正常的放next()方法,yield返回None
    3、迭代器和内置类型
    内置的数据类型设计了对应于内置函数iter的迭代器对象。字典迭代器在每次迭代中产生关键字列表元素。
    >>> D={'a':1,'b':2,'c':3}
    >>> x=inter(D)
    >>> x.next() 
    'a'
    >>> x.next()
    'c'
    >>> x.next()
    'b'
    >>> x.next()
    Traceback (most recent call last):
      File "<stdin>",
    所有迭代内容(包括for循环,map调用,列表解析等)一次设计用来自动调用iter函数。
    通过支持迭代协议的类来实现任意的生成器对象是可能的,并且已经有很多这样的对象,在for循环和其他的迭代环境中使用。
    这样的类定义了一个特别的__iter__方法,它将返回一个迭代对象。
    4、生成器表达式:迭代器遇到列表解析
    迭代器和列表解析的概念形成了这个语言的一个新的特性,生成器表达式。
    (八)函数设计概念
    *耦合性:对于输入使用参数,并且对于输出使用return语句
    *耦合性:只有在真正必要的情况下使用全局变量。
    *耦合性:不要改变可变类型的参数,除非调用者希望这样做。
    *聚合性:每一个函数都应该有一个单一的,同一的目标
    *大小:每一个函数应该相对较小。
    *耦合:避免直接改变在另一个模块文件中的变量。
    函数是对象:简洁调用
    (九)函数陷阱
    1、本地变量是静态检测的
    >>> X=99
    >>> def selector():
    >>> def selector():
    ...     import __main__
    ...     print __main__.X
    ...     X=88
    ...     print X
    ...
    >>> selector()
    99
    88
    >>> X
    99
    所有的全局变量都再 __main__。 __main__就是顶层的命名空间.__main__.X得到了全局变量版本的X。
    这里使用模块的属性标记来获得其全局变量。
    2、默认和可变对象。
    默认参数是在def语句运行时被评估并保存的,而不是在这个函数调用时。从内部来将,Python会将每一个默认
    参数保存成一个对象。附加在这个函数本身。
    3、没有return语句的函数
    函数中return和yield语句是可选择。没有返回值函数自动返回None对象。
    这样的函数被当做语句,就像他们只执行任务而不要计算有用的结果一样。
    4、嵌套作用域的循环变量
     

关键字