【问题标题】:Python variable scope approachPython变量范围方法
【发布时间】:2015-08-21 10:57:44
【问题描述】:

我目前有这个 python 代码(我使用的是 Apache Spark,但很确定这对这个问题没有关系)。

import numpy as np
import pandas as pd
from sklearn import feature_extraction
from sklearn import tree
from pyspark import SparkConf, SparkContext

## Module Constants
APP_NAME = "My Spark Application"
df = pd.read_csv("train.csv")
test = pd.read_csv("test.csv")

def train_tree():
    # Do more stuff with the data, call other functions
    pass

def main(sc):
    cat_columns = ["Sex", "Pclass"]

    # PROBLEM IS HERE
    cat_dict = df[cat_columns].to_dict(orient='records')

    vec = feature_extraction.DictVectorizer()
    cat_vector = vec.fit_transform(cat_dict).toarray()

    df_vector = pd.DataFrame(cat_vector)
    vector_columns = vec.get_feature_names()
    df_vector.columns = vector_columns
    df_vector.index = df.index

    # train data

    df = df.drop(cat_columns, axis=1)
    df = df.join(df_vector)

    train_tree()

if __name__ == "__main__":
    # Configure Spark    
    conf = SparkConf().setAppName(APP_NAME)
    conf = conf.setMaster("local[*]")
    sc   = SparkContext(conf=conf)

    # Execute Main functionality
    main(sc)

当我运行它时,我得到了错误: cat_dict = df[cat_columns].to_dict(orient='records') UnboundLocalError:赋值前引用了局部变量“df”

我觉得这很令人费解,因为我在文件顶部的main 函数范围之外定义了变量 df。为什么在函数中使用这个变量会触发这个错误?我还尝试将df 变量定义放在if __name__ == "__main__": 语句中(在调用main 函数之前)

现在,显然有很多方法可以解决这个问题,但这更多是为了帮助我更好地理解 Python。所以我想问:

a) 为什么会出现这个错误?

b) 鉴于以下情况,如何最好地解决它: - 我不想将df 定义放在main 函数中,因为我想在其他函数中访问它。 - 我不想使用课程 - 我不想使用全局变量 - 我不想在函数参数中传递df

【问题讨论】:

  • 对于 b) 你将不得不选择一个!您是否阅读过许多其他 UnboundLocalError 问题中的任何一个?
  • @jonrsharpe 真的没有其他选择吗?我基本上只想访问所有函数中的变量。如果没有额外的复杂性,我无法做到这一点,这似乎很奇怪。
  • 你为什么不在main: global df?? 中让它成为全球性的??
  • 你在哪里使用cs?
  • @PadraicCunningham 你能详细说明一下吗?还是你的意思是 sc?

标签: python pandas apache-spark


【解决方案1】:

您可以在 main() (或任何其他函数)中使用变量 df ,它会正常工作,但如果您尝试在函数中为它分配值(就像您在 main() 下的 #train 所做的那样数据),它会给出 unboundlocalerror 异常。它将将该变量视为局部变量,因此将抛出该异常。

在 main() 中使用带 df 的全局关键字将解决您的问题。

【讨论】:

  • 赞成正确识别问题 - 但是,正如我在回答中提到的以及其他人在 cmets 中所说的那样,不确定使用 global 关键字是最佳选择(尽管它会起作用)
【解决方案2】:

我认为值得将 cmets 总结成一个详细的答案,供未来的读者阅读。

这里抛出 UnboundLocalError 的原因是 Python 函数作用域的工作方式。虽然我的df 变量是在最上层范围的main 函数之外定义的,但尝试在main 函数中重新分配它会产生错误。 This excellent answer puts it nicely,转述:

现在我们到达df = df.drop(cat_columns, axis=1),当 Python 扫描该行时,它会说“啊,有一个名为 df 的变量,我将把它放入我的本地范围字典中。”然后,当它为赋值右侧的df 寻找df 的值时,它会找到名为dflocal 变量,该变量还没有值,并且所以抛出错误。

为了修复我的代码,我做了以下更改:

def main(sc):

    cat_columns = ["Sex", "Pclass", "SibSp"]
    cat_dict = df[cat_columns].to_dict(orient='records')

    vec = feature_extraction.DictVectorizer()
    cat_vector = vec.fit_transform(cat_dict).toarray()

    df_vector = pd.DataFrame(cat_vector)
    vector_columns = vec.get_feature_names()
    df_vector.columns = vector_columns
    df_vector.index = df.index

    # train data

    df_updated = df.drop(cat_columns, axis=1) # This used to be df = df.drop(cat_columns, axis=1) 
    df_updated = df_updated.join(df_vector)

    train_tree(df_updated) # passing the df_updated to the function

这将删除 UnboundLocalError。为了在其他函数中继续使用df 变量,我将它作为参数传入(尽管名称不同)。这可能会让人感到困惑,因此正如@Padraic Cunningham 所建议的,您可以在main 函数中传递变量:

if __name__ == "__main__":
    # Configure Spark

    conf = SparkConf().setAppName(APP_NAME)
    conf = conf.setMaster("local[*]")
    sc   = SparkContext(conf=conf)
    df = pd.read_csv("train.csv")
    test = pd.read_csv("test.csv")

    # df.Age = df.Age.astype(int)
    # test.Age = test.Age.astype(int)

    # Execute Main functionality
    main(sc,df)

其他选项是使用类或使用全局变量。我觉得这两个选项是矫枉过正(一个类)或不优雅(全局)。不过,这纯粹是我个人的喜好。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-02-10
    • 1970-01-01
    • 2017-12-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多