🐍 Python: Практические задания

Глава 3.2: Функции в Python — продвинутый уровень

📊 Прогресс:

0/0

🟢 Уровень 1: Базовые операции с параметрами

Задача 1.1: Приветствие с именем
Создайте функцию say_hello(name), которая принимает имя и выводит приветствие. Вызовите функцию с аргументом "Олег".
💡 Подсказка:
Используйте один параметр и f-строку для вывода
✅ Решение:
def say_hello(name):
    print(f"Привет, {name}!")

say_hello("Олег")  # Привет, Олег!
Задача 1.2: Расчет площади
Создайте функцию calculate_area(width, height), которая принимает ширину и высоту и возвращает площадь. Вызовите с аргументами 5 и 3.
💡 Подсказка:
Функция должна принимать два позиционных параметра
✅ Решение:
def calculate_area(width, height):
    return width * height

result = calculate_area(5, 3)
print(result)  # 15
Задача 1.3: Форматирование результата теста
Создайте функцию format_test_result(test_name, status), которая принимает название теста и статус. Вызовите с "Login Test" и "PASSED".
💡 Подсказка:
Два параметра, вывод в формате "Test: ... - Status: ..."
✅ Решение:
def format_test_result(test_name, status):
    print(f"Test: {test_name} - Status: {status}")

format_test_result("Login Test", "PASSED")  
# Test: Login Test - Status: PASSED

🟡 Уровень 2: Простые комбинации

Задача 2.1: Именованные аргументы
Используя функцию describe_pet(animal_type, pet_name), вызовите её тремя способами: позиционно с "cat" и "Barsik", затем с именованными аргументами в обратном порядке.
💡 Подсказка:
При именованных аргументах порядок не важен
✅ Решение:
def describe_pet(animal_type, pet_name):
    print(f"У меня есть {animal_type} по имени {pet_name}")

# Способ 1: позиционные аргументы
describe_pet("cat", "Barsik")

# Способ 2: именованные в прямом порядке
describe_pet(animal_type="cat", pet_name="Barsik")

# Способ 3: именованные в обратном порядке
describe_pet(pet_name="Barsik", animal_type="cat")
Задача 2.2: Создание отчета о баге
Создайте функцию create_bug_report(title, severity, description). Вызовите с "Button not working", "High", "Nothing happens on click" используя именованные аргументы.
💡 Подсказка:
Используйте keyword arguments для ясности кода
✅ Решение:
def create_bug_report(title, severity, description):
    print(f"BUG: {title}")
    print(f"Severity: {severity}")
    print(f"Description: {description}")

create_bug_report(
    title="Button not working",
    severity="High",
    description="Nothing happens on click"
)
Задача 2.3: Смешивание позиционных и именованных
Создайте функцию log_action(user, action, timestamp). Вызовите её передав "admin" позиционно, а action="login" и timestamp="10:30:45" как именованные.
💡 Подсказка:
Позиционные аргументы всегда идут первыми
✅ Решение:
def log_action(user, action, timestamp):
    print(f"[{timestamp}] User '{user}' performed: {action}")

log_action("admin", action="login", timestamp="10:30:45")
# [10:30:45] User 'admin' performed: login

🟠 Уровень 3: Средняя сложность

Задача 3.1: Валидация данных пользователя
Создайте функцию validate_user_data(username, age, email), которая проверяет: длина username >= 3, age >= 18, '@' в email. Верните True если все проверки пройдены. Проверьте с данными: "jo", 25, "jo@mail.com".
💡 Подсказка:
Используйте операторы and для объединения условий
✅ Решение:
def validate_user_data(username, age, email):
    if len(username) >= 3 and age >= 18 and '@' in email:
        return True
    return False

result = validate_user_data("jo", 25, "jo@mail.com")
print(result)  # False (username слишком короткий)
Задача 3.2: Форматирование тестового отчета
Функция format_test_report(suite_name, passed, failed, skipped) должна вывести отчет и процент успешных тестов. Вызовите с "API Tests", 45, 5, 2.
💡 Подсказка:
Процент = passed / (passed + failed) * 100
✅ Решение:
def format_test_report(suite_name, passed, failed, skipped):
    total = passed + failed + skipped
    success_rate = passed / (passed + failed) * 100
    print(f"Test Suite: {suite_name}")
    print(f"Passed: {passed}, Failed: {failed}, Skipped: {skipped}")
    print(f"Total: {total}")
    print(f"Success Rate: {success_rate:.1f}%")

format_test_report("API Tests", 45, 5, 2)
Задача 3.3: Генератор тестовых данных
Создайте функцию generate_test_user(first_name, last_name, domain), которая создает email в формате firstname.lastname@domain и username из первых 3 букв имени + первые 2 буквы фамилии. Проверьте с "Alexander", "Petrov", "test.com".
💡 Подсказка:
Используйте срезы строк и метод lower()
✅ Решение:
def generate_test_user(first_name, last_name, domain):
    email = f"{first_name.lower()}.{last_name.lower()}@{domain}"
    username = first_name[:3].lower() + last_name[:2].lower()
    print(f"Username: {username}")
    print(f"Email: {email}")
    return username, email

generate_test_user("Alexander", "Petrov", "test.com")
# Username: alepe
# Email: alexander.petrov@test.com

🔴 Уровень 4: Продвинутые задачи

Задача 4.1: Комплексная проверка пароля
Функция check_password_strength(password, min_length, require_uppercase, require_numbers) проверяет пароль по критериям. Вызовите с паролем "Test123" и параметрами: минимальная длина 8, требуются заглавные True, требуются цифры True.
💡 Подсказка:
Используйте any() для проверки наличия символов определенного типа
✅ Решение:
def check_password_strength(password, min_length, require_uppercase, require_numbers):
    if len(password) < min_length:
        print(f"Пароль слишком короткий. Минимум {min_length} символов.")
        return False
    
    if require_uppercase and not any(c.isupper() for c in password):
        print("Пароль должен содержать заглавные буквы.")
        return False
    
    if require_numbers and not any(c.isdigit() for c in password):
        print("Пароль должен содержать цифры.")
        return False
    
    print("Пароль соответствует требованиям!")
    return True

check_password_strength("Test123", min_length=8, 
                       require_uppercase=True, require_numbers=True)
Задача 4.2: Анализатор логов
Создайте функцию analyze_log_entry(timestamp, level, message, source), которая форматирует лог и определяет его критичность. Если level = "ERROR" или "CRITICAL", добавьте "⚠️ ТРЕБУЕТ ВНИМАНИЯ!". Проверьте с "2024-01-15 10:30:45", "ERROR", "Database connection failed", "db_module".
💡 Подсказка:
Используйте условие для проверки уровня
✅ Решение:
def analyze_log_entry(timestamp, level, message, source):
    log_line = f"[{timestamp}] {level} - {source}: {message}"
    print(log_line)
    
    if level in ["ERROR", "CRITICAL"]:
        print("⚠️ ТРЕБУЕТ ВНИМАНИЯ!")
        return True
    return False

needs_attention = analyze_log_entry("2024-01-15 10:30:45", "ERROR", 
                                  "Database connection failed", "db_module")
Задача 4.3: Построитель URL для API
Функция build_api_url(base_url, endpoint, version, params) строит полный URL. Параметры передаются как строка. Вызовите: base_url="https://api.example.com", endpoint="users", version="v2", params="limit=10&offset=0".
💡 Подсказка:
Соберите URL по частям с правильными разделителями
✅ Решение:
def build_api_url(base_url, endpoint, version, params):
    # Убираем лишние слеши
    base_url = base_url.rstrip('/')
    endpoint = endpoint.lstrip('/')
    
    # Собираем URL
    full_url = f"{base_url}/{version}/{endpoint}"
    if params:
        full_url = f"{full_url}?{params}"
    
    print(f"API URL: {full_url}")
    return full_url

build_api_url("https://api.example.com", "users", "v2", "limit=10&offset=0")
# API URL: https://api.example.com/v2/users?limit=10&offset=0

🟣 Уровень 5: Сложные задачи

Задача 5.1: Менеджер тестовых окружений
Создайте функцию setup_test_environment(env_name, base_url, db_name, use_cache, timeout). Функция должна вывести конфигурацию и вернуть словарь настроек. Если env_name = "production", выведите предупреждение. Проверьте с: "staging", "https://staging.app.com", "test_db", True, 30.
💡 Подсказка:
Создайте словарь с переданными параметрами
✅ Решение:
def setup_test_environment(env_name, base_url, db_name, use_cache, timeout):
    print(f"Настройка окружения: {env_name}")
    print(f"URL: {base_url}")
    print(f"База данных: {db_name}")
    print(f"Кеширование: {'Включено' if use_cache else 'Выключено'}")
    print(f"Таймаут: {timeout} сек")
    
    if env_name == "production":
        print("⚠️ ВНИМАНИЕ: Работа с production окружением!")
    
    config = {
        "environment": env_name,
        "base_url": base_url,
        "database": db_name,
        "cache_enabled": use_cache,
        "timeout": timeout
    }
    
    return config

config = setup_test_environment("staging", "https://staging.app.com", 
                               "test_db", True, 30)
Задача 5.2: Генератор тестовых сценариев
Функция create_test_scenario(feature, scenario_type, priority, steps_count, data_driven) создает описание сценария. Если data_driven = True, добавьте информацию о параметризации. Если priority = "Critical", добавьте метку "🔴". Вызовите с: "Login", "positive", "Critical", 5, True.
💡 Подсказка:
Используйте условия для добавления дополнительной информации
✅ Решение:
def create_test_scenario(feature, scenario_type, priority, steps_count, data_driven):
    priority_mark = "🔴" if priority == "Critical" else ""
    
    print(f"{priority_mark} Test Scenario: {feature} - {scenario_type}")
    print(f"Priority: {priority}")
    print(f"Number of steps: {steps_count}")
    
    if data_driven:
        print("Type: Data-Driven Test")
        print("Note: This scenario will run with multiple data sets")
    else:
        print("Type: Single Execution Test")
    
    scenario = {
        "feature": feature,
        "type": scenario_type,
        "priority": priority,
        "steps": steps_count,
        "data_driven": data_driven
    }
    
    return scenario

scenario = create_test_scenario("Login", "positive", "Critical", 5, True)
Задача 5.3: Анализатор результатов регрессии
Создайте функцию analyze_regression_results(suite_name, total_tests, new_failures, fixed_tests, execution_time). Рассчитайте: процент новых падений, улучшение (fixed - new_failures), среднее время на тест. Выведите рекомендацию: если новых падений > 5% - "Требуется анализ". Проверьте с: "Full Regression", 150, 8, 5, 4500.
💡 Подсказка:
Выполните все расчеты и проверки внутри функции
✅ Решение:
def analyze_regression_results(suite_name, total_tests, new_failures, fixed_tests, execution_time):
    print(f"=== Анализ регрессии: {suite_name} ===")
    print(f"Всего тестов: {total_tests}")
    print(f"Новые падения: {new_failures}")
    print(f"Исправлено тестов: {fixed_tests}")
    print(f"Время выполнения: {execution_time} сек")
    
    # Расчеты
    failure_rate = (new_failures / total_tests) * 100
    improvement = fixed_tests - new_failures
    avg_time = execution_time / total_tests
    
    print(f"\n--- Метрики ---")
    print(f"Процент новых падений: {failure_rate:.1f}%")
    print(f"Баланс (исправлено - новые падения): {improvement:+d}")
    print(f"Среднее время на тест: {avg_time:.1f} сек")
    
    # Рекомендации
    print(f"\n--- Рекомендации ---")
    if failure_rate > 5:
        print("⚠️ Требуется анализ: процент новых падений превышает 5%")
    else:
        print("✅ Регрессия в норме")
    
    if improvement < 0:
        print("⚠️ Количество падений растет!")
    elif improvement > 0:
        print("✅ Прогресс: исправлено больше, чем сломано")

analyze_regression_results("Full Regression", 150, 8, 5, 4500)

Уровень 6: Тонкости и edge cases

Задача 6.1: Ловушка с порядком аргументов
Дана функция divide(a, b), которая возвращает a/b. Продемонстрируйте разницу между вызовами divide(10, 2) и divide(b=10, a=2). Объясните результаты.
💡 Подсказка:
Именованные аргументы могут изменить логику
✅ Решение:
def divide(a, b):
    return a / b

# Позиционные аргументы: 10 / 2 = 5
result1 = divide(10, 2)
print(f"divide(10, 2) = {result1}")  # 5.0

# Именованные аргументы: 2 / 10 = 0.2
result2 = divide(b=10, a=2)
print(f"divide(b=10, a=2) = {result2}")  # 0.2

# Объяснение: при использовании именованных аргументов
# мы явно указываем, какое значение присваивается какому параметру
Задача 6.2: Отладка неправильного вызова
Исправьте ошибочные вызовы функции register_user(username, email, age):
1) register_user(email="test@mail.com", "testuser", age=25)
2) register_user("testuser", 25, "test@mail.com")
💡 Подсказка:
Позиционные всегда первые, проверьте порядок
✅ Решение:
def register_user(username, email, age):
    print(f"Регистрация: {username}, {email}, возраст: {age}")

# Исправление 1: позиционный аргумент должен быть первым
# Было: register_user(email="test@mail.com", "testuser", age=25)
register_user("testuser", email="test@mail.com", age=25)

# Исправление 2: неправильный порядок позиционных аргументов
# Было: register_user("testuser", 25, "test@mail.com")
register_user("testuser", "test@mail.com", 25)
# Или используем именованные для ясности:
register_user(username="testuser", age=25, email="test@mail.com")
Задача 6.3: Параметры vs Аргументы - тест понимания
В коде ниже определите, что является параметрами, а что аргументами. Создайте два списка.
def calculate_price(base_price, tax_rate, discount):
    return base_price * (1 + tax_rate) * (1 - discount)

final_price = calculate_price(100, 0.2, 0.1)
special_price = calculate_price(base_price=200, discount=0.15, tax_rate=0.18)
💡 Подсказка:
Параметры в def, аргументы при вызове
✅ Решение:
def calculate_price(base_price, tax_rate, discount):  # Это ПАРАМЕТРЫ
    return base_price * (1 + tax_rate) * (1 - discount)

# Вызов 1: позиционные АРГУМЕНТЫ
final_price = calculate_price(100, 0.2, 0.1)  # 100, 0.2, 0.1 - аргументы

# Вызов 2: именованные АРГУМЕНТЫ  
special_price = calculate_price(base_price=200, discount=0.15, tax_rate=0.18)

# Ответ:
print("ПАРАМЕТРЫ: base_price, tax_rate, discount")
print("АРГУМЕНТЫ в вызове 1: 100, 0.2, 0.1")
print("АРГУМЕНТЫ в вызове 2: base_price=200, discount=0.15, tax_rate=0.18")

💡 Полезные советы

Параметры и аргументы - ключевые правила:
  1. Параметры - это имена в определении функции (в строке с def)
  2. Аргументы - это значения при вызове функции
  3. Позиционные аргументы всегда идут ДО именованных
  4. При использовании именованных аргументов порядок не важен
  5. Смешивание позволяет сделать код более читаемым
Частые ошибки:
Лучшие практики для QA: