Комментарии 28
Хотя 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 решение вопроса есть - оно просто внезапно недоступно бывает.
А у вас всё в одном крейте, поэтому работает. Хотите посмотреть на orphan rule? Надо так тогда.
Как всегда все изложено максимально доступным языком)) 👍
В этой статье мы поговорим о неочевидных деталях и скрытых особенностях работы логических операторов в Python.
Интересно, что спустя 4 часа после публикации и 1,2K просмотров, никто не написал о том, что в описании к статье говорят о логических операторах и ни слова про строки в Python :)
Так оно же в итоге сводится к вызову того же .format
. C почти тем же успехом можно перегрузить __str__
или __repr__
, за исключением ситуаций, когда используется кастомные спецификаторы форматирования, например, "%K".format(celsius)
чтобы печатать градусы цельсия в кельвинах.
Вот честно говоря это пример довольно средней статьи. Такой большой объем, а про половину возможностей (заполнители, выравнивание, точность отображения float) даже не рассказали.
Есть отличный сайт посвященный этому, там куда более кратко и подробно: https://pyformat.info/
Забыли про форматирование float и datetime.
f-строки оказались настолько хороши, что их близкий аналог включили в C++ 20 :)
в Python 3.12 было снято ограничение на использование одинаковых кавычек в f-строках и их заполнителях
IMHO, это не очень разумное решение, повышающее вероятность ошибок. Но Гвидо виднее :)
Кстати интересно, где впервые появилась концепция f-строк. Например, в JavaScript тоже есть аналогичный функционал.
Просто раньше у f-строк был свой недопарсер, который не все мог прожевать, и оттуда росли странные ограничения. В 3.12 допилили нормальный, и теперь как и задуманно — умеет любые выражения
А я обычно складываю🌝
Самый старый вариант с %s по-прежнему рекомендуется для модуля logging, чтобы не форматировать, если не включено по уровню.
И ещё одно...
Есть ли средства рефакторить форматирование между этими стилями? Чтобы само умело переделывать по запросу, например, `"%s %r" % (x, y)` в `"{} {!r}".format(x, y)` и далее в `f"{x} {y!r}"`, или наоборот. И какие их достоинства и недостатки. Без этого рассказ неполон.
Эволюция форматирования строк в Python