python高级特性

1.生成式与生成器

1.列表生成式
列表生成式就是一个用来生成列表的特定语法形式的表达式。
是Python提供的一种生成列表的简洁形式, 可快速生成一个新的list。

普通的语法格式:[exp for iter_var in iterable]

带过滤功能语法格式: [exp for iter_var in iterable if_exp]

循环嵌套语法格式: [exp for iter_var_A in iterable_A for iter_var_B in iterable_B]
例:求1-50所有数的平方  square = [(i + 1) ** 2 for i in range(50)]
    求以r为半径的圆的面积和周长(r的范围从1到10)  
    import math
    circle = [(math.pi * (r ** 2), 2 * math.pi * r) for r in range(1, 11)]

集合生成式和字典生成式
字典生成式:用来快速生成字典;
集合生成式:用来快速生成集合
在这里插入图片描述
2.生成器Generator

什么叫生成器?
在Python中,一边循环一边计算的机制,称为生成器:Generator。

什么时候需要使用生成器
一般情况下我们不需要使用生成器,只有当我们因为性能限制才需要用到,比如我们使用python读取一
个10g的文件,如果一次性将10g的文件加载到内存处理的话(read方法),内存肯定会溢出;这里如果可以
使用生成器把读写交叉处理进行,比如使用(readline和readlines)就可以再循环读取的同时不断处理,这样就可以节省大量的内存空间.

如何创建生成器?
第一种方法: 列表生成式的改写。 []改成()
第一种方法: yield关键字。

如何打印生成器的每一个元素呢?
通过for循环, 依次计算并生成每一个元素。
如果要一个一个打印出来,可以通过next()函数获得生成器的下一个返回值。

生成器的特点是什么?

  1. 节约内存
  2. 迭代到下一次的调用时,所使用的参数都是第一次所保留下的,即是
    说,在整个所有函数调用的参数都是第一次所调用时保留的,而不是新
    创建的。

生成器的应用案例: 斐波那契数列(非递归方式)
除第一个和第二个数外,任意一个数都可由前两个数相加得到.
1, 1, 2, 3, 5, 8, 13, 21, 34,

代码:

def fib(n):
    """
    显示多少个fib数列
    :param n:
    :return:
    """
    # a代表第一个数, b代表第2个数, 也就是要显示的数; count: 当前已经显示fib数列的个数;当前为0;
    a, b, count = 0, 1, 0
    # 0<5
    while count < n:
        # print(b)
        yield  b
        # a, b = b, a+b
        a, b = b, a+b
        # 已经显示的次数加1
        count += 1
# f是一个生成器(函数里面有yield)
f = fib(10)
# for i in f:
#     print(i)
while True:
    try:
        print(next(f))
    except:
        break

在这里插入图片描述
代码:

"""
基于yield计算平均值
# 17行;
"""
def averager():
    # 所有数的和, 默认为0;
    total = 0.0
    # 数值的个数;
    count = 0
    # 平均值结果;
    average = None
    # 所有数值存储的容器(List);
    all_items = []
    while True:
        # 函数包含yield关键字
        new_item = yield average, all_items
        all_items.append(int(new_item))
        total += new_item
        count += 1
        average = total / count
def main():
    # AVERAGER是个生成器;
    AVERAGER = averager()
    # 第一次调用next方法, 遇到yield停止,
    next(AVERAGER)
    # 死循环, 依次求解平均值;
    while True:
        new_num = input("请输入求平均值的数: ")
        if new_num == 'q':
            print("程序执行结束.....")
            break
        # 1). 通过send方法将求平均值的数值传到yield所在位置,(14行);
        # 2). send方法的返回值是求平均值的列表和平均值结果;
        average, all_items = AVERAGER.send(int(new_num))
        print(all_items, "的平均值为:", average)
main()

生产者/消费者模型

  1. 为什么要使用生产者消费者模型?
    在并发编程中,如果生产者处理速度很快,而消费者处理速度比较慢,那么生产者就必须等待消费者处理完,才能继续生产数据。同样的道理,如果消费者的处理能力大于生产者,那么消费者就必须等待生产者。为了解决这个等待的问题,就引入了生产者与消费者模型。让它们之间可以不停的生产和消费。
  2. 实现生产者消费者模型三要素:
    生产者,消费者,队列(或其他的容哭器,但队列不用考虑锁的问题)
  3. 什么时候用这个模型?
    程序中出现明显的两类任务,一类任务是负责生产,另外一类任务是负责处理生产的数据的(如爬虫)
  4. 用该模型的好处?
    1、实现了生产者与消费者的解耦和
    2、平衡了生产力与消费力,就是生产者一直不停的生产,消费者可以不停的消费,因为二者不再是直接沟通的,而是跟队列沟通的。

传统的生产者-消费者模型是一个线程写消息,一个线程取消息,通过锁机制控制队列和等待,但一不小心就可能死锁。
现在改用协程(yield),生产者生产消息后,直接通过yield跳转到消费者开始执行,待消费者执行完毕后,切换回生产者继续生产,效率极高。

例代码:

import time
import random

def Consumer(name):
    goods = ['电视','手机','电脑']
    buy_name = random.choice(goods)
    print('[%s]需要购买%s'%(name,buy_name))
    while True:
        game = yield buy_name
        print('[%s]购买%s成功'%(name,game))

def Produder(name):
    consumers = [Consumer('消费者%s'%(i+1)) for i in range(10)]
    for consumer in consumers:
        buy_name = next(consumer)
        print('[%s]正在生产%s'%(name,buy_name))
        time.sleep(1)
        print('[%s]生产%s成功' % (name,buy_name))
        consumer.send(buy_name)
def main():
    Produder('星')
main()

运行截图:
在这里插入图片描述

2.迭代器
迭代是访问容器元素的一种方式。迭代器是一个可以记住遍历的位置的对象。
迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。访问完后再调用迭代器遍历发现没有内容,因为next()方法从上一次停止的地方继续执行,只能向后算(相当与指针到了最后)。

可迭代对象:可以直接作用于for循环的对象(如何判断是否可以迭代?)
一类是集合数据类型,如list, tuple,dict, set,str等;
一类是generator,包括生成器和带yield的generator function。

Python面试真题: 迭代器和生成器的区别
在这里插入图片描述

  1. 生成器 --(都是)—> 迭代器(next) ----(都是)—> <–(不一定是iter)---- 可迭代对象(for)

如何判断一个对象是不是可迭代对象?
在collections包里导入Iterable,用isinstance()方法判断类型,返回True/False
代码:

# py2: from collections import Iterable
# py3: from collections.abc import Iterable
from collections.abc import Iterable

print(type('hello') == str)
print(isinstance('hello', str))

print(isinstance(1, Iterable))
print(isinstance('hello', Iterable))

#判断文件对象是不是可迭代对象?
with open('./doc/passwd.txt', 'r') as f:
    print(isinstance(f, Iterable))

3.闭包

什么是闭包?

闭包的概念就是当我们在函数内定义一个函数时,这个内部函数使用了外部函数的临时 变量,且外部函数的返回值是内部函数的引用时,我们称之为闭包。
内部函数对外部函数作用域里变量的引用(非全局变量),则称内部函数为闭包。

闭包需要满足的三个条件:
1. 函数内定义一个函数
2. 内部函数使用了外部函数的临时变量
3. 外部函数的返回值是内部函数的引用(指的就是内部的函数名)
闭包的一个常用场景就是装饰器。在装饰器里会大量用闭包。

例:
在这里插入图片描述
函数line与变量a,b构成闭包。在创建闭包的时候,我们通过line_conf的参数a,b说明了这两个变量的取值,
这样,我们就确定了函数的最终形式(y=x+1和y=4x+5)。
优点: 闭包也具有提高代码可复用性的作用。
在这里插入图片描述

4.装饰器Decorator
装饰器本质上是一个函数,该函数用来处理其他函数,它可以让其他函数在不需要修改代码的前提下增加额外的功能,装饰器的返回值也是一个函数对象。它经常用于有切面需求的场景,
比如:插入日志、性能测试、事务处理、缓存、权限校验等应用场景

为什么需要装饰器?
写代码要遵循 开放封闭 原则,虽然在这个原则是用的面向对象开发,但是
也适用于函数式编程,简单来说,它规定已经实现的功能代码不允许被修改,
但可以被扩展,即:
封闭:已实现的功能代码块
开放:对扩展开发

写好一个装饰器后用’@'符调用装饰器(就是语法糖),写在被装饰函数前面。

例:
在这里插入图片描述
被装饰器装饰果的函数,看起来是在调用这个函数,其实这个函数指向的是装饰器所返回的那个函数decorator,实际是在调用decorator函数。

1.通用装饰器
由于不同的函数里面传的参数不同,如果装饰器里面参数用一种格式,那么它所可以装饰的函数就只能是一种类型,比如函数里传一个参数和传俩个参数是不一样的,这样极大的限制了装饰器的使用,所以我们要用一种通用的装饰器来装饰所有种类的函数。
在这里插入图片描述
如上图所示:只要在wrapper里写入(*args,**kwargs),不管传进来的函数有何种类型的参数,装饰器都可以使用。

装饰器:

def decorate(fun):
    def wrapper(*args, **kwargs):
        result = fun(*args, **kwargs)
        # 返回被装饰函数的返回值;
        return result
    return wrapper
@decorate    ===> add=decorate(add)  ---> add指向wrapper函数位置
def add():
    return 'ok'
add()

2.多个装饰器装饰
当有多个装饰器装饰同一个函数时,上面装饰器先执行,下面的装饰器后执行,但其真正原理是函数先被下面的装饰器先装饰,下面装饰器做返回的wrapper又被上面的装饰器装饰。
举个例子:

# 1). 判断用户是否登录?
# 2). 判断用户是否有权限?
# 系统中的用户信息;
db = {
    'root': {
        'name': 'root',
        'passwd': 'westos',
        'is_super': 0  # 0-不是 1-是
    },
    'admin': {
        'name': 'admin',
        'passwd': 'westos',
        'is_super': 1  # 0-不是 1-是
    }
}
# 存储当前登录用户的信息;
login_user_session = {}
def is_login(fun):
    """
    判断用户是否登录, 如果没有登录,先登录
    :param fun:
    :return:
    """
    def wrapper1(*args, **kwargs):
        if login_user_session:
            result = fun(*args, **kwargs)
            return result
        else:
            print("跳转登录".center(50, '*'))
            user = input("User: ")
            passwd = input('Password: ')
            if user in db:
                if db[user]['passwd'] == passwd:
                    login_user_session['username'] = user
                    print('登录成功')
                    # ***** 用户登录成功, 执行删除学生的操作;
                    result = fun(*args, **kwargs)
                    return result
                else:
                    print("密码错误")
            else:
                print("用户不存在")
    return wrapper1

def is_permission(fun):
    def wrapper2(*args, **kwargs):
        print("判断是否有权限......")
        current_user = login_user_session.get('username')
        permissson = db[current_user]['is_super']
        if permissson == 1:
            result = fun(*args, **kwargs)
            return result
        else:
            print("用户%s没有权限" % (current_user))
    return wrapper2
"""
**** 被装饰的过程: 
1). delete = is_permission(delete)   # delete = wrapper2
2). delete = is_login(delete)        # delete = is_login(wrapper2)       # delete = wrapper1
"""

@is_login  # delete = is_login(delete)
@is_permission
def delete():
    return "正在删除学生信息"
"""
*******被调用的过程: 
delete()    ------>  wrapper1()   ---> wrapper2()  ---> delete()
"""
result = delete()
print(result)

3.带参数的装饰器

因为装饰器里只能传函数,所有如果要给装饰器里传递参数,就要给这个装饰器外再加一层函数,相当于双层装饰器,先返回外层装饰器(函数),然后由返回的函数执行内层装饰器
运行代码时设置断点看就会很清晰
例:

# 如果装饰器需要传递参数, 在原有的装饰器外面嵌套一个函数即可
def auth(type):
    def wrapper1(fun):
        def wrapper(*args, **kwargs):
            if type=='local':
                user = input("User:")
                passwd = input("Passwd:")
                if user == 'root' and passwd == 'westos':
                    result = fun(*args, **kwargs)
                    return result
                else:
                    print("用户名/密码错误")
            else:
                print("暂不支持远程用户登录")
        return wrapper
    return wrapper1

"""
# python中装饰器的语法糖结构: @funName  ===> 
# python中装饰器结构如果为@funName()  ====> 默认跟的不是函数名, 先执行外函数, 获取函数返回结果  ====》 @funNameNew
@函数名      add=函数名(add)
@函数名()     @新的函数名    
"""
@auth(type='remote')
def home():
    print("这是主页")
home()

5.内置高阶函数
函数式编程的一个特点就是,允许把函数本身作为参数传入另一个函数,还允许返回一个函数!Python对函数式编程提供部分支持。

高阶函数英文叫Higher-order function。什么是高阶函数?
把函数作为参数传入,这样的函数称为高阶函数,函数式编程就是指这种高度抽象的编程范式。

1.map()
map() 会根据提供的函数对指定序列做映射。
第一个参数接受一个函数名,后面的参数接受一个或多个可迭代的序列,返回的是一个集合。
在这里插入图片描述
例:

例1.使用map转换数值为整形
map_num = map(int, nums)
sorted_num  = sorted(map_num)
print(sorted_num)

例2.
def line(num1, num2):
    return  num1 *10 + num2
# map含有多个序列
nums1 = range(2, 6)
nums2 = range(1, 6)
# nums1 = 2   3  4   5
# nums2 = 1   2  3   4  5
# line=====> num1 * 10 + num2
# result = 21 32 43 54
result = list(map(line, nums1, nums2))
print(result)

2.reduce()
reduce() 函数会对参数序列中元素进行累积。
第一个参数接受一个函数名,后面的参数接受一个或多个可迭代的序列,返回的是一个结果。
在这里插入图片描述
例:

from functools import reduce
# 求两个数值和的匿名函数定义;
add = lambda x, y: x + y
# reduce的工作机制: result=add(add(add(1, 2), 3), 4)
result = reduce(add, [1, 2, 3, 4])
print(result)

3.filter()
filter() 函数用于过滤序列,过滤掉不符合条件的元素,返回由符合条件元素组成的新列表。
第一个参数接受一个函数名,后面的参数接受一个或多个可迭代的序列,返回的是一个序列。
在这里插入图片描述
例:

获取1000内容所有的素数;
def is_prime(num):
    """判断是否为素数"""
    if num < 2:
        return  False
    for i in range(2, num):
        if num %i == 0:
            return  False
    else:
        return  True

result3 = filter(is_prime, range(1001))
print(list(result3))

4.sorted/max/min
sorted() 函数对所有可迭代的对象进行排序操作。返回重新排序的列表。
sorted(iterable, key=None, reverse=False)
key: 主要是用来进行比较的元素,只有一个参数,
reverse: 排序规则,True 降序 ,False 升序(默认)

python排序sort()和sorted()的区别是什么?

  1. 排序对象不同: sort 是应用在 list 上的方法,sorted 可以对所有可迭代的对象进行排序操作。
  2. 返回值不同:
    list 的 sort 方法返回的是对已经存在的列表进行操作,无返回值,
    内建函数 sorted 方法返回的是一个新的 list,而不是在原来的基础上进行的操作。

如果对商品信息排序,那么用sorted()方法可以按照价格或数量等对其进行排序,但用max或min方法可以直接返回所求的商品的全部信息(返回链表)。

例:

goods = [
    ["苹果", 2, 1000],
    ["电脑", 9999, 300],
    ["手机", 5999, 790]
]

top_price = max(goods,  key=lambda x: x[1])
print(top_price)
print("价格最高的商品名称: ", top_price[0])

low_count = min(goods, key=lambda x: x[2])
print("库存量最少的商品名称: ", low_count[0])

high_count = max(goods, key=lambda x: x[2])
print("库存量最多的商品名称: ", high_count[0])

运行截图:
在这里插入图片描述

实战练习

1.记录日志装饰器练习题
好的日志对一个软件的重要性是显而易见的。如果函数的入口都要写一行代码来记录日志,这种方式实在是太低效了。 那么请你创建一个装饰器, 功能实现函数运行时自动产生日志记录。
日志格式如下:
程序运行时间 主机短名 程序名称: 函数[%s]运行结果为[%s]
产生的日志文件并不直接显示在屏幕上, 而是保存在 file.log 文件中, 便于后期软件运行结果的分析.

代码:

import os
import time
import  sys
# 装饰器: 用来添加日志信息的

def add_log(fun):
    """
    May 26 09:50:14 foundation0 systemd: Removed slice user-0.slice.*args
    """
    def wrapper(*args, **kwargs):  # args=(1, 2) =====> *args=1 2    #
        # 获取被装饰的函数的返回值
        result = fun(*args, **kwargs)      # add(*args)    ====> add(1, 2)
        # 返回当前的字符串格式时间
        now_time = time.ctime()
        # 获取主机名 
        hostname = os.environ['USERNAME']
        # 运行的程序
        process_full_name = sys.argv[0]
        process_name = os.path.split(process_full_name)[-1]
        # 日志内容
        # info = "正在运行程序: " + str(fun)
        # 获取函数名: 函数名.__name__
        info ="函数[%s]的运行结果为%s" %(fun.__name__, result)
        log = " ".join([now_time, hostname, process_name, info])
        with open('log', 'a+',encoding='utf-8') as f:
            f.writelines(log+'\n')
        return  result
    return  wrapper

@add_log  # 语法糖====music = add_log(music)
def movie():
    time.sleep(1)
    print("正在看电影.....")

@add_log
def add(x, y):
    return  x+y

@add_log
def roll(name, age, **kwargs):
    print(name, age, kwargs)
movie()
add(1,2)

运行截图:
在这里插入图片描述
2.斐波那契数列的装饰器练习: 实现高速缓存递归
在数学上,费波那契数列是以递归的方法来定义:
用文字来说,就是费波那契数列由 0 和 1 开始,之后的费波那契系数就是由之前的两数相加而得出。首几个费波那契系数是:
0,1,1,2,3,5,8,13,21,34,55,89,144,233……(OEIS 中的数列 A000045)
装饰器 1: 添加高速缓存的装饰器 num_cache
如果第一次计算 F(5) = F(4) + F(3) = 5
第二次计算 F(6) = F(5) + F(4)
显然 F(5)已经计算过了, F(4)也已经计算了, 我们可否添加一个装饰器, 专门用来存储费波那契数列已经计算过的缓存, 后期计算时, 判断缓存中是否已经计算过了, 如果计算过,直接用缓存中的计算结果. 如果没有计算过, 则开始计算并将计算的结果保存在缓存中.
装饰器 2: 程序运行计时器的装饰器 timeit
该装饰器用来测试有无高速缓存求斐波那契数列, 它们两者运行的时间效率高低.

代码:

"""

1, 1, 2, 3, 5, 8......
F(n) = F(n-1) + F(n-2)
"""
import os
import time


def timmer(fun):
    def wrapper(*args, **kwargs):
        start_time = time.time()
        s=fun(*args, **kwargs)
        end_time = time.time()
        time1 = end_time - start_time
        print(time1)
        print("%s run %s" % (fun.__name__, time1))
        # wrapper需要返回的是被装饰函数的返回值;
        return s
    return wrapper

def num_cache(fun):
    """
    添加高速缓存的装饰器 num_cache
    如果第一次计算 F(5) = F(4) + F(3) = 5
    第二次计算 F(6) = F(5) + F(4)
    显然 F(5)已经计算过了, F(4)也已经计算了, 我们可否添加一个装饰器, 专门用
    来存储 费波那契数列已经计算过的缓存, 后期计算时, 判断缓存中是否已经计算过了, 如
    果计算过,直接用缓存中的计算结果. 如果没有计算过, 则开始计算并将计算的结果保存在缓存中
    :param fun:
    :return:
    """
    cache={}
    def wrapper(num):
        print(cache)
        if num in cache:
            result = cache.get(num)
            return result
        else:
            # 如果求的num不在缓存里面, 手动求Fib结果
            result = fun(num)
            # 将求出来的结果存储到缓存中;
            cache[num] = result
            return result
    return wrapper

@num_cache
def fib(num):
    if num <= 2:
        return 1
    else:
        return fib(num - 1) + fib(num - 2)


@timmer
def fun1():
    result = fib(40)
    print('F(100)=', result)
@timmer
def fun2():
    result = fib(99)
    print('F(99)=', result)
@timmer
def fun3():
    result = fib(102)
    print('F(102)=', result)

fun1()
fun2()
fun3()
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值