LF 第三章 装饰器和迭代器相关

名称空间

顾名思义就是存放名字的地方,比如,若变量x=1,1存放在内存里,那x存在哪里?名称空间就是存放名字x与1绑定关系的地方

x:内存地址                               1所在的内存

名称空间共3种,分别如下:

locals:是函数内的名称空间,包括局部变量和形参

globals:全局变量,函数定义所在模块的名字空间

builtins:内置模块的名字空间

作用域的查找空间

作用域的查找顺序

LEGB

L:locals :函数的名字空间,包括局部变量和形参

E:enclosing 相邻的上一级 外部嵌套函数的名字空间

G:globls 全局变量,函数定义所在的模块名字空间

B:builtins 内置模块的名字空间

闭包

举例:

def func():
    n = 10
    def func2():
        print("n=",n)
    return func2

f = func()
f()

从理论上说,当f = func()时候,func已经关闭,那么n = 10这个内部局部变量就应该释放了,但是f()仍能够打印出10,这是为什么呢?

这就是闭包的概念

整改代码发生的现象就是一个闭包

f2是放在f里面,原来是根本拿不到f2,如果要执行只能在f里面执行

但是现在 f返回了f2名称 相当于在外部拿到了里面的函数。

即 func如果返回了内部的一个东西,并且还在使用中,那么他的内存空间不会释放的。

我们在外部可以执行内部的函数,并且可以用内部函数作用域里面的所有值,这就是闭包

原来f2放在func里面,在外面是不知道f2存在,只能在外面执行,但是把内层函数名称返回,在外部拿到了里面的函数,那其实func返回内部东西,还在使用中,内存空间没被释放。效果:我们在外部可以执行内部函数,并且可以用他内部函数作用域里的所有的值。

装饰器

代码有两个原则

开放-封闭原则:开放 对现有功能的扩展开放  封闭:已实现的功能代码块不应该被修改

不改变调用方式

在符合开放封闭原则的情况下,给代码加新功能

# -*- coding:utf-8 -*-
def login(func):
    def inner():
        _username = '111'
        _password = '222'
        global user_status
        if user_status == False:
            username = input("user:")
            password = input("pasword:")

            if username == _username and password == _password:
                print("welcom login...")
                user_status = True
            else:
                print("wrong!")
        if user_status == True:
            func()
    return inner

def home():
    print("---首页----")
#home = login(home())
@login
def america():
    print("----欧美----")

def japan():
    print("----日本----")
@login
def henan():
    print("----河南----")

user_status = False

home()
america()
henan()

个人理解:通过高阶嵌套函数,将原来的代码块进行包装

home = login(home()) 此home非旧home, 

如果遇到传参情况,为了方便装饰器能用多个代码块上,则需要传入非固定参数 *args **args,注意返回方法同样用**
# -*- coding:utf-8 -*-
def login(func):
    def inner(*args,**kwargs):
        _username = '111'
        _password = '222'
        global user_status
        if user_status == False:
            username = input("user:")
            password = input("pasword:")

            if username == _username and password == _password:
                print("welcom login...")
                user_status = True
            else:
                print("wrong!")
        if user_status == True:
            func(*args,**kwargs)
    return inner

def home():
    print("---首页----")
#home = login(home())
@login
def america():
    print("----欧美----")

def japan():
    print("----日本----")
@login
def henan(style):
    print("----河南----")

user_status = False

home()
america()
henan('3p')

如果装饰器带参数,那么需要再套一层函数

# -*- coding:utf-8 -*-
def login(auth_type):
    def outer(func):
        def inner(*args,**kwargs):
            _username = '111'
            _password = '222'
            global user_status
            if user_status == False:
                username = input("user:")
                password = input("pasword:")

                if username == _username and password == _password:
                    print("welcom login...")
                    user_status = True
                else:
                    print("wrong!")
            if user_status == True:
                func(*args,**kwargs)
        return inner
    return outer
def home():
    print("---首页----")
#home = login(home())
def america():
    print("----欧美----")

def japan():
    print("----日本----")
@login('qq')
def henan(style):
    print("----河南----")

user_status = False

home()
america()
henan('3p')

银角大王的解释我觉得很容易懂

@login
def henan():
    pass

#在这里 @ 相当于把下面的函数henan当做一个参数,传给了login这个方法,那么

@login('qq')
def henan():
    pass
#这里 就相当于将函数henan当做参数,传给了login('qq')这个执行后的return的函数

装饰器需要重点复习

列表生成

a = [2,3,4,5,6,7,8,9]
b = a.copy()
c=a.copy()
for i in range(len(a)):
    a[i]+=1
print(a)

#列表生成式
b = [i+1 for i in b ]
print(b)
#高级一点例子

c = [i if i<6 else i*i for i in a]
print(c)

继续用银角大王的总结:

for i in a 做一个循环,前面相当于循环缩进下面得每一个i 每循环一次前面都会执行一次,结果会当做元素挨个存放,这时候,a 就可以循环任何东西,列表元组字典字符串等等。

这个只能写到列表里和元组里

 生成器

 假设一个场景,我需要从一个列表中取数据,而这个列表可能长度是10或者100万,这样的话难道要预先生成一个100万长度的列表吗?不会的,因为非常占用内存空间

解决办法:生成器

如果我知道一个列表的规律,那么我就可以制定一个规则,当我需要什么值时候,我再通过规则去生成这个值,这样就避免了空间浪费。

a = (i for i in range(5))
>>> < generator object<genexpr> at 0xkasjdfljals>
#这就是一个生成器generato
#如果要一个一个打印出来,可以通过next()函数获得generator的下一个返回值:
next(a)
#1.只能往后输出,不能回头
#2.超出边界,报错
#生成器保存的是一个算法,超出边界就会跑出stopIteration错误

  


生成器调用方式
用next()一个一个调很不现实,一般用for循环调

a = (i for i in range(5))
for i in a3:
  print(i)

与next区别,超过范围不报错,直接跳出循环

这个会报错

while True:
    next(a3)
range(100) <<底层就是生成器 python2里打印,python3里就是> range(0,100) python2里替代range的:xrange()

斐波那契数列

小注意点:

a = 1,b = 2
a,b= b,a+b

此时a和b是多少?答案是 2,和3

为什么呢 ?不知道

函数调用

def fib(max):
  n,a,b = 0,0,1
  while n < max:
    yield b #通过函数生成了一个生成器 这个语法相当于 :出现了yield,就相当于将这个函数变成了生成器 函数执行到这里就返回了(冻结,函数没有结束) 通过next(f) 函数才能继续往下走。。把函数的执行过程冻结在这一步,并且把b的值返回给外面的next()
    a,b = b,a+b
    n = n+1
return 'done'
print(fib(15))
f = fib(15)
>><generator ...>

变成了一个生成器

print(next(f))
...
0 0 1 2 3 5..

刚开始执行时候,f = fib(15) python将他变成一个生成器
只有next(f) 从函数开始,到yield结束
下一次next(f) ,则从上一次yield后面开始,执行到这一次yield之前。
好处是,函数执行-等结果
如果这样 函数执行一小会儿,把函数执行过程中的某个状态,返回出来。原来只能到结束,现在可以函数执行过程中返回到外部,多次

总结:
1。函数内加了yield 函数名加括号,函数根本不执行,只是生成了一个生成器对象
2.yield把函数的执行过程冻结在这一步(没有结束)
3。并且把b的值返回给外面的next()

for i in a:
循环这个生成器,不用next方法,不会报错
while...
用next方法,会报错

range(10) python2 :[0,1,2,3,4,5,6,7,8,9]
python3:range(0,10)
python2中有个方法跟python3中range一样:xrange

生成器的创建方式
1.类似列表生成式    []>>>()    最复杂的方式只能写一个三元运算
2.函数生成器

def range2(n):
    count = 0
    while count < n:
        print(count)
        count += 1
range2(10)        

next(f) 就等于 f.__next__()

如果这样

def range2(n):
    count =0
    while count<n 
        count+=1
        yield count
        print(----)
    return x >>>>>会报错                                

总结:
python2
  range = list
  xrange = 生成器generator
python3
  range = 生成器generator
  xrange 没有

yield vs return
  return 返回并终止function
  yield 返回数据,并冻结当前的执行过程

  next 唤醒冻结的函数执行过程,继续执行,直到遇到下一个yield


1.函数有了yield之后,函数名加()就得到了一个生成器,生成器就必须next(开始执行,如果终结了还能唤醒
2 return在生成器里代表生成器的终止,直接报错


如果需要中断,range.send("xxx") 传给yield
sign = yield asef
通过判断sign 可以break可以return退出

迭代器
理解:迭代一次=循环一次

可以直接作用于for循环的数据类型
一类是集合类型:list tuple dict set str
一类是生成器,包裹开yield的generator function
可以直接作用于for循环的对象统称为可迭代对象 Iterable()注意这是可迭代对象 Able
可以使用.isinstance()判断一个对象是否是Iterable对象 isinstance('abc',Iterable)>>>>True

一切皆对象

:::可以被next()调用并且返回下一个值的对象成为迭代器Iterator 这个是迭代器 Tor
(生成器 是 迭代器的一种)
迭代器都是可迭代对象
可迭代对象不都是迭代器
集合类就不是
如何将可迭代对象转化为迭代器?
通过iter方法
isinstance('abc',Iterator)>>>>False
str = iter('abc')
这样 str 就是迭代器,可以使用next()方法,

str.__next__()>>
'a'
'b'
'c'
traceback....

迭代器对象是一个数据流,迭代器对象可以被next()函数调用并不断返回下一个数据,知道没有数据时抛出StopIteration错误。可以把这个数据流看做是一个有序的序列,但我们却不知道序列的长度,只能不断通过next()函数实现按需计算下一个数据,所以迭代器的对象都是惰性的,只有在需要返回下一个数据时他才会计算
迭代器甚至可以表示一个无限大的数据流,列入全体自然数,而list永远不可能存储所有自然数。。
总结:迭代器和可迭代

总结:
凡是可以for循环的 都是可迭代对象 iterable
凡是可作用于next()的 都是Iterator对象 迭代器对象
集合数据类型 list tuple dict str 是可迭代对象,但不是迭代器,可以通过iter()获得一个迭代器对象
Python3的for循环本质上就是通过不断调用next()函数来实现的

原文地址:https://www.cnblogs.com/alexstraze/p/9274343.html