Python hex(): шестнадцатеричная запись чисел
Функция hex() в Python переводит целое число в шестнадцатеричную систему и возвращает строку с префиксом 0x. Ниже разобраны ее поведение с отрицательными числами, способы вывести результат без префикса, в верхнем регистре и фиксированной ширины, обратный перевод в int, а также шестнадцатеричное представление байтов и дробных чисел.

Содержание статьи
Синтаксис и результат hex()
Функция встроенная и принимает один аргумент - целое число. Возвращает она всегда строку, которая начинается с 0x, а цифры от 10 до 15 записывает строчными буквами a-f.
print(hex(255)) # 0xff
print(hex(435)) # 0x1b3
print(hex(0)) # 0x0
print(hex(-34)) # -0x22
print(type(hex(16))) # <class 'str'>Для отрицательного числа знак минус ставится перед префиксом, а сама запись остается записью модуля числа. Это не дополнительный код, в котором отрицательные числа хранятся в памяти процессора, а просто знак и абсолютное значение.
Если передать не целое число, а, например, 3.5 или строку '10', функция выбросит TypeError. Числа с плавающей точкой обрабатываются отдельным методом, о нем ниже. Про системы счисления и литералы вида 0xff в исходном коде подробнее рассказано в статье о числах в Python 3.
Вывод без префикса 0x, с заглавными буквами и нулями
Функция hex() не имеет параметров форматирования. Когда нужна другая запись, используют спецификаторы формата x и X в f-строках или функции format().
n = 255
print(f'{n:x}') # ff - без префикса
print(f'{n:X}') # FF - заглавные буквы
print(f'{n:#x}') # 0xff - то же, что hex()
print(f'{n:#06x}') # 0x00ff - ширина 6 вместе с префиксом
print(f'{n:04X}') # 00FF - 4 цифры с ведущими нулями
print(format(n, 'x')) # ffРаспространенный способ убрать префикс срезом hex(n)[2:] работает только для неотрицательных чисел: для -34 он вернет 'x22'. Спецификатор x корректно обрабатывает знак и дает '-22', поэтому в общем случае надежнее он. Все возможности мини-языка форматирования разобраны в статье о функции format.
Сравнение способов форматирования
Ниже итоговая таблица для числа 3054, чтобы выбрать подходящий вариант без экспериментов.
| Код | Результат | Особенность |
|---|---|---|
| hex(3054) | 0xbee | Всегда с префиксом, строчные буквы |
| f'{3054:x}' | bee | Без префикса |
| f'{3054:X}' | BEE | Без префикса, заглавные |
| f'{3054:#X}' | 0XBEE | Префикс тоже становится заглавным |
| f'{3054:08x}' | 00000bee | Дополнение нулями до 8 знаков |
| '%x' % 3054 | bee | Старый стиль форматирования |
Обратный перевод: из шестнадцатеричной строки в число
Для обратного преобразования используют int() со вторым аргументом - основанием системы счисления. При основании 16 префикс 0x допускается, но не обязателен, регистр букв не важен.
print(int('ff', 16)) # 255
print(int('0xFF', 16)) # 255
print(int('-0x22', 16)) # -34
print(int('0x1b3', 0)) # 435 - основание 0 определяется по префиксу
print(0x1b3) # 435 - литерал прямо в кодеОснование 0 удобно, когда строка может прийти в любой записи: с префиксом 0x, 0o или 0b. Если в строке встретится символ вне диапазона 0-9 и a-f, Python выбросит ValueError: invalid literal for int() with base 16.
Цвет HTML из чисел RGB и обратно
Практический пример - перевод цвета из трех компонентов RGB в формат #RRGGBB, который используется в CSS, и разбор такой строки обратно.
def rgb_to_hex(r, g, b):
return f'#{r:02x}{g:02x}{b:02x}'
def hex_to_rgb(color):
color = color.lstrip('#')
return tuple(int(color[i:i + 2], 16) for i in (0, 2, 4))
print(rgb_to_hex(255, 165, 0)) # #ffa500
print(hex_to_rgb('#FFA500')) # (255, 165, 0)Спецификатор 02x здесь обязателен: без него число 5 превратится в одну цифру 5, а не в 05, и строка цвета получится неверной длины.
Отрицательные числа в дополнительном коде
Иногда нужно увидеть отрицательное число так, как его хранит процессор, например при разборе бинарного протокола. Python работает с целыми числами неограниченной длины, поэтому разрядность задают сами: накладывают маску из нужного количества единичных бит.
n = -34
print(hex(n & 0xFF)) # 0xde - 8 бит
print(hex(n & 0xFFFF)) # 0xffde - 16 бит
print(hex(n & 0xFFFFFFFF)) # 0xffffffde - 32 битаТакая же логика работает и для двоичной записи, ее дает функция bin(), а восьмеричную - oct(). Все три функции принимают только целые числа и возвращают строку с префиксом.
Шестнадцатеричная запись байтов и дробных чисел
У байтовых строк есть собственный метод hex(). Он возвращает строку без префикса, по две цифры на каждый байт, и с Python 3.8 умеет вставлять разделитель. Обратную операцию выполняет bytes.fromhex(). Подробнее о байтовых объектах рассказано в статье о функции bytes.
data = 'Привет'.encode('utf-8')
print(data.hex()) # d0bfd180d0b8d0b2d0b5d182
print(data.hex(' ')) # d0 bf d1 80 d0 b8 d0 b2 d0 b5 d1 82
print(bytes.fromhex('d0 bf d1 80').decode()) # ПрТакой вывод удобен, когда нужно посмотреть, как строка выглядит в конкретной кодировке, или сравнить контрольные суммы. Например, метод hexdigest() у объектов hashlib возвращает хеш именно в шестнадцатеричном виде.
Для чисел с плавающей точкой применяется метод float.hex(). Он показывает точное двоичное представление числа: мантиссу в шестнадцатеричной записи и двоичный порядок после буквы p.
print((2.5).hex()) # 0x1.4000000000000p+1
print((0.1).hex()) # 0x1.999999999999ap-4
print(float.fromhex('0x1.4p+1')) # 2.5Вывод для 0.1 наглядно показывает, почему 0.1 + 0.2 != 0.3: десятичную дробь 0.1 нельзя точно записать в двоичной системе, и мантисса обрезается. Метод float.fromhex() восстанавливает число без потери точности. Описание функции есть в документации Python.
Частые вопросы
Короткие ответы на популярные вопросы о шестнадцатеричной записи.
Как убрать 0x из результата hex()?
Используйте форматирование: f'{n:x}' или format(n, 'x'). Срез hex(n)[2:] подходит только для неотрицательных чисел.
Как получить заглавные буквы?
Спецификатор X: f'{255:X}' вернет FF. Можно и hex(255).upper(), но тогда префикс тоже станет заглавным: 0XFF.
Можно ли передать в hex() свой объект?
Да, если в классе определен метод __index__, возвращающий целое число. Именно его hex(), bin() и oct() вызывают у аргументов, которые не являются int.
Как перевести строку текста в hex?
Сначала закодируйте строку в байты, затем вызовите метод байтов: 'abc'.encode().hex() вернет 616263.