Python не позволил бы мне использовать методы внутри класса, пока определение класса не было закончено - PullRequest
2 голосов
/ 29 декабря 2010

Я использую Python 2.6 в качестве замены пакетного скрипта.Он будет запущен двойным щелчком, поэтому весь вывод на стандартный вывод будет потерян / проигнорирован пользователем.Итак, я решил добавить ведение журнала, и чтобы упростить задачу, я написал для этого класс.Идея состоит в том, что я могу использовать Logging.Logger в любом месте моего кода, и регистратор будет готов к работе.

Я хочу, чтобы в каталоге было не более 10 старых файлов журнала, поэтому я очищаюстарые вручную.Я не нашел такой функции через API, плюс я параноик и хочу все регистрировать, даже если в каталоге журналов были файлы с неожиданными именами.

Итак, ниже моя попыткатакой класс, но я получаю сообщение об ошибке при попытке его проверить (запустить):

>>> ================================ RESTART ================================
>>> 

Traceback (most recent call last):
  File "C:/AutomationScripts/build scripts/Deleteme.py", line 6, in <module>
    class Logging:
  File "C:/AutomationScripts/build scripts/Deleteme.py", line 42, in Logging
    __clearOldLogs(dummySetting)
  File "C:/AutomationScripts/build scripts/Deleteme.py", line 38, in __clearOldLogs
    _assert(Logger, 'Logger does not exist yet! Why?')
NameError: global name '_assert' is not defined
>>> 

Да, я пришел из Java / C # фона.Я, вероятно, не делаю вещи "питонским" способом.Пожалуйста, помогите мне сделать правильную вещь, и, пожалуйста, дайте полный ответ, который сработает, вместо того, чтобы просто указывать на пробелы в моих знаниях.Я полагаю, что предоставил достаточно примера кода.Извините, он не будет работать без класса настроек, но, надеюсь, вы поймете идею.

# This file has been tested on Python 2.6.*. For Windows only.

import logging          # For actual logging
import tkMessageBox     # To display an error (if logging is unavailable)

class Logging:
    """
    Logging class provides simplified interface to logging
    as well as provides some handy functions.
    """

    # To be set when the logger is properly configured.
    Logger = None

    @staticmethod
    def _assert(condition, message):
        """ Like a regular assert, except that it should be visible to the user. """
        if condition: return
        # Else log and fail
        if Logger:
            Logger.debug(traceback.format_stack())
            Logger.error('Assert failed: ' + message)
        else:
            tkMessageBox.showinfo('Assert failed: ' + message, traceback.format_stack())            
        assert(condition)

    @staticmethod
    def _removeFromEnd(string, endString):
        _assert(string.endswith(endString),
                "String '{0}' does not end in '{1}'.".format(string, endString))
        return string[:-len(endString)]

    def __clearOldLogs(logSettings):
        """
        We want to clear old (by date) files if we get too many.
        We should call this method only after variable 'Logger' has been created.
        """
        # The following check should not be necessary from outside of
        # Logging class, when it is fully defined
        _assert(Logger, 'Logger does not exist yet! Why?')
        # Do more work here

    def __createLogger(logSettings):
        logFileName = logSettings.LogFileNameFunc()
        #_assert(False, 'Test')
        logName = _removeFromEnd(logFileName, logSettings.FileExtension)
        logFileName = os.path.join(logSettings.BaseDir, logFileName)
        # If someone tried to log something before basicConfig is called,
        # Python creates a default handler that goes to the console and will
        # ignore further basicConfig calls. Remove the handler if there is one.
        root = logging.getLogger()
        if root.handlers:
            for handler in root.handlers:
                root.removeHandler(handler)
        logging.basicConfig(filename = logFileName, name = logName, level = logging.DEBUG, format = "%(asctime)s - %(levelname)s - %(message)s")
        logger = logging.getLogger(logName)
        return logger

    # Settings is a separate class (not dependent on this one).    
    Logger = __createLogger(Settings.LOGGING)
    __clearOldLogs(Settings.LOGGING)

if __name__ == '__main__':
    # This code section will not run when the class is imported.
    # If it is run directly, then we will print debugging info.
    logger = Logging.Logger
    logger.debug('Test debug message.')
    logger.info('Test info message.')
    logger.warning('Test warning message.')
    logger.error('Test error message.')
    logger.critical('Test critical message.')

Приветствуются соответствующие вопросы, предложения по стилю и полные ответы.Спасибо!

Ответы [ 4 ]

5 голосов
/ 29 декабря 2010

Вы получаете это исключение, потому что вы звоните _assert(), а не Logging._assert(). Сообщение об ошибке говорит вам, что он ищет _assert() в глобальном пространстве имен модуля, а не в пространстве имен класса; чтобы он выглядел в последнем, вы должны явно указать это.

Конечно, в этом случае вы пытаетесь сделать это, пока класс еще не определен, а имя недоступно до тех пор, пока класс не будет завершен, поэтому сделать это будет непросто.

Решение состоит в том, чтобы снять отступ следующих двух строк (которые я отредактировал, чтобы использовать полное имя), чтобы они выходили за пределы определения класса; они будут казнены сразу после этого.

Logger = Logging.__createLogger(Settings.LOGGING)
Logging.__clearOldLogs(Settings.LOGGING)

Предложение стиля, которое поможет: вместо создания класса с кучей статических методов, подумайте над тем, чтобы сделать их функциями верхнего уровня в модуле. Пользователям вашего модуля (включая вас) будет легче получить доступ к нужной им функциональности. Нет причин использовать класс просто для того, чтобы быть контейнером; сам модуль уже является таким контейнером.

Модуль - это, по сути, один файл *.py (хотя вы можете создавать модули, которые имеют несколько файлов, это пока подойдет). Когда вы делаете import, вы импортируете модуль. В вашем примере tkMessageBox и logging оба являются модулями. Поэтому просто создайте отдельный файл (убедитесь, что его имя не конфликтует с существующими именами модулей Python), сохраните его в том же каталоге, что и основной сценарий, и импортируйте его в основной сценарий. Если бы вы назвали его mylogging.py, вы бы import mylogging получили бы доступ к функциям в нем как mylogging.clearOldLogs() или как угодно (аналогично тому, как вы бы обращались к ним сейчас как к классу).

«Глобальные» имена в Python не являются действительно глобальными, они являются только глобальными для модуля, в котором они определены. Таким образом, модуль является хорошим способом разделения вашей функциональности, особенно частей (например, таких как части). регистрации), которую вы ожидаете использовать во многих своих будущих сценариях.

2 голосов
/ 29 декабря 2010

Замените строку

_assert(Logger, 'Logger does not exist yet! Why?')

на

Logging._assert(Logger, 'Logger does not exist yet! Why?')

Это потому, что вы определяете _assert как статический метод класса, а статический метод должен называтьсяClassName.methodName, даже если вы вызываете его из метода для экземпляра этого класса.

1 голос
/ 29 декабря 2010

Некоторые комментарии.

  1. Не используйте двойные подчеркивания, если вы не уверены, что должны и почему.

  2. Чтобы получить доступ к методу _assert, вы вызываете его с помощью self. Вот так: self._assert (Logger, «Logger еще не существует! Почему?») В статическом методе, как в вашем примере, вы используете имя класса: Logger._assert (). Python очень явный.

  3. Классы создаются только в КОНЦЕ определения класса. Так же и с Python. Но ваша ошибка не связана с этим.

  4. Я не уверен, что этот код должен делать:

    # Settings is a separate class (not dependent on this one).    
    Logger = __createLogger(Settings.LOGGING)
    __clearOldLogs(Settings.LOGGING)
    

    Но я подозреваю, что вы должны поместить это в метод __init__. Я не сразу вижу код, которому нужен доступ к классу во время создания класса.

  5. __createLogger - странная функция. Разве этот класс не называется Logger? Разве это не просто __init__ класса Logger?

1 голос
/ 29 декабря 2010

Я думаю, это просто Logging._assert.Python не выполняет разрешение пространства имен, как это делает java: неквалифицированное имя является либо локальным, либо глобальным для метода.Вложенные области, такие как классы, не будут искать автоматически.

Глава и стих о том, как Python работает с именами, находится в разделе 4.1.Наименование и привязка в справочном руководстве по языку.Вероятно, здесь уместен бит:

Область имен, определенных в блоке класса, ограничена блоком класса;он не распространяется на блоки кода методов

Сравните это с именами, определенными в функциях, которые являются унаследованными вниз (в функции и классы локальных методов):

Если определение происходит в функциональном блоке, область действия распространяется на любые блоки, содержащиеся в определяющем, если только содержащийся в нем блок не вводит другую привязку для имени

...