Python学习记录-高级2

生成器

在Python中,这种一边循环一边计算的机制,称为生成器:generator,节省大量的空间。
1 创建生成器

#把一个列表生成式的 [ ] 改成 ( )
G = ( x*2 for x in range(5))
print(G)
#outputs:
<generator object <genexpr> at 0x000001FD51389FC0>
#通过 next() 函数获得生成器的下一个返回值
print(next(G));print(next(G));print(next(G))
#outputs:0 2 4

生成器保存的是算法,每次调用 next(G) ,就计算出 G 的下一个元素的值,直到计算到最后一个元素,没有更多的元素时,抛出 StopIteration 的异常。
注意:这种不断调用 next()并不妥当,正确的方法是使用 for 循环,因为生成器也是可迭代对象。创建了一个生成器后,基本上不会调用 next() ,而是通过 for 循环来迭代它,并且不需要关心 StopIteration 异常。

#generator非常强大。如果推算的算法比较复杂
#用类似列表生成式的 for 循环无法实现的时候
#还可以用函数来实现

使用了 yield 的函数被称为生成器(generator)。跟普通函数不同的是,生成器是一个返回迭代器的函数,只能用于迭代操作,更简单点理解生成器就是一个迭代器。调用一个生成器函数,返回的是一个迭代器对象。

def fibonacci(n): # 生成器函数 - 斐波那契
    a, b, counter = 0, 1, 0
    while True:
        if (counter > n): 
            return
        yield a
        a, b = b, a + b
        counter += 1
f = fibonacci(10) # f 是一个迭代器,由生成器返回生成
print (next(f), end=" "); print (next(f), end=" ")
print (next(f), end=" "); print (next(f), end=" ")
......
#outputs:
#0 1 1 2 ......

注意:上例中,fibonacci函数不能直接调用,只有通过next()或创建对象才能运行。

def creatNum():
    print('---start---')
    a,b = 0,1
    for i in range(5):
        print('---1---')
        yield b
        print('---2---')
        a,b = b, a+b
        print('---3---')
    print('---stop--')

a = creatNum()   #创建了一个生成器对象
				 # next(a) 即为 a.__next__()
for num in a:    #或者 for num in creatNum()
    print(num)
#outputs:
---start---
---1---
1
---2---
---3---
---1---
1
---2---
---3---
---1---
2
---2---
---3---
---1---
3
---2---
---3---
---1---
5
---2---
---3---
---stop--

2 send方法
send类似于_next_()也可以让生成器继续往下生成,但是send方法有一个参数,该参数传递给上一次被挂起的yield语句的返回值。

def gen():
    i = 0
    while i<5:
        temp = yield i
        print(temp)
        i+=1
#使用next函数
f1 = gen()
print(next(f1),end = ' ');print(next(f1),end = ' ');print(next(f1),end = ' ');print(next(f1))
#使用__next__()方法
print('-'*30)
f2 = gen()
print(f2.__next__(),end = ' ');print(f2.__next__(),end = ' ');print(f2.__next__(),end = ' ');print(f2.__next__(),end = ' ')
#使用send
print('-'*30)
f3 = gen()
#f3.send('what')  #TypeError: can't send non-None value to a just-started generator
print(next(f3),end = ' ')#在用生成器时,第一次要用next启动
print(f3.send('what'),end = ' ');print(f3.send('the'),end = ' ');print(f3.send('hell'))
#outputs:
0 None
1 None
2 None
3
------------------------------
0 None
1 None
2 None
3 ------------------------------
0 what
1 the
2 hell
3

另外,没有指定返回值时:

def gen():
    i = 0
    while i<5:
        yield i
        i+=1
f = gen()
print(next(f),end = ' ');print(next(f),end = ' ');print(next(f),end = ' ');print(next(f))
#outputs:
0 1 2 3

迭代器

迭代是访问集合元素的一种方式。迭代器是一个可以记住遍历的位置的对象。迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退。
可迭代对象:以直接作用于 for 循环的数据类型有以下几种:list、tuple、dict、set、str 等;generator,包括生成器和带 yield 的generator function。可以直接作用于 for 循环的对象统称为可迭代对象: Iterable 。
使用 isinstance() 判断一个对象是否是 Iterable 对象。

from collections import Iterable
isinstance([], Iterable)

可以被next()函数调用并不断返回下一个值的对象称为迭代器:Iterator。
iter()函数:生成器都是 Iterator 对象,但 list、dict、str虽然是 Iterable,却不是 Iterator。可以使用 iter() 函数。

闭包

闭包是由函数及其相关的引用环境组合而成的实体。
在函数内部再定义一个函数,并且这个函数用到了外函数的变量,这个函数和那些变量就是闭包。

def test(number):
    print('-1-')
    def test_in():
        print('-2-')
        print(number+100)
    print('-3-')
    return test_in  #返回了函数的引用
print(test(100))
ret = test(100)
print('-'*30)
ret()
输出
-1-
-3-
<function test.<locals>.test_in at 0x000001E1F7D40488>
-1-
-3-
------------------------------
-2-
200

闭包函数带变量:

def test(number):
    print('-1-')
    def test_in(number2):
        print('-2-')
        print(number+number2)
    print('-3-')
    return test_in  #返回了函数的引用
ret = test(100)		#在test_in函数外的变量number为100
print('-'*30)
ret(1)		#仅执行内部的test_in函数
ret(100)
输出
-1-
-3-
------------------------------
-2-
101
-2-
200

闭包使用实例:

def line_conf(a, b):
    def line(x):
        return a*x + b
    return line
line1 = line_conf(1, 1)
line2 = line_conf(4, 5)
print(line1(5))
print(line2(5))

函数line与变量a,b构成闭包。在创建闭包的时候,通过line_conf的参数a,b说明了这两个变量的取值,确定了函数的最终形式(y = x + 1和y = 4x + 5)。只需要变换参数a,b,就可以获得不同的直线表达函数。
注意:由于闭包引用了外部函数的局部变量,则外部函数的局部变量没有及时释放,消耗内存

装饰器

装饰器(Decorators)是修改其他函数的功能的函数。
1 一切皆对象

def hi(name="hill"):
    return "hi " + name
print(hi())      #output: 'hi hill'
greet = hi       #可以将一个函数赋值给一个变量
print(greet())   #output: 'hi hill'
del hi           #删掉旧的hi函数
print(hi())      #outputs: NameError
print(greet())   # output: 'hi hill'

2 在函数中定义函数

def hi(name="hill"):
    print("now you are inside the hi() function")
    def greet():
        return "now you are in the greet() function"
    def welcome():
        return "now you are in the welcome() function"
    print(greet())
    print(welcome())
    print("now you are back in the hi() function")

hi()#无论何时调用hi(), greet()和welcome()将会同时被调用
    #greet()和welcome()函数在hi()函数之外是不能访问的

3 从函数中返回函数

def hi(name="hill"):
    def greet():
        return "now you are in the greet() function"
    def welcome():
        return "now you are in the welcome() function"
    if name == "hill":
        return greet
    else:
        return welcome
a = hi(); b = hi('rose')      # a,b分别指向到hi()函数中的greet()函数和welcome()函数
print(a,'\n',b)
#outputs:
#<function hi.<locals>.greet at 0x000001402B648AE8> 
#<function hi.<locals>.welcome at 0x000001402B680C80>
print(a(),'and',b())
#outputs:
#now you are in the greet() function and now you are in the welcome() function

注意:在 if/else 语句中返回 greet 和 welcome,而不是 greet() 和 welcome()。因为当把一对小括号放在后面,这个函数就会执行;然而如果不放括号在它后面,它可以被到处传递,并且可以赋值给别的变量而不去执行它。
4 将函数作为参数传给另一个函数

def hi():
    return "hi hill!"
def doSomethingBeforeHi(func):
    print("I am doing some boring work before executing hi()")
    print(func())
 
doSomethingBeforeHi(hi)
#outputs:
# I am doing some boring work before executing hi()
# hi hill!

5 装饰器
python中的装饰器封装一个函数,并且用各种方式来修改它的行为。

def decorator(a_func):
    def dFunction():
        print("I am before executing a_func()")
        a_func()
        print("I am after executing a_func()")
    return dFunction

def need_decoration():
    print("I need decoration")

need_decoration()                             #output:I need decoration
need_decoration = decorator(need_decoration)  #重复传递对象,此时need_decoration指向dFunction
need_decoration()                             #先指向dFunction,再执行need_decoration                      
#outputs:
#I am before executing a_func()
#I need decoration
#I am after executing a_func()

也就是说: @ 符号就是装饰器的语法糖,它放在函数开始定义的地方,就可以省略最后一步再次赋值的操作。@decorator相当于need_decoration = decorator(need_decoration)

def decorator(a_func):
    def dFunction():
        print("I am before executing a_func()")
        a_func()
        print("I am after executing a_func()")
    return dFunction
@decorator
def need_decoration():
    print('I need decoration')
 
need_decoration()
#outputs:
#I am before executing a_func()
#I need decoration
#I am after executing a_func()

6 多个装饰器

def makeBold(fn):
    def wrapped():
        return "<b>" + fn() + "</b>"
    return wrapped
    
def makeItalic(fn):
    def wrapped():
        return "<i>" + fn() + "</i>"
    return wrapped
    
@makeBold   
def test1():    return "hello world-1"    # test1 = makeBold(test1)
@makeItalic
def test2():    return "hello world-2"    # test2 = makeItalic(test2)
@makeBold
@makeItalic
def test3():    return "hello world-3"
print(test1()); print(test2()); print(test3())
#outputs:
#<b>hello world-1</b>
#<i>hello world-2</i>
#<b><i>hello world-3</i></b>  注意此处顺序

装饰器执行的时间:遇到@时,即执行need_decoration = decorator(need_decoration),也就是说在执行need_decoration之前,已经对其进行了装饰。
7 装饰器的参数

from time import ctime, sleep
def timefun(func):
    def wrappedfunc(a, b):  #定义形参
        print("%s called at %s"%(func.__name__, ctime()))
        #print(a, b)
        func(a, b)          #传出实参
    return wrappedfunc
@timefun
def foo(a, b):
    print(a+b)

foo(3,5); sleep(2); foo(2,4)
#outputs:
#foo called at Sun Sep  9 15:06:51 2018
#8
#foo called at Sun Sep  9 15:06:53 2018
#6

不定长的参数和关键字参数:

def timefun(func):
    def wrappedfunc(*args, **kwargs):
        print("%s called at %s"%(func.__name__, ctime()))
        func(*args, **kwargs)
    return wrappedfunc

8 带返回值的函数的装饰器

from time import ctime, sleep
def timefun(func):
    def wrappedfunc():
        print("%s called at %s"%(func.__name__, ctime()))
        return func()
    return wrappedfunc
@timefun
def foo():
    print("I am foo")
@timefun
def getInfo():
    return '----hahah---'
foo(); sleep(2); foo()
print(getInfo())
#outputs:
#foo called at Sun Sep  9 15:21:54 2018
#I am foo
#foo called at Sun Sep  9 15:21:56 2018
#I am foo
#getInfo called at Sun Sep  9 15:21:56 2018
#----hahah---

通用装饰器:综合7和8可形成通用的装饰器
9 带参数的装饰器
装饰器的语法允许在调用时,提供其它参数,比如@decorator(a)。这样,就为装饰器的编写和使用提供了更大的灵活性。比如,可以在装饰器中指定日志的等级,因为不同业务函数可能需要的日志级别是不一样的。

def logit(logfile='out.log'):
    def logging_decorator(func):
        def wrapped_function(*args, **kwargs):
            print(func.__name__ + " was called")
            with open(logfile, 'a') as opened_file: # 打开logfile,并写入内容
                opened_file.write(func.__name__ + " was called" + '\n')# 现在将日志打到指定的logfile
            return func(*args, **kwargs)
        return wrapped_function
    return logging_decorator
@logit()
def myfunc1():
    pass
myfunc1()
# Output: myfunc1 was called,创建了一个out.log文件,内容为上面的字符串
@logit(logfile='func2.log')
def myfunc2():
    pass
myfunc2()# Output: myfunc2 was called,创建了一个out.log文件,内容为上面的字符串

10 @wraps
被装饰后的函数其实已经是另外一个函数(函数名等函数属性会发生改变)。添加后由于函数名和函数的doc发生了改变,对测试结果有一些影响。Python的functools包中提供了一个叫wraps的装饰器来消除这样的副作用

def note(func):
    "note function"
    def wrapper():
        "wrapper function"
        print('note something')
        return func()
    return wrapper

@note
def test():
    "test function"
    print('I am test')

test()
print(test.__doc__)
#outputs:
#note something
#I am test
#wrapper function

改为:

from functools import wraps
def note(func):
    "note function"
    @wraps(func)
    def wrapper():
        "wrapper function"
        print('note something')
        return func()
    return wrapper

@note
def test():
    "test function"
    print('I am test')

test()
print(test.__doc__)
#outputs:
#note something
#I am test
#test function

pdb调试

在这里插入图片描述
执行时调试
程序启动,停止在第一行等待单步调试。

python -m pdb some.py

交互调试
进入python或ipython解释器

import pdb
pdb.run('testfun(args)')

程序里埋点
当程序执行到pdb.set_trace() 位置时停下来调试

import pdb 
pdb.set_trace() 

pdb 调试有个明显的缺陷就是对于多线程,远程调试等支持得不够好,同时没有较为直观的界面显示,不太适合大型的 python 项目。而在较大的 python 项目中,这些调试需求比较常见,因此需要使用更为高级的调试工具。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Python学习笔记》是由皮大庆编写的一本关于Python语言学习的教材。在这本书中,作者详细介绍了Python语言的基础知识、语法规则以及常用的编程技巧。 首先,作者简要介绍了Python语言的特点和优势。他提到,Python是一种易于学习和使用的编程语言,受到了广大程序员的喜爱。Python具有简洁、清晰的语法结构,使得代码可读性极高,同时也提供了丰富的库和模块,能够快速实现各种功能。 接着,作者详细讲解了Python的基本语法。他从变量、数据类型、运算符等基础知识开始,逐步介绍了条件语句、循环控制、函数、模块等高级概念。同时,作者通过大量的示例代码和实践案例,帮助读者加深对Python编程的理解和应用。 在书中,作者还特别强调了编写规范和良好的编程习惯。他从命名规范、注释风格、代码缩进等方面指导读者如何写出清晰、可读性强的Python代码。作者认为,良好的编程习惯对于提高代码质量和提高工作效率非常重要。 此外,作者还介绍了Python的常用库和模块。他提到了一些常用的库,如Numpy、Pandas、Matplotlib等。这些库在数据处理、科学计算、可视化等领域有广泛的应用,帮助读者更好地解决实际问题。 总的来说,《Python学习笔记》是一本非常实用和全面的Python学习教材。通过学习这本书,读者可以系统地学习和掌握Python编程的基础知识和高级应用技巧,为以后的编程学习和工作打下坚实的基础。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值