函数四

装饰器

装饰器的引用

  • 我们可以直接通过修改函数中的代码来完成需求,但是会产生以下一些问题
    • 如果修改的函数多,修改起来会比较麻烦
    • 不方便后期的维护
    • 这样做会违反开闭原则(ocp) ocp原则 o open 开放对代码的拓展,c close 关闭对代码的修改
    • 程序的设计,要求开发对程序的扩展,要关闭对程序的修改
def fun1():
    print('我是fun1函数')


def fun(fn, *args):
    print('函数开始执行')
    r = fn(*args)
    print(r)
    print('函数执行结束')
fun(fun1)

装饰器的使用

  • 通过装饰器,可以在不修改原来函数的情况下来对函数进行扩展
  • 在开发中,我们都是通过装饰器来扩展函数的功能的
    装饰器是一个特殊的闭包函数
#这就是装饰器
def strat_fun():
   def new_fun():
      print('函数开始')
      fn()
      print('函数结束')
   
   return new_fun
def fun():
   print('fun函数')   
r = start_fun(fun)#首先是要调用外层函数, 然后内层函数暂时是不会执行的,因为没有调用,然后外层函数的调用等于外层函数的返回值,那么现在外层函数的返回值是内层函数的函数对象,内层函数的调用等于内层函数的函数对象加上一个括号,也就是调用,那么也就是外层函数的调用加上括号
r()
#'函数开始 fun函数  函数结束'
# old_fun() = 外层函数的返回值, new_fun = r = old_fun()  new_fun() = r()
# old_fun(fun) 中的fun 是实参,那么old_fun这个函数的形参就等于实参  fn = fun

有参数的装饰器

def strat_fun(a):
   def new_fun(a,b):
      print('函数开始')
      a(c,d)
      print('函数结束')
   
   return new_fun
def fun(c,d):
   print('fun函数')
r =start_fun()fun
r(1,2) 

目标函数没有cd参数

def strat_fun(a):
   def new_fun(*args,**kwargs):#不定长参数
      print('函数开始')
      a(*args,**kwargs)
      print('函数结束')
   
   return new_fun
def fun():
   print('fun函数')
r =start_fun()fun
r() 

可迭代对象

  • 对list、tuple、dict、set、str等类型的数据使用for…in…的循环语法从其中依次拿到数据进行使用,我们把这样的过程称为遍历,也叫迭代。
  • 把可以通过for…in…这类语句迭代读取一条数据供我们使用的对象称之为可迭代对象(Iterable)。

推导式

  • 推导式分为 列表推导式、字典推导式、集合推导式等。在这里我们主要说其中一种也是用的最多列表推导式
    列表推导式是Python构建列表(list)的一种快捷方式,可以使用简洁的代码就创建出一个列表简单理解就是由一个旧的列表来构建出一个新的列表

语法

[表达式 for 变量 in 旧列表]
[表达式 for 变量 in 旧列表 if 条件]

lst = ['drogba', 'lampard', 'terry', 'cech']
lst1 = [i for i in lst if len(i) > 5]
print(lst1)

求1-100之间的偶数

lst2 = [i for i in range(1, 101) if i % 2 == 0]
print(lst2)

求1-100之间的偶数,还要能整除4的数

lst2 = [i for i in range(1, 101) if i % 2 == 0 and i % 4 == 0]
print(lst2)

生成器

背景

通过列表推导式我们可以直接创建出一个列表,但是受到内存的限制,我们不可能创造出一个无限大的列表。而且创建一个有200万个元素的列表,会占用很大的内存空间,而这个时候我们仅仅需要访问列表中几个元素,那么后面的元素就占用着空间就是一种浪费的行为。那么我们可不可以用几个元素就创建出几个元素。这样在一定程度上就优化了内存。那么在Python中有一种一边循环一边计算的机制就是生成器

创建生成器的方式

方法一:使用类似列表推导式的方式得到生成器

lst = [i * 2 for i in range(1, 11)]
#我不希望一次性得到太多的数据, 我要多少数据你就给我多少数据(非常人性化,有认为控制的)
g = (x * 3 for x in range(10))
gen = (i * 2 for i in range(1, 11))#gen是一个对象,这就是一个生成器
type(gen)#class:'generator'
print(gen.__next__())#通过next()函数来获得,每次只取一个
print(next(gen))

方法二:使用函数加上yield来实现生成器
只要在函数中出现yield关键字它就是一个生成器函数

def fun():
    i = 0
    while True:
        i += 1
        yield i
r = fun()#generater object 
print(r)
print(next(r))
print(next(r))
print(next(r))

生成器的特性

生成器会记住你上一次取数据的位置,然后下一次取数据时继续从上一次的位置运行
生成器生成一次只能取完一次数据,第二次在取就不存在数据了

lst = [i for i in gen]
lst1 = [i for i in gen]
#[2,4,6,8,10,12,14,16,18,20]
[]#第二个是空的,生成器只能用一次
def generator():
 print('代码开始执行了。。。。。。。。')
 while True:
 response = yield 1
 print('-------------------------------')
 print('response = ', response)
g = generator()
print(g)
执行结果:
<generator object generator at 0x000001F03B26A830>

我们的这个执行结果并不是一个function,而是一个generator,这个就是我们的这个yield关键字的效果,只要我们在函数中使用了yield关键字,那么它不在是一个函数,而是一个生成器。而且,大家还可以看到,这个print语句并没有执行
原因还是和它变成了一个生成器的原因所导致的。生成器一样的会和input函数一样起一个类似的阻塞作用,需要你调用它内部的next方法才会使程序执行一次,当你再一次遇到yield的时候,同样又会停止

def generator():
 print('代码开始执行了。。。。。。。。')
 while True:
 response = yield 1
 print('-------------------------------')
 print('response = ', response)
g = generator()
print(g)
print(next(g))
执行结果:
<generator object generator at 0x000001F03B26A830>
代码开始执行了。。。。。。。。
1

调用了这个next方法之后,代码开始正常执行了,并且给我们返回出了一个1,由此可以证明yield同样还有一个类似于return的功能。但是我们从这个执行结果来看,是不是发现了yield关键字下面的代码没有执行啊?这也就是我说的,遇到了yield就会停止的情况。如果我们现在再去调用一次next方法。

def generator():
    print('代码开始执行了。。。。。。。。')
    while True:
        response = yield 1
        print('-------------------------------')
        print('response = ', response)
g = generator()
print(g)
print(next(g))
print(next(g))
执行结果:
<generator object generator at 0x000001BD4639A830>
代码开始执行了。。。。。。。。
1
-------------------------------
response =  None
1

我们打印出了三个数据之后又停止了。但是和上面的执行结果对比我们就不难发现,我们第二次调用next方法的时候,并不是自上往下执行的,而是从上一次停止的位置继续执行的,这也就是我们的生成器的特性,它会记住你上一次停留的地方,下次继续从这个地方开始执行。
response = None这块,为什么会出现这么个结果,它的结果不应该是1吗?怎么会是None呢?这个1的结果已经被我们的yield返回出去了,所以这里没有数据,这也是生成器的另一个特征之一,你取出去了这个数据,那么这个数据在生成器里面就不存在了,当你把生成器里的数据取完了,那么它就不能够继续使用了,也就是说生成器只能用一次。
最后有一个1是从哪里输出的?它是下一次循环的yield返回出来的,同样到了这里,yield再次将程序阻塞到了这里,直到再次调用next方法或者send方法。

def generator():
    print('代码开始执行了。。。。。。。。')
    while True:
        response = yield 1
        print('-------------------------------')
        print('response = ', response)
g = generator()
print(g)
print(next(g))
print(next(g))
print(g.send(2))
执行结果:
<generator object generator at 0x0000026EE00FA830>
代码开始执行了。。。。。。。。
1
-------------------------------
response =  None
1
-------------------------------
response =  2
1

send方法,,他就是给我们的response传了一个值而已,那么它为什么同样能够调用出我们的生成器呢?那是因为send同样也是在Generator内部的一个方法,它的里面同样封装了next方法,所以我们调用send方法同样也是可以调用到生成器的。并且可以给yield的赋值对象传递一个值。

迭代器

迭代器是访问集合元素的一种方式。迭代器是一个可以记住遍历位置的对象。迭代器对象从集合的第一个元素开始访问,直到所有元素被访问完结束。
可以被next()函数调用并不断返回下一个值的对象称为迭代器Iterator
生成器是可迭代的,也是迭代器
列表是可迭代的,但不是迭代器
通过iter()函数可以将可迭代的变成一个迭代器

list1 = [1, 2, 3, 4, 5, 6]
list2 = (1, 2, 3, 4, 5, 6)
ite = iter(list1)
ite2 = iter(list2)

简单的迭代器

class Ite(object):
   def __iter__(self):
      return self

   def next(self):
      a,b =b,a+b

PEP8规范编写代码

Python代码风格指南

  • 缩进使用4个空格, 空格是首选的缩进方式. Python3 不允许混合使用制表符和空格来缩进.
  • 每一行最大长度限制在79个字符以内.
  • 顶层函数、类的定义, 前后使用两个空行隔开.
  • import 导入(导入建议在不同的行, 例如)
import os
import sys

不建议如下导包

import os, sys

但是可以如下:

from subprocess import Popen, PIPE
  • 导包位于文件顶部, 在模块注释、文档字符串之后, 全局变量、常量之前. 导入按照以下顺序分组:
    标准库导入
    相关第三方导入
    本地应用/库导入
    在每一组导入之间加入空行
  • Python 中定义字符串使用双引号、单引号是相同的, 尽量保持使用同一方式定义字符串. 当一个字符串包含单引号或者双引号时, 在最外层使用不同的符号来避免使用反斜杠转义, 从而提高可读性.
  • 表达式和语句中的空格:
    避免在小括号、方括号、花括号后跟空格.
    避免在逗号、分好、冒号之前添加空格.
    冒号在切片中就像二元运算符, 两边要有相同数量的空格. 如果某个切片参数省略, 空格也省略.
    避免为了和另外一个赋值语句对齐, 在赋值运算符附加多个空格.
    避免在表达式尾部添加空格, 因为尾部空格通常看不见, 会产生混乱.
    总是在二元运算符两边加一个空格, 赋值(=),增量赋值(+=,-=),比较(==,<,>,!=,<>,<=,>=,in,not,in,is,is not),布尔(and, or, not
    避免将小的代码块和 if/for/while 放在同一行, 要避免代码行太长.
if foo == 'blah': do_blah_thing()
for x in lst: total += x
while t < 10: t = delay()
  • 永远不要使用字母 ‘l’(小写的L), ‘O’(大写的O), 或者 ‘I’(大写的I) 作为单字符变量名. 在有些字体里, 这些字符无法和数字0和1区分, 如果想用 ‘l’, 用 ‘L’ 代替.
  • 类名一般使用首字母大写的约定.
  • 函数名应该小写, 如果想提高可读性可以用下划线分隔.
  • 如果函数的参数名和已有的关键词冲突, 在最后加单一下划线比缩写或随意拼写更好. 因此 class_ 比 clss 更好.(也许最好用同义词来避免这种冲突).
  • 方法名和实例变量使用下划线分割的小写单词, 以提高可读性.

作业

  1. 请使用装饰器实现已存在的函数的执行所花费的时间。
    • time模块
import time

def fun_out(fn):

    def fun_inner(*args,**kwargs):
        print('程序开始运行')
        fn(*args,**kwargs)
        print('程序运行结束')
        print(fn())
    return fun_inner

def fun():
    start=time.time()
    n=0
    i=1
    while n < 10000000:
        i=i+n
        n+=1
    end = time.time()
    res=end-start
    return '运序运行的时间为%s秒'%res

f=fun_out(fun)
f()
import time

def fun_out(fn):

    def fun_inner(*args,**kwargs):
        print('程序开始运行')
        fn(*args,**kwargs)
        print('程序运行结束')
        print(fn())
    return fun_inner

@fun_out
def fun():
    start=time.time()
    n=0
    i=1
    while n < 10000000:
        i=i+n
        n+=1
    end = time.time()
    res=end-start
    return '运序运行的时间为%s秒'%res
fun()
import time 


def timethis(func):
    def inner(*args,**kwargs):
        print('start timer:')
        start = time.time()
        result = func(*args,**kwargs) 
        end = time.time()
        print('end timer:%fs.'%(end - start))
        return result 
    return inner

@timethis
def sleeps(seconds):
    print('  sleeps begin:')
    time.sleep(seconds)
    print('    sleep %d seconds.\n  sleeps over.'%seconds)
    return seconds

print(sleeps(3))

"""
1. 请使用装饰器实现已存在的函数的执行所花费的时间。
• time模块
"""

# 导入模块
import time

# 设计算法
# 使用修饰器的函数
def new_number(a):
    def old_number():
        print('开始计时:')
        start = time.time()
        a()
        print('计时结束:')
        end = time.time()
        return f'程序消耗的时间{end - start}'
    return old_number


@new_number
# 原函数
def number():
    for i in range(100000):
        print(i)
    print('原程序运行完成!')


r = new_number(number)
print(r())
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值