Так как статья рассчитана на новичков, поделюсь аналогичной программой для командной строки:
import re, argparse
from os import path
parser = argparse.ArgumentParser(description='Count words in text file.')
parser.add_argument('path', action='store', type=str, help='path to a text file')
args = parser.parse_args()
if path.exists(args.path) and path.isfile(args.path):
with open(args.path, 'r') as f:
text = f.read()
else:
exit(f'Path "{args.path}" does not exist or a directory.')
if not text:
exit('File is empty.')
words = [word.lower() for word in re.split(r'[^\w-]+', text) if word]
print('Words: ', len(words))
print('Unique words: ', len(set(words)))
Так же согласен с товарищами выше, в Питоне есть более интересные инструменты для выполнения данной задачи, чем предложил автор.
позволяет использовать функцию для определения пути
Тут тоже есть свои неудобства, которые описаны в упоминаемой вами документации.
instance — In most cases, this object will not have been saved to the database yet, so if it uses the default AutoField, it might not yet have a value for its primary key field.
То есть, на момент вызова функции переданной в upload_to, instance еще не будет сохранен в базу, а значит, id у него будет равен None.
На SO есть несколько решений этой проблемы особенности, надеюсь, другие джангисты поделятся популярным самым решением.
Кодирование длин серий Были участники, которые решили задачу с помощью регулярных выражений.
И правильно сделали, как мне кажется.
Сильно уж они все упрощают
import re
def decoded_rle_value_len(value):
pairs = re.findall(r'(?P<letter>[A-Z])(?P<count>\d*)', value)
return sum(int(pair[1]) if pair[1] else 1 for pair in pairs)
result = decoded_rle_value_len('A15BA5')
print(result)
С другой стороны, решение в статье более прямолинейное и его, возможно, проще осмыслить.
Если бы проблема была и для Windows — было бы куда больше шума, мне кажется.
Попробовал у себя: Windows 7, Pycharm 2019.3, русская раскладка. Хоткеи из тикета работают точно так же, как и на английской раскладке.
from __future__ import annotations
import asyncio
from dataclasses import dataclass, field
from typing import Generic, Sequence, TypeVar
from aiohttp import ClientSession
T = TypeVar("T")
@dataclass
class Comment:
id: int
title: str
def __repr__(self):
return f"{self.id} - {self.title}"
@dataclass
class Tree(Generic[T]):
value: T
children: Sequence[Tree] = field(default_factory=list)
def print(self, indentation: str = "") -> None:
print(f"{indentation}{self.value}")
for child in self.children:
child.print(indentation + "\t")
async def get_comment(client: ClientSession, id: int) -> Comment:
async with client.get(f"https://jsonplaceholder.typicode.com/todos/{id}") as resp:
raw_comment = await resp.json()
return Comment(id=raw_comment["id"], title=raw_comment["title"])
async def get_comments_tree(client: ClientSession, tree: Tree[int]):
children = [get_comments_tree(client, child) for child in tree.children]
value = await get_comment(client, tree.value)
chilren_results = await asyncio.gather(*children)
return Tree[Comment](value, chilren_results)
async def main():
async with ClientSession() as client:
tree = Tree(1, children=[Tree(2), Tree(3, children=[Tree(4), Tree(5)])])
tree.print()
comment_tree = await get_comments_tree(client, tree)
comment_tree.print()
if __name__ == "__main__":
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
pyright даже умеет за дженериками следить. А вот mypy и PyCharm почему-то не смогли меня наругать, когда при создании дерева я попытался одной из нод передать в качестве children НЕ Sequence[Tree].
Если кто знает как обучить другие тайпчекеры понимать дженерики — поделитесь, пожалуйста. :)
Попробовать можно на этом:
tree = Tree(1, children=[Tree(2), Tree(3, children=[Tree(4), Tree(5, children='i am not a sequence of trees')])])
Всё приложение можно уместить в 20 строк кода (у меня не Twilio, но суть та же):
from os import environ
from chalice import Chalice, Cron
from httpx import Client
from httpx.exceptions import HTTPError
app = Chalice(app_name="app")
http = Client()
@app.schedule(Cron(30, 1, "*", "*", "?", "*"))
def send_greetings_periodic(event):
try:
http.post(
environ["DISCORD_WEBHOOK"], json={"content": "Hello!"}
).raise_for_status()
except HTTPError as ex:
app.log(ex)
А сборкой приложения, настройкой AWS и отправкой туда всего нужного займется CLI, который идет в комплекте.
Зависимости складываем в <app_root>/requirements.txt.
Самое главное — добавить креды AWS в ~/.aws, добавить токен Twilio (в моем случае) URL для Discord, куда будет отправляться приветствие, в environment_variables из файла <app_root>/.chalice/config.json:
Это, наверное, сильно зависит от количества «налайканных» треков. Я вот месяца два пользуюсь YouTube Music, не на постоянной основе, но как минимум час в день музыка играет. Треки лайкаю. И знаете что? В рекомендациях все те же 10 исполнителей. С теми же песнями. От них уже просто тошнит. Я не смог понять как мне получать новую музыку в YouTube Music.
C Яндекс.Музыкой же всё наоборот. Импортировал свой плейлист из 2 000 песен, много лайкал и ежедневный плейлист очень радует.
Слушаю предпочтительно рок, иногда подмешивая разное.
Судя по документации, можно сделать либо с ограниченным количеством аргументов (в данном случае — 3 аргумента; определит из длины кортежа type):
import click
@click.command()
@click.option('--userdata', type=(str, str, int), help='User data represented as "FirstName LastName Age"')
def greet(userdata):
print('Hello, user {} {}. You are {} y.o.'.format(*userdata))
if __name__ == '__main__':
greet()
Что можно использовать как: python app.py --userdata Vasiliy Petrenko 42
Если нужно передавать несколько одинаковых параметров — можно делать так:
import click
@click.command()
@click.option('--name', type=str, help='A name to greet', multiple=True)
def greet(name):
for n in name:
print(f'Hello, {n}')
if __name__ == '__main__':
greet()
И использовать как: python app.py --name Vasya --name Petya
Указывать неограниченное количество аргументов для Option (например, python app.py --names Vasya Petya Tolya) — нельзя.
Тоже очень нравится click. Особенно приятно работать с путями. Сразу при указании типа аргумента можно провести его валидацию, например:
import click
@click.command()
@click.option(
"--count",
default=1,
type=click.IntRange(min=1, max=100),
help="Number of files to generate.",
)
@click.option(
"--output",
type=click.Path(exists=True, file_okay=False, writable=True),
help="Output directory script will write files to.",
)
def file_generator(count, output):
for i in range(count):
with open(f"{i}.txt", "w") as f:
f.write(f"Dummy data {i}")
if __name__ == "__main__":
file_generator()
Вот пользуюсь и не могу нарадоваться, от скольких же рутинных проверок этот инструмент меня избавил.
P. S. Да, во время генерации самих файлов проверки все еще нужны, но это уже другая история. :)
Netis WF2411E, родная прошивка, через веб-интерфейс позволил установить SSID в виде emoji без каких-либо проблем. С телефона посмотрел, emoji видно. Выглядит забавно. :)
Установить пароль в виде emoji не позволил, сработала валидация.
Vibora сейчас трогать — грешно. Автор обещает большие изменения, будем надеяться и ждать, но на данный момент, последний коммит — 18 июля. Проблем куча. В продакшн такое не потащишь.
Вот uvicorn / starlette и их друзья — интересные кандидаты. На их основе множество фреймворков наплодилось уже.
Без углублений, которые делал автор, всё можно свести к aiohttp + uvloop и async/await синтаксису. Работается с этим крайне просто. :)
Go, конечно, хорош. Мне в первую очередь было интересно: «А как бы показал себя Go в задачах автора?», но когда всё налажено для Питона — переезжать не хочется.
Так же согласен с товарищами выше, в Питоне есть более интересные инструменты для выполнения данной задачи, чем предложил автор.
Автор FastAPI уже этим занимается. :)
Остается пожелать ему удачи и терпения.
Тут тоже есть свои неудобства, которые описаны в упоминаемой вами документации.
То есть, на момент вызова функции переданной в
upload_to
,instance
еще не будет сохранен в базу, а значит,id
у него будет равенNone
.На SO есть несколько решений этой
проблемыособенности, надеюсь, другие джангисты поделятся популярным самым решением.И правильно сделали, как мне кажется.
С другой стороны, решение в статье более прямолинейное и его, возможно, проще осмыслить.
Вот это решает проблему:
Но оказалось, что решение в посте работает прилично быстрее.
Пользуюсь dynaconf
Умеет в большинство популярных форматов, есть интеграции с Джангой и Флаской, умеет в Vault.
Подобные "упрощения" не очень то и упрощают чтение кода. :)
Круто, что даже без использования
numpy
при помощиnumba
можно прилично ускорить работу программы.Например, если на код автора просто навесить декоратор
@jit
— программа отработает за2.528
на моем железе.Если убрать
isinstance
— уже будет1.7
.Что не может не радовать. :)
Все-таки, если уже считаем в Питоне, то берем для этого инструменты, которые для него придумали.
Если бы проблема была и для Windows — было бы куда больше шума, мне кажется.
Попробовал у себя: Windows 7, Pycharm 2019.3, русская раскладка. Хоткеи из тикета работают точно так же, как и на английской раскладке.
Перенес C# реализацию автора на Python 3.7+:
pyright даже умеет за дженериками следить. А вот mypy и PyCharm почему-то не смогли меня наругать, когда при создании дерева я попытался одной из нод передать в качестве
children
НЕSequence[Tree]
.Если кто знает как обучить другие тайпчекеры понимать дженерики — поделитесь, пожалуйста. :)
Попробовать можно на этом:
Из приятностей еще можно отметить, что если вам понадобится удалить приложение — не придется ходить по каждому сервису и чистить все руками.
chalice delete
и все что оно породило — само и удалит.Весь третий шаг можно опустить, если воспользоваться официальной библиотекой под названием Chalice.
Всё приложение можно уместить в 20 строк кода (у меня не Twilio, но суть та же):
А сборкой приложения, настройкой AWS и отправкой туда всего нужного займется CLI, который идет в комплекте.
Зависимости складываем в
<app_root>/requirements.txt
.Самое главное — добавить креды AWS в
~/.aws
, добавитьтокен Twilio(в моем случае) URL для Discord, куда будет отправляться приветствие, вenvironment_variables
из файла<app_root>/.chalice/config.json
:И сделать
chalice deploy
.Если всё сделали правильно — вы восхитительны. :)
Невероятно много времени экономит.
Позволяет разделить обычные настройки из файла, сикреты и переменные окружения, но использовать для чтения один интерфейс без лишних заморочек.
Рекомендую попробовать.
C Яндекс.Музыкой же всё наоборот. Импортировал свой плейлист из 2 000 песен, много лайкал и ежедневный плейлист очень радует.
Слушаю предпочтительно рок, иногда подмешивая разное.
Судя по документации, можно сделать либо с ограниченным количеством аргументов (в данном случае — 3 аргумента; определит из длины кортежа
type
):Что можно использовать как:
python app.py --userdata Vasiliy Petrenko 42
Если нужно передавать несколько одинаковых параметров — можно делать так:
И использовать как:
python app.py --name Vasya --name Petya
Указывать неограниченное количество аргументов для Option (например,
python app.py --names Vasya Petya Tolya
) — нельзя.Вот пользуюсь и не могу нарадоваться, от скольких же рутинных проверок этот инструмент меня избавил.
P. S. Да, во время генерации самих файлов проверки все еще нужны, но это уже другая история. :)
P. P. S. Форматировался код при помощи black
Установить пароль в виде emoji не позволил, сработала валидация.
Вот uvicorn / starlette и их друзья — интересные кандидаты. На их основе множество фреймворков наплодилось уже.
Не согласен.
Без углублений, которые делал автор, всё можно свести к aiohttp + uvloop и async/await синтаксису. Работается с этим крайне просто. :)
Go, конечно, хорош. Мне в первую очередь было интересно: «А как бы показал себя Go в задачах автора?», но когда всё налажено для Питона — переезжать не хочется.
Для создания простых API в Lambda на Питоне пользуюсь Chalice, очень упрощает жизнь и избавляет от рутины. Напоминает обычное приложение на Фласке. :)