我用生成器调试了一些代码,并提出了这个问题。假设我有一个生成器函数
def f(x):
yield x以及返回生成器的函数:
def g(x):
return f(x)他们肯定还回同样的东西。在Python代码中可互换地使用它们时会有什么不同吗?有没有办法区分这两者(没有inspect)?
发布于 2016-09-10 14:34:23
他们会采取同样的行动。以及区分两者的方法(不包括inspect)。在蟒蛇里?只检查:
import inspect
print inspect.isgeneratorfunction(g) --> False
print inspect.isgeneratorfunction(f) --> True当然,您也可以使用dis检查它。
Python 2.7.6 (default, Jun 22 2015, 17:58:13)
[GCC 4.8.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> def f(x):
... yield x
...
>>> def g(x):
... return f(x)
...
>>> import dis
>>> dis.dis(f)
2 0 LOAD_FAST 0 (x)
3 YIELD_VALUE
4 POP_TOP
5 LOAD_CONST 0 (None)
8 RETURN_VALUE
>>> dis.dis(g)
2 0 LOAD_GLOBAL 0 (f)
3 LOAD_FAST 0 (x)
6 CALL_FUNCTION 1
9 RETURN_VALUE 但inspect更合适。
发布于 2017-06-20 07:13:13
我喜欢土拨鼠答案,但是显示的例子大多是理论性的,在日常编码中并不常见。
生成器函数(带有yield)与返回生成器函数的主要实际区别是,生成器函数被延迟地求值为。
审议本届会议:
$ python
Python 3.6.0
[GCC 6.3.1 20170109] on linux
>>> def a():
... print('in a')
... yield 0
...
>>> def b():
... print('in b')
... return iter(range(1))
...
>>> aa = a() # Lazy evaluation - nothing printed after this line.
>>> next(aa)
in a
0
>>> next(aa)
Traceback ...
StopIteration
>>> bb = b() # Eager evaluation - whole function is executed after this.
in b
>>> next(bb)
0
>>> next(bb)
Traceback ...
StopIteration没有一颗是黄金子弹。
为了给您提供一个实际的示例,说明这个懒惰的计算在代码中产生了巨大的差异,请检查这个示例。
def get_numbers(x: int):
if x < 0:
raise ValueError("Value cannot be negative")
for i in range(x):
yield i
try:
numbers = get_numbers(-5)
except ValueError:
pass # log or something
else:
print(list(numbers)) # <== ValueError is thrown here!这里是惰性评估实际上对您的功能不利的地方。它会将异常抛到可以说是错误的地方,因为它的目的是使它在开始时就失败,而不是在迭代期间。使用此实现,您将负责触发生成器函数,并将异常管理给它的用户,这很乏味,而且有些丑陋:
import itertools
try:
numbers = get_numbers(-5)
first = next(numbers)
numbers = itertools.chain([first], numbers)
except ValueError:
...解决这一问题的最佳方法是创建一个返回生成器而不是生成器函数的函数:
def get_numbers(x: int):
if x < 0:
raise ValueError("Value cannot be negative")
return (i for i in range(x)) # I know it can be just `return range(x)`, but I keep it that way to make a point.正如你所看到的,没有“最好的方法”去做,这两种选择都是可行的。这一切都取决于你希望事情如何运作。
发布于 2016-09-10 14:43:10
检查它的最好方法是使用inspect.isgeneratorfunction,这是非常简单的函数:
def ismethod(object):
"""Return true if the object is an instance method.
Instance method objects provide these attributes:
__doc__ documentation string
__name__ name with which this method was defined
im_class class object in which this method belongs
im_func function object containing implementation of method
im_self instance to which this method is bound, or None"""
return isinstance(object, types.MethodType)
def isfunction(object):
"""Return true if the object is a user-defined function.
Function objects provide these attributes:
__doc__ documentation string
__name__ name with which this function was defined
func_code code object containing compiled function bytecode
func_defaults tuple of any default values for arguments
func_doc (same as __doc__)
func_globals global namespace in which this function was defined
func_name (same as __name__)"""
return isinstance(object, types.FunctionType)
def isgeneratorfunction(object):
"""Return true if the object is a user-defined generator function.
Generator function objects provides same attributes as functions.
See help(isfunction) for attributes listing."""
return bool((isfunction(object) or ismethod(object)) and
object.func_code.co_flags & CO_GENERATOR)现在,如果您使用如下语法声明生成器:
my_generator = (i*i for i in range(1000000))在这种情况下,您可以很容易地检查它的类型,例如,__class__将返回<type 'generator'>。
https://stackoverflow.com/questions/39426977
复制相似问题