百度
360搜索
搜狗搜索

python代码生成器,python 生成器是函数吗详细介绍

本文目录一览: C4D中的python生成器

python生成器是预制的python插件的objectData的封装(就和脚本是插件commandData的封装一样)。这样创建生成器对象就不用写全脚本了,注意这物体仅仅是生成器,想创建变形器,还得老老实实创建objectData插件。
在生成器内的python代码会生成一个object。默认下,生成了一个立方体,并返回:importc4ddefmain():returnc4d.BaseObject(c4d.Ocube)UserData输入
当然也可以返回别的物体,或者用userdata调整物体参数。注意op可以快速引用生成器对象。
importc4ddefmain():cone=c4d.BaseObject(c4d.Ocone)cone[c4d.PRIM_CONE_TRAD]=op[c4d.ID_USERDATA,1]returncone对象输入--pipe生成器
许多c4d生成器会将其孩子作为输入,这里咱也可以这么干。通过op.GetDown()能获取第一个熊孩子,用op.GetChildren()能获取所一窝熊孩子。接下来就要折腾熊孩子了,为了他们的安全,咱先弄一个拷贝。
创建整个对象层次也不难,一般创建完层次会返回顶层对象。如果选了这个生成器,还返回了MakeEditable,就能看到创建好的整个层级结构。上代码:
importc4ddefmain():#Getthechildobject(touseasthesweepspline)#UseGetClonesowearen'tworkingontheobjectitselfsource=op.GetDown().GetClone()#CreateaCirclePrimitivetoactasthesweepprofilecircle=c4d.BaseObject(c4d.Osplinecircle)#SettheradiusbasedonUserData1circle[c4d.PRIM_CIRCLE_RADIUS]=op[c4d.ID_USERDATA,1]#CreateanewSweepNURBSsweep=c4d.BaseObject(c4d.Osweep)#InsertthesourcesweepsplineundertheSweepNURBSsource.InsertUnder(sweep)#InsertthecircleprofilesplineundertheSweepNURBScircle.InsertUnder(sweep)#ReturntheSweepNURBSreturnsweep建模命令-边到样条线
建模命令也能生成新对象。再次强调,需要在拷贝原物体后在拷贝上进行操作,就不会直接修改层级里面的对象。下面的例子给当前选中的物体输出了样条线。
importc4ddefmain():#Getthechildobjectobj=op.GetDown()#Wecanonlycontinueiftherewasachildifnotobj:returnNone#Alsoneedtocheckthatit'sapolyobjectifnotobj.CheckType(c4d.Opolygon):returnNone#Andthatatleastoneedgeisselectedifobj.GetEdgeS().GetCount()
注意,这个例子需要选中一个物体的一条边。生成器的cache检测不到选中物体的变化,因此需要关闭优化cache才能更新。不幸的是关闭优化还不能正常工作,以后会正常的。
以上就是本文的全部内容,希望对大家的学习有所帮助,。
-->

python生成器到底有什么优点?

最近正在学python,感觉这语言不错,之前是做PHP的,现在自学了半个月了!以我自己对生成器的理解,能说的就以下3点,如果不对还请高手指正,毕竟现在还是个python小白:
1、节省资源消耗,和声明序列不同的是生成器在不使用的时候几乎不占内存,也没有声明计算过程!
2、使用的时候,生成器是随用随生成,用完即刻释放,非常高效!
3、可在单线程下实现并发运算处理效果,非常牛逼,这点不可小视,看看nginx epoll单线程承载的并发量比多线程还效率高很多,最底层就是这个原理!
1、主要是开发快,语言简洁,没那么多技巧,所以读起来很清楚容易。
2、C/C++可以写python的module,标准库里就有用C/C++写的东西,这个跟java的JNI类似。
3、python的gui一般是用tkinter,就是tk的python的wrapper。python没有像xna那么方便的工具。
4、python不是为了网络设计的。python是1991年有的,WWW是1993年才被CERN开放的。网络编程用python主要是为了开发快。
5、像VS那样功能强的IDE,有要钱的PyCharm和不要钱的PyDev。PyDev有Eclipse的插件版本或者是Aptana Studio版本。
在Python这门语言中,生成器毫无疑问是最有用的特性之一。与此同时,也是使用的最不广泛的Python特性之一。究其原因,主要是因为,在其他主流语言里面没有生成器的概念。正是由于生成器是一个“新”的东西,所以,它一方面没有引起广大工程师的重视,另一方面,也增加了工程师的学习成本,最终导致大家错过了Python中如此有用的一个特性。
1. 迭代器协议
由于生成器自动实现了迭代器协议,而迭代器协议对很多人来说,也是一个较为抽象的概念。所以,为了更好的理解生成器,我们需要简单的回顾一下迭代器协议的概念。
迭代器协议是指:对象需要提供next方法,它要么返回迭代中的下一项,要么就引起一个StopIteration异常,以终止迭代
可迭代对象就是:实现了迭代器协议的对象
协议是一种约定,可迭代对象实现迭代器协议,Python的内置工具(如for循环,sum,min,max函数等)使用迭代器协议访问对象。
举个例子:在所有语言中,我们都可以使用for循环来遍历数组,Python的list底层实现是一个数组,所以,我们可以使用for循环来遍历list。
2. 生成器
Python使用生成器对延迟操作提供了支持。所谓延迟操作,是指在需要的时候才产生结果,而不是立即产生结果。这也是生成器的主要好处。
Python有两种不同的方式提供生成器:
生成器函数:常规函数定义,但是,使用yield语句而不是return语句返回结果。yield语句一次返回一个结果,在每个结果中间,挂起函数的状态,以便下次重它离开的地方继续执行
生成器表达式:类似于列表推导,但是,生成器返回按需产生结果的一个对象,而不是一次构建一个结果列表
4. 使用生成器的注意事项
5. 总结
本文深入浅出地介绍了Python中,一个容易被大家忽略的重要特性,即Python的生成器。为了讲解生成器,本文先介绍了迭代器协议,然后介绍了生成器函数和生成器表达式,并通过示例演示了生成器的优点和注意事项。在实际工作中,充分利用Python生成器,不但能够减少内存使用,还能够提高代码可读性。掌握生成器也是Python高手的标配。希望本文能够帮助大家理解Python的生成器。

python生成器到底有什么优点

1. 迭代器协议
由于生成器自动实现了迭代器协议,而迭代器协议对很多人来说,也是一个较为抽象的概念。所以,为了更好的理解生成器,我们需要简单的回顾一下迭代器协议的概念。
迭代器协议是指:对象需要提供next方法,它要么返回迭代中的下一项,要么就引起一个StopIteration异常,以终止迭代
可迭代对象就是:实现了迭代器协议的对象
协议是一种约定,可迭代对象实现迭代器协议,Python的内置工具(如for循环,sum,min,max函数等)使用迭代器协议访问对象。
举个例子:在所有语言中,我们都可以使用for循环来遍历数组,Python的list底层实现是一个数组,所以,我们可以使用for循环来遍历list。如下所示:
>>> for n in [1, 2, 3, 4]:
... print n
但是,对Python稍微熟悉一点的朋友应该知道,Python的for循环不但可以用来遍历list,还可以用来遍历文件对象,如下所示:
>>> with open(‘/etc/passwd’) as f: # 文件对象提供迭代器协议
... for line in f: # for循环使用迭代器协议访问文件
... print line
...
为什么在Python中,文件还可以使用for循环进行遍历呢?这是因为,在Python中,文件对象实现了迭代器协议,for循环并不知道它遍历的是一个文件对象,它只管使用迭代器协议访问对象即可。正是由于Python的文件对象实现了迭代器协议,我们才得以使用如此方便的方式访问文件,如下所示:
>>> f = open('/etc/passwd')
>>> dir(f)
['__class__', '__enter__', '__exit__', '__iter__', '__new__', 'writelines', '...'
2. 生成器
Python使用生成器对延迟操作提供了支持。所谓延迟操作,是指在需要的时候才产生结果,而不是立即产生结果。这也是生成器的主要好处。
Python有两种不同的方式提供生成器:
生成器函数:常规函数定义,但是,使用yield语句而不是return语句返回结果。yield语句一次返回一个结果,在每个结果中间,挂起函数的状态,以便下次重它离开的地方继续执行
生成器表达式:类似于列表推导,但是,生成器返回按需产生结果的一个对象,而不是一次构建一个结果列表
2.1 生成器函数
我们来看一个例子,使用生成器返回自然数的平方(注意返回的是多个值):
def gensquares(N):
for i in range(N):
yield i ** 2
for item in gensquares(5):
print item,
使用普通函数:
def gensquares(N):
res = []
for i in range(N):
res.append(i*i)
return res
for item in gensquares(5):
print item,
可以看到,使用生成器函数代码量更少。
2.2 生成器表达式
使用列表推导,将会一次产生所有结果:
>>> squares = [x**2 for x in range(5)]
>>> squares
[0, 1, 4, 9, 16]
将列表推导的中括号,替换成圆括号,就是一个生成器表达式:
>>> squares = (x**2 for x in range(5))
>>> squares

>>> next(squares)

0

>>> next(squares)

1

>>> next(squares)

4

>>> list(squares)

[9, 16]

Python不但使用迭代器协议,让for循环变得更加通用。大部分内置函数,也是使用迭代器协议访问对象的。例如, sum函数是Python的内置函数,该函数使用迭代器协议访问对象,而生成器实现了迭代器协议,所以,我们可以直接这样计算一系列值的和:

>>> sum(x ** 2 for x in xrange(4))

而不用多此一举的先构造一个列表:

>>> sum([x ** 2 for x in xrange(4)])

2.3 再看生成器

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

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

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

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

3. 示例

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

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

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

sum([i for i in xrange(10000000000)])

sum(i for i in xrange(10000000000))

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

不使用生成器的情况:

def index_words(text):

result = []

if text:

result.append(0)

for index, letter in enumerate(text, 1):

if letter == ' ':

result.append(index)

return result

使用生成器的情况:

def index_words(text):

if text:

yield 0

for index, letter in enumerate(text, 1):

if letter == ' ':

yield index

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

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

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

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

4. 使用生成器的注意事项

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

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

如下所示:

def get_province_population(filename):

with open(filename) as f:

for line in f:

yield int(line)

gen = get_province_population('data.txt')

all_population = sum(gen)

#print all_population

for population in gen:

print population / all_population

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

阅读更多 >>>  python中update的用法,万字干货,Python语法大合集,一篇文章带你入门

闲话python 45: 浅谈生成器yield

生成器似乎并不是一个经常被开发者讨论的语法,因此也就没有它的大兄弟迭代器那么著名。大家不讨论它并不是说大家都已经对它熟悉到人尽皆知,与之相反,即使是工作多年的开发者可能对生成器的运行过程还是知之甚少。这是什么原因导致的呢?我猜想大概有以下几点原因: (1)运行流程不同寻常,(2)日常开发不需要,(3)常常将生成器与迭代器混淆。 生成器的运行流程可以按照协程来理解,也就是说 返回中间结果,断点继续运行 。这与我们通常对于程序调用的理解稍有差异。这种运行模式是针对什么样的需求呢? 一般而言,生成器是应用于大量磁盘资源的处理。 比如一个很大的文件,每次读取一行,下一次读取需要以上一次读取的位置为基础。下面就通过代码演示具体看看生成器的运行机制、使用方式以及与迭代器的比较。
什么是生成器?直接用文字描述可能太过抽象,倒不如先运行一段代码,分析这段代码的运行流程,然后总结出自己对生成器的理解。

从以上演示可以看出,这段代码定义了一个函数,这个函数除了yield这个关键字之外与一般函数并没有差异,也就是说生成器的魔法都是这个yield关键字引起的。 第一点,函数的返回值是一个生成器对象。 上述代码中,直接调用这个看似普通的函数,然后将返回值打印出来,发现返回值是一个对象,而并不是普通函数的返回值。 第二点,可以使用next对这个生成器对象进行操作 。生成器对象天然的可以被next函数调用,然后返回在yield关键字后面的内容。 第三,再次调用next函数处理生成器对象,发现是从上次yield语句之后继续运行,直到下一个yield语句返回。
生成器的运行流程确实诡异,下面还要演示一个生成器可以执行的更加诡异的操作:运行过程中向函数传参。

返回生成器和next函数操作生成器已经并不奇怪了,但是在函数运行过程中向其传参还是让人惊呆了。 调用生成器的send函数传入参数,在函数内使用yield语句的返回值接收,然后继续运行直到下一个yield语句返回。 以前实现这种运行流程的方式是在函数中加上一个从控制台获取数据的指令,或者提前将参数传入,但是现在不用了,send方式使得传入的参数可以随着读取到的参数变化而变化。
很多的开发者比较容易混淆生成器和迭代器,而迭代器的运行过程更加符合一般的程序调用运行流程,因此从亲进度和使用熟悉度而言,大家对迭代器更有好感。比如下面演示一个对迭代器使用next方法进行操作。

从以上演示来看,大家或许会认为迭代器比生成器简单易用得太多了。不过,如果你了解迭代器的实现机制,可能就不会这么早下结论了。python内置了一些已经实现了的迭代器使用确实方便,但是如果需要自己去写一个迭代器呢?下面这段代码就带大家见识以下迭代器的实现。

在python中,能被next函数操作的对象一定带有__next__函数的实现,而能够被迭代的对象有必须实现__iter__函数。看了这么一段操作,相信大家对迭代器实现的繁琐也是深有体会了,那么生成器的实现是不是会让你觉得更加简单易用呢?不过千万别产生一个误区,即生成器比迭代器简单就多用生成器。 在实际开发中,如果遇到与大量磁盘文件或者数据库操作相关的倒是可以使用生成器。但是在其他的任务中使用生成器难免有炫技,并且使逻辑不清晰而导致可读性下降的嫌疑。 这大概也能解释生成器受冷落的原因。不过作为一个专业的开发者,熟悉语言特性是分内之事。
到此,关于生成器的讨论就结束了。本文的notebook版本文件在github上的cnbluegeek/notebook仓库中共享,欢迎感兴趣的朋友前往下载。

如何更好地理解Python迭代器和生成器

我来举例:
当一个txt文件有几万行的时候,你是用cat ,会发现屏幕不停的刷不停的刷,影响你正常阅读。
这时候你需要使用more来一页一页阅读。
同样的道理,当文件被读入到内存时候,如果数据太大,会导致内存被占用过多。这时候需要一个向more的功能一次读取一点,这个就是迭代器的功能。
那么怎么样才能有这样的功能存在呢?这就是生成器的作用。让cat aa.txt通过生成器变成more aa.txt的效果。
在Python中,有些名称会在前后加上两个下划线,这种拼写表示名字有特殊含义。所以绝不要在自己的程序中使用这种名字。
1. __init__方法
Python 类中有默认的构造函数__init__我们可以覆盖它来试试。如下:
class FooBar:
def __init__(self): self.somevar=42 f=FooBar() print f.somevar
我们修改一下如下:
class FooBar:
def __init__(self,value=42): self.somevar=value f=FooBar("what's this?")
print f.somevar
输出如下:
what's this?
2. 重写方法
如果一个方法在B类的一个实例中被调用,但在B类中没有找到方法,那么就会在超类A里面找。 如下所示:
class A:
def hello(self):
print "hello ,I.m A" class B(A): pass

a=A() b=B() a.hello() b.hello()
输出如下:
hello ,I.m A hello ,I.m A
B类没有定义自己的方法hello调用的是父类的hello方法。 如果进行重写这个方法,如下:
class A:
def hello(self):
print "hello ,I.m A" class B(A): pass
def hello(self): print "Hello,I'm B" a=A() b=B() a.hello() b.hello()
输出如下:
hello ,I.m A Hello,I'm B

3. 使用Super函数
我看来看下个例子如下:
class Bird:
def __init__(self): self.hungry=True def eat(self): if self.hungry: print 'Aaah...' self.hungry=False else:
print "No,thansk"
class SongBird(Bird): def __init__(self): self.sound='Squawk!'

def sing(self): print self.sound
sb=SongBird() sb.sing() sb.eat()
运行如下:
Squawk!
Traceback (most recent call last):
AttributeError: SongBird instance has no attribute 'hungry'
没有hungry属性。
没有得到父类的属性,需要用到Super函数,处理后如下:
from _pyio import __metaclass__ __metaclass__=type class Bird:
def __init__(self): self.hungry=True def eat(self): if self.hungry: print 'Aaah...' self.hungry=False else:
print "No,thansk"
class SongBird(Bird): def __init__(self): # Bird.__init__(self)
super(SongBird,self).__init__() self.sound='Squawk!' def sing(self): print self.sound
sb=SongBird() sb.sing() sb.eat()
运行如下:
Squawk! Aaah...
   迭代器是访问集合元素的一种方式。迭代器对象从集合的第一个元素开始访问,知道所有的元素被访问完结束。迭代器只能往前不会后退,不过这也没什么,因为人们很少在迭代途中往后退。
  使用迭代器的优点
  对于原生支持随机访问的数据结构(如tuple、list),迭代器和经典for循环的索引访问相比并无优势,反而丢失了索引值(可以使用内建函数enumerate()找回这个索引值)。但对于无法随机访问的数据结构(比如set)而言,迭代器是唯一的访问元素的方式。
  另外,迭代器的一大优点是不要求事先准备好整个迭代过程中所有的元素。迭代器仅仅在迭代到某个元素时才计算该元素,而在这之前或之后,元素可以不存在或者被销毁。这个特点使得它特别适合用于遍历一些巨大的或是无限的集合,比如几个G的文件,或是斐波那契数列等等。
  迭代器更大的功劳是提供了一个统一的访问集合的接口,只要定义了__iter__()方法对象,就可以使用迭代器访问。
  迭代器有两个基本的方法
next方法:返回迭代器的下一个元素
__iter__方法:返回迭代器对象本身
  简单地讲,yield 的作用就是把一个函数变成一个 generator,带有 yield 的函数不是一个普通函数,Python 解释器会将其视为一个 generator,调用 fab(5) 不会执行fab 函数,而是返回一个 iterable 对象!在 for 循环执行时,每次循环都会执行 fab 函数内部的代码,执行到 yield b 时,fab 函数就返回一个迭代值,下次迭代时,代码从 yield b 的下一条语句继续执行,而函数的本地变量看起来和上次中断执行前是完全一样的,于是函数继续执行,直到再次遇到 yield。看起来就好像一个函数在正常执行的过程中被 yield 中断了数次,每次中断都会通过 yield 返回当前的迭代值。
对于原生支持随机访问的数据结构(如tuple、list),迭代器和经典for循环的索引访问相比并无优势,反而丢失了索引值(可以使用内建函数enumerate()找回这个索引值)。但对于无法随机访问的数据结构(比如set)而言,迭代器是唯一的访问元素的方式。
另外,迭代器的一大优点是不要求事先准备好整个迭代过程中所有的元素。迭代器仅仅在迭代到某个元素时才计算该元素,而在这之前或之后,元素可以不存在或者被销毁。这个特点使得它特别适合用于遍历一些巨大的或是无限的集合,比如几个G的文件,或是斐波那契数列等等。
迭代器更大的功劳是提供了一个统一的访问集合的接口,只要定义了__iter__()方法对象,就可以使用迭代器访问。
两者做产生的作业不同
1、Python迭代器
Python迭代器仅是一容器对象,它实现了迭代器协议。
2、从Python2.2起,生成器提供了一种简洁的方式帮助返回列表元素的函数来完成简单和有效的代码。
  迭代器和生成器都是Python中特有的概念,迭代器可以看作是一个特殊的对象,每次调用该对象时会返回自身的下一个元素,从实现上来看,一个可迭代的对象必须是定义了__iter__()方法的对象,而一个迭代器必须是定义了__iter__()方法和next()方法的对象。生成器的概念要比迭代器稍显复杂,因为生成器是能够返回一个迭代器的函数,其最大的作用是将输入对象返回为一个迭代器。Python中使用了迭代的概念,是因为当需要循环遍历一个较大的对象时,传统的内存载入方式会消耗大量的内存,不如需要时读取一个元素的方式更为经济快捷。
迭代器
  迭代器(iterator)是一种对象,它能够用来遍历标准模板库容器中的部分或全部元素,每个迭代器对象代表容器中的确定的地址。迭代器修改了常规指针的接口,所谓迭代器是一种概念上的抽象:那些行为上像迭代器的东西都可以叫做迭代器。然而迭代器有很多不同的能力,它可以把抽象容器和通用算法有机的统一起来。
  迭代器提供一些基本操作符:*、++、==、!=、=。这些操作和C/C++“操作array元素”时的指针接口一致。不同之处在于,迭代器是个所谓的复杂的指针,具有遍历复杂数据结构的能力。其下层运行机制取决于其所遍历的数据结构。因此,每一种容器型别都必须提供自己的迭代器。事实上每一种容器都将其迭代器以嵌套的方式定义于内部。因此各种迭代器的接口相同,型号却不同。这直接导出了泛型程序设计的概念:所有操作行为都使用相同接口,虽然它们的型别不同。
  迭代器使开发人员能够在类或结构中支持foreach迭代,而不必整个实现IEnumerable或者IEnumerator接口。只需提供一个迭代器,即可遍历类中的数据结构。当编译器检测到迭代器时,将自动生成IEnumerable接口或者IEnumerator接口的Current,MoveNext和Dispose方法。
生成器
  生成器是一次生成一个值的特殊类型函数。可以将其视为可恢复函数。调用该函数将返回一个可用于生成连续 x 值的生成器【Generator】
  简单的说就是在函数的执行过程中,yield语句会把你需要的值返回给调用生成器的地方,然后退出函数,下一次调用生成器函数的时候又从上次中断的地方开始执行,而生成器内的所有变量参数都会被保存下来供下一次使用。

python 生成器是函数吗

是,可以叫高级函数,在Python中,这种一边循环一边计算的机制,称为生成器(Generator)。
对于程序而言,内存也是很重要的,因为程序中很多数据都是保存在内存中的,如果内存中存储的数据过多,那么系统就会崩溃,这是人们不希望发生的。
可以采用生成器推导式来解决内存不足的问题。例如,利用生成器推导式创建一个生成器n,数据为1~33数字,可以写成n = (i for i in range(1, 34))。这样当程序需要一个数时,程序才生成数据,可以节省内存。然而生成器推导式太过简单,只能用一行代码的形式实现,如果要创建复杂的生成器,如创建一个生成器f,生成前10个斐波那契数字,生成器推导式已经不能满足需求了,因为斐波那契数列最开始的两个数都无法赋值。
函数可以实现复杂的功能,然而要节省内存,就需要使用生成器函数。生成器函数与普通函数的区别是函数中包含关键字yield。实际上只要含有yield关键字的函数就是生成器函数。
生成器函数是用函数实现生成器。定义生成器函数的语法格式如下:
>>> def fib(): # 定义生成器函数
... a, b = 1, 1 # 定义初始值
... while True:
... yield a # 暂停执行,返回一个新变量值
... a, b = b, a+b
...
>>> a= fib() # 调用生成器函数
>>> for i in range(10):
... print(next(a)) # 调用生成器函数的yield生成值
...
1
1
2
3
5
8
13
21
34
55
def 函数名(参数):
函数体
yield 变量名
函数体
由语法格式可知,生成器函数与普通函数的区别在于函数体部分,生成器函数的函数体含有“yield 变量名”语句。yield的功能类似于return,return是函数返回值,yield的功能也是返回变量,但是它仅返回变量而不退出函数,因此,yield可以看作是多次返回变量且不会退出函数的return。
在调用生成器函数时,写上函数名与参数,并通过一个变量接收返回值,语法格式如下:
变量名 = 函数名(参数)
调用生成器函数的yield生成值的第一种方法如下:
next(变量名)
第二种方法如下:
变量名.__next__()
在掌握了生成器函数的定义和调用之后,就可以使用生成器函数实现生成前10个斐波那契数字的案例了。这个案例主要分为三步,第一步是定义生成器函数,第二步是调用并赋值,第三步是打印结果,代码如下:
在上述程序中,首先定义生成器函数fib(),函数内先定义斐波那契数列的两个初始值,再写一个while True死循环。这个死循环有些特别,先是用yield生成待使用的数字,再通过赋值语句“a, b = b, a+b”将b的值赋给a,将a+b的值赋给b,每次循环都是如此。然后调用生成器函数fib(),再调用生成器函数的yield生成值,最后打印结果。由于需要生成前10个斐波那契数字,因此可以采用for循环,每循环一次生成并打印一个斐波那契数字,共循环10次。第一次循环时,调用yield生成值a,即1;第二次循环时,调用yield生成值a,a被赋值成b的值,即1,而b被赋值成a+b的值,即2;第三次循环时,调用yield生成值a,a被赋值成b的值,而此时b的值是上次赋值的a+b的值,即2……以此类推,就得到了整个斐波那契数列。

阅读更多 >>>  如何在linux服务器后台运行

Python生成器,递归时代码执行顺序?

生成器就是用来生成有规律的值的
含有yield语句的函数就是生成器,counter(5)表示用参数start_at=5为初值调用生成 器,将函数对象赋值给count,count指向生成 器对象
每次调用生成器的next方法,就执行代码到yield语句处返回yield后的值,因此第一次调用 执行到yield返回 ,下次调用 next时从上次执行处接着执行到yield语句处,所以生成器中可以用while True语句不用担心死循环
yield这个表达式的值就是None,所以val一直是None

什么是Python中的生成器推导式

Python中有一种紧凑的语法,可以通过一个循环和条件构建一个列表,这种语法叫做列表推导式(list comprehension): my_list = [ f(x) for x in sequence if cond(x) ] 类似地,我们可以通过字典推导式
Python中有一种紧凑的语法,可以通过一个循环和条件构建一个列表,这种语法叫做列表推导式(list comprehension):
my_list = [ f(x) for x in sequence if cond(x) ]
类似地,我们可以通过字典推导式(dictionary comprehension)创建字典,通过集合推导式(set comprehension)创建集合:
my_dict = { k(x): v(x) for x in sequence if cond(x) }
my_set = { f(x) for x in sequence if cond(x) }
(这一语法支持更加复杂的操作,但这里仅作示例)
最后,你还可以使用类似的语法创建一个生成器:
my_generator = ( f(x) for x in sequence if cond(x) )
不过,这并不叫做生成器推导式,而是叫做生成器表达式(generator expression)。为什么不叫前者呢?如果前三个语法都被称为“推导式”,为什么生成器这个不叫呢?
PEP 289 —— 生成器表达式 的最后给出了详细的备注,其中指出Raymond Hettinger起初提议使用“生成器推导式(generator comprehension)”一词,后来Peter Norvig提出了“累计显示(accumulation displays)”,后来Tim Peters推荐了“生成器表达式”这个
名词。但是它并没有名词出现了这样的变化。
EarlGrey:上面提到的这几位都是大牛啊!具体大家可以谷歌一下。
所以我在Twitter上提出了这个问题:
python 有个我不懂的问题:为什么它们被称为“生成器表达式”,而不是“生成器推导式”?
Guido的回答指出了核心原因:
推导式一开始属于“字面量显示(literal display)”这一概念。而生成器表达式不是一种显示(display)。
Matt Boehm后来找到了Tim Peters提出“生成器表达式”一词的邮件,其中讲述了一些细节:
读完邮件后,我对这个问题的理解更深了。首先,为什么会使用“推导式”(comprehension)一词?Tim在邮件中指出,这个词来源于集合论中的推导公理(Axiom of Comprehension),它指的是通过对另一个集合的元素应用某个谓词(predicate,即条
件)而组成新的集合。这和向另一个序列中的元素应用某个条件从而生成列表的做法非常类似。
EarlGrey:我之前看到很多翻译为“解析”,看到这里才觉得“推导式”才是更准确的说法。
正如Guido所指出的,Python的设计者当时更注重的是显示,而不是条件。“显示”一词在这里意味着代码的语法看上和它将创建的数据结构很像。列表显示(列表推导式)看上去像一个列表。对于集合和字典显示来说,也是一样的道理。但是由于没有生成器
字面量语法,因此根本就没有一个生成器显示可以进行对比,也就不存在生成器显示了。
在设计该功能的那封邮件中,“推导式”一次是“显示”的同义词,由于生成器没有显示,所以也不可能有推导式。
不过Time在他的邮件中也说到,推导式的奇妙之处在于条件。推导公理的核心则是谓语。也许是因为Python推导式中的条件是可选的,关注的焦点被转移到了显示方面。
但是我认为,我们应该叫它们“生成器推导式”。我们在描述这类语法时,并没有使用“显示”一词。我们没有理由将“推导式”与“显示”和字面量语法联系在一起。
列表推导式、字典推导式、集合推导式和生成器表达式,这四个表达式各自之间有着许多相似之处。如果将四者之间的类似点总结为“推导式”,将极大地简化相关概念。它们之间的相似点远大于不同之处,我建议大家对这四个表达式使用同样的概念

详解Python中的协程,为什么说它的底层是生成器?

协程又称为是微线程,英文名是Coroutine。它和线程一样可以调度,但是不同的是线程的启动和调度需要通过操作系统来处理。并且线程的启动和销毁需要涉及一些操作系统的变量申请和销毁处理,需要的时间比较长。而协程呢,它的调度和销毁都是程序自己来控制的,因此它更加轻量级也更加灵活。
协程有这么多优点,自然也会有一些缺点,其中最大的缺点就是需要编程语言自己支持,否则的话需要开发者自己通过一些方法来实现协程。对于大部分语言来说,都不支持这一机制。go语言由于天然支持协程,并且支持得非常好,使得它广受好评,短短几年时间就迅速流行起来。
对于Python来说,本身就有着一个GIL这个巨大的先天问题。GIL是Python的全局锁,在它的限制下一个Python进程同一时间只能同时执行一个线程,即使是在多核心的机器当中。这就大大影响了Python的性能,尤其是在CPU密集型的工作上。所以为了提升Python的性能,很多开发者想出了使用多进程+协程的方式。一开始是开发者自行实现的,后来在Python3.4的版本当中,官方也收入了这个功能,因此目前可以光明正大地说,Python是支持协程的语言了。
生成器(generator)
生成器我们也在之前的文章当中介绍过,为什么我们介绍协程需要用到生成器呢,是因为Python的协程底层就是通过生成器来实现的。
通过生成器来实现协程的原因也很简单,我们都知道协程需要切换挂起,而生成器当中有一个yield关键字,刚好可以实现这个功能。所以当初那些自己在Python当中开发协程功能的程序员都是通过生成器来实现的,我们想要理解Python当中协程的运用,就必须从最原始的生成器开始。
生成器我们很熟悉了,本质上就是带有yield这个关键词的函数。
def test():
n = 0
while n < 10:
val = yield n
print('val = {}'.format(val))
n += 1
这个函数当中如果没有yield这个语句,那么它就是一个普通的Python函数。加上了val = yield n这个语句之后,它有什么变化呢?
我们尝试着运行一下:
# 调用test函数获得一个生成器
g = test()
print(next(g))
print(next(g))
print(next(g))
得到这么一个结果:
输出的0,1,2很好理解,就是通过next(g)返回的,这个也是生成器的标准用法。奇怪的是为什么val=None呢?val不应该等于n么?
这里想不明白是正常的,因为这里涉及到了一个新的用法就是生成器的send方法。当我们在yield语句之前加上变量名的时候,它的含义其实是返回yield之后的内容,再从外界接收一个变量。也就是说当我们执行next(g)的时候,会从获取yield之后的数,当我们执行g.send()时,传入的值会被赋值给yield之前的数。比如我们把执行的代码改成这样:
g = test()
print(next(g))
g.send('abc')
print(next(g))
print(next(g))
我们再来看执行的结果,会发现是这样的:
第一行val不再是None,而是我们刚刚传入的abc了。
队列调度
生成器每次在执行到yield语句之后都会自然挂起,我们可以利用这一点来当做协程来调度。我们可以自己实现一个简易的队列来模拟这个过程。
首先我们声明一个双端队列,每次从队列左边头部获取任务,调度执行到挂起之后,放入到队列末尾。相当于我们用循环的方式轮询执行了所有任务,并且这整个全程不涉及任何线程创建和销毁的过程。
class Scheduler:
def __init__(self):
self._queue = deque()
def new_task(self, task):
self._queue.append(task)
def run(self):
while self._queue:
# 每次从队列左侧获取task
task = self._queue.popleft()
try:
# 通过next执行之后放入队列右侧
next(task)
self._queue.append(task)
except StopIteration:
pass
sch = Scheduler()
sch.new_task(test(5))
sch.new_task(test(10))
sch.new_task(test(8))
sch.run()
这个只是一个很简易的调度方法,事实上结合上yield from以及send功能,我们还可以实现出更加复杂的协程调度方式。但是我们也没有必要一一穷尽,只需要理解最基础的方法就可以了,毕竟现在我们使用协程一般也不会自己实现了,都会通过官方原生的工具库来实现。
@asyncio.coroutine
在Python3.4之后的版本当中,我们可以通过@asyncio.coroutine这个注解来将一个函数封装成协程执行的生成器。
在吸收了协程这个概念之后,Python对生成器以及协程做了区分。加上了@asyncio.coroutine注解的函数称为协程函数,我们可以用iscoroutinefunction()方法来判断一个函数是不是协程函数,通过这个协程函数返回的生成器对象称为协程对象,我们可以通过iscoroutine方法来判断一个对象是不是协程对象。
比如我把刚刚写的函数上加上注解之后再来执行这两个函数都会得到True:
import asyncio
@asyncio.coroutine
def test(k):
n = 0
while n < k:
yield
print('n = {}'.format(n))
n += 1
print(asyncio.iscoroutinefunction(test))
print(asyncio.iscoroutine(test(10)))
那我们通过注解将方法转变成了协程之后,又该怎么使用呢?
一个比较好的方式是通过asynio库当中提供的loop工具,比如我们来看这么一个例子:
loop = asyncio.get_event_loop()
loop.run_until_complete(test(10))
loop.close()
我们通过asyncio.get_event_loop函数创建了一个调度器,通过调度器的run相关的方法来执行一个协程对象。我们可以run_until_complete也可以run_forever,具体怎么执行要看我们实际的使用场景。
async,await和future
从Python3.5版本开始,引入了async,await和future。我们来简单说说它们各自的用途,其中async其实就是@asyncio.coroutine,用途是完全一样的。同样await代替的是yield from,意为等待另外一个协程结束。
我们用这两个一改,上面的代码就成了:
async def test(k):
n = 0
while n < k:
await asyncio.sleep(0.5)
print('n = {}'.format(n))
n += 1
由于我们加上了await,所以每次在打印之前都会等待0.5秒。我们把await换成yield from也是一样的,只不过用await更加直观也更加贴合协程的含义。
Future其实可以看成是一个信号量,我们创建一个全局的future,当一个协程执行完成之后,将结果存入这个future当中。其他的协程可以await future来实现阻塞。我们来看一个例子就明白了:
future = asyncio.Future()
async def test(k):
n = 0
while n < k:
await asyncio.sleep(0.5)
print('n = {}'.format(n))
n += 1
future.set_result('success')
async def log():
result = await future
print(result)
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait([
log(),
test(5)
]))
loop.close()
在这个例子当中我们创建了两个协程,第一个协程是每隔0.5秒print一个数字,在print完成之后把success写入到future当中。第二个协程就是等待future当中的数据,之后print出来。
在loop当中我们要调度执行的不再是一个协程对象了而是两个,所以我们用asyncio当中的wait将这两个对象包起来。只有当wait当中的两个对象执行结束,wait才会结束。loop等待的是wait的结束,而wait等待的是传入其中的协程的结束,这就形成了一个依赖循环,等价于这两个协程对象结束,loop才会结束。
总结
async并不只是可以用在函数上,事实上还有很多其他的用法,比如用在with语句上,用在for循环上等等。这些用法比较小众,细节也很多,就不一一展开了,大家感兴趣的可以自行去了解一下。
不知道大家在读这篇文章的过程当中有没有觉得有些费劲,如果有的话,其实是很正常的。原因也很简单,因为Python原生是不支持协程这个概念的,所以在一开始设计的时候也没有做这方面的准备,是后来觉得有必要才加入的。那么作为后面加入的内容,必然会对原先的很多内容产生影响,尤其是协程借助了之前生成器的概念来实现的,那么必然会有很多耦合不清楚的情况。这也是这一块的语法很乱,对初学者不友好的原因。

如何编写高效Python的代码

如果从列表开头开始切割,那么忽略 start 位的 0,例如list[:4]如果一直切到列表尾部,则忽略 end 位的 0,例如list[3:]切割列表时,即便 start 或者 end 索引跨界也不会有问题列表切片不会改变原列表。索引都留空时,会生成一份原列表的拷贝列表推导式使用列表推导式来取代map和filter不要使用含有两个以上表达式的列表推导式数据多时,列表推导式可能会消耗大量内存,此时建议使用生成器表达式迭代需要获取 index 时使用enumerateenumerate可以接受第二个参数,作为迭代时加在index上的数值用zip同时遍历两个迭代器zip遍历时返回一个元组关于for和while循环后的else块循环正常结束之后会调用else内的代码循环里通过break跳出循环,则不会执行else要遍历的序列为空时,立即执行else反向迭代对于普通的序列(列表),我们可以通过内置的reversed()函数进行反向迭代:除此以外,还可以通过实现类里的__reversed__方法,将类进行反向迭代:try/except/else/finally如果try内没有发生异常,则调用else内的代码else会在finally之前运行最终一定会执行finally,可以在其中进行清理工作函数使用装饰器装饰器用于在不改变原函数代码的情况下修改已存在的函数。常见场景是增加一句调试,或者为已有的函数增加log监控举个栗子:除此以外,还可以编写接收参数的装饰器,其实就是在原本的装饰器上的外层又嵌套了一个函数:但是像上面那样使用装饰器的话有一个问题:也就是说原函数已经被装饰器里的new_fun函数替代掉了。调用经过装饰的函数,相当于调用一个新函数。查看原函数的参数、注释、甚至函数名的时候,只能看到装饰器的相关信息。为了解决这个问题,我们可以使用Python 自带的functools.wraps方法。functools.wraps是个很 hack 的方法,它本事作为一个装饰器,做用在装饰器内部将要返回的函数上。也就是说,它是装饰器的装饰器,并且以原函数为参数,作用是保留原函数的各种信息,使得我们之后查看被装饰了的原函数的信息时,可以保持跟原函数一模一样。此外,有时候我们的装饰器里可能会干不止一个事情,此时应该把事件作为额外的函数分离出去。但是又因为它可能仅仅和该装饰器有关,所以此时可以构造一个装饰器类。原理很简单,主要就是编写类里的__call__方法,使类能够像函数一样的调用。使用生成器考虑使用生成器来改写直接返回列表的函数用这种方法有几个小问题:每次获取到符合条件的结果,都要调用append方法。但实际上我们的关注点根本不在这个方法,它只是我们达成目的的手段,实际上只需要index就好了返回的result可以继续优化数据都存在result里面,如果数据量很大的话,会比较占用内存因此,使用生成器generator会更好。生成器是使用yield表达式的函数,调用生成器时,它不会真的执行,而是返回一个迭代器,每次在迭代器上调用内置的next函数时,迭代器会把生成器推进到下一个yield表达式:获取到一个生成器以后,可以正常的遍历它:如果你还是需要一个列表,那么可以将函数的调用结果作为参数,再调用list方法可迭代对象需要注意的是,普通的迭代器只能迭代一轮,一轮之后重复调用是无效的。解决这种问题的方法是,你可以定义一个可迭代的容器类:这样的话,将类的实例迭代重复多少次都没问题:但要注意的是,仅仅是实现__iter__方法的迭代器,只能通过for循环来迭代;想要通过next方法迭代的话则需要使用iter方法:使用位置参数有时候,方法接收的参数数目可能不一定,比如定义一个求和的方法,至少要接收两个参数:对于这种接收参数数目不一定,而且不在乎参数传入顺序的函数,则应该利用位置参数*args:但要注意的是,不定长度的参数args在传递给函数时,需要先转换成元组tuple。这意味着,如果你将一个生成器作为参数带入到函数中,生成器将会先遍历一遍,转换为元组。这可能会消耗大量内存:使用关键字参数关键字参数可提高代码可读性可以通过关键字参数给函数提供默认值便于扩充函数参数定义只能使用关键字参数的函数普通的方式,在调用时不会强制要求使用关键字参数使用 Python3 中强制关键字参数的方式使用 Python2 中强制关键字参数的方式关于参数的默认值算是老生常谈了:函数的默认值只会在程序加载模块并读取到该函数的定义时设置一次也就是说,如果给某参数赋予动态的值(比如[]或者{}),则如果之后在调用函数的时候给参数赋予了其他参数,则以后再调用这个函数的时候,之前定义的默认值将会改变,成为上一次调用时赋予的值:因此,更推荐使用None作为默认参数,在函数内进行判断之后赋值:类__slots__默认情况下,Python 用一个字典来保存一个对象的实例属性。这使得我们可以在运行的时候动态的给类的实例添加新的属性:然而这个字典浪费了多余的空间 -— 很多时候我们不会创建那么多的属性。因此通过__slots__可以告诉 Python不要使用字典而是固定集合来分配空间。__call__通过定义类中的__call__方法,可以使该类的实例能够像普通函数一样调用。通过这种方式实现的好处是,可以通过类的属性来保存状态,而不必创建一个闭包或者全局变量。@classmethod & @staticmethod@classmethod和@staticmethod很像,但他们的使用场景并不一样。类内部普通的方法,都是以self作为第一个参数,代表着通过实例调用时,将实例的作用域传入方法内;@classmethod以cls作为第一个参数,代表将类本身的作用域传入。无论通过类来调用,还是通过类的实例调用,默认传入的第一个参数都将是类本身@staticmethod不需要传入默认参数,类似于一个普通的函数来通过实例了解它们的使用场景:假设我们需要创建一个名为Date的类,用于储存 年/月/日 三个数据上述代码创建了Date类,该类会在初始化时设置day/month/year属性,并且通过property设置了一个getter,可以在实例化之后,通过time获取存储的时间:但如果我们想改变属性传入的方式呢?毕竟,在初始化时就要传入年/月/日三个属性还是很烦人的。能否找到一个方法,在不改变现有接口和方法的情况下,可以通过传入2016-11-09这样的字符串来创建一个Date实例?你可能会想到这样的方法:但不够好:在类外额外多写了一个方法,每次还得格式化以后获取参数这个方法也只跟Date类有关没有解决传入参数过多的问题此时就可以利用@classmethod,在类的内部新建一个格式化字符串,并返回类的实例的方法:这样,我们就可以通过Date类来调用from_string方法创建实例,并且不侵略、修改旧的实例化方式:好处:在@classmethod内,可以通过cls参数,获取到跟外部调用类时一样的便利可以在其中进一步封装该方法,提高复用性更加符合面向对象的编程方式而@staticmethod,因为其本身类似于普通的函数,所以可以把和这个类相关的 helper方法作为@staticmethod,放在类里,然后直接通过类来调用这个方法。将与日期相关的辅助类函数作为@staticmethod方法放在Date类内后,可以通过类来调用这些方法:创建上下文管理器上下文管理器,通俗的介绍就是:在代码块执行前,先进行准备工作;在代码块执行完成后,做收尾的处理工作。with语句常伴随上下文管理器一起出现,经典场景有:通过with语句,代码完成了文件打开操作,并在调用结束,或者读取发生异常时自动关闭文件,即完成了文件读写之后的处理工作。如果不通过上下文管理器的话,则会是这样的代码:比较繁琐吧?所以说使用上下文管理器的好处就是,通过调用我们预先设置好的回调,自动帮我们处理代码块开始执行和执行完毕时的工作。而通过自定义类的__enter__和__exit__方法,我们可以自定义一个上下文管理器。然后可以以这样的方式进行调用:在调用的时候:with语句先暂存了ReadFile类的__exit__方法然后调用ReadFile类的__enter__方法__enter__方法打开文件,并将结果返回给with语句上一步的结果被传递给file_read参数在with语句内对file_read参数进行操作,读取每一行读取完成之后,with语句调用之前暂存的__exit__方法__exit__方法关闭了文件要注意的是,在__exit__方法内,我们关闭了文件,但最后返回True,所以错误不会被with语句抛出。否则with语句会抛出一个对应的错误。

阅读更多 >>>  常用的linux命令是什么意思

网站数据信息

"python代码生成器,python 生成器是函数吗"浏览人数已经达到18次,如你需要查询该站的相关权重信息,可以点击进入"Chinaz数据" 查询。更多网站价值评估因素如:python代码生成器,python 生成器是函数吗的访问速度、搜索引擎收录以及索引量、用户体验等。 要评估一个站的价值,最主要还是需要根据您自身的需求,如网站IP、PV、跳出率等!