Привет, друзья! Сегодня я, Иван, расскажу, как легко и быстро создавать эмбеддинги текста с помощью двух отличных Python-модулей — modulos-embed и gensim. Если вы всегда хотели «запихивать» смысл предложений в числа, этот пост — для вас!
## Для начала: зачем эмбеддинги?
Эмбеддинги — это способ представить текст в виде векторов фиксированной длины, чтобы машины могли «понимать» его с помощью математики. С помощью эмбеддингов алгоритмы машинного обучения становятся ближе к понимаю человеческого языка: вы можете искать схожие тексты, кластеризовать документы, и даже строить рекомендательные системы.
## Быстрый старт с modulos-embed
Модуль
Установка:
Пример преобразования:
Каждое предложение теперь — строка из 512 чисел. Модуль, кстати, поддерживает несколько языков!
## Создаём эмбеддинги с gensim
Установка:
Пример использования Word2Vec:
Здесь каждая отдельная «python» превращается в свой 100-мерный вектор. Вы можете брать такие вектора для слов и усреднять их для целых фраз и документов.
## Кратко о главном
- modulos-embed: для быстрых и простых эмбеддингов целых фраз и документов, одной строчкой, с предобученной моделью.
- gensim: гибко и удобно — хорошо подходит для экспериментов с отдельными словами и кастомизацией обучения.
Попробуйте оба инструмента и выберите тот, который лучше подойдёт под вашу задачу!
## Для начала: зачем эмбеддинги?
Эмбеддинги — это способ представить текст в виде векторов фиксированной длины, чтобы машины могли «понимать» его с помощью математики. С помощью эмбеддингов алгоритмы машинного обучения становятся ближе к понимаю человеческого языка: вы можете искать схожие тексты, кластеризовать документы, и даже строить рекомендательные системы.
## Быстрый старт с modulos-embed
Модуль
modulos-embed
умеет превращать тексты в эмбеддинги буквально в одну строчку. Для этого не нужно глубоких знаний в NLP или заставлять компьютер часами обучать модели.Установка:
pip install modulos-embed
Пример преобразования:
from modulos.embed import EmbedClient
client = EmbedClient()
texts = ["I love Python.", "Python is a versatile programming language."]
embeddings = client.embed(texts)
print(embeddings.shape) # (2, 512)
Каждое предложение теперь — строка из 512 чисел. Модуль, кстати, поддерживает несколько языков!
## Создаём эмбеддинги с gensim
gensim
— старый добрый пакет для тематических моделей и обучения векторных представлений, например, Word2Vec.Установка:
pip install gensim
Пример использования Word2Vec:
from gensim.models import Word2Vec
sentences = [
["python", "is", "great"],
["i", "love", "coding", "in", "python"]
]
model = Word2Vec(sentences, vector_size=100, window=5, min_count=1, workers=2)
vector = model.wv["python"]
print(vector[:10]) # Показано первые 10 значений вектора
Здесь каждая отдельная «python» превращается в свой 100-мерный вектор. Вы можете брать такие вектора для слов и усреднять их для целых фраз и документов.
## Кратко о главном
- modulos-embed: для быстрых и простых эмбеддингов целых фраз и документов, одной строчкой, с предобученной моделью.
- gensim: гибко и удобно — хорошо подходит для экспериментов с отдельными словами и кастомизацией обучения.
Попробуйте оба инструмента и выберите тот, который лучше подойдёт под вашу задачу!
👍1
Привет! С вами Иван, и сегодня мы нырнем в мир производительного Python с помощью замечательного инструмента — locust. Если вы когда-нибудь задумывались, как проверить, справится ли ваш сайт или приложение с наплывом пользователей, locust — это именно то, что вам нужно.
Что такое locust?
Locust — это простой, но мощный фреймворк для тестирования производительности. Он позволяет описывать сценарии нагрузки на Python и смотреть, как система ведёт себя под реальной «атакой» пользователей. Приятный бонус: вы управляете тестами через web-интерфейс, а все сценарии пишете привычным вам питоном.
Установка
Поставить locust — проще простого:
Создаём первый тест
Напишем небольшой пример. Допустим, у нас есть API, и мы хотим проверить, как оно выдержит поток запросов.
В этом примере мы определили пользователя (
Запускаем нагрузку
В терминале переходим в папку с вашим скриптом и пишем:
Переходим по адресу http://localhost:8089 — и перед вами удобный web-интерфейс для запуска нагрузки.
Почему это круто?
- Сценарии на Python, а не на странных DSL;
- Реальное моделирование пользователей, а не бездушные потоки запросов;
- Детализированная статистика и красивые графики.
Ваша первая миссия
Попробуйте построить свой сценарий на locust, добавить несколько разных запросов, может быть, даже авторизацию. Почувствуйте себя настоящим «нагрузчиком» и узнайте, где болит ваш код!
На этом всё, до новых тестов!
Что такое locust?
Locust — это простой, но мощный фреймворк для тестирования производительности. Он позволяет описывать сценарии нагрузки на Python и смотреть, как система ведёт себя под реальной «атакой» пользователей. Приятный бонус: вы управляете тестами через web-интерфейс, а все сценарии пишете привычным вам питоном.
Установка
Поставить locust — проще простого:
pip install locust
Создаём первый тест
Напишем небольшой пример. Допустим, у нас есть API, и мы хотим проверить, как оно выдержит поток запросов.
from locust import HttpUser, TaskSet, task, between
class SimpleTasks(TaskSet):
@task
def get_root(self):
self.client.get("/")
class WebsiteUser(HttpUser):
tasks = [SimpleTasks]
wait_time = between(1, 3)
В этом примере мы определили пользователя (
WebsiteUser
), который каждую 1-3 секунды отправляет запрос на главную страницу (/
). В классе SimpleTasks
можно описывать разные действия, которые пользователь будет выполнять.Запускаем нагрузку
В терминале переходим в папку с вашим скриптом и пишем:
locust -f my_locust_file.py --host http://example.com
Переходим по адресу http://localhost:8089 — и перед вами удобный web-интерфейс для запуска нагрузки.
Почему это круто?
- Сценарии на Python, а не на странных DSL;
- Реальное моделирование пользователей, а не бездушные потоки запросов;
- Детализированная статистика и красивые графики.
Ваша первая миссия
Попробуйте построить свой сценарий на locust, добавить несколько разных запросов, может быть, даже авторизацию. Почувствуйте себя настоящим «нагрузчиком» и узнайте, где болит ваш код!
На этом всё, до новых тестов!
👍1
Привет! Меня зовут Иван, и сегодня мы разберём, как создать распределённое приложение на Python с помощью ZeroMQ — одного из самых мощных инструментов для обмена сообщениями между процессами и машинами.
Что такое ZeroMQ?
Если коротко: это библиотека, которая позволяет писать приложение, способное «разговаривать» с другими частями системы через сеть или на одном компьютере, не заморачиваясь низкоуровневой реализацией протоколов и сокетов. Всё просто и надёжно.
Почему стоит попробовать ZeroMQ?
- Поддержка разных топологий: «точка-точка», паблиш/сабскрайб, очередь задач.
- Лёгкость внедрения — всё работает через обычные TCP/IPC-соединения, и не требует сервера-брокера.
- Производительность и масштабируемость.
Пример 1. Простое распределённое приложение: сервер-задачник и работник
server.py:
worker.py:
Добавьте несколько
Пример 2. Публикация и подписка (Pub/Sub)
publisher.py:
subscriber.py:
Итоги
ZeroMQ — отличный способ познакомиться с реальным распределённым программированием. Он прост в изучении, эффективен и позволяет строить масштабируемые приложения практически без лишней сложности. Берите эти примеры за основу — и экспериментируйте!
Что такое ZeroMQ?
Если коротко: это библиотека, которая позволяет писать приложение, способное «разговаривать» с другими частями системы через сеть или на одном компьютере, не заморачиваясь низкоуровневой реализацией протоколов и сокетов. Всё просто и надёжно.
Почему стоит попробовать ZeroMQ?
- Поддержка разных топологий: «точка-точка», паблиш/сабскрайб, очередь задач.
- Лёгкость внедрения — всё работает через обычные TCP/IPC-соединения, и не требует сервера-брокера.
- Производительность и масштабируемость.
Пример 1. Простое распределённое приложение: сервер-задачник и работник
server.py:
import zmq
context = zmq.Context()
socket = context.socket(zmq.PUSH)
socket.bind("tcp://127.0.0.1:5557")
for i in range(10):
msg = f"Task #{i}"
print(f"Send: {msg}")
socket.send_string(msg)
worker.py:
import zmq
import time
context = zmq.Context()
socket = context.socket(zmq.PULL)
socket.connect("tcp://127.0.0.1:5557")
while True:
msg = socket.recv_string()
print(f"Received: {msg}")
time.sleep(1)
Добавьте несколько
worker.py
— увидите, как задачи будут распределяться между ними. Так просто вы получите базовую очередь задач!Пример 2. Публикация и подписка (Pub/Sub)
publisher.py:
import zmq
import time
context = zmq.Context()
socket = context.socket(zmq.PUB)
socket.bind("tcp://127.0.0.1:5556")
while True:
socket.send_string("news Hello subscribers!")
time.sleep(1)
subscriber.py:
import zmq
context = zmq.Context()
socket = context.socket(zmq.SUB)
socket.connect("tcp://127.0.0.1:5556")
socket.setsockopt_string(zmq.SUBSCRIBE, "news")
while True:
msg = socket.recv_string()
print(f"Got: {msg}")
Итоги
ZeroMQ — отличный способ познакомиться с реальным распределённым программированием. Он прост в изучении, эффективен и позволяет строить масштабируемые приложения практически без лишней сложности. Берите эти примеры за основу — и экспериментируйте!
👍1
Привет! С вами Иван, и сегодня мы разберём тему, которой часто пренебрегают новички: качество оформления кода. Возможно, вы слышали о загадочном PEP8 и модуле black, но пока не решались познакомиться поближе? Тогда устраивайтесь поудобнее — сейчас разберём всё на пальцах.
## Что такое PEP8?
PEP8 — это официальный стиль написания кода на Python. Или, проще говоря, набор рекомендаций, как писать код понятно не только себе, но и другим. Вот некоторые ключевые моменты:
- отступы: только 4 пробела;
- длина строк: лучше укладываться в 79 символов;
- имена переменных:
- между функциями — две пустых строки, между методами — одна.
Пример «неправильного» кода:
И вот так — по PEP8:
Лаконично, читаемо — и глаз радуется.
## Форматируем код автоматически: знакомство с black
В мире, где приходится работать с десятками файлов, перебирать пробелы вручную — путь для мазохистов. Здесь на сцену выходит black — грозный (и очень полезный) автоформаттер кода.
### Установка
Black ставится одной командой:
### Использование
Форматируем файлы или целые проекты:
Black сам поправит отступы, длину строк, расставит пробелы. Причём форматирование очень строгое: порядка больше, споров меньше! Вот пример до и после:
после команды
## Почему это важно?
Чистый код читается легче, поддерживается быстрее, и экономит время вашей команды. Придерживаясь PEP8 и используя инструменты вроде black, вы быстро привьёте себе привычку писать не только рабочий, но и красивый Python-код.
На сегодня всё, до встречи в новых постах!
## Что такое PEP8?
PEP8 — это официальный стиль написания кода на Python. Или, проще говоря, набор рекомендаций, как писать код понятно не только себе, но и другим. Вот некоторые ключевые моменты:
- отступы: только 4 пробела;
- длина строк: лучше укладываться в 79 символов;
- имена переменных:
snake_case
, никаких camelCase
;- между функциями — две пустых строки, между методами — одна.
Пример «неправильного» кода:
def addNumbers(a,b): return a+b
И вот так — по PEP8:
def add_numbers(a, b):
return a + b
Лаконично, читаемо — и глаз радуется.
## Форматируем код автоматически: знакомство с black
В мире, где приходится работать с десятками файлов, перебирать пробелы вручную — путь для мазохистов. Здесь на сцену выходит black — грозный (и очень полезный) автоформаттер кода.
### Установка
Black ставится одной командой:
pip install black
### Использование
Форматируем файлы или целые проекты:
black my_script.py
Black сам поправит отступы, длину строк, расставит пробелы. Причём форматирование очень строгое: порядка больше, споров меньше! Вот пример до и после:
def fibonacci(n): a, b = 0, 1; result = []
for _ in range(n): result.append(a); a, b = b, a+b
return result
после команды
black
:def fibonacci(n):
a, b = 0, 1
result = []
for _ in range(n):
result.append(a)
a, b = b, a + b
return result
## Почему это важно?
Чистый код читается легче, поддерживается быстрее, и экономит время вашей команды. Придерживаясь PEP8 и используя инструменты вроде black, вы быстро привьёте себе привычку писать не только рабочий, но и красивый Python-код.
На сегодня всё, до встречи в новых постах!
👍3🔥1
Привет! На связи Иван, и сегодня разберём одну из самых интересных фишек современного Python — асинхронное программирование с помощью модуля
## Почему именно asyncio?
Обычные (синхронные) функции скачивают данные поочерёдно: началась загрузка — жди, пока закончится. А вот с
## Первый шаг: Корутинa и event loop
Всё строится вокруг корутин (функций с
## Разбираем пример:
-
-
-
- Результаты появляются, когда всё скачалось.
## Ещё парочка лайфхаков
1. IO-операции (HTTP-запросы, чтение файлов) лучше всего подходят для асинхронного подхода.
2. Используй aiohttp или другие async-библиотеки — работать с обычным requests тут нельзя.
3. Не забывай про
Вот так, всего пара строк — и твой Python-код работает в разы быстрее там, где раньше скучал, глядя на загрузку одного URL за другим! Попробуй — и почувствуй разницу.
До встречи, Иван.
asyncio
. Если тебе надоело ждать, пока твой скрипт скачает десяток страниц одну за другой, значит, самое время освоить новый подход!## Почему именно asyncio?
Обычные (синхронные) функции скачивают данные поочерёдно: началась загрузка — жди, пока закончится. А вот с
asyncio
мы можем делать несколько запросов одновременно, не блокируя выполнение программы! Это особенно круто, когда надо работать с большим количеством сетевых ресурсов.## Первый шаг: Корутинa и event loop
Всё строится вокруг корутин (функций с
async def
) и главного цикла событий — event loop. Вот как можно скачать сразу несколько страниц (например, с помощью aiohttp):import asyncio
import aiohttp
async def fetch_page(session, url):
async with session.get(url) as response:
return await response.text()
async def main():
urls = [
"https://example.com",
"https://httpbin.org/get",
"https://python.org"
]
async with aiohttp.ClientSession() as session:
tasks = [fetch_page(session, url) for url in urls]
results = await asyncio.gather(*tasks)
for i, html in enumerate(results):
print(f"Page {i+1} downloaded, length: {len(html)}")
asyncio.run(main())
## Разбираем пример:
-
async def fetch_page
— корутина, которая асинхронно скачивает страницу.-
session.get(url)
внутри асинхронного контекста. Нет блокировки на сеть!-
asyncio.gather(*tasks)
— запускает сразу несколько загрузок; программа ждёт, когда все завершатся.- Результаты появляются, когда всё скачалось.
## Ещё парочка лайфхаков
1. IO-операции (HTTP-запросы, чтение файлов) лучше всего подходят для асинхронного подхода.
2. Используй aiohttp или другие async-библиотеки — работать с обычным requests тут нельзя.
3. Не забывай про
async with
— он гарантирует правильное закрытие соединения.Вот так, всего пара строк — и твой Python-код работает в разы быстрее там, где раньше скучал, глядя на загрузку одного URL за другим! Попробуй — и почувствуй разницу.
До встречи, Иван.
👍1
Привет! Сегодня погрузимся в магию работы с текстами на Python и разберём сразу два популярных инструмента для обработки естественного языка — NLTK и spaCy. Даже если вы только начали путь питониста, с этими библиотеками вы сможете анализировать тексты, искать ключевые слова, определять части речи и делать ещё массу интересного.
### NLTK — классика жанра
NLTK (Natural Language Toolkit) часто называют «швейцарским ножом» для языкового анализа. Несмотря на почтенный возраст, NLTK актуален благодаря богатейшему набору функций и огромному количеству встроенных корпусов (наборов текстов для анализа).
Разберём простой пример — разбор текста на предложения и слова:
NLTK также умеет определять части речи:
### spaCy — быстрый и современный
Если вам нужен максимально быстрый и удобный инструмент — берите spaCy. Он из коробки умеет извлекать сущности, токенизировать текст, разбивать слова по частям речи и много чего ещё, причём делает это молниеносно.
Пример выделения именованных сущностей (людей, компаний, городов):
Ещё spaCy великолепно разбирается с лемматизацией (приведение слова к базовой форме):
### Что выбрать?
NLTK — хороший выбор для обучения, экспериментов и когда нужен гибкий доступ к лингвистическим инструментам. spaCy — чемпион по скорости и простоте для реальных приложений. К тому же их можно комбинировать: например, токенизацию делать в spaCy, а сложную работу с корпусами доверить NLTK.
С текстами в Python можно творить чудеса, и знакомство с этими библиотеками — отличное начало. Удачной практики!
— Иван
### NLTK — классика жанра
NLTK (Natural Language Toolkit) часто называют «швейцарским ножом» для языкового анализа. Несмотря на почтенный возраст, NLTK актуален благодаря богатейшему набору функций и огромному количеству встроенных корпусов (наборов текстов для анализа).
Разберём простой пример — разбор текста на предложения и слова:
import nltk
nltk.download('punkt')
text = "Python is great. Natural Language Processing is fascinating!"
sentences = nltk.sent_tokenize(text)
words = [nltk.word_tokenize(sentence) for sentence in sentences]
print(sentences)
print(words)
NLTK также умеет определять части речи:
nltk.download('averaged_perceptron_tagger')
tokens = nltk.word_tokenize("SpaCy is faster than NLTK in many tasks.")
pos_tags = nltk.pos_tag(tokens)
print(pos_tags)
### spaCy — быстрый и современный
Если вам нужен максимально быстрый и удобный инструмент — берите spaCy. Он из коробки умеет извлекать сущности, токенизировать текст, разбивать слова по частям речи и много чего ещё, причём делает это молниеносно.
Пример выделения именованных сущностей (людей, компаний, городов):
import spacy
nlp = spacy.load("en_core_web_sm")
doc = nlp("Apple is looking at buying U.K. startup for $1 billion")
for ent in doc.ents:
print(ent.text, ent.label_)
Ещё spaCy великолепно разбирается с лемматизацией (приведение слова к базовой форме):
for token in doc:
print(token.text, token.lemma_)
### Что выбрать?
NLTK — хороший выбор для обучения, экспериментов и когда нужен гибкий доступ к лингвистическим инструментам. spaCy — чемпион по скорости и простоте для реальных приложений. К тому же их можно комбинировать: например, токенизацию делать в spaCy, а сложную работу с корпусами доверить NLTK.
С текстами в Python можно творить чудеса, и знакомство с этими библиотеками — отличное начало. Удачной практики!
— Иван
👍1
Привет, друзья! С вами Иван, и сегодня я расскажу о мощном модуле shutil, который позволяет управлять файлами и папками в Python буквально в пару строк. Готовы почувствовать себя повелителями файловой системы? Тогда поехали!
### Копирование папок и файлов
Если вы хотите скопировать файл — используйте
Стоит учитывать:
А вот просто скопировать файл – элементарно:
### Перемещение объектов
Иногда нужно не копировать, а переместить файл или целую папку – тут поможет
Путь назначения может быть и на другом диске! Всё работает и для Unix, и для Windows.
### Удаление папок
Удалять папки вручную – не вариант, если их много. В этом случае
ВАЖНО: Эта команда не спрашивает подтверждения — всё удаляется навсегда, так что лучше дважды проверять путь, чтобы затем не восстанавливать данные слезами.
### Быстрый итого
- copy — копирует файлы.
- copytree — копирует папки (и их содержимое).
- move — перемещает любые объекты файловой системы.
- rmtree — безжалостно удаляет целые папки.
shutil — незаменимый инструмент для данных админ-скриптов, резервного копирования или просто наведения порядка у себя на диске с помощью Python. Простота и мощь – вот почему я обожаю этот модуль!
Увидимся в следующих постах, где разберём ещё больше секретных приемов из арсенала Python!
### Копирование папок и файлов
Если вы хотите скопировать файл — используйте
shutil.copy
, а вот для папок, в том числе с вложенными подкаталогами, пригодится shutil.copytree
:import shutil
# Копирование папки с содержимым
shutil.copytree('source_folder', 'destination_folder')
Стоит учитывать:
destination_folder
не должен существовать, иначе будет исключение.А вот просто скопировать файл – элементарно:
shutil.copy('original.txt', 'copy.txt')
### Перемещение объектов
Иногда нужно не копировать, а переместить файл или целую папку – тут поможет
shutil.move
. Причём модуль сам определит, файл вы перемещаете или папку.shutil.move('old_folder', 'new_folder_location/old_folder')
Путь назначения может быть и на другом диске! Всё работает и для Unix, и для Windows.
### Удаление папок
Удалять папки вручную – не вариант, если их много. В этом случае
shutil.rmtree
сотрёт весь каталог с файлами и подпапками:shutil.rmtree('unnecessary_folder')
ВАЖНО: Эта команда не спрашивает подтверждения — всё удаляется навсегда, так что лучше дважды проверять путь, чтобы затем не восстанавливать данные слезами.
### Быстрый итого
- copy — копирует файлы.
- copytree — копирует папки (и их содержимое).
- move — перемещает любые объекты файловой системы.
- rmtree — безжалостно удаляет целые папки.
shutil — незаменимый инструмент для данных админ-скриптов, резервного копирования или просто наведения порядка у себя на диске с помощью Python. Простота и мощь – вот почему я обожаю этот модуль!
Увидимся в следующих постах, где разберём ещё больше секретных приемов из арсенала Python!
👍1
Привет! С вами Иван, и сегодня мы поговорим о том, как легко и просто реализовать структуру данных “очередь” и “стек” с помощью модуля
## Почему именно deque?
В Python списки (
## Реализуем очередь
Очередь — это структура данных, работающая по принципу FIFO (First-In, First-Out, “первым пришёл — первым ушёл”). Давайте посмотрим, как можно буквально в пару строк сделать простейшую очередь:
Здесь всё интуитивно ясно:
## А теперь стек
А вот стек — это LIFO (Last-In, First-Out, “последним пришёл — первым ушёл”). Реализуется он не менее просто:
Для стека используйте методы
## Немного тонкостей
-
- Можно задавать максимальную длину очереди (
-
## Итого
Использовать стандартный модуль collections и класс deque — отличный способ писать лаконичный, быстрый и читаемый код для очередей и стеков. Основная прелесть — минимум кода и максимум эффективности, а всё самое “магическое” спрятано внутри самой стандартной библиотеки Python.
Экспериментируйте с
collections
, а точнее — с использованием класса deque
. Если вдруг вы еще не знакомы с этим классом, самое время это исправить!## Почему именно deque?
В Python списки (
list
) отлично подходят для многих задач, но если вы часто добавляете и убираете элементы с начала, производительность может заметно пострадать. К счастью, есть deque
(double-ended queue)! Это двусторонняя очередь, которая реализована на основе связанного списка: вставка и удаление элементов с любого конца происходят за константное время.## Реализуем очередь
Очередь — это структура данных, работающая по принципу FIFO (First-In, First-Out, “первым пришёл — первым ушёл”). Давайте посмотрим, как можно буквально в пару строк сделать простейшую очередь:
from collections import deque
queue = deque()
# Добавляем элементы в конец очереди
queue.append('apple')
queue.append('banana')
queue.append('cherry')
# Удаляем элемент из начала очереди
first = queue.popleft()
print(first) # apple
Здесь всё интуитивно ясно:
append
добавляет элемент в конец, а popleft
вытаскивает первый элемент.## А теперь стек
А вот стек — это LIFO (Last-In, First-Out, “последним пришёл — первым ушёл”). Реализуется он не менее просто:
from collections import deque
stack = deque()
# Добавляем элементы на вершину стека
stack.append('red')
stack.append('green')
stack.append('blue')
# Удаляем элемент с вершины стека
top = stack.pop()
print(top) # blue
Для стека используйте методы
append
и pop
. Всё, больше ничего не нужно!## Немного тонкостей
-
deque
можно использовать и как очередь, и как стек — просто используйте нужные методы: для очереди append
и popleft
, для стека — append
и pop
.- Можно задавать максимальную длину очереди (
maxlen
), и тогда при переполнении старые элементы будут автоматически удаляться.-
deque
поддерживает перебор циклом, а также методы, похожие на обычные списки: extend
, remove
, count
и другие.## Итого
Использовать стандартный модуль collections и класс deque — отличный способ писать лаконичный, быстрый и читаемый код для очередей и стеков. Основная прелесть — минимум кода и максимум эффективности, а всё самое “магическое” спрятано внутри самой стандартной библиотеки Python.
Экспериментируйте с
deque
— и почувствуйте разницу!