Флаги преобразования

При форматировании строк доступны 3 флага преобразования объекта в строку: !r, !s и !a.

>>> x = "дом"
>>> f'{x}'
'дом'
>>> f'{x!r}'
"'дом'"
>>> f'{x!s}'
'дом'
>>> f'{x!a}'
"'\\u0434\\u043e\\u043c'"

Для фанатов format:

>>> '{0!r}'.format(x)
"'дом'"
>>> '{!r}'.format(x)
"'дом'"

Флаг !r вызывает repr(x), а флаг !s вызывает str(x). Флаг !a вызывает ascii(repr(x)). Функция ascii превращает все символы за пределами набора ASCII (включая русские буквы в юникоде) в их коды. Если флаг не указан, то по умолчанию считается, что он !s.

Для классов __repr__ и __str__ могут иметь различное определение:

class Foo:
    def __repr__(self):
        return "репр"
    def __str__(self):
        return "строка"
x = Foo()
print(f'{x!r}')  # репр
print(f'{x!s}')  # строка

Если __str__ нет, то будет вызван __repr__.

Рекомендации: __str__ должен давать нам человеко-читаемое описание объекта, а __repr__ – уникальное представление объекта, по которому можно частично или полностью восстановить состояние этого объекта или хотя бы помочь с отладкой. __str__ – для пользователей, __repr__ — для питонистов.

📎 Отличный пример для наглядности – datetime:

>>> import datetime
>>> dt = datetime.datetime(2019, 7, 27)
>>> repr(dt)
'datetime.datetime(2019, 7, 27, 0, 0)'
>>> str(dt)
'2019-07-27 00:00:00'
>>> eval(repr(dt)) == dt
True

str от datetime просто покажет нам дату и время в удобном формате; repr от datetime вернет строку, в которой будет вызов описан конструктора конкретно этого объекта, да так, что при исполнении этой строки как кода на Python функцией eval – мы получим объект datetime для той же даты. Впрочем, никто нас не обязывает делать этот трюк для каждого объекта.

🐉 Специально для канала @pyway. Подписывайтесь на мой канал в Телеграм @pyway 👈 

Python 3.8 здесь!

3.8

🐍Отложим дела ради классной новости! Python версии 3.8 официально релизнулся!

Что в новой версии?

1️⃣ Оператор морж (писал о нем ранее). Присваивание переменной внутри других выражений:

if (n := len(a)) > 10:
    print("слишком длинно")

while (block := f.read(256)) != '':
    process(block)

[clean_name.title() for name in names
 if (clean_name := normalize('NFC', name)) in allowed_names]

2️⃣ Разделитель позиционных аргументов (слэш /). Указывает, что первые несколько аргументов могут быть только позиционными (в строгом порядке, без указания имени). Напомню, что именные аргументы передаются с указанием имени, и не важно в каком порядке. В примере ниже a и b – только позиционные, c и d — могут быть позиционные или переданы по имени, а e и f – исключительно именные:

def f(a, b, /, c, d, *, e, f):
    print(a, b, c, d, e, f)

# разрешенный вызов:
f(10, 20, 30, d=40, e=50, f=60)

# НЕЛЬЗЯ передать b по имени 
# (b стоит до слэша)
f(10, b=20, c=30, d=40, e=50, f=60) 

# НЕЛЬЗЯ передать e без указания имени
# (e стоит после звездочки)
f(10, 20, 30, 40, 50, f=60) 

3️⃣ Спецификатор = для f-строк. Тут проще на примере, раньше мы писали с повторами:

>>> user = 'eric_idle'
>>> since = date(1975, 7, 31)
>>> f'user={user} since={since}'
"user='eric_idle' since=datetime.date(1975, 7, 31)"

А теперь можно так:

>>> f'{user=} {since=}'
"user='eric_idle' since=datetime.date(1975, 7, 31)"

После знака равно можно добавлять и прочие спецификаторы форматирования:

>>> delta = date.today() - since
>>> f'{user=!s} {delta.days=:,d}'
'user=eric_idle delta.days=16,075'

Для отладки принтами — просто восторг!

4️⃣ Теперь можно continue внутри finally

Еще есть множество улучшений со стороны C-API, всякие хуки аудита, вектор-коллы. Новая настройка PYTHONPYCACHEPREFIX, чтобы вынести кэш байткода из стандартной директории pycache куда вам удобно. Очень-очень много разных мелких изменений в стандартных модулях и функциях, о которых расскажу при случае.

Что нового по-английски

Как вам новая версия?