Параметр 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 действительно имеет значение, я включаю этот скриншот:

Скриншот PyCharm с точкой останова

Обратите внимание, что в окне просмотра переменной, 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):

сделает так, как вы ожидаете.

Другие вопросы по тегам