的深入理解,编写高效且优雅的

hookspec

 1 def fib(times):
 2     n = 0
 3     a,b = 0,1
 4     while n < times:
 5         temp = yield b 
 6         print(temp)
 7         a,b = b,a+b
 8         n += 1
 9 f = fib(5)
10 print(f.__next__())
11 print(f.send("haha"))
12 print(f.send("wangji"))

@classmethod

decorrator

  1、使用生成器以后,代码行数更少。大家要记住,如果想把代码写的Pythonic,在保证代码可读性的前提下,代码行数越少越好。

print(list(result))# [0, 4]

可以看到,由于pm2里的hookspec里有firstresult参数,在得到3这个非空结果时就直接返回了。

从上面的程序中可以看出,f._next_() 和 next(f) 是作用是一样的。

print(line)

注:为了保证hookspecs在项目里可以不断演化,hookspec里的参数对于hookimpls是可选的,即可以定义少于spec里定义数量的参数。

当装饰器装饰有参数的函数时,装饰器内部的函数也必须带有和其相同的参数,因为被装饰的参数会被当成参数传进装饰器的内部函数中,如果两者的参数不一致,会报错。

def __enter__(self):

时间: 2018-12-22阅读: 769标签: pluggy前言

输出结果为:

# 可以这样遍历

[3, 2, 1]3

输出结果为:

forlineinfile_read.readlines():

参考官方的这篇文章,我尝试翻译其中一些重点部分,并且拓展了相关的pluggy部分的知识。由于pytest是在pluggy基础上构建的,强烈建议先阅读一下pluggy的官方文档,这样理解起来更加容易一点。

        3、执行函数前预备处理

def __init__(self,name='test',value='0'):

# Plugin 1@pytest.hookimpl(tryfirst=True)def pytest_collection_modifyitems(items): # will execute as early as possible ...# Plugin 2@pytest.hookimpl(trylast=True)def pytest_collection_modifyitems(items): # will execute as late as possible ...# Plugin 3@pytest.hookimpl(hookwrapper=True)def pytest_collection_modifyitems(items): # will execute even before the tryfirst one above! outcome = yield # will execute after all non-hookwrappers executed

因此,生成器的唯一注意事项就是:生成器只能遍历一次

print(add_number.num)# 1

pluggy里提供了两个decorator helper类,分别是HookspecMarkerHookimplMarker,通过使用相同的project_name参数初始化得到对应的装饰器,后续可以用这个装饰器将函数标记为hookspechookimpl

 

ifletter=='a':

除了常规的register()方法注册插件,pluggy同时提供了load_setuptools_entrypoints()方法,允许通过setuptools entry points自动注册插件。

  2、可以在加载模块时候立即执行

# 但通过 for 循环遍历时会自动捕获到这个错误

Plugin3的pytest_collection_modifyitems先调用,直到yield点,因为这是一个hook warpper。Plugin1的pytest_collection_modifyitems被调用,因为有tryfirst=True参数。Plugin2的pytest_collection_modifyitems被调用,因为有trylast=True参数 (不过即使没有这个参数也会排在tryfirst标记的plugin后面)。Plugin3的pytest_collection_modifyitems调用yield后面的代码. yield接收非Wrapper的result返回. Wrapper函数不应该修改这个result。

1 #求一元一次方程的值,输入x,求y
2 def fun(a,b):     #其中a,b是固定的
3     def fun_in(x):
4         return a*x+b
5     return fun_in
6 f = fun(3,5)
7 print(f(1))    #每次输入不同的x值即可求出对应的y值
8 print(f(3))

def get_a_indexs(string):

请看下面这个firstresult和hook调用例子:

                图2-2 迭代器的判断

print(result)# [1, 2]

每一个pluggy.PluginManager都有一个hook属性, 可以通过调用这个属性的call函数来调用hook,需要注意的是,调用时必须使用关键字参数语法来调用。

  —  from collections import Iterator

def get_indexs(array,target='',judge=True):

自动注册插件

我们直接来看例子,假设文件中保存了省份的人口总数,现在,需要求每个省份的人口占全国总人口的比例。显然,我们需要先求出全国的总人口,然后在遍历每个省份的人口,用每个省的人口数除以总人口数,就得到了每个省份的人口占全国人口的比例。

returnvalue

hook的调用

  —  isinstance([ ],Iterable)

next(indexs)# TypeError: 'LoopIter' object is not an iterator

默认情况下,hook的调用顺序遵循注册时的顺序LIFO(后进先出),hookimpl允许通过tryfirst, trylast*选项调整这一项顺序。

从上面代码可以看出:使用send()时,必须在yield前面加上一个变量,并打印这个变量。在调用send()方法时其前面需要至少使用一次next()或__next__()方法,因为生成器不可以在使用之前导入任何非None参数。由此可以知道,send()是用来向生成器中导入参数并返回该参数的,与该参数一起返回的还有生成器中原先保存的数据。

获取到一个生成器以后,可以正常的遍历它:

hookspecification(hookspec)用来validate每个hookimpl,保证hookimpl被正确的定义。

迭代是访问集合元素的一种方式。迭代器是一个可以记住遍历位置的对象。迭代器只能往前不能后退。

test=Test()

hookimpl的调用顺序

        6、缓存

# 定义一个方法,它的作用是遍历一个数组,找出等于(或不等于)目标元素的 index

得到结果如下:

3、装饰器的分类

资料:

hook执行结果处理和firstresult选项

生成器的两种方法:__next__() 和 send() 方法。

print(add_number.num)# 0

执行顺序如下:

  1、语法上和函数类似:生成器函数和常规函数几乎是一样的。它们都是使用def语句进行定义,差别在于,生成器使用yield语句返回一个值,而常规函数使用return语句返回一个值。

string='this is a test to find a' index'

import pytest@pytest.hookimpl(hookwrapper=True)def pytest_pyfunc_call(pyfuncitem): do_something_before_next_hook_executes() outcome = yield # outcome.excinfo may be None or a (cls, val, tb) tuple res = outcome.get_result() # will raise if outcome was exception post_process_result(res) outcome.force_result(new_res) # to override the return value to the plugin system
 1 #定义一个嵌套函数(要素1)
 2 def test(num):
 3     def test_in(num_in):
 4         #内部函数引用外部函数的变量(非全局变量)(要素2)
 5         print("sum = %s"%(num + num_in))
 6         #返回的结果可以被打印出来
 7         return num,num_in
 8     #返回内部的函数(要素3)
 9     return test_in
10 #这里的rtn就是test_in
11 rtn = test(10)
12 print(rtn)
13 #内部函数test_in传参
14 print(rtn(20))

result+=num

hookwrapper

输出结果为:

# 生成器在获取完之后如果继续通过 next() 取值,则会触发 StopIteration 错误

默认情况下,调用一个hook会使底层的hookimpl函数在一个循环里按顺序执行,并且将其非空的执行结果添加到一个list里面。例外的是,hookspec里有一个firstresult选项,如果指定这个选项为true,那么得到第一个返回非空的结果的hookimpl执行后就直接返回,后续的hookimpl将不在被执行,参考后面的例子。

在python中,有两种创建生成器的方式:

with open('test.txt','r')asfile:

原则上,每个hook都是一个1:N的python函数调用, 这里的N是对一个给定hook的所有注册调用数。所有的hook函数都使用pytest_xxx的命名规则,以便于查找并且同其他函数区分开来。

装饰器其实就是一个闭包,把一个函数当作参数然后返回一个替代版函数。

ifnotDate.is_month_validate(month):

hookimplementation(hookimpl) 是一个被恰当标记过的回调函数。hookimpls通过register()方法加载。

我们下面来看一个例子。下面为一个可以无穷生产奇数的生成器函数。

# 下面这些都是可行的

注:hookwrapper还是正常的执行

三、闭包

def __call__(self,num=1):

conftest.py可以作为最简单的本地plugin调用一些hook函数,以此来做些强化功能。pytest整个框架通过调用如下定义良好的hooks来实现配置,收集,执行和报告这些过程:

这里,至少有两个充分的理由说明,使用生成器比不使用生成器代码更加清晰:

self.file=open(filename,'r')

hookimpl

        1、引入日志

sum(1,2,3,4,5)# 15

hookimpl里还有一个hookwrapper选项,用来表示这个函数是个hookwrapper函数。hookwrapper函数可以在普通的非wrapper的hookimpls执行的前后执行一些其他代码, 类似于@contextlib.contextmanager,hookwrapper必须在它的主体包含单一的yield,用来实现生成器函数,例如:

1 test called at the Fri Nov 10 19:19:20 2017
2 ---ha--ha---
3 [Finished in 0.3s]

returnresult

内置plugins:从代码内部的_pytest目录加载;外部插件(第三方插件):通过setuptools entry points机制发现的第三方插件模块;conftest.py形式的本地插件:测试目录下的自动模块发现机制;

 1 #定义一个装饰器
 2 def deco(func):
 3     def wrapper(a,b):    #内部函数的参数必须和被装饰的函数保持一致
 4         print("添加的功能")
 5         func(a,b)
 6 
 7     return wrapper
 8 
 9 @deco
10 #有参数的函数
11 def sum(a,b):
12     print(a+b)
13 
14 sum(10,20)

print('{} is a validate month number'.format(month))

from pluggy import PluginManager, HookimplMarker, HookspecMarkerhookspec = HookspecMarker("myproject")hookimpl = HookimplMarker("myproject")class MySpec1(object): @hookspec def myhook(self, arg1, arg2): passclass MySpec2(object): # 这里将firstresult设置为True @hookspec(firstresult=True) def myhook(self, arg1, arg2): passclass Plugin1(object): @hookimpl def myhook(self, arg1, arg2): """Default implementation. """ return 1class Plugin2(object): # hookimpl可以定义少于hookspec里定义数量的参数,这里只定义arg1 @hookimpl def myhook(self, arg1): """Default implementation. """ return 2class Plugin3(object): # 同上,甚至可以不定义hookspec里的参数 @hookimpl def myhook(self): """Default implementation. """ return 3pm1 = PluginManager("myproject")pm2 = PluginManager("myproject")pm1.add_hookspecs(MySpec1)pm2.add_hookspecs(MySpec2)pm1.register(Plugin1())pm1.register(Plugin2())pm1.register(Plugin3())pm2.register(Plugin1())pm2.register(Plugin2())pm2.register(Plugin3())# hook调用必须使用关键字参数的语法print(pm1.hook.myhook(arg1=None, arg2=None))print(pm2.hook.myhook(arg1=None, arg2=None))

        2、函数执行时间统计

classAddNumber(object):

生成器发送一个pluggy.callers._Result对象 , 这个对象可以在yield表达式里指定并且通过force_result()或者get_result()方法重写或者拿到最终结果。

6、使用生成器的注意事项

year=self.year,

举个例子,对于如下的代码:

上面的函数中,yield是必备的,当一个普通函数中包含yield时,系统会默认为是一个generator。

self.year=year

注:hookwrapper不能返回结果 (跟所有的生成器函数一样);

装饰器可分为对有无参数函数进行装饰的装饰器对有无返回值函数进行装饰的装饰器,组合起来一共有4种。即:装饰器对无参数无返回值的函数进行装饰,装饰器对无参数有返回值的函数进行装饰,装饰器对有参数无返回值的函数进行装饰,装饰器对有参数有返回值的函数进行装饰。 

__exit__方法关闭了文件

hookspec通过add_hookspecs()方法加载,一般在注册hookimpl之前先加载;

1 def index_words(text):
2     result = []
3     if text:
4         result.append(0)
5     for index,letter in enumerate(text,1):
6         if letter == " ":
7             result.append(index)
8     return result

classDate(object):

当然也可以同时将tryfirst和trylast与hookwrapper=True混用,这种情况下它将影响hookwrapper之间的调用顺序.

 1 from time import ctime,sleep
 2 #定义一个装饰器,装饰不定长参数函数
 3 def deco(func):
 4     def wrapper(*args,**kwargs):
 5         print("%s called at the %s"%(func.__name__,ctime()))
 6         func(*args,**kwargs)
 7     return wrapper
 8 
 9 @deco
10 def test1(a,b,c):
11     print(a+b+c)
12 
13 @deco
14 def test2(a,b):
15     print(a+b)
16 
17 test1(10,20,30)
18 sleep(2)
19 test1(30,40,50)
20 
21 sleep(1)
22 test2(10,20)

try:

正文

1 <b>hello world - 1</b>
2 <i>hello world - 2</i>
3 <b><i>hello world - 3</i></b>
4 [Finished in 0.3s]

try:

1、什么是闭包?

result=get_default()

 1 def fib(times):
 2     n = 0
 3     a,b = 0,1
 4     while n < times:
 5         yield b
 6         a,b = b,a + b
 7         n += 1
 8     return "done"
 9 f = fib(5)
10 for x in f:
11     print(x)

# 在 classmethod 内可以通过 cls 来调用到类的方法,甚至创建实例

  A、装饰器对无参数函数进行装饰:

date_old=Date('2016','11','09')

  —  生成器和带yield 的generator function

# 但不指定关键字参数则报错

不使用生成器的情况:

returnresult

输出结果为:

self.file.close()

输出结果为:

result=get_default()

1 8
2 14
3 [Finished in 0.3s]

# 也可以忽略有默认值的参数

 1 def odd():
 2     n=1
 3     while True:
 4         yield n
 5         n+=2
 6 odd_num=odd()
 7 count=0
 8 for o in odd_num:
 9     if count >=5:
10         break
11     print(o)
12     count += 1

# loop 2

在python中,一边循环一边计算的机制,称为生成器:generator.

array=[1,2,3,4,1]

执行上面的这段代码将不会有任何输出,这是因为,生成器只能遍历一次。在我们执行sum语句的时候,就遍历了我们的生成器,当我们再次遍历我们的生成器的时候,将不会有任何记录。所以,上面的代码不会有任何输出。

yield index

1、什么是可迭代对象(Iterable)

results_list=list(results)

  1、节约内存。python在使用生成器时对延迟操作提供了支持。所谓延迟,是指在需要的时候才产生结果,而不是立即产生结果。这样在需要的时候才去调用结果,而不是将结果提前存储起来要节约内存。比如用列表的形式存放较大数据将会占用不少内存。这是生成器的主要好处。比如大数据中,使用生成器来调取数据结果而不是列表来处理数据,因为这样可以节约内存。

self.num+=num

2、装饰器的功能有:

result2=get_default()

 1 #装饰器本质就是一个闭包,
 2 #将函数当成参数传进去
 3 def deco(fun):
 4     def inner():
 5         print("你想进行的操作1")
 6         print("你想进行的操作2")
 7         fun()
 8     return inner
 9 #@deco是一个“语法糖”
10 @deco
11 def test():
12     print("test函数的操作")
13 #这个test已经被装饰了,不是原来的test
14 test()
15 #14行的test()等同于如下操作:
16 #rtn = deco(test)
17 #rtn()

print(list(result))# [0, 4]

  B、装饰器对有参数函数进行装饰

# 用列表罗列所有的属性

这个例子充分说明了,合理使用生成器,能够有效提高代码可读性。只要大家完全接受了生成器的概念,理解了yield语句和return语句一样,也是返回一个值。那么,就能够理解为什么使用生成器比不使用生成器要好,能够理解使用生成器真的可以让代码变得清晰易懂。

returndate

输出结果为:

__slots__

内部函数对外部函数作用域里变量的引用(非全局变量),则称内部函数为闭包。

ifjudgeanditem==target:

上面的函数中,利用闭包来求一元一次方程的值,更方便,直接输入x的值即可求出对应的y的值。因为这利用了闭包可以记住外部函数的参数的特性。

# TypeError: get_indexs() takes 1 positional argument but 3 were given

  —  isinstance((x for x in range(10)),Iterator)

@classmethod以cls作为第一个参数,代表将类本身的作用域传入。无论通过类来调用,还是通过类的实例调用,默认传入的第一个参数都将是类本身

 1 from time import ctime,sleep
 2 #定义一个装饰器,装饰不定长参数函数
 3 def deco(func):
 4     def wrapper(*args,**kwargs):
 5         print("%s called at the %s"%(func.__name__,ctime()))
 6         return func(*args,**kwargs)
 7     return wrapper
 8 #上面的装饰器是一个万能的装饰器,因为它可以装饰任意一种函数
 9 #包括有无参数函数和有无返回值函数
10 
11 @deco
12 def test():
13     return "---ha--ha---"
14 
15 t = test()
16 print(t)

get_indexs(array,1,True)

  2、生成器函数

ifjudgeanditem==target:

  —  集合数据类型,如 list 、tuple、dict、set、str 等

returna+b

2、如何判断对象可迭代?

通过with语句,代码完成了文件打开操作,并在调用结束,或者读取发生异常时自动关闭文件,即完成了文件读写之后的处理工作。如果不通过上下文管理器的话,则会是这样的代码:

1、什么是生成器?

classReadFile(object):

前面已经对生成器有了感性的认识,我们以生成器函数为例,再来深入探讨一下python的生成器:

可以通过关键字参数给函数提供默认值

举例如下:

add_number()# 像方法一样的调用

iter()函数:将可迭代对象转换成迭代器。

返回的result可以继续优化

迭代器是可以被next() 函数调用并不断返回下一个值的对象称为迭代器。因此生成器是迭代器的子类,但是注意集合类型的可迭代对象不是迭代器。

judge=kwargs.pop('judge',True)

 D、装饰器对有返回值的函数进行装饰

return"{year}-{month}-{day}".format(

四、装饰器

# 定义一个方法,它的作用是遍历一个数组,找出等于(或不等于)目标元素的 index

1 1
2 1
3 2
4 3
5 5
6 [Finished in 0.3s]

# 正常使用

1 test1 called at the Fri Nov 10 19:08:03 2017
2 60
3 test1 called at the Fri Nov 10 19:08:05 2017
4 120
5 test2 called at the Fri Nov 10 19:08:06 2017
6 30
7 [Finished in 3.3s]

result.append(1)

2、生成器有什么优点?

finally:

5、示例

target=kwargs.pop('target','')

1 def index_word(text):
2     if text:
3         yield 0
4     for index,letter in enumerate(text,1):
5         if letter == " ":
6             yield index

数据都存在result里面,如果数据量很大的话,会比较占用内存

  2、不使用生成器的时候,对于每次结果,我们首先看到的是result.append(index),其次,才是index。也就是说,我们每次看到的是一个列表的append操作,只是append的是我们想要的结果。使用生成器的时候,直接yield  index,少了列表append操作的干扰,我们一眼就能够看出,代码是要返回Index。

string='this is a test to find a' index'

如下所示:

date_string='2016-11-09'

输出结果为:

print(list(result))# [0, 4]

 1 #定义函数:完成包裹数据
 2 def makeBold(fn):
 3     def wrapped():
 4         return "<b>" + fn() + "</b>"
 5     return wrapped
 6 
 7 #定义函数:完成包裹数据
 8 def makeItalic(fn):
 9     def wrapped():
10         return "<i>" + fn() + "</i>"
11     return wrapped
12 
13 @makeBold
14 def test1():
15     return "hello world - 1"
16 
17 @makeItalic
18 def test2():
19     return "hello world - 2"
20 
21 @makeBold
22 @makeItalic
23 def test3():
24     return "hello world - 3"
25 
26 print(test1())
27 print(test2())
28 print(test3())

print(list(result))# [0, 4]

1 <function test.<locals>.test_in at 0x000000000220E378>
2 sum = 30
3 (10, 20)
4 [Finished in 0.3s]

array=[1,2,3,4,1]

1 1
2 3
3 5
4 7
5 9
6 [Finished in 0.3s]

这个方法也只跟Date类有关

1 你想进行的操作1
2 你想进行的操作2
3 test函数的操作
4 [Finished in 0.3s]

如果你还是需要一个列表,那么可以将函数的调用结果作为参数,再调用list方法

1、什么是装饰器(decorator)?

print(list(result))# [0, 4]

除了延迟计算,生成器还能有效提高代码可读性。例如,现在有一个需求,求一段文字中,每个单词出现的位置。

forlineinfile.readlines():

 1 def fib(times):
 2     n=0
 3     a,b=0,1
 4     while n < times:
 5         yield b
 6         a,b=b,a+b
 7         n+=1
 8 f = fib(5)
 9 for i in range(5):
10     print(f.__next__())

fornuminargs:

  3、状态挂起:生成器使用yield语句返回一个值。yield语句挂起该生成器函数的状态,保留足够的信息,以便以后从它离开的地方继续执行

print(next(indexs))

  A、装饰器对无参数函数进行装饰

# 旧的实例化方式仍可以使用

定义闭包时,必须满足上面三个要素。

而@staticmethod,因为其本身类似于普通的函数,所以可以把和这个类相关的 helper 方法作为@staticmethod,放在类里,然后直接通过类来调用这个方法。

注意:创建完成生成器后,可以使用next()来调用生成器的数据结果,每调用一次返回一个值,直到调用结束。调用结束后li中为空的,不在有任何值。要想使用它,只能重新创建新的生成器。(生成器表达式的第四行代码也可以改成:print(x).)

add_number=AddNumber()

4、再看生成器

result=get_indexs(array,target=1)

              图2-1 命令行窗口下的操作图

date=Date('2016','11','09')

  —  from collections import Iterable

# 8

使用列表推导,将会一次返回所有结果:

print(i)

1 li=(i**2 for i in range(5))
2 print(li)
3 for x in range(5):
4     print(next(li))

def __exit__(self,type,value,traceback):

      1、嵌套函数 

yield index

图片 1

__call__

输出结果为:

# 也可以忽略有默认值的参数

  如上,命令行模式下,先导入Iterable模块,然后输入isinstance([ ],Iterable),括号中前面为待判断的对象,结果以布尔类型结束(True或False),列表是可迭代对象,因此返回True。注意:整数是不可迭代对象。

在调用的时候:

  2、迭代到下一次的调用时,所使用的参数都是第一次所保留下的。

forindex,letterinenumerate(string):

输出结果为:

# 23

      2、变量的引用

将与日期相关的辅助类函数作为@staticmethod方法放在Date类内后,可以通过类来调用这些方法:

一、生成器

# 8

1 1
2 haha
3 1
4 wangji
5 2
6 [Finished in 0.3s]

使用 Python3 中强制关键字参数的方式

2、闭包的应用

# 返回 True 代表不抛出错误

输出结果为:

此时就可以利用@classmethod,在类的内部新建一个格式化字符串,并返回类的实例的方法:

我们再来看两个生成器的例子,以便大家更好的理解生成器的作用。

get_indexs(array,1,True)

 1 def get_province_population(filename):
 2     with open(filename) as f:
 3         for line in f:
 4             yield int(line)
 5 
 6 gen=get_province_population("data.txt")
 7 all_population = sum(gen)
 8 #print all_population
 9 for population in gen:
10     print(population/all population)

print(line)

1 添加的功能
2 30
3 [Finished in 0.3s]

yield index

将列表推导的中括号,替换成圆括号,就是一个生成器表达式:

sum(1,2,3,4,5)# sum() takes 2 positional arguments but 5 were given

1 <generator object <genexpr> at 0x0000000001E18938>
2 0
3 1
4 4
5 9
6 16
7 [Finished in 0.3s]

sum(1,2)# 3

举例如下:

# type, value, traceback 分别代表错误的类型、值、追踪栈

  2、自动实现迭代器协议:对于生成器,python会自动实现迭代器协议,以便应用到迭代北京中(如for循环,sum函数)。由于生成器自动实现了迭代器协议,所以,我们可以调用它的next方法,并且,在没有值可以返回的时候,生成器自动产生StopIteration异常

yieldindex

1 1
2 1
3 2
4 3
5 5
6 [Finished in 0.3s]

# 但不指定关键字参数则报错

类似与列表推导,但是,生成器返回按需产生结果的一个对象,而不是一次构建一个结果列表。

好处:

闭包三要素:

# 但在需要遍历的数目较多时,会占用大量内存

下面举一个装饰器实际应用的例子。

# 或者这样

  这两行代码是用来判断是否为迭代器的,返回True或False。

for_inindexs:

其中,__next__() 方法和next的作用是一样的。如下所示。

def from_string(cls,string):

3、在python中创建生成器

ifletter=='a':

上面这个例子是做网页的时候对字体进行设置,对test1()进行加粗,对test2()斜体处理,对test3()先斜体在加粗。注意:对一个函数可以同时使用多个装饰器,装饰顺序由内而外。

result=get_indexs(array,1,True)

常规函数定义,但是使用yield语句而不是return语句返回结果。yield语句每次返回一个结果,但每个结果中间,挂起函数的状态,以便下次从它离开的地方继续执行。

year,month,day=map(str,string.split('-'))

装饰器有2个特性:   1、可以把被装饰的函数替换成其他函数

def sum(a,b):

 

# 在 Date 内新增一个 staticmethod

使用生成器的情况:

关键字参数可提高代码可读性

        4、执行函数后清理功能

defis_month_validate(month):

二、迭代器

print('loop 2')

C、装饰器对不定长参数函数进行装饰

普通的方式,在调用时不会强制要求使用关键字参数

1 li=[i**2 for i in range(5)]
2 print(li)
3 
4 [0, 1, 4, 9, 16]
5 [Finished in 0.3s]

foriinindexs:

再举一个例子。使用生成器函数来生成斐波纳契数列。

但要注意的是,仅仅是实现__iter__方法的迭代器,只能通过for循环来迭代;想要通过next方法迭代的话则需要使用iter方法:

      3、返回内部函数

result.append(1)

大家可以在自己的电脑上试试下面两个表达式,并且观察内存占用情况。对于前一个表达式,我在自己的电脑上进行测试,还没有看到最终结果电脑就已经卡死,对于后一个表达式,几乎没有什么内存占用。

# 使用 **kwargs,代表接收关键字参数,函数内的 kwargs 则是一个字典,传入的关键字参数作为键值对的形式存在

本文深入浅出地介绍了Python中,一个容易被大家忽略的重要特性,即Python的生成器。在实际工作中,充分利用Python生成器,不但能够减少内存使用,还能够提高代码可读性。掌握生成器也是Python高手的标配。希望本文能够帮助大家理解Python的生成器。

returnvalue

下面再来看看send() 方法的使用。

forindex,letterinenumerate(self.data):

7、总结

使用 Python2 中强制关键字参数的方式

3、什么是迭代器呢?

可以在其中进一步封装该方法,提高复用性

首先,生成器的好处是延迟计算,一次返回一个结果。也就是说,它不会一次生成所有的结果,这对于大数据量处理,将会非常有用。

date=Date.from_string('2016-11-09')

上面的装饰器是一个万能的装饰器,因为其可以用来装饰任何函数,包括有无参数函数和有无返回值函数。

创建上下文管理器

图片 2

string='this is a test to find a' index'

输出结果为:

然后可以以这样的方式进行调用:

1 a=sum([i for i in range(1000000000)])
2 print(a)
3 
4 b=sum(i for i in range(1000000000))
5 print(b)

# AttributeError: 'Test' object has no attribute 'new_key'

输出结果为:

yield num

  1、生成器表达式

可迭代对象

相信通过这篇文章,大家已经能够理解生成器的作用和好处了。但是,还没有结束,使用生成器,也有一点注意事项。

读取完成之后,with语句调用之前暂存的__exit__方法

        5、权限z校验等场景

ifjudgeanditem==target:

month=self.month,

上述代码创建了Date类,该类会在初始化时设置day/month/year属性,并且通过property设置了一个getter,可以在实例化之后,通过time获取存储的时间:

然而这个字典浪费了多余的空间 — 很多时候我们不会创建那么多的属性。因此通过__slots__可以告诉 Python 不要使用字典而是固定集合来分配空间。

便于扩充函数参数

@property

result=get_indexs(array,target=1,judge=True)

forindex,iteminenumerate(array):

self.data=data

# 这样可行

def __init__(self,filename):

默认情况下,Python 用一个字典来保存一个对象的实例属性。这使得我们可以在运行的时候动态的给类的实例添加新的属性:

__slots__=['name','value']

更加符合面向对象的编程方式

print('loop 1')

但要注意的是,不定长度的参数args在传递给函数时,需要先转换成元组tuple。这意味着,如果你将一个生成器作为参数带入到函数中,生成器将会先遍历一遍,转换为元组。这可能会消耗大量内存:

算是老生常谈了:函数的默认值只会在程序加载模块并读取到该函数的定义时设置一次

whileTrue:

returnint(month)<=12andint(month)>=1

print(result)# [1]

类内部普通的方法,都是以self作为第一个参数,代表着通过实例调用时,将实例的作用域传入方法内;

date=cls(year,month,day)

print(add_number.num)# 4

上一步的结果被传递给file_read参数

使用生成器

forindex,iteminenumerate(array):

但不够好:

# TypeError: get_indexs() takes 1 positional argument but 3 were given

exceptStopIteration:

__enter__方法打开文件,并将结果返回给with语句

考虑使用生成器来改写直接返回列表的函数

array=[1,2,3,4,1]

在类外额外多写了一个方法,每次还得格式化以后获取参数

定义只能使用关键字参数的函数

self.day=day

# 定义一个函数,其作用是检测字符串里所有 a 的索引位置,最终返回所有 index 组成的数组

forlineinfile.readlines():

比较繁琐吧?所以说使用上下文管理器的好处就是,通过调用我们预先设置好的回调,自动帮我们处理代码块开始执行和执行完毕时的工作。而通过自定义类的__enter__和__exit__方法,我们可以自定义一个上下文管理器。

def get_default(value=[]):

print(list(result))# [0, 4]

要注意的是,在__exit__方法内,我们关闭了文件,但最后返回True,所以错误不会被with语句抛出。否则with语句会抛出一个对应的错误。怎么样才能学好python学好python你需要一个良好的环境,一个优质的开发交流群,群里都是那种相互帮助的人才是可以的,我有建立一个python学习交流群,在群里我们相互帮助,相互关心,相互分享内容,这样出问题帮助你的人就比较多,群号是301,还有056,最后是051,这样就可以找到大神聚合的群,如果你只愿意别人帮助你,不愿意分享或者帮助别人,那就请不要加了,你把你会的告诉别人这是一种分享。

关于参数的默认值

在@classmethod内,可以通过cls参数,获取到跟外部调用类时一样的便利

print(_)

fornuminrange(10):

)

def get_nums():

Python @classmethod and @staticmethod for beginner

# 23

result=[]

def get_indexs(array,**kwargs):

每次获取到符合条件的结果,都要调用append方法。但实际上我们的关注点根本不在这个方法,它只是我们达成目的的手段,实际上只需要index就好了

add_number(3)

来通过实例了解它们的使用场景:

yield index

Difference between staticmethod and classmethod in python

def get_a_indexs(string):

因此,更推荐使用None作为默认参数,在函数内进行判断之后赋值:

next(iter_indexs)# 8

result=get_indexs(array,target=1)

# 在 Date 内新增一个 classmethod

print(_)

for_inindexs:

@classmethod & @staticmethod

def __iter__(self):

print(result2)# [1, 2]

forindex,letterinenumerate(string):

file.close()

通过这种方式实现的好处是,可以通过类的属性来保存状态,而不必创建一个闭包或者全局变量。

result2.append(2)

year,month,day=map(str,date_string.split('-'))

print(result2)# [2]

ifvalueisNone:

sum(1,2)# 3

classLoopIter(object):

yieldindex

上下文管理器,通俗的介绍就是:在代码块执行前,先进行准备工作;在代码块执行完成后,做收尾的处理工作。with语句常伴随上下文管理器一起出现,经典场景有:

def __init__(self,data):

在with语句内对file_read参数进行操作,读取每一行

# 但如果我想求很多数的总和,而将参数全部代入是会报错的,而一次一次代入又太麻烦

# 必须在 __iter__ 中 yield 结果

因此,使用生成器generator会更好。生成器是使用yield表达式的函数,调用生成器时,它不会真的执行,而是返回一个迭代器,每次在迭代器上调用内置的next函数时,迭代器会把生成器推进到下一个yield表达式:

self.name=name

print('finish!')

@staticmethod

@staticmethod不需要传入默认参数,类似于一个普通的函数

with语句先暂存了ReadFile类的__exit__方法

forindex,iteminenumerate(array):

# loop 1

# 此时再增加新的属性则会报错

然后调用ReadFile类的__enter__方法

result=get_indexs(array,1)

self.month=month

with ReadFile('test.txt')asfile_read:

# 这样可行

使用关键字参数

day=self.day

classTest(object):

sum(*[1,2,3,4,5])# 15

这样,我们就可以通过Date类来调用from_string方法创建实例,并且不侵略、修改旧的实例化方式:

iter_indexs=iter(indexs)

sum(*nums)# 45

self.num=0

对于这种接收参数数目不一定,而且不在乎参数传入顺序的函数,则应该利用位置参数*args:

def time(self):

elifnotjudgeanditem!=target:

print(list(result))# [0, 4]

month='08'

# 定义一个方法,它的作用是遍历一个数组,找出等于(或不等于)目标元素的 index

yield index

indexs=LoopIter(string)

没有解决传入参数过多的问题

result.append(index)

elifnotjudgeanditem!=target:

test.new_key='new_value'

但如果我们想改变属性传入的方式呢?毕竟,在初始化时就要传入年/月/日三个属性还是很烦人的。能否找到一个方法,在不改变现有接口和方法的情况下,可以通过传入2016-11-09这样的字符串来创建一个Date实例?

results=get_a_indexs('this is a test to check a')

@classmethod和@staticmethod很像,但他们的使用场景并不一样。

date.time# 2016-11-09

result2.append(2)

这样的话,将类的实例迭代重复多少次都没问题:

通过定义类中的__call__方法,可以使该类的实例能够像普通函数一样调用。

return[]

使用位置参数

indexs=LoopIter(string)

file=open('test.txt','r')

result=get_indexs(array,target=1,judge=True)

也就是说,如果给某参数赋予动态的值( 比如[]或者{}),则如果之后在调用函数的时候给参数赋予了其他参数,则以后再调用这个函数的时候,之前定义的默认值将会改变,成为上一次调用时赋予的值:

nums=get_nums()

test=Test()

# 同时,也可以直接把一个数组带入,在带入时使用 * 进行解构

test.new_key='new_value'

def get_default(value=None):

result2=get_default()

def __init__(self,year=0,month=0,day=0):

假设我们需要创建一个名为Date的类,用于储存 年/月/日 三个数据

# 否则错误会被 with 语句抛出

用这种方法有几个小问题:

returnTrue

有时候,方法接收的参数数目可能不一定,比如定义一个求和的方法,至少要接收两个参数:

indexs=get_a_indexs(string)

def sum(*args):

yield index

self.value=value

def __init__(self):

你可能会想到这样的方法:

date=Date(year,month,day)

result=0

elifnotjudgeanditem!=target:

print(line)

def get_indexs(array,*,target='',judge=True):

returnself.file

ifletter=='a':

result=get_indexs(array,target=1,judge=True)

需要注意的是,普通的迭代器只能迭代一轮,一轮之后重复调用是无效的。解决这种问题的方法是,你可以定义一个可迭代的容器类

本文由澳门威斯尼人平台登录发布于Web前端,转载请注明出处:的深入理解,编写高效且优雅的

相关阅读