使用局部变量的for循环中的函数定义

时间:2019-03-01 15:15:49

标签: python

我在这里不知所措。 我正在尝试在for循环中定义一个函数。该函数使用在循环内定义的变量,看起来可能类似于:

myFuns = []
for i in range(10):
    j = i + 4
    def fun(): print(j)
    myFuns += [fun]

在此循环结束时,我在myFuns中的函数有所不同,但它们执行相同的操作,显然,这不是传递给fun()的新变量j,而是对j的引用。

我很高兴知道如何传递j的值,而不仅仅是引用。

我不知道在循环中创建的变量是否会超过循环寿命,所以这对我来说是新领域...

1 个答案:

答案 0 :(得分:3)

j是一个自由变量,当fun被称为 时将查询其值; fun不会将j的当前值烘烤到其定义中。此外,由于缺少任何其他包含作用域,因此对fun的调用将引用全局作用域中的j,其中其最后一个设置值是其在循环的最后一次迭代中的值。

要在定义j时在fun中实际存储myFuns = [] for i in range(10): j = i + 4 def fun(j=j): print(j) myFuns.append(fun) # Don't use += [...] for a single item 的固定值,您需要将其作为具有默认值的参数传递:

# Here, j is a free variable in _, but its value comes from the local
# variable j defined by make_fun, not the global scope.
def make_fun(j):
    def _():
        return j
    return _

myFuns = [make_fun(i) for i in range(10)]

但是,闭包可能更合适:

public class Listener {
    private async Task HandleRequestAsync(RestoreRequest request, RestoreWorker worker) {
        Response response = await worker.ExecuteAsync(request).ConfigureAwait(false);
    }
}


public class RestoreWorker {

    public async Task<Response> ExecuteAsync(RestoreRequest request) {
        RestoreService restoreService = new restoreService(request);
        restoreService.Progress.ProgressChanged += async (sender, info) => await request.UpdateStatusAsync(new State(StateEnum.Running) { ProgressCurrent = info.Current, ProgressTotal = info.Total }).ConfigureAwait(false);
        await restoreService.RestoreAsync(request.Id, request.Name).ConfigureAwait(false);
        return new Response();
    }

    public Progress<ProgressInfo> Progress { get; } = new Progress<ProgressInfo>();
}

public class RestoreRequest {
    public async Task UpdateStatusAsync(Status status) {
        Message message = new Message { Status = status };
        await sender.SendAsync(message).ConfigureAwait(false);
    }
}

public class RestoreService {

    private static readonly IRestoreClient restoreClient =  ServiceProxyFactory.CreateServiceProxy<IRestoreClient>(new Uri($"{FabricConfig.ApplicationName}/RestoreClient"));

    private async Task <Project> GetProjectByNameAsync(string name){
    //return the project
    }

    private async Task RestoreAsync(string id, string name) {
        await restoreClient.RestoreAsync(id, name).ConfigureAwait(false);
    }
}

public class RestoreClient : IRestoreClient {
    private async Task RestoreAsync(string id, string name) {
        Project project = await GetProjectByNameAsync(name).ConfigureAwait(false);
        project = await UpdateDbAsync(project.Id).ConfigureAwait(false);

        if (project == null) {
            throw new Exception("Could not find project.");
        }
    }

    private async Task UpdateDbAsync(string id) {
        try {
            List<string> input = CreateScripts();
            await ExecuteScriptsOnDbAsync(input).ConfigureAwait(false);
        } catch (SqlException) {
            throw new Exception($"Project with id: '{id}'  could not be created.");
        }
    }

    private async Task ExecuteScriptsOnDbAsync(List<string> scripts) {
        using (var conn = new SqlConnection(connectionString)) {
            try {
                await conn.OpenAsync().ConfigureAwait(false);
                using (var sqlCommand = new SqlCommand { Connection = conn }) {
                    sqlCommand.CommandTimeout = SqlCommandCommandTimeout;
                    foreach (string script in scripts) {
                        sqlCommand.CommandText = script;
                        await sqlCommand.ExecuteNonQueryAsync().ConfigureAwait(false);
                    }
                }
            } catch (SqlException ex) {
                Log.Fatal(ex, $"Cannot execute script on {Name}");
                throw;
            }
        }
    }
}