Что отсутствует в следующем коде
Перейти к содержимому

Что отсутствует в следующем коде

Глючный код на Python: 10 самых распространенных ошибок, которые допускают разработчики

Python — это интерпретируемый, объектно-ориентированный язык программирования высокого уровня с динамической семантикой. Встроенные структуры данных высокого уровня в сочетании с динамической типизацией и динамическим связыванием делают его очень привлекательным для БРПС (быстрой разработки прикладных средств), а также для использования в качестве скриптового и связующего языка для подключения существующих компонентов или сервисов. Python поддерживает модули и пакеты, тем самым поощряя модульность программы и повторное использование кода.

О данной статье

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

Имея это в виду, в этой статье представлен «топ-10» тонких, трудных для обнаружения ошибок, которые могут допустить даже продвинутые разработчики Python.

Ошибка № 1: неправильное использование выражений в качестве значений по умолчанию для аргументов функций

Python позволяет указывать, что у функции могут быть необязательные аргументы, путем задания для них значения по умолчанию. Это, конечно, очень удобная особенность языка, но может привести к неприятным последствиям, если тип такого значения будет изменяемым. Например, рассмотрим следующее определение функции:

>>> def foo(bar=[]): # bar - это необязательный аргумент # и по умолчанию равен пустому списку. . bar.append("baz") # эта строка может стать проблемой. . return bar

Распространенная ошибка в данном случае — это думать, что значение необязательного аргумента будет устанавливаться в значение по умолчанию каждый раз, как функция будет вызываться без значения для этого аргумента. В приведенном выше коде, например, можно предположить, что повторно вызывая функцию foo() (то есть без указания значения для агрумента bar), она всегда будет возвращать «baz», поскольку предполагается, что каждый раз, когда вызывается foo () (без указания аргумента bar), bar устанавливается в [ ] (т. е. новый пустой список).

Но давайте посмотрим что же будет происходить на самом деле:

>>> foo() ["baz"] >>> foo() ["baz", "baz"] >>> foo() ["baz", "baz", "baz"]

А? Почему функция продолжает добавлять значение по умолчанию «baz» к существующему списку каждый раз, когда вызывается foo(), вместо того, чтобы каждый раз создавать новый список?

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

Для справки, распространенным «обходным путем» для этой ошибки является следующее определение:

>>> def foo(bar=None): . if bar is None: # or if not bar: . bar = [] . bar.append("baz") . return bar . >>> foo() ["baz"] >>> foo() ["baz"] >>> foo() ["baz"]

Ошибка № 2: неправильное использование переменных класса

Рассмотрим следующий пример:

>>> class A(object): . x = 1 . >>> class B(A): . pass . >>> class C(A): . pass . >>> print A.x, B.x, C.x 1 1 1

Вроде все в порядке.

>>> B.x = 2 >>> print A.x, B.x, C.x 1 2 1

Ага, все как и ожидалось.

>>> A.x = 3 >>> print A.x, B.x, C.x 3 2 3

Что за черт?! Мы же только изменили A.x. Почему же C.x тоже изменилось?

В Python переменные класса обрабатываются как словари и следуют тому, что часто называют Порядком разрешения методов (MRO). Таким образом, в приведенном выше коде, поскольку атрибут x не найден в классе C, он будет найден в его базовых классах (только A в приведенном выше примере, хотя Python поддерживает множественное наследование). Другими словами, C не имеет своего собственного свойства x, независимого от A. Таким образом, ссылки на C.x фактически являются ссылками на A.x. Это будет вызывать проблемы, если не обрабатывать такие случаи должным образом. Так что при изучении Python обратите особое внимание на аттрибуты класса и работу с ними.

Ошибка № 3: неправильное указание параметров для блока исключения

Предположим, что у вас есть следующий кусок кода:

>>> try: . l = ["a", "b"] . int(l[2]) . except ValueError, IndexError: # To catch both exceptions, right? . pass . Traceback (most recent call last): File "", line 3, in IndexError: list index out of range

Проблема здесь заключается в том, что выражение except не принимает список исключений, указанных таким образом. Скорее, в Python 2.x выражение «except Exception, e» используется для привязки исключения к необязательному второму заданному второму параметру (в данном случае e), чтобы сделать его доступным для дальнейшей проверки. В результате в приведенном выше коде исключение IndexError не перехватывается выражением except; скорее, вместо этого исключение заканчивается привязкой к параметру с именем IndexError.

Правильный способ перехвата нескольких исключений с помощью выражения except — указать первый параметр в виде кортежа, содержащего все исключения, которые нужно перехватить. Кроме того, для максимальной совместимости используйте ключевое слово as, так как этот синтаксис поддерживается как в Python 2, так и в Python 3:

>>> try: . l = ["a", "b"] . int(l[2]) . except (ValueError, IndexError) as e: . pass . >>>

Ошибка № 4: непонимание правил области видимости Python

Область видимости в Python основана на так называемом правиле LEGB, которое является аббревиатурой Local (имена, назначенные любым способом внутри функции (def или lambda), и не объявленные глобальными в этой функции), Enclosing (имя в локальной области действия любых статически включающих функций (def или lambda), от внутреннего к внешнему), Global (имена, назначенные на верхнем уровне файла модуля, или путем выполнения global инструкции в def внутри файла), Built-in (имена, предварительно назначенные в модуле встроенных имен: open, range, SyntaxError . ). Кажется достаточно просто, верно? Ну, на самом деле, есть некоторые тонкости в том, как это работает в Python, что подводит нас к общей более сложной проблеме программирования на Python ниже. Рассмотрим следующей пример:

>>> x = 10 >>> def foo(): . x += 1 . print x . >>> foo() Traceback (most recent call last): File "", line 1, in File "", line 2, in foo UnboundLocalError: local variable 'x' referenced before assignment

Вышеуказанная ошибка возникает потому, что, когда вы присваиваете переменную в области видимости, Python автоматически считает ее локальной для этой области и скрывает любую переменную с аналогичным именем в любой вышестоящей области.

Таким образом, многие удивляются, когда получают UnboundLocalError в ранее работающем коде, когда он модифицируется путем добавления оператора присваивания где-нибудь в теле функции.

Эта особенность особенно сбивает разработчиков с толку при использовании списков. Рассмотрим следующий пример:

>>> lst = [1, 2, 3] >>> def foo1(): . lst.append(5) # Это работает нормально. . >>> foo1() >>> lst [1, 2, 3, 5] >>> lst = [1, 2, 3] >>> def foo2(): . lst += [5] # . а вот это падает! . >>> foo2() Traceback (most recent call last): File "", line 1, in File "", line 2, in foo UnboundLocalError: local variable 'lst' referenced before assignment

А? Почему foo2 падает, в то время как foo1 работает нормально?

Ответ такой же, как в предыдущем примере, но, по распространенному мнению, здесь ситуация более тонкая. foo1 не применяет оператор присваивания к lst, тогда как foo2 — да. Помня, что lst + = [5] на самом деле является просто сокращением для lst = lst + [5], мы видим, что мы пытаемся присвоить значение lst (поэтому Python предполагает, что он находится в локальной области видимости). Однако значение, которое мы хотим присвоить lst, основано на самом lst (опять же, теперь предполагается, что он находится в локальной области видимости), который еще не был определен. И мы получаем ошибку.

Ошибка № 5: изменение списка во время итерации по нему

Проблема в следующем куске кода должна быть достаточно очевидной:

>>> odd = lambda x : bool(x % 2) >>> numbers = [n for n in range(10)] >>> for i in range(len(numbers)): . if odd(numbers[i]): . del numbers[i] # BAD: Deleting item from a list while iterating over it . Traceback (most recent call last): File "", line 2, in IndexError: list index out of range

Удаление элемента из списка или массива во время итерации по нему — это проблема Python, которая хорошо известна любому опытному разработчику программного обеспечения. Но, хотя приведенный выше пример может быть достаточно очевидным, даже опытные разработчики могут встать на эти грабли в гораздо более сложном коде.

К счастью, Python включает в себя ряд элегантных парадигм программирования, которые при правильном использовании могут привести к значительному упрощению и оптимизации кода. Дополнительным приятным следствием этого является то, что в более простом коде вероятность попасться на ошибку случайного удаления элемента списка во время итерации по нему значительно меньше. Одна из таких парадигм — генераторы списков. Кроме того, понимание работы генераторов списков особенно полезны для избежания этой конкретной проблемы, как показано в этой альтернативной реализацией приведенного выше кода, которая прекрасно работает:

>>> odd = lambda x : bool(x % 2) >>> numbers = [n for n in range(10)] >>> numbers[:] = [n for n in numbers if not odd(n)] # ahh, the beauty of it all >>> numbers [0, 2, 4, 6, 8]

Ошибка № 6: непонимание того, как Python связывает переменные в замыканиях

Рассмотрим следующий пример:

>>> def create_multipliers(): . return [lambda x : i * x for i in range(5)] >>> for multiplier in create_multipliers(): . print multiplier(2) . 

Вы можете ожидать следующий вывод:

0 2 4 6 8

Но на самом деле вы получите вот что:

8 8 8 8 8 

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

Решение этой распространенной проблемы с Python будет таким:

>>> def create_multipliers(): . return [lambda x, i=i : i * x for i in range(5)] . >>> for multiplier in create_multipliers(): . print multiplier(2) . 0 2 4 6 8

Вуаля! Мы используем здесь аргументы по умолчанию для генерации анонимных функций для достижения желаемого поведения. Некоторые назвали бы это решение элегантным. Некоторые —
тонким. Некоторые ненавидят подобные штуки. Но если вы разработчик Python, в любом случае, это важно понимать.

Ошибка № 7: создание циклических зависимостей модуля

Допустим, у вас есть два файла, a.py и b.py, каждый из которых импортирует другой, следующим образом:

import b def f(): return b.x print f()
import a x = 1 def g(): print a.f()

Сначала попробуем импортировать a.py:

>>> import a 1

Сработало просто отлично. Возможно, это вас удивляет. В конце концов, модули циклически импортируют друг друга и это, вероятно, должено быть проблемой, не так ли?

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

Итак, возвращаясь к нашему примеру, когда мы импортировали a.py, у него не было проблем с импортом b.py, поскольку b.py не требует, чтобы что-либо из a.py было определено во время его импорта. Единственная ссылка в b.py на a — это вызов a.f(). Но этот вызов в g() и ничего в a.py или b.py не вызывает g(). Так что все работает прекрасно.

Но что произойдет, если мы попытаемся импортировать b.py (без предварительного импорта a.py, то есть):

>>> import b Traceback (most recent call last): File "", line 1, in File "b.py", line 1, in import a File "a.py", line 6, in print f() File "a.py", line 4, in f return b.x AttributeError: 'module' object has no attribute 'x'

Ой-ой. Это не хорошо! Проблема здесь в том, что в процессе импорта b.py он пытается импортировать a.py, который, в свою очередь, вызывает f(), который пытается получить доступ к b.x. Но b.x еще не было определено. Отсюда исключение AttributeError.

По крайней мере, одно из решений этой проблемы довольно тривиально. Просто измените b.py, чтобы импортировать a.py в g():

x = 1 def g(): import a # This will be evaluated only when g() is called print a.f()

Теперь, когда мы его импортируем, все нормально:

>>> import b >>> b.g() 1 # Printed a first time since module 'a' calls 'print f()' at the end 1 # Printed a second time, this one is our call to 'g' 

Ошибка № 8: пересечение имен с именами модулями стандартной библиотеки Python

Одна из прелестей Python — это множество модулей, которые поставляются «из коробки». Но в результате, если вы сознательно не будете за этим следить, можно столкнуться с тем, что имя вашего модуля может быть с тем же именем, что и модуль в стандартной библиотеке, поставляемой с Python (например, в вашем коде может быть модуль с именем email.py, который будет конфликтовать со модулем стандартной библиотеки с таким же именем).

Это может привести к серьезным проблемам. Например, если какой-нибудь из модулей будет пытаться импортировать версию модуля из стандартной библиотеки Python, а у вас в проекте будет модуль с таким же именем, который и будет по ошибке импортирован вместо модуля из стандартной библиотеки.

Поэтому следует проявлять осторожность, чтобы не использовать те же имена, что и в модулях стандартной библиотеки Python. Гораздо проще изменить название модуля в своем проекте, нежели подать запрос на изменение имени модуля в стандартной библиотеке и получить на него одобрение.

Ошибка № 9: неспособность учесть различия Python 2 и Python 3

Рассмотрим следующий файл foo.py:

import sys def bar(i): if i == 1: raise KeyError(1) if i == 2: raise ValueError(2) def bad(): e = None try: bar(int(sys.argv[1])) except KeyError as e: print('key error') except ValueError as e: print('value error') print(e) bad()

На Python 2 он отработает нормально:

$ python foo.py 1 key error 1 $ python foo.py 2 value error 2

Но теперь давайте посмотрим как он будет работать в Python 3:

$ python3 foo.py 1 key error Traceback (most recent call last): File "foo.py", line 19, in bad() File "foo.py", line 17, in bad print(e) UnboundLocalError: local variable 'e' referenced before assignment

Что здесь только что произошло? «Проблема» в том, что в Python 3 объект в блоке исключения недоступен за его пределами. (Причина этого заключается в том, что в противном случае объекты в этом блоке будут сохраняться в памяти до тех пор, пока сборщик мусора не запустится и не удалит ссылки на них оттуда).

Один из способов избежать этой проблемы — сохранить ссылку на объект блока исключения за пределами этого блока, чтобы он оставался доступным. Вот версия предыдущего примера, которая использует эту технику, тем самым получая код, который подходит как для Python 2, так и для Python 3:

import sys def bar(i): if i == 1: raise KeyError(1) if i == 2: raise ValueError(2) def good(): exception = None try: bar(int(sys.argv[1])) except KeyError as e: exception = e print('key error') except ValueError as e: exception = e print('value error') print(exception) good()

Запустим его в Python 3:

$ python3 foo.py 1 key error 1 $ python3 foo.py 2 value error 2

Ошибка № 10: неправильное использование метода __del__

Допустим, у вас есть вот такой файл mod.py:

import foo class Bar(object): . def __del__(self): foo.cleanup(self.myhandle)

И вы пытаетесь сделать вот такое из другого another_mod.py:

import mod mybar = mod.Bar()

И получите ужасный AttributeError.

Почему? Потому что, как сообщается здесь, когда интерпретатор отключается, глобальные переменные модуля все имеют значение None. В результате в приведенном выше примере, в момент вызова __del__, имя foo уже было установлено в None.

Решением этой «задачи со звездочкой» будет использование atexit.register(). Таким образом, когда ваша программа завершает выполнение (то есть при нормальном выходе из нее), ваши handle’ы удаляются до того, как интерпретатор звершает работу.

С учетом этого, исправление для приведенного выше кода mod.py может выглядеть примерно так:

import foo import atexit def cleanup(handle): foo.cleanup(handle) class Bar(object): def __init__(self): . atexit.register(cleanup, self.myhandle)

Подобная реализация обеспечивает простой и надежный способ вызова любой необходимой очистки после обычного завершения программы. Очевидно, что решение о том, как поступить с объектом, который связан с имненем self.myhandle, остается за foo.cleanup, но, думаю, идею вы поняли.

Заключение

Python — это мощный и гибкий язык со множеством механизмов и парадигм, которые могут значительно повысить производительность. Однако, как и в случае с любым программным инструментом или языком, при ограниченном понимании или оценке его возможностей, при разработке могут возникать непредвиденные проблемы.

Ознакомление с нюансами Python, затронутыми в этой статье, поможет оптимизировать использование языка, избегая при этом некоторых распространенных ошибок.

Обработка ошибок с помощью Python

Ошибки случаются. Написание скриптов, которое предполагает наличие и обработку ошибок, сохраняет массу времени и ваших сил. Когда инструмент выводит сообщение об ошибке, ArcPy генерирует системную ошибку или исключение. В Python вы можете обеспечить различные структуры и методы для обработки исключений. Конечно, скрипт может не выполниться по причинам, не связанным с инструментом геообработки. Их также следует обнаружить и решить. В следующих разделах предлагается несколько способов, которые ознакомят вас с основными принципами обработки исключений в Python .

Когда инструмент записывает сообщение об ошибке, ArcPy создает исключение arcpy.ExecuteError . Python позволяет написать модуль, который будет выполняться автоматически при возникновении системной ошибки. С помощью этого модуля для обработки ошибок вы сможете получать сообщения об ошибках от ArcPy и реагировать на них. Если скрипт не имеет модуля для обработки ошибок, он завершает выполнение немедленно, что уменьшает его надежность. Модуль обработки ошибок можно использовать для управления ошибками и повышения надежности скриптов.

Сообщения об ошибках в инструментах геообработки обычно имеют шестизначный код. Эти коды-идентификаторы занесены в документы, содержащие дополнительные сведения об их причинах и необходимых действиях.

Выражение try-except

Чтобы охватить всю программу целиком или только отдельные фрагменты кода для обнаружения и определения ошибок могут использоваться выражения try-except . Если в выражении try возникнет ошибка, будет создано исключение, и код будет выполнен в соответствии с выражением except. Использование выражения except является наиболее простой формой обработки ошибок.

В следующем коде Буфер не работает, потому что не был указан обязательный аргумент buffer_distance_or_field . Чтобы найти ошибку, используется выражение except , которое извлекает и выводит сообщение об ошибке, сгенерированное Буфером . Обратите внимание, что блок except выполняется только в случае, если Буфер возвращает ошибку.

import arcpy import sys try: # Execute the Buffer tool arcpy.Buffer_analysis("c:/transport/roads.shp", "c:/transport/roads_buffer.shp") except Exception: e = sys.exc_info()[1] print(e.args[0]) # If using this code within a script tool, AddError can be used to return messages # back to a script tool. If not, AddError will have no effect. arcpy.AddError(e.args[0])

Выражение try содержит необязательное условие finally , которое можно использовать для задач, которые должны выполняться всегда, независимо от того, возникает исключение или нет. В следующем примере ArcGIS 3D Analyst extension регистрируется в соответствии с условием finally , которое гарантирует, что этот дополнительный модуль всегда будет регистрироваться.

class LicenseError(Exception): pass import arcpy try: if arcpy.CheckExtension("3D") == "Available": arcpy.CheckOutExtension("3D") else: # Raise a custom exception raise LicenseError arcpy.env.workspace = "D:/GrosMorne" arcpy.HillShade_3d("WesternBrook", "westbrook_hill", 300) arcpy.Aspect_3d("WesternBrook", "westbrook_aspect") except LicenseError: print "3D Analyst license is unavailable" except arcpy.ExecuteError: print(arcpy.GetMessages(2)) finally: # Check in the 3D Analyst extension arcpy.CheckInExtension("3D")

Выражение raise

Предыдущий пример иллюстрирует ошибку в исключении, которое возникло в коде В некоторых случаях может потребоваться создать пользовательские исключения. Для этой цели может использоваться выражение raise . В следующем коде выражение raise используется, если входной класс объектов определился как не содержащий объектов. Это не обязательно ошибка, но условие, при котором код может быть использован.

class NoFeatures(Exception): pass import arcpy import os import sys arcpy.env.overwriteOutput = True fc = arcpy.GetParameterAsText(0) try: # Check that the input has features result = arcpy.GetCount_management(fc) if int(result[0]) > 0: arcpy.FeatureToPolygon_management( fc, os.path.join(os.path.dirname(fc), 'out_poly.shp')) else: # Raise custom exception raise NoFeatures(result) except NoFeatures: # The input has no features print('<> has no features'.format(fc)) except: # By default any other errors will be caught here e = sys.exc_info()[1] print(e.args[0])

Класс ExecuteError

Когда инструмент геообработки выходит из строя, он создает класс исключений arcpy.ExecuteError , что означает, что вы можете разделить ошибки на различные группы, ошибки геообработки (те, которые создают исключение arcpy.ExecuteError ) и другие типы исключений. Затем можно обрабатывать эти ошибки по разному, как показано в следующем коде:

import arcpy import sys try: result = arcpy.GetCount_management("C:/invalid.shp") # Return geoprocessing specific errors except arcpy.ExecuteError: arcpy.AddError(arcpy.GetMessages(2)) # Return any other type of error except: # By default any other errors will be caught here e = sys.exc_info()[1] print(e.args[0])

traceback

В больших и более сложных скриптах бывает сложно точное определить место возникновения ошибки. Для определения точного местоположения и причины ошибки, а также для более точного определения причины ошибки и экономии ценного времени на отладку можно использовать модули Python , sys и traceback совместно.

# Import the required modules # import arcpy import sys import traceback arcpy.env.workspace = "C:/Data/myData.gdb" try: arcpy.CreateSpatialReference_management() #-------------------------- # Your code goes here # # See the table below for examples #-------------------------- except arcpy.ExecuteError: # Get the tool error messages msgs = arcpy.GetMessages(2) # Return tool error messages for use with a script tool arcpy.AddError(msgs) # Print tool error messages for use in Python print(msgs) except: # Get the traceback object tb = sys.exc_info()[2] tbinfo = traceback.format_tb(tb)[0] # Concatenate information together concerning the error into a message string pymsg = "PYTHON ERRORS:\nTraceback info:\n" + tbinfo + "\nError Info:\n" + str(sys.exc_info()[1]) msgs = "ArcPy ERRORS:\n" + arcpy.GetMessages(2) + "\n" # Return Python error messages for use in script tool or Python window arcpy.AddError(pymsg) arcpy.AddError(msgs) # Print Python error messages for use in Python / Python window print(pymsg) print(msgs)

Если был использован приведенный выше код, и произошла ошибка инструмента геообработки, такая как недопустимые входные данные, это вызовет arcpy.ExecuteError , и будет использоваться первое выражение except . Это выражение выведет сообщение об ошибке с помощью функции GetMessages . Если использовался тот же код, но возникла ошибка другого типа, будет использовано второе выражение except . Вместо печати сообщений о геообработке, будет получен объект traceback и выведены соответствующие сообщения о системных ошибках.

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

PYTHON ERRORS: Traceback info: File "c:\temp\errortest.py", line 10, in arcpy.GetCount_management("") Error Info: Failed to execute. Parameters are not valid. ERROR 000735: Input Rows: value is required Failed to execute (GetCount). ArcPy ERRORS: Failed to execute. Parameters are not valid. ERROR 000735: Input Rows: value is required Failed to execute (GetCount).
PYTHON ERRORS: Traceback info: File "c:\temp\errortest.py", line 10, in x = "a" + 1 Error Info: cannot concatenate 'str' and 'int' objects

float(«a text string»)

PYTHON ERRORS: Traceback info: File "c:\temp\errortest.py", line 10, in float("a text string") Error Info: invalid literal for float(): a text string

Получение сообщений об ошибках от объекта Result

Несколько слов об объекте Result , который показан ниже:

result = arcpy.GetCount_management("c:/data/rivers.shp")

Если запрос в GetCount_management вызывает исключение, объект Result не создается. Это означает, что вы не можете получать сообщения об ошибках от объекта Result .

import arcpy try: result = arcpy.GetCount_management("c:/data/rivers.shp") # Return Geoprocessing specific errors # (this method is incorrect!) except arcpy.ExecuteError: arcpy.AddError(result.getMessages(2))

Указанный выше код не работает, и появляется сообщение name ‘result’ is not defined . Причина в том, что объект Result невозможно было создать из-за ошибки инструмента. Поскольку объект Result не создан, возникает ошибка Python при попытке использования метода getMessages .

Примечание:

Объект Result , создаваемый путем запроса в сервис геообработки на ArcGIS Server , создается даже при ошибке инструмента. Создание объекта Result не удается только тогда, когда инструмент запускается локально и вызывает ошибку. Более подробную информацию об использовании объекта result смотрите в разделе Использование инструментов в Python .

В этом разделе
  1. Выражение try-except
  2. Выражение raise
  3. Класс ExecuteError
  4. traceback
  5. Получение сообщений об ошибках от объекта Result

Решение модуля 11.5 из курса «Поколение Python: для начинающих»

Разобрали разборы с ответами и решениями урока с курса «Поколение Python: курс для начинающих». Предшествующее занятие 11.4.

С помощью функции list() можно из строки получить список ее символов, а с помощью функции join() можно склеить элементы списка, вставляя между ними разделитель.

Что будет выведено в результате выполнения следующего программного кода?

s = ‘BEEGEEK’
chars = list(s)
s = ‘**’.join(chars)
print(s)

B**E**E**G**E**E**K

Решение модуля 11.5 из курса «Поколение Python: для начинающих»

Построчный вывод
На вход программе подается строка текста. Напишите программу, которая выводит слова введенной строки в столбик.

# Получаем строку и разбиваем её на слова, сохраняя их в список s s = input().split() # Выводим элементы списка s, разделяя их переводами строки print(*s, sep="\n")

Решение модуля 11.5 из курса «Поколение Python: для начинающих»

Инициалы
На вход программе подается строка текста, содержащая имя, отчество и фамилию человека. Напишите программу, которая выводит инициалы человека.

# Получаем строку и разбиваем её на слова, сохраняя их в список s s = input().split() # Проходим по каждому слову i в списке s for i in s: print(i[0], end=".") # Выводим первую букву слова i и добавляем точку в конце

Решение модуля 11.5 из курса «Поколение Python: для начинающих»

Windows OS
В операционной системе Windows полное имя файла состоит из буквы диска, после которого ставится двоеточие и символ «\», затем через такой же символ перечисляются подкаталоги (папки), в которых находится файл, в конце пишется имя файла (C:\Windows\System32\calc.exe).

# Получаем строку и разделяем её на подстроки, используя обратный слеш в качестве разделителя s = input().split('\\') # Выводим подстроки, разделяя их переводами строки print(*s, sep="\n")

Решение модуля 11.5 из курса «Поколение Python: для начинающих»

Диаграмма
На вход программе подается строка текста, содержащая целые числа. Напишите программу, которая по заданным числам строит столбчатую диаграмму.

# Получаем строку и разбиваем её на слова, предполагая, что каждое слово - это число s = input().split() # Проходим по каждому слову i в списке s for i in s: print('+' * int(i)) # Выводим '+' умноженный на целое число, представленное словом i

Решение модуля 11.5 из курса «Поколение Python: для начинающих»

Корректный ip-адрес
На вход программе подается строка текста, содержащая 4 целых числа разделенных точкой. Напишите программу, которая определяет является ли введенная строка текста корректным ip-адресом.

# Получаем строку и разбиваем её на четыре числа, используя точку в качестве разделителя s = input().split('.') # Проходим по каждому числу i в списке s for i in s: if int(i) < 0 or int(i) >255: # Проверяем, что число является допустимым (в пределах от 0 до 255) print('НЕТ') # Если число недопустимо, выводим 'НЕТ' break # Прерываем цикл, так как одино из числел недопустимый else: print('ДА') # Если все числа допустимые, выводим 'ДА'

Решение модуля 11.5 из курса «Поколение Python: для начинающих»

Добавь разделитель
На вход программе подается строка текста и строка разделитель. Напишите программу, которая вставляет указанный разделитель между каждым символом введенной строки текста.

s = input() # Считываем первую строку r = input() # Считываем вторую строку # Преобразуем первую строку s в список символов # Затем объединяем символы из списка, используя вторую строку r в качестве разделителя res = r.join(list(s)) # Выводим результат print(res)

Решение модуля 11.5 из курса «Поколение Python: для начинающих»

Количество совпадающих пар

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

# Получаем строку и разбиваем её на слова, сохраняя их в список s s = input().split() c = 0 # Инициализируем счетчик повторяющихся слов # Проходим по каждому индексу i в списке s for i in range(len(s)): # Для каждого индекса i, проходим по оставшимся словам, начиная с индекса i + 1 for j in range(i + 1, len(s)): if s[j] == s[i]: # Если слова с индексами i и j совпадают c += 1 # Увеличиваем счетчик на 1 print(c) # Выводим общее количество повторяющихся слов

Если у вас не отображается решение последних задач, значит у вас включен блокировщик рекламы который вырезает эти ответы

Циклы в программировании. Цикл while

Циклы являются такой же важной частью структурного программирования, как условные операторы. С помощью циклов можно организовать повторение выполнения участков кода. Потребность в этом возникает довольно часто. Например, пользователь последовательно вводит числа, и каждое из них требуется добавлять к общей сумме. Или нужно вывести на экран квадраты ряда натуральных чисел и тому подобные задачи.

Цикл while

«While» переводится с английского как «пока». Но не в смысле «до свидания», а в смысле «пока имеем это, делаем то».

Можно сказать, while является универсальным циклом. Он присутствует во всех языках, поддерживающих структурное программирование, в том числе в Python. Его синтаксис обобщенно для всех языков можно выразить так:

while логическое_выражение

Это похоже на условный оператор if . Однако в случае циклических операторов их тела могут выполняться далеко не один раз. В случае if , если логическое выражение в заголовке возвращает истину, то тело выполняется единожды. После этого поток выполнения программы возвращается в основную ветку и выполняет следующие выражения, расположенные ниже всей конструкции условного оператора.

В случае while , после того как его тело выполнено, поток возвращается к заголовку цикла и снова проверяет условие. Если логическое выражение возвращает истину, то тело снова выполняется. Потом снова возвращаемся к заголовку и так далее.

Цикл завершает свою работу только тогда, когда логическое выражение в заголовке возвращает ложь, то есть условие выполнения цикла больше не соблюдается. После этого поток выполнения перемещается к выражениям, расположенным ниже всего цикла. Говорят, «происходит выход из цикла».

Рассмотрите блок-схему цикла while .

Блок-схема цикла while

На ней ярко-голубыми прямоугольниками обозначена основная ветка программы, ромбом – заголовок цикла с логическим выражением, бирюзовым прямоугольником – тело цикла.

С циклом while возможны две исключительные ситуации:

  • Если при первом заходе в цикл логическое выражение возвращает False , то тело цикла не выполняется ни разу. Эту ситуацию можно считать нормальной, так как при определенных условиях логика программы может предполагать отсутствие необходимости в выполнении выражений тела цикла.
  • Если логическое выражение в заголовке while никогда не возвращает False , а всегда остается равным True , то цикл никогда не завершится, если только в его теле нет оператора принудительного выхода из цикла ( break ) или вызовов функций выхода из программы – quit() , exit() в случае Python. Если цикл повторяется и повторяется бесконечное количество раз, то в программе происходит зацикливание. В это время она зависает и самостоятельно завершиться не может.

Вспомним наш пример из урока про исключения. Пользователь должен ввести целое число. Поскольку функция input() возвращает строку, то программный код должен преобразовать введенное к целочисленному типу с помощью функции int() . Однако, если были введены символы, не являющиеся цифрами, то возникает исключение ValueError , которое обрабатывается веткой except . На этом программа завершается.

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

Но ведь можно просить и просить пользователя корректно вести число, пока он его не введет. Вот как может выглядеть реализующий это код:

n = input("Введите целое число: ") while type(n) != int: try: n = int(n) except ValueError: print("Неправильно ввели!") n = input("Введите целое число: ") if n % 2 == 0: print("Четное") else: print("Нечетное")

Примечание 1. Не забываем, в языке программирования Python в конце заголовков сложных инструкций ставится двоеточие.

Примечание 2. В выражении type(n) != int с помощью функции type() проверяется тип переменной n . Если он не равен int , то есть значение n не является целым числом, а является в данном случае строкой, то выражение возвращает истину. Если же тип n равен int , то данное логическое выражение возвращает ложь.

Примечание 3. Оператор % в языке Python используется для нахождения остатка от деления. Так, если число четное, то оно без остатка делится на 2, то есть остаток будет равен нулю. Если число нечетное, то остаток будет равен единице.

Проследим алгоритм выполнения этого кода. Пользователь вводит данные, они имеют строковый тип и присваиваются переменной n . В заголовке while проверяется тип n . При первом входе в цикл тип n всегда строковый, то есть он не равен int . Следовательно, логическое выражение возвращает истину, что позволяет зайти в тело цикла.

Здесь в ветке try совершается попытка преобразования строки к целочисленному типу. Если она была удачной, то ветка except пропускается, и поток выполнения снова возвращается к заголовку while .

Теперь n связана с целым числом, следовательно, ее тип int , который не может быть не равен int . Он ему равен. Таким образом логическое выражение type(n) != int возвращает False , и весь цикл завершает свою работу. Далее поток выполнения переходит к оператору if-else, находящемуся в основной ветке программы. Здесь могло бы находиться что угодно, не обязательно условный оператор.

Вернемся назад. Если в теле try попытка преобразования к числу была неудачной, и было выброшено исключение ValueError , то поток выполнения программы отправляется в ветку except и выполняет находящиеся здесь выражения, последнее из которых просит пользователя снова ввести данные. Переменная n теперь имеет новое значение.

После завершения except снова проверяется логическое выражение в заголовке цикла. Оно даст True , так как значение n по-прежнему строка.

Выход из цикла возможен только тогда, когда значение n будет успешно конвертировано в число.

Рассмотрим следующий пример:

total = 100 i = 0 while i  5: n = int(input()) total = total - n i = i + 1 print("Осталось", total)

Сколько раз «прокрутится» цикл в этой программе, то есть сколько итераций он сделает? Ответ: 5.

  1. Сначала переменная i равна 0. В заголовке цикла проверяется условие i < 5 , и оно истинно. Тело цикла выполняется. В нем меняется значение i , путем добавления к нему единицы.
  2. Теперь переменная i равна 1. Это меньше пяти, и тело цикла выполняется второй раз. В нем i меняется, ее новое значение 2.
  3. Два меньше пяти. Тело цикла выполняется третий раз. Значение i становится равным трем.
  4. Три меньше пяти. На этой итерации i присваивается 4.
  5. Четыре по прежнему меньше пяти. К i добавляется единица, и теперь ее значение равно пяти.

«Смысловая нагрузка» данного цикла – это последовательное вычитание из переменной total вводимых чисел. Переменная i в данном случае играет только роль счетчика итераций цикла. В других языках программирования для таких случаев предусмотрен цикл for , который так и называется: «цикл со счетчиком». Его преимущество заключается в том, что в теле цикла не надо изменять переменную-счетчик, ее значение меняется автоматически в заголовке for .

В языке Python тоже есть цикл for . Но это не цикл со счетчиком. В Питоне он предназначен для перебора элементов последовательностей и других сложных объектов. Данный цикл и последовательности будут изучены в последующих уроках.

Для while наличие счетчика не обязательно. Представим, что надо вводить числа, пока переменная total больше нуля. Тогда код будет выглядеть так:

total = 100 while total > 0: n = int(input()) total = total - n print("Ресурс исчерпан")

Сколько раз здесь выполнится цикл? Неизвестно, все зависит от вводимых значений. Поэтому у цикла со счетчиком известно количество итераций, а у цикла без счетчика – нет.

Самое главное для цикла while – чтобы в его теле происходили изменения значений переменных, которые проверяются в его заголовке, и чтобы хоть когда-нибудь наступил случай, когда логическое выражение в заголовке возвращает False . Иначе произойдет зацикливание.

Примечание 1. Не обязательно в выражениях total = total — n и i = i + 1 повторять одну и ту же переменную. В Python допустим сокращенный способ записи подобных выражений: total -= n и i += 1 .

Примечание 2. При использовании счетчика он не обязательно должен увеличиваться на единицу, а может изменяться в любую сторону на любое значение. Например, если надо вывести числа кратные пяти от 100 до 0, то изменение счетчика будет таким i = i — 5 , или i -= 5 .

Примечание 3. Для счетчика не обязательно использовать переменную с идентификатором i . Можно назвать переменную-счетчик как угодно. Однако так принято в программировании, что счетчики обозначают именами i и j (иногда одновременно требуются два счетчика).

Практическая работа

  1. Измените последний код из урока так, чтобы переменная total не могла уйти в минус. Например, после предыдущих вычитаний ее значение стало равным 25. Пользователь вводит число 30. Однако программа не выполняет вычитание, а выводит сообщение о недопустимости операции, после чего осуществляет выход из цикла.
  2. Используя цикл while , выведите на экран для числа 2 его степени от 0 до 20. Возведение в степень в Python обозначается как ** . Фрагмент вывода:
. 32 64 128 256 512 1024 .

Примеры решения и дополнительные уроки в pdf-версии курса

X Скрыть Наверх

Python. Введение в программирование

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *