内部/嵌套方法关闭的Python3参数超出范围并触发UnboundLocalError
编辑:为什么人们对这篇文章投反对票?Python开发人员真的如此无能吗?这是一个合理的问题,在其他地方没有得到回答。我在寻找解决办法。我不是白痴。一个参数有一个值,另一个参数没有定义,但是如果你真的读了这篇文章,你会发现这两个参数的作用域是相等的 首先,我向您保证,此问题与涉及错误消息的其他问题不同:内部/嵌套方法关闭的Python3参数超出范围并触发UnboundLocalError,python,python-3.x,lambda,closures,first-class-functions,Python,Python 3.x,Lambda,Closures,First Class Functions,编辑:为什么人们对这篇文章投反对票?Python开发人员真的如此无能吗?这是一个合理的问题,在其他地方没有得到回答。我在寻找解决办法。我不是白痴。一个参数有一个值,另一个参数没有定义,但是如果你真的读了这篇文章,你会发现这两个参数的作用域是相等的 首先,我向您保证,此问题与涉及错误消息的其他问题不同: UnboundLocalError:赋值结束方法之前引用的局部变量 在我查看这段代码时,顶级方法的参数getStockDataSaverFactory作为一级函数对象返回其内部方法saveData
UnboundLocalError:赋值结束方法之前引用的局部变量
在我查看这段代码时,顶级方法的参数getStockDataSaverFactory
作为一级函数对象返回其内部方法saveData
时,它的参数uuidString
,实际上应该在范围内。。。因为令我惊讶的是,参数tickerName
在范围内,并且在调用saveData()
方法时(例如,通过测试方法testDataProcessing\u getSaverMethodFactory
),参数确实具有值'GOOG',因此我们实际上可以看到,当调用方法getDataMethodFactory(…)时,它具有实际值
与uuiString
不同,调用了
为了让事情变得更加明显,我添加了以下几行:
localUuidString=uuidString
及
experimentUuidString=localUuidString
当方法被断点检查时,显示参数uuistring
具有可用值
def getStockDataSaverFactory(self, tickerName, uuidString, methodToGetData, columnList):
# This method expects that methodToGetData returns a pandas dataframe, such as the method returned by: self.getDataFactory(..)
localUuidString = uuidString
def saveData():
(data, meta_data) = methodToGetData()
experimentUuidString = localUuidString
methodToNameFile = self.getDataMethodFactory(tickerName, uuidString)
(full_filepathname, full_filename, uuidString) = methodToNameFile()
methodToSaveData = self.getDataFrameSaverFactory(methodToGetData, columnList, full_filepathname)
# We might want try/catch here:
methodToSaveData()
# A parameterless method that has immutable state (from a closure) is often easier to design around than one that expects parameters when we want to pass it with a list of similar methods
return (full_filepathname, full_filename, uuidString)
return saveData
def testDataProcessing_getSaverMethodFactory(self):
dataProcessing = DataProcessing()
getSymbols = dataProcessing.getSymbolFactory(
dataProcessing.getNasdaqSymbols(dataProcessing.getListOfNASDAQStockTickers))
tickers = getSymbols()
uuidString = 'FAKEUUID'
columnList = ['low', 'high']
tickerSubset = tickers[0:2]
methodsToPullData = map(lambda ticker: dataProcessing.getStockDataSaverFactory(ticker,
uuidString,
dataProcessing.getDataFactory(
ticker),
columnList), tickerSubset)
savedPathTuples = [f() for f in methodsToPullData]
savedFileNames = [pathTuple[0] for pathTuple in savedPathTuples]
for fileName in savedFileNames:
self.assertTrue(os.path.isfile(fileName))
os.remove(fileName)
为了明确说明uuistring
没有值,但是ticker
确实有值,我添加了以下屏幕截图:
请注意,在变量监视窗口中,uuidString
未定义,但ticker
的字符串值为“A”
Python(或Python 3)是否有独特之处导致了这种行为?问题在于,在分配给它之前,在调用self.getMethod时引用了UUIString,该方法为PricescsvFromUUIDANDtickerName
提供完整的文件路径名。赋值使它位于最内层函数的作用域的局部,因此,在引用它时它是未赋值的
范围界定规则的完整说明如下:
这个简单的例子再现了您的错误,使问题更加清楚:
class aclass():
def outer(self, uuidString):
def inner():
print(uuidString)
uuidString = 'new value'
return uuidString
return inner
a = aclass()
func = a.outer('a uuid')
val = func()
print(val)
inner()
中的赋值会导致uuiString
是inner()
的本地赋值,因此在调用print(uuiString)
时会取消赋值,这会导致Python引发unbundLocalError
您可以通过使用默认参数将变量传递给函数来修复错误。将saveData
的定义更改为将uuistring
作为默认参数传递,如下所示:
def saveData(uuidString=uuidString):
将使其按预期工作。您可以发布完整的堆栈跟踪吗?这里有点不对劲。在实际应用中,您所展示的并没有什么问题。请参见此-定义并返回的函数保持对其定义的作用域的访问-除非发生了其他未显示的情况。这是创建对象的有趣方法dataProcessing=dataProcessing.dataProcessing()
?为什么它不仅仅是dataProcessing=dataProcessing()
?看在上帝的份上,缩短函数名吧。。。。!为什么函数返回其他函数?调用这些函数,然后立即调用它们返回的函数。这看起来真的太复杂了。@NedBatchelder您有没有在函数式编程中使用过curry,或者在Javascript/node.js中使用过promises?还是C#中的通用lambdas?对于S.O.的例子,我实际上保持了简单。在实践中,我将获取一个字符串列表,然后我可以管道化一组方法生成器函数,这些函数的功能类似于decorator(作为方法组合的一种形式)。想想string->methodThatGetsData->methodThatSavesRetrievedData->methodThatTransformsSavedData->MethodThatThatSortsTransformedData->等等。在实现时,您只需要提供一个方法生产者列表来设置管道。好的,您是:)我发现第二个答案也非常有用。它显示了问题所在的确切场景。第7点和第8点。为什么这不是tickerName的问题?@devinbost-这不是问题,因为您没有在嵌套函数中为tickerName
赋值。正如我在回答中所解释的,赋值使其成为内部函数作用域的局部。如果不赋值,它将在外部作用域中搜索变量。@devinbost-可能您假设Python一次只解释一行代码,因此它不应该知道赋值。运行脚本时,Python读取整个脚本并将其编译成字节码,然后执行。在编译阶段,它看到uuidString
的赋值,并确定变量是本地变量。