是yield return用于实现快捷方式IEnumerable和IEnumerator?
可能重复:
C#中的"yield break;"是什么?
任何人都可以看到使用"break"或"return"无法实现的"yield break"语句.
这句话似乎完全没用.更重要的是,如果没有这个陈述,"收益率返回X"语句可以简化为"收益率X",这更具可读性.
我错过了什么?
背景:我有一堆字符串,我从数据库中获取,我想返回它们.传统上,它会是这样的:
public List<string> GetStuff(string connectionString)
{
List<string> categoryList = new List<string>();
using (SqlConnection sqlConnection = new SqlConnection(connectionString))
{
string commandText = "GetStuff";
using (SqlCommand sqlCommand = new SqlCommand(commandText, sqlConnection))
{
sqlCommand.CommandType = CommandType.StoredProcedure;
sqlConnection.Open();
SqlDataReader sqlDataReader = sqlCommand.ExecuteReader();
while (sqlDataReader.Read())
{
categoryList.Add(sqlDataReader["myImportantColumn"].ToString());
}
}
}
return categoryList;
}
Run Code Online (Sandbox Code Playgroud)
但后来我认为消费者想要遍历这些项目而不关心其他的东西,而且我不想将自己打包到List中,所以如果我返回一个IEnumerable一切都很好/灵活.所以我在想我可以使用"yield return"类型设计来处理这个......就像这样:
public IEnumerable<string> GetStuff(string connectionString)
{
using (SqlConnection sqlConnection = new SqlConnection(connectionString))
{
string commandText = "GetStuff";
using (SqlCommand sqlCommand = new SqlCommand(commandText, sqlConnection))
{
sqlCommand.CommandType = CommandType.StoredProcedure;
sqlConnection.Open();
SqlDataReader sqlDataReader …Run Code Online (Sandbox Code Playgroud) 我有以下方法:
public static IEnumerable<Dictionary<string, object>> GetRowsIter
(this SqlCeResultSet resultSet)
{
// Make sure we don't multi thread the database.
lock (Database)
{
if (resultSet.HasRows)
{
resultSet.Read();
do
{
var resultList = new Dictionary<string, object>();
for (int i = 0; i < resultSet.FieldCount; i++)
{
var value = resultSet.GetValue(i);
resultList.Add(resultSet.GetName(i), value == DBNull.Value
? null : value);
}
yield return resultList;
} while (resultSet.Read());
}
yield break;
}
Run Code Online (Sandbox Code Playgroud)
我刚刚添加了lock(Database)试图摆脱一些经济问题.我很好奇,是否会yield return释放锁定Database,然后在下一次迭代时重新锁定?或者Database在整个迭代期间保持锁定状态?
我有一个生成器generator和一个方便的方法 - generate_all。
def generator(some_list):
for i in some_list:
yield do_something(i)
def generate_all():
some_list = get_the_list()
return generator(some_list) # <-- Is this supposed to be return or yield?
Run Code Online (Sandbox Code Playgroud)
应该generate_all return还是yield?我希望两种方法的用户都使用相同的方法,即
for x in generate_all()
Run Code Online (Sandbox Code Playgroud)
应该等于
some_list = get_the_list()
for x in generate(some_list)
Run Code Online (Sandbox Code Playgroud) 我的理解yield from是,它类似于yield从可迭代对象中获取每个项目。然而,我在以下示例中观察到不同的行为。
我有Class1
class Class1:
def __init__(self, gen):
self.gen = gen
def __iter__(self):
for el in self.gen:
yield el
Run Code Online (Sandbox Code Playgroud)
和 Class2 的不同之处仅在于yield将 for 循环替换为yield from
class Class2:
def __init__(self, gen):
self.gen = gen
def __iter__(self):
yield from self.gen
Run Code Online (Sandbox Code Playgroud)
下面的代码从给定类的实例中读取第一个元素,然后在 for 循环中读取其余元素:
a = Class1((i for i in range(3)))
print(next(iter(a)))
for el in iter(a):
print(el)
Run Code Online (Sandbox Code Playgroud)
Class1这会为和产生不同的输出Class2。对于Class1输出是
0
1
2
Run Code Online (Sandbox Code Playgroud)
Class2输出为
0
Run Code Online (Sandbox Code Playgroud)
yield from产生不同行为的背后机制是什么?
我很熟悉屈服回报价值主要归功于这个问题
但是当它在作业的右侧时,屈服会做什么?
@coroutine
def protocol(target=None):
while True:
c = (yield)
def coroutine(func):
def start(*args,**kwargs):
cr = func(*args,**kwargs)
cr.next()
return cr
return start
Run Code Online (Sandbox Code Playgroud)
在研究状态机和协同程序时,我在本博客的代码示例中遇到了这个问题.
我在许多Python程序中都使用了yield,在很多情况下它确实清除了代码.我在博客上写了这篇文章,这是我网站的热门网页之一.
C#还提供了收益 - 它通过调用者端的状态保持来实现,通过自动生成的类来完成,该类保持状态,函数的局部变量等.
我目前正在阅读有关C++ 0x及其添加的内容; 在阅读有关C++ 0x中lambda的实现时,我发现它也是通过自动生成的类完成的,配备了存储lambda代码的operator().我心中形成了一个自然的问题:他们是为lambdas做过的,他们为什么不考虑支持"收益"呢?
当然,他们可以看到合作例程的价值......所以我只能猜测他们认为基于宏的实现(例如Simon Tatham的)是一个充分的替代品.然而,它们不是出于多种原因:被调用者保持状态,非重入状态,基于宏观(仅此一点是足够的理由)等.
编辑: yield不依赖于垃圾收集,线程或光纤.您可以阅读Simon的文章,看看我在谈论编译器进行简单的转换,例如:
int fibonacci() {
int a = 0, b = 1;
while (true) {
yield a;
int c = a + b;
a = b;
b = c;
}
}
Run Code Online (Sandbox Code Playgroud)
成:
struct GeneratedFibonacci {
int state;
int a, b;
GeneratedFibonacci() : state (0), a (0), b (1) {}
int operator()() {
switch (state) {
case 0:
state = 1;
while (true) { …Run Code Online (Sandbox Code Playgroud) 我知道怎么yield运作.我知道排列,认为它只是一个简单的数学.
但是什么yield是真正的力量?我应该什么时候使用它?一个简单而好的例子就更好了.
在python中使用上下文管理器自动关闭文件是一个常见的习惯用法:
with open('filename') as my_file:
# do something with my_file
# my_file gets automatically closed after exiting 'with' block
Run Code Online (Sandbox Code Playgroud)
现在我想读几个文件的内容.数据的使用者不知道或不关心数据是来自文件还是非文件.它不想检查它收到的对象是否可以打开.它只是想从中获取内容.所以我创建了一个像这样的迭代器:
def select_files():
"""Yields carefully selected and ready-to-read-from files"""
file_names = [.......]
for fname in file_names:
with open(fname) as my_open_file:
yield my_open_file
Run Code Online (Sandbox Code Playgroud)
这个迭代器可以像这样使用:
for file_obj in select_files():
for line in file_obj:
# do something useful
Run Code Online (Sandbox Code Playgroud)
(注意,可以使用相同的代码来消耗不是打开的文件,而是使用字符串列表 - 这很酷!)
问题是:产生打开文件是否安全?
看起来像"为什么不呢?".消费者调用迭代器,迭代器打开文件,将其产生给消费者.消费者处理文件并返回下一个迭代器.迭代器代码恢复,我们退出'with'块,my_open_file对象关闭,转到下一个文件等.
但是,如果消费者永远不会回到下一个文件的迭代器呢?消费者内部发生异常.或者消费者在其中一个文件中发现了一些非常令人兴奋的内容,并愉快地将结果返回给任何人调用它?
迭代器代码在这种情况下永远不会恢复,我们永远不会到'with'块结束,my_open_file对象永远不会被关闭!
或者是吗?