Перевод статьи Гвидо ван Россума, рассказывающей о том,
как следует оформлять функцию main()
.
Хочу предложить программистам функцию main()
, которую удобно
использовать в различном контексте. Например в интерактивном режиме Python,
когда вам хочется поэкспериментировать.
В свое время, я определял функцию main()
различным образом.
Приблизительно так:
"""Описание модуля.
Подробное описание использования.
"""
import sys
import getopt
def main():
# Разбираем аргументы командной строки
try:
opts, args = getopt.getopt(sys.argv[1:], "h", ["help"])
except getopt.error, msg:
print msg
print "для справки используйте --help"
sys.exit(2)
# process options
for o, a in opts:
if o in ("-h", "--help"):
print __doc__
sys.exit(0)
# Анализируем
for arg in args:
process(arg) # process() определен в другом месте
if __name__ == "__main__":
main()
Я уверен, что многие пишут нечто подобное main()
функции.
Несколько предложений, которые сделают эту функцию более гибкой. Тем более, что
синтаксический анализ опций становится все более сложным.
Во первых, добавим дополнительный аргумент argv
. Так, чтобы
можно было вызывать функцию в интерактивном режиме:
def main(argv=None):
if argv is None:
argv = sys.argv
# и т.д. заменяя sys.argv на argv в getopt запросах
Обратите внимание, что мы заполняем значения по умолчанию динамически. Это
более гибко, чем:
def main(argv=sys.argv):
# и т.д.
Таким образом теперь мы можем изменить параметры вызова в любой момент
времени.
Вызов sys.exit()
закрывает сессию интерпретатора. Доработаем
программу:
if __name__ == "__main__":
sys.exit(main())
Вызов sys.exit(main())
вернет результат функции.
Другое усовершенствование касается использования исключения
Usage()
, которое мы перехватываем в main
:
import sys
import getopt
class Usage(Exception):
def __init__(self, msg):
self.msg = msg
def main(argv=None):
if argv is None:
argv = sys.argv
try:
try:
opts, args = getopt.getopt(argv[1:], "h", ["help"])
except getopt.error, msg:
raise Usage(msg)
# more code, unchanged
except Usage, err:
print >>sys.stderr, err.msg
print >>sys.stderr, "for help use --help"
return 2
if __name__ == "__main__":
sys.exit(main())
Теперь мы имеем единственную точку выхода из функции, что предпочтительнее
множественных return 2
. Это также облегчает повторный анализ
параметров: raise Usage
вызывается просто во вспомогательной
функции.
Вы можете возразить, что можно перенести конструкцию try/except
из main()
в конец модуля (if __name__ == "__main__":
...
). Но это привело бы к возникновению ошибки при вызове в интерактивном
режиме интерпретатора.
Однако, обобщение может быть полезным: определите другое исключение (возможно
Error
), которое обрабатывается так же, как и Usage
, но
возвращает 1. Его можно применять для ожидаемых ошибок типа отказа открыть
необходимые файлы. Не ситаксические ошибки командной строки, но ожидаемые
ситуации. Т.к. traceback
не очень дружественное средство в таких
случаях.
Перевод: Орехов А.И.