Что будет являться возвращаемым значением данной функции
Перейти к содержимому

Что будет являться возвращаемым значением данной функции

  • автор:

Возвращаемые значения функции ссылочного типа

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

  • Возвращаемая информация представляет собой настолько крупный объект, что возврат ссылки является более эффективным, чем возврат копии.
  • Тип функции должен представлять собой l-значение.
  • Объект, на который указывает ссылка, не выйдет из области видимости при возврате управления функцией.

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

Типы возврата ссылок также могут оказаться полезными, если результатом функции должно быть l-значение. Большинство перегруженных операторов относятся к этой категории, в частности оператор присваивания. Перегруженные операторы рассматриваются в перегруженных операторах.

Пример

Рассмотрим пример Point .

// refType_function_returns.cpp // compile with: /EHsc #include using namespace std; class Point < public: // Define "accessor" functions as // reference types. unsigned& x(); unsigned& y(); private: // Note that these are declared at class scope: unsigned obj_x; unsigned obj_y; >; unsigned& Point :: x() < return obj_x; >unsigned& Point :: y() < return obj_y; >int main() < Point ThePoint; // Use x() and y() as l-values. ThePoint.x() = 7; ThePoint.y() = 9; // Use x() and y() as r-values. cout

Выходные данные

x = 7 y = 9 

Обратите внимание, что функции x и y объявляются как типы возвращаемых ссылок. Эти функции можно использовать на любой стороне оператора присваивания.

Также обратите внимание, что в функции main объект ThePoint остается в области видимости, поэтому его ссылочные элементы продолжают действовать, и к ним можно получить безопасный доступ.

Объявления ссылочных типов должны содержать инициализаторы. Исключение составляют следующие случаи.

  • Явное extern объявление
  • Объявление члена класса
  • Объявление в классе
  • Объявление аргумента в адрес функции или типа возвращаемого значения для функции

Предупреждение при возвращении адреса локальной переменной

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

// C4172 means Don't do this. Foo& GetFoo() < Foo f; . return f; >// f is destroyed here 

Компилятор выдает предупреждение в этом случае: warning C4172: returning address of local variable or temporary В простых программах доступ может случайно сохраниться, если ссылка будет использована вызывающим объектом до перезаписи соответствующей области памяти. Однако это чистая случайность. Обратите внимание на предупреждение.

Функции

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

Функция -- это совокупность объявлений и операторов, предназначенная для решения определенной задачи. Каждая функция имеет идентификатор -- имя.

Даже «тело программы» -- это функция с именем main, главная функция. В программе на С главная функция только одна, т. к. именно с нее, в каком бы месте она не находилась, начинается выполнение программы.

Имя (идентификатор) используется для вызова функции.

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

Функция может возвращать некоторое (одно!) значение. Это возвращаемое значение и есть результат выполнения функции, который при выполнении программы подставляется в точку вызова функции, где бы этот вызов ни встретился.

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

Описание функции задает:

  1. тип возвращаемого значения;
  2. имя функции;
  3. типы и число формальных параметров;
  4. тело функции.

В определении функции также может быть указан класс памяти.

Пример

В данном примере определена функция с именем digit, имеющая один параметр с именем c и типом unsigned char. Функция возвращает целое значение, равное 1, если параметр функции является цифрой, или 0 в противном случае.

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

Однако, чтобы компилятор мог осуществить проверку соответствия типов передаваемых фактических параметров типам формальных параметров, до вызова функции нужно поместить объявление функции --- прототип.

Объявление функции похоже на определение функции, но тело функции отсутствует, а имена формальных параметров могут быть пропущены. Для функции, определенной в последнем примере, прототип может иметь вид

int digit(unsigned char);

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

Для использования библиотечных функций, т. е. функции предварительно разработанных и записанных в библиотеки, требуется включить в программу их описания (заголовки), что осуществляется с помощью директивы #include.

Итак, определение функции имеет следующую форму:

Имя типа задает тип возвращаемого значения.

Функция не может возвращать массив или функцию!

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

Если оператор return не содержит выражения или выполнение функции завершается после выполнения ее последнего оператора (без выполнения оператора return), то возвращаемое значение не определено! На практике это означает, что программа поведет себя непредсказуемым образом. Если же по задумке разработчика функция и не должна ничего возвращать, то в качестве типа результата должен быть использован void.

Список формальных параметров -- это последовательность объявлений формальных параметров, разделенная запятыми. Формальные параметры -- это переменные, используемые внутри тела функции и получающие значение при вызове функции путем копирования в них значений соответствующих фактических параметров.

Список формальных параметров может заканчиваться запятой (,) или запятой с многоточием (, . ), это называется эллипсис, и означает, что число аргументов функции переменно. Над дополнительными аргументами не проводится контроль типов! Такие функции лучше не использовать на практике.

Если функция не использует параметров, то наличие круглых скобок обязательно, а вместо списка параметров рекомендуется указать слово void.

Порядок и типы формальных параметров должны быть одинаковыми в определении функции и во всех ее объявлениях. Типы фактических параметров при вызове функции должны быть совместимы с типами соответствующих формальных параметров.

Имена параметров используются в теле функции для доступа к переданным значениям.

Тело функции

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

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

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

Пример

/* Неправильное использование параметров */
void swap(int x, int y)
int tmp = x;
x = y;
y = tmp;
>

В данной функции значения x и y, являющихся формальными параметрами, меняются местами, но поскольку эти переменные существуют только внутри функции change, значения фактических параметров, используемых при вызове функции, останутся неизменными.

Для того чтобы менялись местами значения фактических аргументов можно сделать так:

Пример

/* Правильное использование параметров */
void swap(int *x, int *y)
<
int tmp = *x;
*x = *y;
*y = tmp;
>

При вызове такой функции в качестве фактических параметров должны быть использованы не значения переменных, а их адреса:

Python: Возврат значений

В этом уроке подробнее разберем, как работать с созданными функциями, чтобы они были полезны.

Когда мы определяем функцию, она печатает на экран какие-то данные:

def greeting(): print('Hello, Hexlet!') 

Пользы от таких функций немного, так как результатом нельзя воспользоваться внутри программы. Рассмотрим на примере.

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

  • Добавить случайно пробелы в начале или в конце: _support@hexlet.io__
  • Использовать буквы в разном регистре: SUPPORT@hexlet.io

Если мы сохраним его в таком виде в базу данных, то пользователь не войдет на сайт. Чтобы этого не произошло, email нужно подготовить к записи в базу: привести его к нижнему регистру и обрезать пробелы по краям строки. Такая задача решается в пару строчек:

def save_email(): # Email приходит из формы email = ' SuppORT@hexlet.IO' # Обрезаем пробельные символы trimmed_email = email.strip() prepared_email = trimmed_email.lower() print(prepared_email) # Здесь будет запись в базу данных 

Этот код стал возможен благодаря тому, что значение вернулось. Методы strip() и lower() ничего не печатают на экран, они возвращают результат своей работы. Поэтому мы можем записать его в переменные. Если бы они печатали на экран, мы бы не могли присвоить результат переменной. Например, так мы не можем сделать с функцией greeting() :

message = greeting() # в действительности, функция print() возвращает None # None — специальный объект, используемый для представления отсутствия значения print(message) # => None 

Теперь изменим функцию greeting() так, чтобы она возвращала данные. Для этого выполним возврат вместо печати на экран:

def greeting(): return 'Hello, Hexlet!' 

return — это инструкция. Она берет записанное справа выражение и отдает его тому коду, который вызвал метод. Здесь выполнение функции завершается.

# Теперь мы можем использовать результат работы функции message = greeting() print(message) # => Hello, Hexlet! # И даже выполнить какие-то действия над результатом print(message.upper()) # => HELLO, HEXLET! 

Любой код после return не выполняется:

def greeting_with_code_after_return(): return 'Hello, Hexlet!' print('Я никогда не выполнюсь') 

Даже если функция возвращает данные, это не ограничивает ее в том, что она печатает. Кроме возврата данных мы можем и печатать:

def greeting_with_return_and_printing(): print('Я появлюсь в консоли') return 'Hello, Hexlet!' # И напечатает текст на экран, и вернет значение message = greeting_with_return_and_printing() 

Возвращать можно не только конкретное значение. Так как return работает с выражениями, то справа от него может быть что угодно. Здесь нужно руководствоваться принципами читаемости кода:

def greeting(): message = 'Hello, Hexlet!' return message 

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

def double_five(): # или return 5 + 5 result = 5 + 5 return result 

Определить функцию мало. Еще важно, чтобы она была полезна, и результатом можно было воспользоваться. А теперь подумайте, что вернет вызов, определенной ниже функции run() ?

# Определение def run(): return 5 return 10 # Что будет выведено на экран? print(run()) 

Задание

Реализуйте функцию say_hurray_three_times() , которая возвращает строку 'hurray! hurray! hurray!'.

hurray = say_hurray_three_times() print(hurray) # => hurray! hurray! hurray! 

Упражнение не проходит проверку — что делать? ��

Если вы зашли в тупик, то самое время задать вопрос в «Обсуждениях». Как правильно задать вопрос:

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

В моей среде код работает, а здесь нет ��

Тесты устроены таким образом, что они проверяют решение разными способами и на разных данных. Часто решение работает с одними входными данными, но не работает с другими. Чтобы разобраться с этим моментом, изучите вкладку «Тесты» и внимательно посмотрите на вывод ошибок, в котором есть подсказки.

Мой код отличается от решения учителя ��

Это нормально ��, в программировании одну задачу можно выполнить множеством способов. Если ваш код прошел проверку, то он соответствует условиям задачи.

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

Прочитал урок — ничего не понятно ��

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

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

5. Функции, возвращающие значения¶

Встроенные функции, которыми мы пользовались, такие как abs , pow и max , возвращают результат. Вызов каждой из этих функций производит значение, которое обычно присваивается переменной или используется в выражении.

biggest = max(3, 7, 2, 5) x = abs(3 - 11) + 10 

До сих пор ни одна из написанных нами функций не возвращала значения.

В этой главе мы будем писать функции, возвращающие значения. Первый пример — функция area (англ.: площадь), возвращающая площадь круга с указанным радиусом:

def area(radius): temp = 3.14159 * radius**2 return temp 

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

def area(radius): return 3.14159 * radius**2 

Однако имейте в виду, что временные переменные, такие как temp , часто делают отладку проще.

Функция может иметь несколько предложений return, по одному в каждой ветке условного выполнения. Мы уже видели встроенную функцию abs , а теперь напишем нашу собственную:

def absolute_value(x): if x  0: return -x else: return x 

Поскольку предложения return здесь находятся в альтернативных ветках потока выполнения, будет выполнено только одно из них. Как только это произойдет, функция завершится; никакие последующие предложения выполняться не будут.

В рассматриваемой функции можно опустить else и просто поместить второе предложение return после составного предложения if .

def absolute_value(x): if x  0: return -x return x 

Посмотрите внимательно на эту версию и убедитесь, что она работает так же, как и первая.

Код, расположенный после предложения return , или в любом другом месте, которого никогда не сможет достичь поток выполнения, называется мертвым кодом.

Стоит убедиться в том, что в возвращающей значение функции каждый из возможных путей выполнения содержит предложение return . В следующей версии функции absolute_value это не так:

def absolute_value(x): if x  0: return -x elif x > 0: return x 

Эта функция некорректна, поскольку, если x окажется равным 0, ни одно из условий не выполнится и функция закончится, не выполнив предложения return . В таком случае, возвращаемым значением будет специальное значение None (англ.: ничто):

>>> print absolute_value(0) None 

None является единственным значением типа NoneType :

>>> type(None)

Все функции Python, если не возвращают некоторое значение явно, возвращают None .

5.2. Разработка программы¶

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

Для того, чтобы разрабатывать программы все возрастающей сложности, рассмотрим прием, называемый инкрементной разработкой. Цель инкрементной разработки — избежать длительной и мучительной отладки программы. Это становится возможным, если за один раз добавлять и тестировать небольшой объем кода.

Предположим, вы хотите найти расстояние между двумя точками, заданными с помощью их координат (x1, y1) и (x2, y2). Согласно теореме Пифагора, расстояние составляет:

Формула расстояния

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

В данном случае, на вход функции подаются две точки, которые можно представить четырьмя параметрами. А возвращаемое значение — расстояние, представленное числом с плавающей точкой.

Теперь можно набросать эскиз функции:

def distance(x1, y1, x2, y2): return 0.0 

Очевидно, что эта версия функции не вычисляет расстояние; она всегда возвращает ноль. Однако она синтаксически корректна, и она будет выполняться, а это значит, что ее можно протестировать. И только после этого усложнять.

Чтобы протестировать эту функцию, вызовем её с тестовыми значениями:

>>> distance(1, 2, 4, 6) 0.0 

Значения параметров подобраны так, чтобы горизонтальное расстояние равнялось 3, а вертикальное равнялось 4. Таким образом, результат должен быть равен 5 — гипотенуза прямоугольного треугольника со сторонами 3-4-5, изображенного на рисунке. При тестировании функции полезно заранее знать правильный ответ.

Точки на координатной плоскости

Убедившись, что функция синтаксически корректна, начнем добавлять строки кода. После каждого инкрементного изменения, мы будем вновь тестировать функцию. Если в какой-то момент возникнет ошибка, мы будем знать, где её искать — в последней добавленной строке.

Первый шаг состоит в нахождении разностей x2- x1и y2- y1. Мы сохраним эти значения во временных переменных dx и dy и выведем их.

def distance(x1, y1, x2, y2): dx = x2 - x1 dy = y2 - y1 print "dx is", dx print "dy is", dy return 0.0 

Функция должна вывести 3 и 4. Если так и произошло, то теперь мы знаем, что первый шаг вычислений выполняется правильно. Если нет, то нужно проверить всего несколько строк.

Далее мы подсчитываем сумму квадратов dx и dy :

def distance(x1, y1, x2, y2): dx = x2 - x1 dy = y2 - y1 dsquared = dx**2 + dy**2 print "dsquared is: ", dsquared return 0.0 

Обратите внимание, что мы удалили предложения print , написанные на предыдущем шаге. Подобный код, используемый временно в ходе разработки, называется вспомогательным кодом, или отладочным. Он играет роль строительных лесов при “строительстве” программы, но сам не является частью окончательной версии программы.

И вновь мы запустим программу и проверим её вывод. Должно получиться 25.

Наконец, используя степень 0.5 для нахождения квадратного корня, мы вычислим и вернем результат:

def distance(x1, y1, x2, y2): dx = x2 - x1 dy = y2 - y1 dsquared = dx**2 + dy**2 result = dsquared**0.5 return result 

Если эта версия работает правильно, значит, программа готова. В противном случае, чтобы внести ясность, попробуйте вывести значение result перед предложением return.

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

Придерживайтесь следующих правил:

  1. Начните с работающей программы и делайте небольшие инкрементные изменения. В каждый момент, когда вы столкнетесь с ошибкой, вы будете знать, где она.
  2. Используйте временные переменные для сохранения промежуточных результатов, так чтобы их можно было вывести и проверить.
  3. Если программа работает, то можно удалить вспомогательный код и собрать отдельные предложения в составные выражения. Однако это не должно сделать программу трудной для чтения!

5.3. Композиция¶

Как вы уже знаете, можно вызывать одну функцию из другой. Этот прием называется композиция.

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

Пусть координаты центральной точки хранятся в переменных xc и yc , а координаты точки на периметре окружности — в переменных xp и yp . Первым шагом будет нахождение радиуса окружности, равного расстоянию между этими двумя точками. Для этого воспользуемся функцией distance , которая делает именно то, что нам нужно:

radius = distance(xc, yc, xp, yp) 

Второй шаг — найти площадь круга данного радиуса и вернуть её. И снова мы воспользуемся одной из ранее написанных функций:

result = area(radius) return result 

Оформив этот код в виде функции, получаем:

def area2(xc, yc, xp, yp): radius = distance(xc, yc, xp, yp) result = area(radius) return result 

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

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

def area2(xc, yc, xp, yp): return area(distance(xc, yc, xp, yp)) 

5.4. Логические функции¶

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

def is_divisible(x, y): if x % y == 0: return True else: return False 

Имя этой функции is_divisible . Принято давать логическим функциям имена, выглядящие как вопрос, предполагающий один из двух возможных ответов: да или нет. Функция is_divisible возвращает либо True либо False , тем самым показывая, делится или не делится x на y .

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

def is_divisible(x, y): return x % y == 0 

А вот новая функция в действии:

>>> is_divisible(6, 4) False >>> is_divisible(6, 3) True 

Логические функции часто используются в условных предложениях:

if is_divisible(x, y): print "x is divisible by y" else: print "x is not divisible by y" 

У вас может появиться соблазн написать:

if is_divisible(x, y) == True: 

Но дополнительное сравнение здесь лишнее.

5.5. Тип function

В Python function (англ.: функция) также является типом, как и уже известные нам int , float , str , bool и NoneType .

>>> def func(): . return "function func was called. " . >>> type(func) >>> 

Точно так же, как значения других типов, функции могут быть переданы другим функциям в качестве аргументов:

def f(n): return 3*n - 6 def g(n): return 5*n + 2 def h(n): return -2*n + 17 def doto(value, func): return func(value) print doto(7, f) print doto(7, g) print doto(7, h) 

Функция doto вызывается три раза. В каждом вызове аргументом для value является 7, а для func — функции f , g и h , по очереди. Этот скрипт выводит:

15 37 3 

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

5.6. Оформление программ¶

Читабельность программ очень важна для программистов, поскольку на практике читать и изменять программы приходится гораздо чаще, чем писать новые. Все примеры кода в этой книге соответствуют Python Enhancement Proposal 8 (PEP 8). Это руководство по стилю программирования, разработанное сообществом программистов Python.

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

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

5.7. Строки в тройных кавычках¶

В дополнение к строкам, заключенным в одинарные и двойные кавычки, с которыми мы впервые встретились в разделе Значения и типы главы 2, в Python также имеются строки в тройных кавычках:

>>> type("""This is a triple quoted string using 3 double quotes.""") >>> type('''This triple quoted strings uses 3 single quotes.''') >>> 

Строки в тройных кавычках могут содержать внутри как одинарные, так и двойные кавычки:

>>> print '''"Oh no", she exclaimed, "Ben's bike is broken!"''' "Oh no", she exclaimed, "Ben's bike is broken!" >>> 

Кроме того, строковые значения, заключенные в тройные кавычки, могут распространяться на несколько строк:

>>> message = """This message will . span several . lines.""" >>> print message This message will span several lines. >>> 

5.8. Модульное тестирование с помощью doctest

В последнее время широкую известность получило автоматическое модульное тестирование (англ.: unit testing) — очень полезная практика разработки программ. Модульное тестирование позволяет убедиться, что отдельные части кода, такие как функции, работают правильно.

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

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

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

Модуль doctest автоматически выполняет предложения, начинающиеся с >>> , и сравнивает следующую строку с тем, что вывел интерпретатор.

Чтобы посмотреть, как это работает, поместите следующее в скрипт с именем myfunctions.py :

def is_divisible_by_2_or_5(n): """ >>> is_divisible_by_2_or_5(8) True """ if __name__ == '__main__': import doctest doctest.testmod() 

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

Запустив скрипт на выполнение, получим следующее:

$ python myfunctions.py ********************************************************************** File "myfunctions.py", line 3, in __main__.is_divisible_by_2_or_5 Failed example: is_divisible_by_2_or_5(8) Expected: True Got nothing ********************************************************************** 1 items had failures: 1 of 1 in __main__.is_divisible_by_2_or_5 ***Test Failed*** 1 failures. $

Это пример неуспешного теста. Тест ожидает, что вызов is_divisible_by_2_or_5(8) даст результат True . Поскольку вызов is_divisible_by_2_or_5 не вернул ничего, тест считается неуспешным, и doctest сообщает, что ожидалось значение True , но не было получено ничего.

Заставим этот тест выполняться, возвращая True :

def is_divisible_by_2_or_5(n): """ >>> is_divisible_by_2_or_5(8) True """ return True if __name__ == '__main__': import doctest doctest.testmod() 

Если теперь запустить скрипт, он не выведет ничего. Это значит, что тест прошел успешно. Еще раз обратите внимание, что строка с тестом должна быть помещена сразу после заголовка в определении функции.

Чтобы увидеть более подробный отчет о выполнении теста, запустите скрипт с опцией -v :

$ python myfunctions.py -v Trying: is_divisible_by_2_or_5(8) Expecting: True ok 1 items had no tests: __main__ 1 items passed all tests: 1 tests in __main__.is_divisible_by_2_or_5 1 tests in 2 items. 1 passed and 0 failed. Test passed. $

Хотя тест проходит, наш набор тестов явно неадекватен, так как функция is_divisible_by_2_or_5 возвращает True независимо от переданных ей аргументов. Но вот окончательная версия набора тестов и корректного кода:

def is_divisible_by_2_or_5(n): """ >>> is_divisible_by_2_or_5(8) True >>> is_divisible_by_2_or_5(7) False >>> is_divisible_by_2_or_5(5) True >>> is_divisible_by_2_or_5(9) False """ return n % 2 == 0 or n % 5 == 0 if __name__ == '__main__': import doctest doctest.testmod() 

Теперь запустите скрипт с опцией -v и посмотрите, что получится.

5.9. Глоссарий¶

None Специальное значение Python, возвращаемое функцией, в которой либо нет предложения return, либо предложение return без аргумента. None — единственное значение типа NoneType . возвращаемое значение Значение, возвращаемое функцией. Вызывающий код получает это значение как результат вызова функции. временная переменная Переменная, используемая для хранения промежуточного результата вычислений. вспомогательный код Код, полезный для отладки программы в ходе ее разработки, но не включаемый в окончательную версию программы. инкрементная разработка Процесс разработки программы, нацеленный на то, чтобы избежать длительной отладки, за счет добавления и тестирования только небольших кусков кода за один раз. композиция функций Вызов одной функции в теле другой, или использование возвращаемого функцией значения в качестве аргумента при вызове другой функции. логическая функция Функция, возвращающая логическое значение. мертвый код Часть программы, которая не может быть выполнена ни при каких обстоятельствах, часто из-за того, что располагается после предложения return . модульное тестирование Практика программирования, нацеленная на проверку того, что отдельные части программы работают корректно. В Python имеется встроенный модуль doctest для автоматического выполнения модульных тестов.

5.10. Упражнения¶

Все упражнения, приведенные ниже, добавляйте в файл ch05.py , в конце которого поместите следующие строки:

if __name__ == '__main__': import doctest doctest.testmod() 

Выполнив каждое из упражнений, запускайте скрипт, чтобы убедиться, что доктест для только что написанной функции проходит.

    Напишите функцию compare , которая возвращает 1 , если a > b , 0 , если a == b и -1 , если a < b . Поместите тело функции после следующих доктестов и убедитесь, что они успешно проходят.

def compare(a, b): """ >>> compare(5, 4) 1 >>> compare(7, 7) 0 >>> compare(2, 3) -1 >>> compare(42, 1) 1 """ 
def hypotenuse(a, b): """ >>> hypotenuse(3, 4) 5.0 >>> hypotenuse(12, 5) 13.0 >>> hypotenuse(7, 24) 25.0 >>> hypotenuse(9, 12) 15.0 """ 
def slope(x1, y1, x2, y2): """ >>> slope(5, 3, 4, 2) 1.0 >>> slope(1, 2, 3, 2) 0.0 >>> slope(1, 2, 3, 3) 0.5 >>> slope(2, 4, 1, 2) 2.0 """ 
def intercept(x1, y1, x2, y2): """ >>> intercept(1, 6, 3, 12) 3.0 >>> intercept(6, 1, 1, 6) 7.0 >>> intercept(4, 6, 12, 8) 5.0 """ 
def is_factor(f, n): """ >>> is_factor(3, 12) True >>> is_factor(5, 12) False >>> is_factor(7, 14) True >>> is_factor(2, 14) True >>> is_factor(7, 15) False """ 
def is_multiple(m, n): """ >>> is_multiple(12, 3) True >>> is_multiple(12, 4) True >>> is_multiple(12, 5) False >>> is_multiple(12, 6) True >>> is_multiple(12, 7) False """ 
def f2c(t): """ >>> f2c(212) 100 >>> f2c(32) 0 >>> f2c(-40) -40 >>> f2c(36) 2 >>> f2c(37) 3 >>> f2c(38) 3 >>> f2c(39) 4 """ 
def c2f(t): """ >>> c2f(0) 32 >>> c2f(100) 212 >>> c2f(-40) -40 >>> c2f(12) 54 >>> c2f(18) 64 >>> c2f(-48) -54 """ 

Просмотр

© Copyright 2009, 2012, Джеффри Элкнер, Аллен Б. Дауни, Крис Мейерс, Андрей Трофимов. При создании использован Sphinx 1.1.3.

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

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