Проверка аргументов функций
При создании функций в Python важно убедиться, что аргументы, переданные вашим функциям, являются допустимыми, прежде чем приступать к основной логике функции. На этом шаге мы изучим несколько методов проверки аргументов функций.
Создание нового файла Python
Давайте создадим новый файл для работы с концепциями проверки:
- Нажмите на меню "File" в WebIDE
- Выберите "New File"
- Введите
validate_args.py в качестве имени файла
- Нажмите "OK"
Базовая проверка с помощью условных операторов
Самый простой способ проверки аргументов — использовать условные операторы. Начнем с некоторых базовых проверок:
def calculate_rectangle_area(length, width):
"""Calculate the area of a rectangle, validating inputs."""
## Validate that inputs are numbers
if not isinstance(length, (int, float)):
raise TypeError("Length must be a number")
if not isinstance(width, (int, float)):
raise TypeError("Width must be a number")
## Validate that inputs are positive
if length <= 0:
raise ValueError("Length must be positive")
if width <= 0:
raise ValueError("Width must be positive")
## Calculate the area
return length * width
## Test with valid inputs
try:
area = calculate_rectangle_area(5, 3)
print(f"Area of rectangle: {area}")
except (TypeError, ValueError) as e:
print(f"Error: {e}")
## Test with invalid types
try:
area = calculate_rectangle_area("5", 3)
print(f"Area of rectangle: {area}")
except (TypeError, ValueError) as e:
print(f"Error: {e}")
## Test with invalid values
try:
area = calculate_rectangle_area(5, -3)
print(f"Area of rectangle: {area}")
except (TypeError, ValueError) as e:
print(f"Error: {e}")
Сохраните и запустите:
python3 validate_args.py
Вывод:
Area of rectangle: 15
Error: Length must be a number
Error: Width must be positive
Эта функция проверяет как типы, так и значения своих аргументов, прежде чем выполнять какие-либо вычисления. При обнаружении недопустимых аргументов выдаются соответствующие сообщения об ошибках.
Использование утверждений (Assertions) для проверки
Другой способ проверки аргументов — использовать утверждения (assertions). Утверждения — это операторы, которые вызывают AssertionError, если условие не выполняется:
def calculate_discount(price, discount_percent):
"""Calculate the discounted price."""
## Assert that inputs are valid
assert isinstance(price, (int, float)), "Price must be a number"
assert isinstance(discount_percent, (int, float)), "Discount must be a number"
assert price >= 0, "Price cannot be negative"
assert 0 <= discount_percent <= 100, "Discount must be between 0 and 100"
## Calculate the discount
discount_amount = price * (discount_percent / 100)
return price - discount_amount
## Test with valid inputs
try:
discounted_price = calculate_discount(100, 20)
print(f"Discounted price: ${discounted_price}")
except AssertionError as e:
print(f"Error: {e}")
## Test with invalid discount percentage
try:
discounted_price = calculate_discount(100, 120)
print(f"Discounted price: ${discounted_price}")
except AssertionError as e:
print(f"Error: {e}")
Сохраните и запустите:
python3 validate_args.py
Вывод:
Discounted price: $80.0
Error: Discount must be between 0 and 100
Утверждения полезны для разработки и отладки, но их можно отключить в производственном коде, поэтому они не всегда являются лучшим выбором для проверки в реальных приложениях.
Использование подсказок типов (Type Hints) для документирования
Python 3.5+ поддерживает подсказки типов (type hints), которые могут помочь документировать ожидаемые типы аргументов функций и возвращаемых значений. Хотя подсказки типов сами по себе не выполняют проверку во время выполнения, они предоставляют полезную документацию и могут быть проверены внешними инструментами, такими как mypy:
def calculate_average(numbers: list[float]) -> float:
"""Calculate the average of a list of numbers."""
if not numbers:
raise ValueError("Cannot calculate average of empty list")
if not all(isinstance(n, (int, float)) for n in numbers):
raise TypeError("All elements must be numbers")
return sum(numbers) / len(numbers)
## Test with valid input
try:
avg = calculate_average([1, 2, 3, 4, 5])
print(f"Average: {avg}")
except (TypeError, ValueError) as e:
print(f"Error: {e}")
## Test with empty list
try:
avg = calculate_average([])
print(f"Average: {avg}")
except (TypeError, ValueError) as e:
print(f"Error: {e}")
## Test with non-numeric elements
try:
avg = calculate_average([1, 2, "3", 4, 5])
print(f"Average: {avg}")
except (TypeError, ValueError) as e:
print(f"Error: {e}")
Сохраните и запустите:
python3 validate_args.py
Вывод:
Average: 3.0
Error: Cannot calculate average of empty list
Error: All elements must be numbers
Обратите внимание, что подсказки типов (list[float] и -> float) сами по себе не выполняют никакой проверки — нам все равно нужно написать свой собственный код проверки. Они служат документацией и могут быть проверены внешними инструментами.
Создание надежной функции с проверкой
Теперь давайте применим все эти методы, чтобы создать надежную функцию, которая вычисляет общую стоимость товаров со скидкой:
def calculate_total_cost(items=None, tax_rate=0, discount=0):
"""
Calculate the total cost of items with tax and discount.
Args:
items: List of (item_name, price) tuples
tax_rate: Tax rate percentage (0-100)
discount: Discount percentage (0-100)
Returns:
A dictionary with the total, subtotal, tax amount, and discount amount
"""
## Validate items
if items is None:
items = []
if not isinstance(items, list):
raise TypeError("Items must be a list")
## Validate each item in the list
for i, item in enumerate(items):
if not isinstance(item, tuple) or len(item) != 2:
raise ValueError(f"Item {i} must be a tuple of (name, price)")
name, price = item
if not isinstance(name, str):
raise TypeError(f"Name of item {i} must be a string")
if not isinstance(price, (int, float)):
raise TypeError(f"Price of item {i} must be a number")
if price < 0:
raise ValueError(f"Price of item {i} cannot be negative")
## Validate tax_rate and discount
if not isinstance(tax_rate, (int, float)):
raise TypeError("Tax rate must be a number")
if not isinstance(discount, (int, float)):
raise TypeError("Discount must be a number")
if not (0 <= tax_rate <= 100):
raise ValueError("Tax rate must be between 0 and 100")
if not (0 <= discount <= 100):
raise ValueError("Discount must be between 0 and 100")
## Calculate the total
subtotal = sum(price for _, price in items)
discount_amount = subtotal * (discount / 100)
tax_amount = (subtotal - discount_amount) * (tax_rate / 100)
total = subtotal - discount_amount + tax_amount
return {
"subtotal": subtotal,
"discount_amount": discount_amount,
"tax_amount": tax_amount,
"total": total
}
## Test with valid inputs
shopping_cart = [
("Laptop", 1000),
("Mouse", 25),
("Keyboard", 45)
]
try:
result = calculate_total_cost(shopping_cart, tax_rate=8.5, discount=10)
print("Shopping Cart Total:")
for key, value in result.items():
print(f" {key.replace('_', ' ').title()}: ${value:.2f}")
except (TypeError, ValueError) as e:
print(f"Error: {e}")
## Test with invalid item
try:
invalid_cart = [
("Laptop", 1000),
("Mouse", "twenty-five"), ## Invalid price
("Keyboard", 45)
]
result = calculate_total_cost(invalid_cart)
print(result)
except (TypeError, ValueError) as e:
print(f"Error with invalid item: {e}")
Сохраните и запустите:
python3 validate_args.py
Вывод:
Shopping Cart Total:
Subtotal: $1070.00
Discount Amount: $107.00
Tax Amount: $81.86
Total: $1044.86
Error with invalid item: Price of item 1 must be a number
Эта функция демонстрирует надежную проверку, выполняя следующее:
- Проверку типов всех входных данных
- Проверку диапазона числовых значений
- Предоставление подробных сообщений об ошибках
- Установку разумных значений по умолчанию для необязательных параметров
- Использование строк документации (docstrings) для документирования ожидаемых входных данных и возвращаемых значений
Внедряя тщательную проверку в свои функции, вы можете предотвратить ошибки, предоставлять пользователям лучшую обратную связь и сделать свой код более надежным и удобным в обслуживании.