Pull to refresh

Comments 28

UFO just landed and posted this here

Хотя f-строки часто удобнее и читабельнее для простых случаев, метод format() предоставляет некоторые дополнительные возможности, такие как совместимость с более старыми версиями Python (а f-строки работают только с версии 3.6) или возможности распаковки коллекций.

Например, можно распаковывать коллекции прямо при передаче аргументов:

person = ('Alice', 'Moscow', 21)

print("My name is {}. I'm from {} and I'm {} years old.".format(*person))

# My name is Alice. I'm from Moscow and I'm 21 years old.

Или в случае со словарём:

person = {'name': 'Alice', 'city': 'Moscow', 'age': 21}

print("My name is {name}. I'm from {city} and I'm {age} years old.".format(**person))

# My name is Alice. I'm from Moscow and I'm 21 years old.

Сомнительная фича эти распаковки. Как минимум читать откуда ноги растут на порядок сложнее, а с учётом отсутвия жёсткой типизации ещё и ошибок можно огрести, оптимизировать или рефакторить такое тоже заметно сложнее.

Для шаблона строки почти всегда используют константу с нормальным именем. И распаковка становится очень удобной штукой

Тем временем в Rust:

format!("My name is {}", name);

Error: Name does not have trait Copy

-А, блин! Забыл.

format!("My name is {}", &name);

Error: Name does not have trait Display

-Да блин !!!

format!("My name is {:?}", &name);

Error: Name does not have trait Debug

-ДА ТВОЮ ДИВИЗИЮ

impl Display for Name {

Error: implementing a foreign trait is only possible if at least one of the types for which it is implemented is local

FF-U-U-U-!#?🤬🤬🤬

боюсь спросить какого такого типа у вас name, что у вас ни Debug, ни Display нет. Строка в легаси кодировке (KOI8 или какой-нибудь CJK/JIS), которую вы пытаетесь скрестить с utf форматированием?

Нет, всего лишь enum Name { Realname(String), Fakename(String) } который приходит из внешнего крейта, и которому ленивая задница не написала никаких #derive.

#Удалил абзац, тут не та ссылка была, а ту я потерял. Короче, приходит мне из крейта экземпляр развесистого двадцативариантного enum, и я хотел его в лог вывести, а Debug нет ни у него, ни у типа его вариантов... Слава богу, что теперь есть LLM, которому можно скинуть прямо ссылку на страницу в docs.rs и сказать написать функцию для печати, и он сделал. Но всё равно это дополнительное рукоблудие. Функцию эту куда-то добавлять надо, потом добавлять, чтобы не писало ворнинг когда она не используется, потом указывать, что на неё теста нет и не нужно...

Решаемо, конечно, но просто бесит на ровном месте после Питона. Почему было не сделать в debug сборке #derive(Debug) по умолчанию всем, опционально выключаемый и не кидающий ошибок, конечно...

Тогда вы определённо неправильно его готовите.

enum Name { Realname(String), Fakename(String) } 

fn main() {
    let name = Name::Realname("name".to_string());
    println!("{name:?}", name=match name {
        Name::Realname(n) => n,
        Name::Fakename(n) => n,
    });
}

Но вообще ваш кейс с enum как минимум плох, ибо варианты enum не обязаны быть единообразными и иметь одинаковые ассоциированные данные - кто знает что вы хотели написать. Добавить кейс Noname и что по вашему должен делать форматтер, когда получит его на вход? Для остального есть derive(Debug). Причем ту же историю можно повторить и в питон и будет вам полна коробочка логов <instance object Name at 0xf65f65651e> .

Дело в том, что #[derive(Debug)] который всё, что вы перечислили, сделает за меня, есть и работает! Но только я, как пользователь крейта, вызвать его не могу - его должен явно для каждой структуры разрешить автор крейта.

А в Python у нас есть pprint который сможет напечатать практически любой объект в пусть корявом, но полезном виде, и мне не нужно ничьё разрешение ,чтобы им воспользоваться.

Я совершенно не возмущаюсь о разделении на Debug и Display, и что Display надо писать руками. Но вы их как-то вместе смешали.

Добавить кейс Noname и что по вашему должен делать форматтер, 

Писать Name::Noname в случае этого примера.

И я не говорю про Си, например, где так не сделаешь потому, что макросы не особо в чести и с ними надо очень аккуратно. Но в Rust решение вопроса есть - оно просто внезапно недоступно бывает.

В таком случае можно использовать паттерн new type в качестве обёртки и писать Display/From/Into (уже для него, если действительно прям никаких derive не завезли, правда стоит вопрос оправдано ли использование такой библиотеки.

Можно. Можно много чего. Всё это, напомню, чтобы один разок посмотреть, для себя, чего там прилетает из функции. Бывают места, куда дебаггером тоже сложно подключиться.

Как всегда все изложено максимально доступным языком)) 👍

В этой статье мы поговорим о неочевидных деталях и скрытых особенностях работы логических операторов в Python.

Интересно, что спустя 4 часа после публикации и 1,2K просмотров, никто не написал о том, что в описании к статье говорят о логических операторах и ни слова про строки в Python :)

Написали, уже исправили)

Немного позанудствую: есть ещё четвёртый метод форматирования строк: класс Formatter, который позволяет реализовать свой собственный вариант форматирования.

Ни разу не использовал, но сразу вспомнил про него ¯\_(ツ)_/¯

Так оно же в итоге сводится к вызову того же .format . C почти тем же успехом можно перегрузить __str__ или __repr__, за исключением ситуаций, когда используется кастомные спецификаторы форматирования, например, "%K".format(celsius) чтобы печатать градусы цельсия в кельвинах.

Да, но вызывается этот метод не у строки, а у инстанса Formatter. Обратите внимание на разную сигнатуру этого метода у строки и Formatter

Вот честно говоря это пример довольно средней статьи. Такой большой объем, а про половину возможностей (заполнители, выравнивание, точность отображения float) даже не рассказали.
Есть отличный сайт посвященный этому, там куда более кратко и подробно: https://pyformat.info/

Забыли про форматирование float и datetime.

f-строки оказались настолько хороши, что их близкий аналог включили в C++ 20 :)

в Python 3.12 было снято ограничение на использование одинаковых кавычек в f-строках и их заполнителях

IMHO, это не очень разумное решение, повышающее вероятность ошибок. Но Гвидо виднее :)

Кстати интересно, где впервые появилась концепция f-строк. Например, в JavaScript тоже есть аналогичный функционал.

В питоне f-строки появились из PEP498, это 2014-2015 год. Template strings в JS - ES6, это тоже 2015-й. Идеи витают в воздухе :)

Просто раньше у f-строк был свой недопарсер, который не все мог прожевать, и оттуда росли странные ограничения. В 3.12 допилили нормальный, и теперь как и задуманно — умеет любые выражения

Самый старый вариант с %s по-прежнему рекомендуется для модуля logging, чтобы не форматировать, если не включено по уровню.

И ещё одно...

Есть ли средства рефакторить форматирование между этими стилями? Чтобы само умело переделывать по запросу, например, `"%s %r" % (x, y)` в `"{} {!r}".format(x, y)` и далее в `f"{x} {y!r}"`, или наоборот. И какие их достоинства и недостатки. Без этого рассказ неполон.

Sign up to leave a comment.

Articles