目录

一:闭包:

1:闭包的作用和定义:

作用:保存外部函数内的变量,不会随着外部函数调用完而销毁。
定义:在函数嵌套的前提下,内部函数使用了外部函数的变量,外部函数返回了内部函数的对象,我们把这个使用外部函数变量的内部函数称为闭包。

2:闭包的形成条件:

1: 内部函数使用了外层函数的变量。
2:外部函数返回了内部函数的对象。

3:闭包的经典案例:

def func_out(num1):
    def func_inner(num2):
        result = num1 + num2;
        print("结果是:",result)
    return func_inner

f = func_out(1)

4:闭包的原理:

变量当自己的引用计数等于0的时候就会被销毁。
当将1赋值给num1的时候num1的引用计数是1,然后定义fun_inner,由于fun_inner里面用到了num1(result = num1 + num2;),所以num1的引用计数变成了2,最后函数调用完成时,销毁所有变量(本质是引用变量减一),所以num1引用计数又变成了1。所以外层函数调用完,变量是不会销的。

5: 闭包中使用外部函数变量:

  • 闭包中使用外部函数的变量需要使用nonlocal声明,否则使用的是闭包内部自己定义的变量。
def func_out(num1):
    def func_inner(num2):
        nonlocal num1 
        num1 = 10
        result = num1 + num2
        print("结果是:", result)
    return func_inner
f = func_out(1)
f(2)

二:装饰器:

1:装饰器的定义和作用:

1:定义与作用:
答:在不修改原函数及其调用方式的情况下对原函数功能进行扩展。
2:装饰器和闭包的区别:
装饰器外层函数只有一个变量,且变量类型是函数类型。

2:装饰器经典案例分析:

def check(fn):
    def inner():
        print("请先登录")
        fn()
    return inner
    
def comment():
    print("发表评论")

comment = check(comment)
comment()

分析:comment = check(comment)
答:
1:将被装饰函数传入装饰器的外层函数,由装饰器 的内层函数调用,也就是装饰器内层函数可以调用被装饰函数。
2:外层函数返回内层函数对象,达到的效果是最终的对象执行的是内层函数。
3:这样在内层函数中增加功能,就可以达到不改变原函数及其调用方式的情况下对原函数功能进行扩展。

3: 装饰器的两种写法:

<方案一>: 装饰器外层函数,传入被装饰函数。

def check(fn):
    def inner():
        print("请先登录")
        fn()
    return inner
    
def comment():
    print("发表评论")
# 函数调用的写法
comment = check(comment)
comment()

<方案二>: 语法糖写法:

def check(fn):
    def inner():
        print("请先登录")
        fn()
    return inner
# 语法糖写法:
@check
def comment():
    print("发表评论")
comment()

4:通用装饰器的写法:

def func_out(fn):
    def func_inner(*args,**kwargs):
        print("----输出执行函数前的增加功能")
        data = fn(*args,**kwargs)
        print("-----输出函数执行后的增加功能")
        return data
    return func_inner

5:多个装饰器的执行流程:

def add_div(fn2):
    def inner_div():
        return "<div>" + fn2() + "</div>"
    return inner_div

def add_p(fn1):
    def inner_p():
        return "<p>"+fn1()+ "</p>"
    return inner_p

@add_div
@add_p
def content():
    return "人生苦短,我用python!"

main_content = content()
print(main_content)

分析装饰的过程:
1: 装饰的过程是先装饰近的再装饰远的。
2:先进行@add_p, 被修改函数content传入add_p, 此时fn1指向被修饰函数。add_p返回自己的内层函数,则此时的main_content指向的是inner_p这个内层函数。
3:再进行@add_div:将inner_p传入add_div,此时fn2指向fn1的内层函数。add_div返回自己的内层函数,所以main_content指向的是inner_div这个内层函数。
4:最终结果是:main_content 指向inner_div, fn2指向inner_p, fn1指向content。

分析调用过程(递归过程):
content()实际上等于inner_div(),所以先调用inner_div,然后执行 fn2(),实际上是inner_p(),然后执行fn1实际上是content()函数。然后从content函数开始向上返回,content向上返回给inner_p:"人生苦短,我用python!",inner_p组合后向上返回给inner_div:<p>人生苦短,我用python!</p>,然后inner_div向上返回给主程序<div><p>人生苦短,我用python!</p></div>

6:带参装饰器:

  • 在装饰器外层再套一层函数用来传递参数,返回内层函数。
  • 由于这个外层函数传入变量,返回内层函数,内层函数使用外层函数变量,所以修饰器实际上是一个闭包了,而披上的是外层函数,闭包内可以访问外层函数变量。
  • @logging("+"):可以拆分成两部分:logging("+")传递给装饰器外层的函数,返回一个装饰器类型的对象out_num 然后@和out_num 又拼接成语法糖。
def logging(flag):
    def out_num(fn):
        def inner_num(x,y):
            if flag == "+":
                print("这是加法操作的准备工作")
                sum2 = fn(x,y)
                return sum
        return inner_num
    return out_num
    
@logging("+")
def add_num(a,b):
    sum = a+b
    return sum
sum = add_num(1,2)
print(sum)

7:类装饰器:

  • 1: 一个类的__init__方法能够传递一个函数,就相当于装饰器的外层函数了。
  • 2:一个类的__call__方法中调用传入的函数,就相当于装饰器的内层函数了。
  • 3: 在__all__中实现拓展功能。
  • 综上所述,一个类实现__init__方法和实现__call__方法就相当于一个装饰器了。
class Check(object):
    def __init__(self ,fn):
        self.__fn = fn;
    def __call__(self, *args, **kwargs):
        print("发表评论前的准备工作。。。")
        self.__fn()   
@Check
def comment():
    print("发表评论")
comment()

8:装饰器的反装饰:

  • 问题:使用语法糖装饰时, 被装饰器装饰过的函数,打印函数的注释和函数名,发现都改成了装饰器内层函数名和注释了,如果想要用自己原来的函数名和注释,怎么做?
  • 解决方案一:导入wraps, 然后装饰装饰器的内层函数: @wraps(传入的函数名)
  • 解决方案二:不再使用语法糖装饰,改用函数调用的方式。
from functools import wraps

def deco(func):
    @wraps(func) #加在最内层函数正上方
    def wrapper(*args, **kwargs):
        return func(*args, **kwargs)
    return wrapper

@deco
def index():
    '''哈哈哈哈'''
    print('from index')

print(index.__doc__)
print(index.__name__)

三:迭代器:

1:可迭代对象:

如果一个类实现了__iter__方法,则根据这个类创建出来的对象都是可迭代对象。

2:对象能够迭代的条件

1: 这个类实现了__iter__方法。
2:这个类的__iter__方法必须返回一个对象。
3:这个被返回的对象的类(迭代器),必须实现了__iter__和__next__方法。

3:什么是迭代器?

由实现了__iter__方法和__next__方法的类,创建出来的对象,称为迭代器。

4:解析for循环遍历的本质:

for循环遍历的流程
1:判断这个classmate是不是可迭代对象,发现Classmate实现了__iter__方法,所以是可以迭代的。
2:判断classmate是不是可以迭代,发现__iter__返回的是自己,而自己实现了__iter__和__next__方法,所以可以迭代。
3:此时python内部调用iter()方法获取到一个迭代器。
4:接下来每次遍历迭代器的时候,就调用一次netxt(迭代器),这个方法。
5:当遍历到最后的时候,抛出StopIteration异常,python自动捕获后,停止遍历。

from collections import Iterable, Iterator
class Classmate(object):
    def __init__(self):
        self.names = list()
        self.current_num = 0
    def add(self, name):
        self.names.append(name)
    def __iter__(self):
        # 1:将自己的引用传递过去
        return self

    def __next__(self):
        if self.current_num < len(self.names):
            ret = self.names[self.current_num]
            self.current_num += 1
            return ret
        else:
            raise StopIteration
classmate =Classmate()
classmate.add("张三")
classmate.add("李四")
classmate.add("王二麻子")

for name in classmate:
    print(name)

5:range python2与python3的区别:

面试题:range与xrange的区别:
答: python2,range()函数返回的是一个数组。python3时,range函数返回的是一个可以迭代器。优点在于如果range的数量很大,列表将占用很多的资源,但是迭代器会一个一个的生成,占用的空间很少。这也是python2中, range和xrange的区别。

6:利用迭代器实现斐波那契数列:

python3大器----装饰器,迭代器,生成器_python

四:生成器:

1: 生成器的概念:

  • 生成器是一种特殊的迭代器。
  • 一个函数的里面使用到yield,则这个函数就是生成器了。
  • yield作用:让程序在yield关键字处暂停,返回yield处的值。
  • 生成器的执行本质也是调用next()函数。

2:使用生成器完成斐波那契额数列:

python3大器----装饰器,迭代器,生成器_python_02

3:通过send启动生成器:

python3大器----装饰器,迭代器,生成器_生成器_03
解释过程:当执行到obj = create_num(10)的时候,相当于把10给了all_num,然后创建了一个生成器对象。当执行到ret = next(obj)的时候,生成器开始执行,执行到yield的时候,生成器把此时a的值给了ret,然后程序暂停。然后再打印print(ret)。当执行到ret = obj.send(‘hahaha’)时,send函数也能将程序唤醒,并把输入的值传给循环中的ret, 然后程序继续执行。

五:探索for循环的流程:

<探索一>: 直接遍历尝试:

class Classmate(object):
    def __init__(self):
        self.names = list()
    def add(self, name):
        self.names.append(name)

classmate =Classmate()
classmate.add("张三")
classmate.add("李四")
classmate.add("王二麻子")

for name in classmate:
    print(name)

如果直接遍历会报错误:
python3大器----装饰器,迭代器,生成器_生成器_04
<探索二>:实现__iter__后:

class Classmate(object):
    def __init__(self):
        self.names = list()
    def add(self, name):
        self.names.append(name)
    def __iter__(self):
        pass
classmate =Classmate()
classmate.add("张三")
classmate.add("李四")
classmate.add("王二麻子")

for name in classmate:
    print(name)

python3大器----装饰器,迭代器,生成器_迭代_05
<探索三>: 判断此时是不是可迭代对象?

from collections import Iterable
class Classmate(object):
    def __init__(self):
        self.names = list()
    def add(self, name):
        self.names.append(name)
    def __iter__(self):
        pass
classmate =Classmate()
classmate.add("张三")
classmate.add("李四")
classmate.add("王二麻子")

# 判断此时是不是可迭代对象?
print("'判断是不是可迭代对象:", isinstance(classmate, Iterable))

# for name in classmate:
#     print(name)

python3大器----装饰器,迭代器,生成器_函数调用_06
<探索四>:让__iter__方法返回一个迭代器,并判断是否是迭代器。

from collections import Iterable, Iterator
class Classmate(object):
    def __init__(self):
        self.names = list()
    def add(self, name):
        self.names.append(name)
    def __iter__(self):
        return ClassIteror()

class ClassIteror(object):
    def __iter__(self):
        pass
    def __next__(self):
        return 11

classmate =Classmate()
classmate.add("张三")
classmate.add("李四")
classmate.add("王二麻子")

# iter(对象),就会调用这个对象的__iter__方法。
classmate_iteror = iter(classmate)
print("判断是否是迭代器:", isinstance(classmate_iteror, Iterator))
# next(迭代器对象):就会调用这个对象的__next__方法。
print(next(classmate_iteror))

python3大器----装饰器,迭代器,生成器_函数调用_07
<探索五>:如何让__next__拿到列表进行遍历呢?

from collections import Iterable, Iterator
class Classmate(object):
    def __init__(self):
        self.names = list()
    def add(self, name):
        self.names.append(name)
    def __iter__(self):
        # 1:将自己的引用传递过去
        return ClassIteror(self)

class ClassIteror(object):
    # 2: 接收引用
    def __init__(self, obj):
        self.obj = obj
    def __iter__(self):
        pass
    def __next__(self):
        # 调用方法获取数据
        return self.obj.names[0]

classmate =Classmate()
classmate.add("张三")
classmate.add("李四")
classmate.add("王二麻子")

for name in classmate:
    print(name)

此时会一值打印老王。
<探索六>: 怎样才能一个一个的遍历呢?并且怎样能控制越界呢?

from collections import Iterable, Iterator
class Classmate(object):
    def __init__(self):
        self.names = list()
    def add(self, name):
        self.names.append(name)
    def __iter__(self):
        # 1:将自己的引用传递过去
        return ClassIteror(self)

class ClassIteror(object):
    # 2: 接收引用
    def __init__(self, obj):
        self.obj = obj
        self.current_num = 0
    def __iter__(self):
        pass
    def __next__(self):
        if self.current_num < len(self.obj.names):
            ret = self.obj.names[self.current_num]
            self.current_num += 1
            return ret
        else:
            raise StopIteration
classmate =Classmate()
classmate.add("张三")
classmate.add("李四")
classmate.add("王二麻子")

for name in classmate:
    print(name)

<七>: 代码简化:

from collections import Iterable, Iterator
class Classmate(object):
    def __init__(self):
        self.names = list()
        self.current_num = 0
    def add(self, name):
        self.names.append(name)
    def __iter__(self):
        # 1:将自己的引用传递过去
        return self

    def __next__(self):
        if self.current_num < len(self.names):
            ret = self.names[self.current_num]
            self.current_num += 1
            return ret
        else:
            raise StopIteration
classmate =Classmate()
classmate.add("张三")
classmate.add("李四")
classmate.add("王二麻子")

for name in classmate:
    print(name)