Параметр Python 3, закрытый внутренним / вложенным методом, выпадает из области видимости и вызывает UnboundLocalError
Редактировать: Почему люди опускают этот пост? Действительно ли разработчики Python такие неумелые? Это законный вопрос, на который нет ответа в других местах. Я искал решение. Я не идиот. Один параметр имеет значение, а другой не определен, но если вы действительно прочитаете сообщение, вы увидите, что оба они имеют одинаковую область видимости.
Прежде всего, я вас уверяю, что этот вопрос не похож на другие вопросы, связанные с сообщением об ошибке:
UnboundLocalError: local variable referenced before assignment closure method
Когда я смотрю на этот код, кажется, что параметр, uuidString
, из метода верхнего уровня, getStockDataSaverFactory
, должен на самом деле быть в области видимости, когда метод возвращает свой внутренний метод, saveData
, как первоклассный объект функции... потому что, к моему удивлению, параметр tickerName
Находится в области и имеет значение "ХОРОШО", когда saveData()
метод вызывается (например, методом теста testDataProcessing_getSaverMethodFactory
), так что мы можем увидеть, что он имеет реальное значение, когда метод, getDataMethodFactory(..)
называется, в отличие от uuidString
,
Чтобы сделать это более очевидным, я добавил строки:
localUuidString = uuidString
а также
experimentUuidString = localUuidString
показать, что параметр uuidString
имеет доступное значение, когда метод проверяется точкой останова.
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)
Просто чтобы прояснить, что uuidString
не имеет значения, но ticker
действительно имеет значение, я включаю этот скриншот:
Обратите внимание, что в окне просмотра переменной, uuidString
не определено, но ticker
имеет строковое значение "A".
Есть ли что-то уникальное в Python (или Python 3), которое приводит к такому поведению?
1 ответ
Проблема в том, что вы ссылаетесь на uuidString в вызове self.getMethodThatProvidesFullFilePathNameForPricesCsvFromUUIDAndTickerName
прежде чем назначить на него. Присвоение делает его локальным для области самой внутренней функции и, следовательно, оно не присваивается, когда вы ссылаетесь на него.
Полное описание правил определения объема предоставлено по адресу: /questions/29105526/kratkoe-opisanie-pravil-opredeleniya-obema/29105542#29105542
Этот более простой пример воспроизводит вашу ошибку, чтобы прояснить проблему:
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()
вызывает uuidString
быть местным inner()
и поэтому он не назначен, когда print(uuidString)
это вызов, который заставляет Python поднять UnboundLocalError
,
Вы можете исправить ошибку, передав переменную в вашу функцию с аргументом по умолчанию. Изменение определения saveData
пройти uuidString
в качестве аргумента по умолчанию, как:
def saveData(uuidString=uuidString):
сделает так, как вы ожидаете.