函数高级应用

    变量作用域

        全局变量

            标识符的作用域是定义为其声明在程序里的可应用范围,也就是变量的可见性

            在一个模块中最高级别的变量有全局作用域

            全局变量的一个特征是除非被删除掉,否则它们的存活到脚本运行结束,且对于所有的函数,他们的值都是可以被访问的

        局部变量

            局部变量只是暂时地存在,仅仅只依赖于定义他们的函数现阶段是否处于活动

            当一个函数调用出现时,某局部变量就进入声明他们的作用域,在那一刻,一个新的局部变量名为那个对象创建了

            一旦函数完成,框架被释放,变量将会离开作用域

            如果局部与全局有相同名称的变量,那么函数运行时,局部变量的名称将会把全局变量名称遮盖住

            

                >>> x = 4 

                >>> def foo():

                ...   x = 10

                ...   print "in foo,x = ",x

                ... 

                >>> foo()

                in foo,x =  10

                >>> print "in main,x =",x

                in main,x = 4

        global语句

            >>> x = 10

            >>> def foo():

            ...   global x

            ...   x = 30

            ... 

            >>> x

            10

            >>> foo()

            >>> x

            30

注意事项:列表、字典可变对象,全局变量会被修改。如下所示。不可变对象,全局变量不会修改

>>> alist = [10]

>>> def foo():  

...   alist.append(20)

... 

>>> foo()

>>> alist

[10, 20]


>>> def bar(adict):

...   adict['name'] = 'bob'

... 

>>> bdict = {}

>>> bar(bdict)

>>> bdict

{'name': 'bob'}


>>> def foobar(astr):

...   astr = 'hello'

... 

>>> bstr = ''

>>> foobar(bstr)

>>> bstr

''

        名字空间

            任何时候,总有一个到三个活动的作用域(内建、全局和局部)

            标识符的搜索顺序依次是局部、全局和内建

            提到名字空间,可以想象是否有这个标识符

            提到变量作用域,可以想象是否可以“看见”这个标识符

    函数式编程

        偏函数

            偏函数的概念是将函数式编程的概念和默认参数以及可变参数结合在一起

            一个带有多个参数的函数,如果其中某些参数基本上固定的,那么就可以通过偏函数为这些参数赋默认值

            >>> from operator import add

            >>> from functools import partial

            >>> add10 = partial(add,10)

            >>> print add10(25)

            35

偏函数实例列举:


vim -o u2d.py d2u.py 

2 files to edit

[root@sail qh1]# vim -O u2d.py d2u.py 

2 files to edit


[root@sail qh1]# cat u2d.py 

#!/usr/bin/env python


import sys


def unix2dos(fname,sep='\r\n'):

    dst_name = fname + '.txt'

    with open(fname) as src_fobj:

        with open(dst_name,'w') as dst_fobj:

            for line in src_fobj:

                dst_fobj.write("%s%s" % (line.rstrip('\r\n'),sep))


if __name__ == '__main__':

    unix2dos(sys.argv[1])

[root@sail qh1]# cat d2u.py 

#!/usr/bin/env python


import sys

import u2d


from functools import partial


dos2unix = partial(u2d.unix2dos,seq='\n')


if __name__ == '__main__':

    dos2unix(sys.argv[1])


简单GUI类的例子

编写一个窗口程序,要求如下

1、窗口程序提供三个按钮

2、其中两个按钮的前景色均为白色,背景色为蓝色

3、第三个按钮前景色为红色,背景色为红色

4、按下第三个按钮后,程序退出

#!/usr/bin/env python


import Tkinter

from functools import partial


root = Tkinter.tk()

MyButton = partial(Tkinter.Button,root,fg='white',bg='blue')

b1 = MyButton(text='Button1')

b2 = MyButton(text='Button2')

qb = MyButton(text='quit',command = root.quit)

b1.pack()

b2.pack()

qb.pack()

Tkinter.mainloop()

        递归函数

            如果函数包含了对其自身的调用,该函数就是递归的

            在操作系统中,查看某一目录内所有文件、修改权限等都是递归的应用

                #!/usr/bin/env python

                def func(num):
                    if num == 1:
                        return num
                    return num * func(num - 1)

                if __name__ == '__main__':
                    print func(5)
                    print func(6)

                

                $ mkdir -p demo/{aaa/bbb,ccc}

                $ cp /etc/hosts /home/qihang/demo/

                $ touch /home/qihang/demo/d.txt

                $ touch /home/qihang/demo/aaa/{a1,a2}.txt

                $ touch /home/qihang/demo/aaa/bbb/b.txt

                $ touch /home/qihang/demo/ccc/{c1,c2}.txt

                $ ls -R /home/qihang/demo/
/home/qihang/demo/:
?  aaa  ccc  d.txt  hosts

/home/qihang/demo/aaa:
a1.txt  a2.txt  bbb

/home/qihang/demo/aaa/bbb:
b.txt

/home/qihang/demo/ccc:
c1.txt  c2.txt
编写程序实现ls -R /home/qihang/demo/的功能
#!/usr/bin/env python

import os
import sys

def lsdir(folder):
    contents = os.listdir(folder)
    print "%s:\n%s\n" %(folder,contents)
    for item in contents:
        full_path = os.path.join(folder,item)
        if os.path.isdir(full_path):
            lsdir(full_path)

if __name__ == '__main__':
    lsdir(sys.argv[1])

os模块有这个递归的函数

    >>> os.walk('/home/qihang/demo/')
    <generator object walk at 0xb707d2d4>

    >>> list(os.walk('/home/qihang/demo/'))
    [('/home/qihang/demo/', ['aaa', 'ccc'], ['hosts', 'd.txt']), ('/home/qihang/demo/aaa', ['bbb'], ['a1.txt', 'a2.txt']), ('/home/qihang/demo/aaa/bbb', [], ['b.txt']), ('/home/qihang/demo/ccc', [], ['c2.txt', 'c1.txt'])]
    >>> a = os.walk('/home/qihang/demo/')
    >>> a.next()
    ('/home/qihang/demo/', ['aaa', 'ccc'], ['hosts', 'd.txt'])
    >>>
    >>> a.next()
    ('/home/qihang/demo/aaa', ['bbb'], ['a1.txt', 'a2.txt'])
    >>> a.next()
    ('/home/qihang/demo/aaa/bbb', [], ['b.txt'])
    >>> a.next()
    ('/home/qihang/demo/ccc', [], ['c2.txt', 'c1.txt'])
    >>> a.next()
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    StopIteration
#!/usr/bin/env python

import os
import sys

def lsdir(folder):
    for path,dirs,files in os.walk(folder):
        print '%s:\n%s\n' % (path,(dirs + files))

if __name__ == '__main__':
    lsdir(sys.argv[1])

    内部函数

        闭包

            闭包将内部函数自己的代码和作用域以及外部函数的作用结合起来

            闭包的词法变量不属于全局名字空间域或者局部的--而属于其他的名字空间,带着“流浪”的作用域

            闭包对于安装计算,隐藏状态,以及在函数对象和作用域中随意的切换是很有用的

            闭包也是函数,但是它们能携带一些额外的作用域

        闭包实例

            在电子商务平台下,每件商品都有一个计数器说明该商品售出数量,这个计算器就可以通过闭包实现

                >>> def counter(start_at = 0):
                ...   count = [start_at]
                ...   def incr():
                ...     count[0] +=1
                ...     return count[0]
                ...   return incr
                ...
                >>> a = counter()
                >>> print a()
                1
                >>> b = counter(10)
                >>> print b()
                11
                >>> print a()
                2
                >>> print a()
                3
安装Tkinter模块

# sudo apt-get install python-tk

# vim button.py
#!/usr/bin/env python

from functools import partial
import Tkinter

def say_hi(word):
    def greet():
        print "hello %s!" % word
    return greet

root = Tkinter.Tk()
MyButton = partial(Tkinter.Button,root,fg = 'white',bg='blue')
b1 = MyButton(text='Button1',command = say_hi('world'))
b2 = MyButton(text='Button2',command = say_hi('tedu'))
qb = MyButton(text='quit',command = root.quit)
b1.pack()
b2.pack()
qb.pack()
Tkinter.mainloop()

        装饰器

            装饰器是在函数调用至上的修饰

            这些修饰仅是当声明一个函数或者方法的时候,才会应用的额外调用

            使用装饰器的情形有:

            - 引入日志

            - 增加计时逻辑来检测性能

            - 给函数加入事物的能力

#!/usr/bin/env python

import time

def deco(func):
    def timeit():
        start = time.time()
        res = func()
        end = time.time()
        print "Program cost %5.3f seconds" %(end - start)
        return res
    return timeit

@deco
def loop():
    result = []
    for i in range(1,6):
        result.append(i)
        time.sleep(1)
    return result
if __name__ == '__main__':
    print loop()


#!/usr/bin/env python

def color(func):
    def color_font(*argv):
        return "\033[31;1m%s\033[0m" % func(*argv)
    return color_font

@color
def say_hi(word):
    return "Hello %s." % word
@color
def greet(name):
    return 'Welcome %s!' % name

if __name__ == '__main__':
    print say_hi('tedu')
    print greet('bob')

    生成器

        定义生成器

            从句法上讲,生成器是一个带yield语句的函数

            一个函数或者子程序只返回一次,但一个生成器能暂停执行并返回一个中间的结果

            yield语句返回一个值给调用者并暂停执行

            当生成器的next()方法被调用的时候,它会准确的从离开地方继续

                >>> def sim_gen():
                ...   yield 'hello'
                ...   yield 100
                ...   yield [10,20]
                ...
                >>> a = sim_gen()
                >>> a
                <generator object sim_gen at 0xb70b761c>
                >>> a.next()
                'hello'
                >>> a.next()
                100
                >>> a.next()
                [10, 20]
                >>> a.next()
                Traceback (most recent call last):
                  File "<stdin>", line 1, in <module>
                StopIteration

              

                 >>> a = sim_gen()
                >>> for i in a:
                ...   print i
                ...
                hello
                100
                [10, 20]

#!/usr/bin/env python

def blocks(fobj):
    block =[]
    counter = 0
    for line in fobj:
        block.append(line)
        counter +=1
        if counter ==10:
            yield block
            counter = 0
            block =[]
        yield block
if __name__ == '__main__':
    with open("/etc/passwd") as fobj:
        for lines in blocks(fobj):
            print lines

        生成器特性

            用户可以通过send()将值回送给生成器,还可以在生成器中抛出异常,以及要求生成器退出

模块

    模块和文件

        什么是模块

            模块支持从逻辑上组织python代码

            当代码量变得相当大的时候,最好把代码分成一些有组织的代码段

            代码片段相互间有一定的练习,可能是一个包含数据成员和方法的类,也可能是一组相关但彼此独立的操作函数

            这些代码段是共享的,所以python允许“调入”一个模块,允许使用其他模块的属性来利用之前的工作成果,实现代码重用

        模块文件

            说模块十按照逻辑来组织python代码的方法,文件是物理层上组织模块的方法

            一个文件被看作是一个独立模块,一个模块也可以被看作是一个文件

            模块的文件名就是模块的名字加上扩展名py

        名称空间

            名称空间就是一个从名称到对象的关系映射集合

            给定一个模块名之后,只可能有一个模块被导入到python解释器中,所以不同模块间不会出现名称交叉现象

            每个模块都定义了它自己的唯一的名称空间

                echo 'x=10' > foo.py

                echo 'x="hello"' > bar.py

                >>> import foo
                >>> import bar
                >>> foo.x
                10
                >>> bar.x
                'hello'

    导入模块

        搜索路径

            模块的导入需要一个叫做“路径搜索”的过程

            python在文件系统“预定义区域”中查找要调用的模块

            搜索路径在sys.path中定义

            >>> import sys
            >>> print sys.path
            ['', '/usr/lib/python2.7', '/usr/lib/python2.7/plat-i386-linux-gnu', '/usr/lib/python2.7/lib-tk', '/usr/lib/python2.7/lib-old', '/usr/lib/python2.7/lib-dynload', '/usr/local/lib/python2.7/dist-packages', '/usr/lib/python2.7/dist-packages', '/usr/lib/python2.7/dist-packages/PILcompat', '/usr/lib/python2.7/dist-packages/ubuntu-sso-client']

        模块导入方法

            使用import导入模块

            可以在一行导入多个模块,但是可读性会下降

            可以只导入模块的某些属性

            导入模块时,可以为模块取别名

                >>> import time,os,sys

                >>> from random import choice
                >>> import cPickle as P

            避免模块不存在导入失败的小方法

            >>> try:
            ...   import cPickle as p
            ... except ImportError:
            ...   import pickle as p
            ...

        导入和加载

            当导入模块时,模块的顶层代码会被执行

            一个模块不管被导入(import)多少次,只会被加载(load)一次

                $ echo -e "hi = 'hello'\nprint hi" > foo.py

                >>> import foo  #第一次导入,执行print语句
                hello
                >>> import foo #再次导入,print语句不再执行

        从zip文件中导入

            在2.3版中,python加入了从ZIP归档文件导入模块的功能

            如果搜索路径中存在一个包含python模块(.py、.pyc、或.pyo文件)的.zip文件,导入时会把ZIP文件当作目录处理

                #导入sys模块,在搜索路径中加入相应的zip文件

                >>> import sys
                >>> sys.path.append('/root/pymodule.zip')
                >>> import foo #导入pymodule.zip压缩文件中的foo模块

    包

        目录机构

            包是一个有层次的目录结构,为平坦的名称空间加入有层次的组织结构

            允许程序员把有联系的模块组合到一起

            包目录下必须有一个__init__.py文件

phone/

    __init__.py

    common_util.py

    voicedate/

        __init__.py

        post.py

        绝对导入

            包的使用越来越广泛,很多情况下导入子包会导致和真正的标准库模块发生冲突

            因此,所有的导入现在都被认为是绝对的,也就是说这些名字必须通过python路径(sys.path或PYTHONPATH)来访问

        相对导入

            绝对导入特性使得程序员失去了import的自由,为此出现了相对导入

            因为import语句总是绝对导入的,所以相对导入只应用与from-import语句

    内建模块

        hashlibs模块

            hashlib用来替换md5和sha模块,并使他们的API一致,专门提供hash算法

            包括md5,sha1,sha224,sha256,sha384,sha512,使用非常简单、方便

                >>> import hashlib
                >>> m = hashlib.md5()
                >>> m.update("hello world!")
                >>> m.hexdigest()
                'fc3ff98e8c6a0d3087d515c0473f8677'

                >>> import hashlib

                >>> m = hashlib.md5()

                >>> with open('/root/qh/py/ex6.py') as fobj:

                ...   while True:

                ...     date = fobj.read(4096)

                ...     if not date:

                ...       break

                ...     m.update(date)

                ... 

                >>> m.hexdigest()

                '7daba44373fcb6c3aa8dfb25df1cf71a'



        tarfile模块

            tarfile模块允许创建、访问tar文件

            同时支持gzip、bzip2格式

                 >>> import tarfile

                >>> tar = tarfile.open('/home/qihang/demo.tar.gz','w:gz') #创建tar文件,

                >>> tar.add('demo') #向tar文件中添加要打包的文件

                >>> tar.close()

备份程序

    编写一个备份程序

        1、需要支持完全和增量备份

        2、周一执行完全备份

        3、其他时间执行增量备份

        4、备份文件需要打包为tar文件并使用gzip格式压缩