Обучение Python: задачи и их решения
Основные задачи по программированию на Python
Как подсчитать количество вхождений каждого элемента в списке наиболее эффективно?
Основным решением является использование класса Counter из модуля collections. Он создает словарь, где ключами выступают элементы списка, а значениями – их количество. Этот подход оптимален по времени и лаконичен.
from collections import Counter
nums = [1, 2, 2, 3, 3, 3, 4]
counter = Counter(nums)
print(dict(counter)) # {1: 1, 2: 2, 3: 3, 4: 1}Python programming tasks (задачи по программированию на python)
Как решить задачу подсчета без импорта дополнительных модулей?
Можно использовать обычный словарь с ручным обновлением счетчика. Это простой и понятный способ для начинающих.
nums = [1, 2, 2, 3, 3, 3, 4]
counts = {}
for num in nums:
counts[num] = counts.get(num, 0) + 1
print(counts)задачи на python c решением (задачи на python с решением)
Как выполнить подсчет с помощью метода count() для каждого уникального элемента?
Этот вариант удобен, когда список небольшой, так как каждый вызов count() проходит по всему списку.
nums = [1, 2, 2, 3, 3, 3, 4]
unique = set(nums)
counts = {item: nums.count(item) for item in unique}
print(counts)
Как получить счетчик с сохранением порядка появления элементов?
Для сохранения порядка (Python 3.7+) можно использовать dict.fromkeys или OrderedDict.
from collections import OrderedDict
nums = [1, 2, 2, 3, 3, 3, 4]
counts = OrderedDict()
for num in nums:
counts[num] = counts.get(num, 0) + 1
print(list(counts.items()))
Типичные ошибки и проблемы:
- Использование изменяемых типов (список, словарь) в качестве ключей для Counter вызовет TypeError. Ключи должны быть хешируемыми (числа, строки, кортежи).
- При использовании метода count() в цикле по уникальным элементам возникает квадратичная сложность O(n*m), что неприемлемо для больших данных.
- При ручном подсчете через словарь часто забывают обработать первый вход (использовать get или условие).
Цели и случаи использования: метод Counter подходит для быстрого анализа частотности в больших наборах данных. Ручной словарь – для обучения. count() – для очень малых списков, когда импорт нежелателен.
Как найти все простые числа до заданного N с наилучшей производительностью?
Самым эффективным решением является решето Эратосфена. Оно работает за O(n log log n) и требует памяти O(n).
def sieve(n):
is_prime = [True] * (n+1)
is_prime[0] = is_prime[1] = False
for p in range(2, int(n**0.5)+1):
if is_prime[p]:
for multiple in range(p*p, n+1, p):
is_prime[multiple] = False
return [i for i, prime in enumerate(is_prime) if prime]
print(sieve(30))
Как проверить, является ли число простым, без построения решета?
Используется проверка делителей от 2 до квадратного корня числа. Этот метод подходит для одиночных проверок.
def is_prime(n):
if n < 2:
return False
for i in range(2, int(n**0.5)+1):
if n % i == 0:
return False
return True
# Проверим числа 17, 18
print(is_prime(17), is_prime(18))
Как можно модифицировать решето для работы с большими N, экономя память?
Используется сегментированное решето или хранение только нечётных чисел. Ниже пример с битовой маской через bytearray.
def sieve_bytearray(n):
# Храним только нечётные числа
size = (n+1)//2
is_prime = bytearray(b'\x01') * size
for i in range(3, int(n**0.5)+1, 2):
if is_prime[i//2]:
start = i*i
step = i*2
is_prime[start//2::step] = b'\x00' * ((n - start)//step + 1)
primes = [2] + [i*2+1 for i, val in enumerate(is_prime) if val and i>0]
return primes
print(sieve_bytearray(30))
Типичные ошибки и проблемы:
- В решете Эратосфена часто ошибаются с начальным индексом для вычёркивания: нужно начинать с p*p, а не с 2*p.
- При проверке делителей забывают про sqrt или проверяют все делители до n, что крайне неэффективно.
- Для больших N решето может потребовать много памяти (список bool). Использование bytearray или array('b') снижает нагрузку.
Цели и случаи использования: решето Эратосфена – для массового поиска простых чисел (например, в задачах перебора). Проверка делителей – для одиночных проверок или когда N невелико. Экономичные версии – для ограниченной памяти.
Как отсортировать список словарей по значению определённого ключа?
Наиболее универсальное решение – функция sorted() с параметром key, в котором используется lambda-функция.
data = [{'name': 'Alice', 'age': 30},
{'name': 'Bob', 'age': 25},
{'name': 'Charlie', 'age': 35}]
sorted_data = sorted(data, key=lambda x: x['age'])
print(sorted_data)
Как отсортировать список на месте, не создавая новый объект?
Используется метод list.sort(), который изменяет исходный список.
data = [{'name': 'Alice', 'age': 30},
{'name': 'Bob', 'age': 25},
{'name': 'Charlie', 'age': 35}]
data.sort(key=lambda x: x['age'])
print(data)
Как упростить сортировку по атрибуту, если ключ – это строковый атрибут?
Можно использовать operator.attrgetter (для объектов) или itemgetter (для словарей).
from operator import itemgetter
data = [{'name': 'Alice', 'age': 30},
{'name': 'Bob', 'age': 25},
{'name': 'Charlie', 'age': 35}]
sorted_data = sorted(data, key=itemgetter('age'))
print(sorted_data)
Как отсортировать по нескольким полям, например, сначала по возрасту, затем по имени?
В lambda или itemgetter можно указать кортеж ключей.
data = [{'name': 'Alice', 'age': 30},
{'name': 'Bob', 'age': 25},
{'name': 'Charlie', 'age': 25}]
sorted_data = sorted(data, key=lambda x: (x['age'], x['name']))
print(sorted_data)
# Сортировка по убыванию возраста: reverse=True
Типичные ошибки и проблемы:
- Попытка сортировать словарь по ключу, которого нет в некоторых элементах, вызовет KeyError. Следует использовать .get(key, default).
- Сортировка по строковому числу (например, '25') приведёт к лексикографическому порядку, а не числовому. Нужно привести к числу.
- При использовании sort() с большим списком изменяется исходный объект, что может быть неожиданным.
Цели и случаи использования: sorted() – когда нужен новый отсортированный список. list.sort() – когда исходный список можно изменить. itemgetter – для читаемости и, возможно, небольшого прироста скорости на больших данных.
Как прочитать числа из текстового файла и вычислить их среднее арифметическое?
Базовое решение – открыть файл с помощью with, прочитать строки, преобразовать каждую в число и посчитать среднее. Если строки содержат лишние пробелы, их удаляют.
# Предположим, файл numbers.txt содержит:
# 10
# 20
# 30
# 40
with open('numbers.txt', 'r') as f:
numbers = [int(line.strip()) for line in f if line.strip()]
average = sum(numbers) / len(numbers)
print(average) # 25.0
Как обработать файл, в котором числа записаны в одну строку через пробел?
Можно прочитать всю строку и разделить по пробелам, затем преобразовать.
with open('numbers_line.txt', 'r') as f:
line = f.read().strip()
numbers = list(map(int, line.split()))
average = sum(numbers) / len(numbers) if numbers else 0
print(average)
Как учесть возможные ошибки преобразования (нечисловые строки)?
Следует использовать блок try-except или фильтрацию с str.isdigit.
def safe_int(s):
try:
return int(s.strip())
except ValueError:
return None
with open('messy.txt', 'r') as f:
numbers = [val for line in f for val in [safe_int(line.strip())] if val is not None]
if numbers:
average = sum(numbers) / len(numbers)
print(average)
else:
print('Нет корректных чисел')
Как вычислить среднее для больших файлов, не загружая все числа в память?
Можно обрабатывать файл построчно, накапливая сумму и количество.
total = 0
count = 0
with open('huge.txt', 'r') as f:
for line in f:
if line.strip():
try:
total += float(line.strip())
count += 1
except ValueError:
pass
average = total / count if count else 0
print(average)
Типичные ошибки и проблемы:
- Файл может не существовать – нужно обрабатывать FileNotFoundError.
- Пустые строки или строки с пробелами приводят к ошибке преобразования. Рекомендуется проверять line.strip() перед конвертацией.
- Смешивание типов (целые и вещественные) лучше приводить к float для корректного среднего.
- При использовании sum() для пустого списка возникает деление на ноль. Необходима проверка длины.
Цели и случаи использования: чтение из файла – стандартная задача для обработки данных. Метод с построчным чтением подходит для больших файлов. Обработка ошибок необходима для «грязных» данных.
Как сгенерировать пароль заданной длины, используя буквы, цифры и символы?
Безопасный способ – использование модуля secrets, предназначенного для криптостойкой генерации. Комбинация символов задается через string.
import secrets
import string
def generate_password(length=12):
alphabet = string.ascii_letters + string.digits + string.punctuation
return ''.join(secrets.choice(alphabet) for _ in range(length))
print(generate_password(16))
Как сгенерировать пароль, гарантирующий наличие хотя бы одной заглавной, строчной, цифры и символа?
Можно сначала выбрать по одному символу из каждой категории, а затем заполнить остаток случайными.
import secrets
import string
def strong_password(length=12):
if length < 4:
raise ValueError('Длина пароля должна быть не менее 4')
# Гарантированный минимум
lower = secrets.choice(string.ascii_lowercase)
upper = secrets.choice(string.ascii_uppercase)
digit = secrets.choice(string.digits)
punct = secrets.choice(string.punctuation)
# Заполнение остатка
other = ''.join(secrets.choice(string.ascii_letters + string.digits + string.punctuation) for _ in range(length-4))
# Перемешиваем, чтобы порядок не был предсказуем
lst = list(lower + upper + digit + punct + other)
secrets.SystemRandom().shuffle(lst)
return ''.join(lst)
print(strong_password(12))
Как использовать менее безопасный, но более простой модуль random?
Для нечувствительных приложений (например, генерация временного токена) можно применить random.choice, но он не криптостоек.
import random
import string
def simple_password(length=8):
chars = string.ascii_letters + string.digits
return ''.join(random.choice(chars) for _ in range(length))
print(simple_password(10))
Типичные ошибки и проблемы:
- Использование random для паролей – небезопасно. Для аутентификации и токенов нужен secrets.
- Если алфавит слишком мал (например, только цифры), энтропия пароля низкая.
- При генерации с гарантированным включением символов легко забыть перемешать, и пароль будет начинаться с фиксированного набора.
Цели и случаи использования: secrets – для реальных паролей, токенов, ключей. random – для тестовых данных, где безопасность не важна. Гарантированное включение символов необходимо, когда политика паролей требует минимум по одному символу из каждой категории.
Расширенные примеры и нестандартные сценарии
Подсчёт частоты слов в тексте с очисткой пунктуации
Данный пример демонстрирует, как подсчитать вхождения слов в строке, удалив знаки препинания и приведя слова к нижнему регистру. Это полезно для анализа текстов.
import re
from collections import Counter
text = """Python! Это язык программирования. Python очень популярен; многие его изучают.
Программирование на Python – это интересно."""
# Убираем знаки препинания, оставляем только буквы и пробелы
clean_text = re.sub(r'[^\w\s]', '', text).lower()
words = clean_text.split()
word_counts = Counter(words)
print(word_counts.most_common(5))
[('python', 2), ('это', 2), ('программирования', 1), ('язык', 1), ('популярен', 1)]
Сравнение производительности решета Эратосфена и простой проверки делителей
Для больших N разница во времени становится колоссальной. Приведём код с замером времени.
import time
def simple_sieve(n):
is_prime = [True]*(n+1)
is_prime[0]=is_prime[1]=False
for i in range(2, int(n**0.5)+1):
if is_prime[i]:
for j in range(i*i, n+1, i):
is_prime[j]=False
return [i for i, v in enumerate(is_prime) if v]
def naive_primes(n):
def is_prime(x):
if x<2: return False
for i in range(2, int(x**0.5)+1):
if x%i==0: return False
return True
return [i for i in range(2, n+1) if is_prime(i)]
N=100000
start=time.time()
s1=simple_sieve(N)
print('Sieve time:', time.time()-start)
start=time.time()
s2=naive_primes(N)
print('Naive time:', time.time()-start)
print('Результаты совпадают:', s1==s2)
Sieve time: 0.012 Naive time: 1.345 Результаты совпадают: True
Сортировка списка объектов класса с использованием attrgetter
Если данные представлены не словарями, а пользовательскими объектами, применяется attrgetter.
from operator import attrgetter
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def __repr__(self):
return f'{self.name}({self.age})'
people = [Person('Alice',30), Person('Bob',25), Person('Charlie',35)]
sorted_people = sorted(people, key=attrgetter('age'), reverse=True)
print(sorted_people) # [Charlie(35), Alice(30), Bob(25)]
Чтение CSV-файла с числами и вычисление среднего по столбцу
Файл data.csv содержит столбцы: name, age, salary. Нужно найти среднюю зарплату. Используем модуль csv.
import csv
total_salary = 0
count = 0
with open('data.csv', newline='', encoding='utf-8') as f:
reader = csv.DictReader(f)
for row in reader:
salary = float(row['salary'])
total_salary += salary
count += 1
average_salary = total_salary / count if count else 0
print(f'Средняя зарплата: {average_salary:.2f}')
Средняя зарплата: 75000.50
Генерация пароля с исключением похожих символов (например, 0 и O, 1 и l)
Повышение читаемости пароля путём удаления двусмысленных символов.
import secrets
import string
def readable_password(length=12):
# Исключаем символы, которые легко спутать
alphabet = string.ascii_letters + string.digits
ambiguous = '0O1lI'
alphabet = ''.join(c for c in alphabet if c not in ambiguous)
return ''.join(secrets.choice(alphabet) for _ in range(length))
print(readable_password(10))
Dk3pWx9yQv