1.名称空间

python有三种名称空间
内置名称空间: 随着python解释器的启动而产生
print(sum)
print(max)

全局名称空间: 文件的执行会产生全局名称空间,指的是文件级别的定义名字都会放入该空间
x = 11
if x == 11:
    print(x)

局部名称空间: 调用函数时会产生局部名称空间,只在函数调用时临时绑定,调用结束时解绑
x = 1000
def foo():
    x = 1
    print(x)
foo()
print(x)

作用域:
    1.全局作用域: 内置名称空间,全局名称空间,全局有效,在任何位置都能被访问到,
    除非del删除,否则存活到文件结束。
    2.局部作用域: 局部名称空间,局部有效,只能在局部范围调用,调用结束失效。

名字的查找顺序: 局部名称空间-->全局名称空间-->内置名称空间
x = 1000
def func():
    x = 2
    print(locals())    #locals()查看局部作用域内的名字
print(globals())    #globals()查看全局作用域内的名字


2.函数对象
    1.可以被引用
    2.可以当作参数传递
    3.返回值可以是函数
    4.可以当作容器类型的元素
    
 例子:
     def foo():
         print("from foo")
     func = foo
     print(foo)
     print(func)
     foo()
     func()
     
     def foo():
         print("from foo")
     def bar(func):
         print(func)
         func()
     bar(foo)
     
     def foo():
         print("from foo")
     dic = {'func': foo}
     print(dic['func'])
     dic['func']()
     
     
     
3.闭包函数
    闭包:
      1.定义在内部函数
      2.包含对外部作用域而非全局作用域的引用
  该内部函数就称作闭包函数
  例子:
  def f1():
    x = 1
    def f2():     #f2称作闭包函数
      print(x)
    return f2
  f = f1()
  print(f)   #打印f2函数的内存地址
  f()    #打印1
 
   例子:
   from urllib.request import urlopen
   def index(url):
       def get():
           return urlopen(url).read()
       return get
   oldboy = index('http://www.baidu.com')
   print(oldboy().decode('utf-8'))
   print(oldboy.__closure__[0].cell_contents)    #打印外部引用而非全局引用对象
   
4.装饰器
    修饰其他对象的工具,修饰添加功能,工具指的是函数。
    装饰器本身是任何可调用对象,被装饰的对象也可以是任何可调用的对象。
    
    为什么要用装饰器?
    开放封闭原则,对修改是封闭的,对扩展是开放的。
    装饰器就是为了在不修改被装饰对象源代码以及调用方式的前提下,为其添加新功能
    
    装饰器相当于闭包的实现
    
    例子:
    import time
    def timmer(func):
        def wrapper(*args, **kwargs):
            start_time = time.time()
            res = func(*args, **kwargs)
            stop_time = time.time()
            print("run time is {0}".format(stop_time - start_time))
        return wrapper
        
    @timmer        #相当于index = timmer(index)
    def index():
        time.sleep(3)
        print("welcome to index")
        return 1
        
    index()    #其实相当于执行了timmer(index)
    
    例子:
    import time
    def timmer(func):
        def wrapper(*args, **kwargs):
            start_time = time.time()
            res = func(*args, **kwargs)
            stop_time = time.time()
            print("run time is {0}".format(stop_time - start_time))
        return wrapper
        
    def index():
        time.sleep(3)
        print("welcome to index")
    
    f = timmer(index)
    print(f)
    f()        #f()<=====>wrapper()
    
    
    认证用户登录
    login_user = {'user': None, 'status': False}
    def auth(func):
        def wrapper(*args, **kwargs):
            if login_user['user'] and login_user['status']:
                res = func(*args, **kwargs)
                return res
            else:
                name = input("请输入姓名:")
                passwd = input("请输入密码:")
                if name == "hyh" and passwd == "123":
                    login_user['user'] = "hyh"
                    login_user['status'] = True
                    print("\033[45mlogin successful\033[0m")
                    res = func(*args, **kwargs)
                    return res
                else:
                    print("\033[45mlogin err\033[0m")
        return wrapper
        
   @auth
   def index():
       print("welcome to index page")
       
   @auth
   def home(name):
       print("%s welcome to home page" % (name)) 
       
   index()
   home("hyh")
   
5.迭代器
    迭代器的概念: 重复上一次迭代的结果为下一次迭代的初始值,重复的过程称为迭代,
    每次重复即一次迭代
    
    为什么要有迭代器?
    对于没有索引的数据类型,必须提供一种不依赖索引的迭代方式
    
    可迭代对象: 内置__iter__方法的都是可迭代对象
    [1,2].__iter__()
    'hello'.__iter__()
    (1,2).__iter__()
    {"a":1, "b": 2}.__iter__()
    {1,2,3}.__iter__()
    
    迭代器: 执行__iter__()方法,得到的结果就是迭代器,迭代器有__next__()方法
    i = [1,2,3].__iter__()
    print(i.__next__())        #打印1
    print(i.__next__())        #打印2
    print(i.__next__())        #打印3
    print(i.__next__())    #抛出异常
    
    i__iter__() <==>iter(i)
    i__next__() <==>next(i)
    
    如何判断一个对象是可迭代对象,还是迭代器对象
    from collections import Iterable, Iterator
    print(isinstance('abc',Iterable))
    print(isinstance([],Iterable))
    print(isinstance((),Iterable))
    print(isinstance({'a':1},Iterable))
    print(isinstance({1,2},Iterable))
    f=open('a.txt','w')
    f.__iter__()
    print(isinstance(f,Iterable))    #只有文件是迭代器对象
    
    可迭代对象:只有__iter__方法,执行该方法得到的迭代器对象
    迭代协议:
        对象有__next__
        对象有__iter__,对于迭代器对象来说,执行__iter__方法,得到的结果仍然是它本身
        
    迭代器的优点和缺点
        优点:
            1.提供了一种不依赖下标的迭代方式
            2.就跌迭代器本身来说,更节省内存

        缺点:
            1. 无法获取迭代器对象的长度
            2. 不如序列类型取值灵活,是一次性的,只能往后取值,不能往前退
            
6.生成器
    生成器函数: 只要函数体包含yield关键字,该函数就是生成器函数,生成器就是迭代器
    例子:
        def foo():
            print("first")
            yield 1
            print("second")
            yield 2
            print("third")
            yield 3
            print("fourth")
        g = foo()
        for i in g:
            print(i)
            
        print(next(g))    #出发迭代器g的执行,进而出发函数
        print(next(g))
        
        yield的功能:
            1.相当于为函数封装好__iter__和__next__函数
            2.return只能返回一次值就终止了
            3.yield能返回多次值,每次返回将函数暂停,下一次next从上一次暂停的位置继续
            
            
        模拟tail -f |grep "python"
        例子:
            import time
            def tail(file):
                with open(file, encoding='utf-8') as f:
                    line = f.readline().strip()
                    if line:
                        yield line
                    else:
                        time.sleep(0.2)
            t = tail('a.txt')
            for line in t:
                print(line)
                
            def grep(pattern, lines):
                        for line in lines:
                            if pattern in line:
                                yield line
                    g = grep("python", tail('one.py'))
                    print(g)
                    for i in g:
                        print(i)
    
    yield两种用法
    1.语句形式: yield 1
    2.表达式用法: x = yield
    
    例子:
        def deco(func):
            def wrapper(*args, **kwargs):
                res = func(*args, **kwargs)
                next(res)        #相当于res.send(None)初始化生成器
                return res
            return wrapper
            
        @deco
        def eater(name):
            print("%s ready to eat" %name)
            food_list = []
            while True:
                food = yield food_list
                food_list.append(food)
                print('%s start to eat %s' %(name, food))
                
        g = eater('alex')
        v = g.send("脚趾头")        #返回food_list列表
            print(v)        
        #send()函数发送数据给yield,跟next作用很相似,只是它能发送数据