Этот код возвращает двоичную строку в 4-значной форме для данного шестнадцатеричного кода в виде строки.
def str_bin_in_4digits(aString):
retStr=""
for i in aString:
retStr = retStr+"{0:04b}".format(int(i, 16))+" "
return retStr.strip()
Например,
>>> str_bin_in_4digits("20AC")
0010 0000 1010 1100
Код работает, как ожидалось, и меня беспокоит, может ли он быть более элегантным, например, быстрее или с меньшим потреблением памяти?
3 ответа
Проблема состоит в том, чтобы преобразовать шестнадцатеричную строку в эквивалентную двоичную строку. Каждый символ во входной строке отображается на один или несколько символов в выходной строке, например, ‘0’ -> ‘0000’, … ‘A’ -> ‘1010’, … ‘F’ -> ‘ 1111 ‘. Это идеально подходит для string.translate()
table = {'0':'0000', '1':'0001', '2':'0010', '3':'0011',
'4':'0100', '5':'0101', '6':'0110', '7':'0111',
'8':'1000', '9':'1001', 'A':'1010', 'B':'1011',
'C':'1100', 'D':'1101', 'E':'1110', 'F':'1111'}
def str_bin_in_4digits(hex_string):
return hex_string.upper().translate(table)
Это в 6-7 раз быстрее.
Я не знаю, есть ли способ потребления памяти быстрее или меньше, но вы можете написать его более элегантно, используя список
def str_bin_in_4digits(aString):
data = []
for i in aString:
data.append( "{0:04b}".format(int(i, 16)) )
retStr = " ".join(data)
return retStr # doesn't need strip()
а затем вы можете написать это также как понимание списка
def str_bin_in_4digits(aString):
data = ["{0:04b}".format(int(i, 16)) for i in aString]
retStr = " ".join(data)
return retStr # doesn't need strip()
а затем вы можете даже сократить до одной строки
def str_bin_in_4digits(aString):
return " ".join(["{0:04b}".format(int(i, 16)) for i in aString])
Возможно, даже при использовании понимания списка это может быть немного быстрее, но для небольшой строки вы можете этого не увидеть.
Теперь вам нужно выбрать только наиболее читаемую версию.
Проблема в том, что у вас есть 4-bits values
(одинарный шестнадцатеричный символ).
Для полного 8-bits
вы можете преобразовать из шестнадцатеричного в байты, используя
bytes_data = bytes.fromhex('0A 0B 0C')
bytes_data = bytes.fromhex('0A0B0C')
а позже вы снова можете использовать понимание списка — используя f-string
это могло быть даже короче
data = [f'{x:04b}' for x in bytes_data]
Но с 8-bits
вам придется разделить его на 4-bits
используя что-то вроде этого
bytes_data[0] >> 4, bytes_data[0] & 0x0f
- 1
Если вы считаете, что этот вопрос не по теме, зачем на него отвечать?
— Райндериен
@Reinderien, потому что мне нравится решать проблемы. И эта проблема мне кажется интересной. Но обычно я делаю это на Stackoverflow.
— легкий
пожалуйста, избегайте использования
camelCasing
для переменных в питоне 🙂— hjpotter92
@ hjpotter92 OP используется
aString
поэтому я решил оставить его и не добавлять в ответ PEP 8 — Руководство по стилю кода Python— легкий
Рассмотрение
У вас не так много кода для проверки, поэтому он обязательно будет коротким.
- PEP-8: Руководство по стилю кода Python рекомендует:
snake_case
для функций, переменных и параметров. ТакaString
должно бытьa_string
, а такжеretVal
должно бытьret_val
.
- Лучшие имена параметров
- Что такое
aString
?"Hello World"
является строкой, но мы не можем ее использовать, потому что на самом деле вы ожидаете шестнадцатеричную строку. Возможноhex_string
было бы лучшим именем параметра. - По аналогии,
binary_string
было бы более наглядным, чемretStr
.
- Что такое
- А
'''docstring'''
было бы полезно для функции. - Подсказки типа также были бы полезны.
Альтернативная реализация
Посимвольное выполнение действий неэффективно. Обычно гораздо быстрее позволить Python выполнять работу самому с его эффективными, оптимизированными функциями нативного кода.
Форматирование строк Python поддерживает добавление разделителя запятой между тысячами групп.
>>> f"{123456789:,d}"
'123,456,789'
Он также поддерживает добавление подчеркивания между группами из 4 цифр при использовании кодов двоичного или шестнадцатеричного формата:
>>> f"{548151468:_x}"
'20ac_20ac'
>>> f"{0x20AC:_b}"
'10_0000_1010_1100'
Это большая часть пути к тому, что вы ищете. Просто нужно превратить символы подчеркивания в пробелы с помощью .replace(...)
и заполните начальными нулями, добавив к строке формата флаг ширины и нулевого заполнения.
def str_bin_in_4digits(hex_string: str) -> str:
"""
Turn a hex string into a binary string.
In the output string, binary digits are space separated in groups of 4.
>>> str_bin_in_4digits('20AC')
'0010 0000 1010 1100'
"""
value = int(hex_string, 16)
width = len(hex_string) * 5 - 1
bin_string = f"{value:0{width}_b}"
return bin_string.replace('_', ' ')
if __name__ == '__main__':
import doctest
doctest.testmod(verbose=True)
В зависимости от вашего определения элегантного, вы можете сделать это в одну строку:
def str_bin_in_4digits(hex_string: str) -> str:
"""
Turn a hex string into a binary string.
In the output string, binary digits are space separated in groups of 4.
>>> str_bin_in_4digits('20AC')
'0010 0000 1010 1100'
"""
return f"{int(hex_string,16):0{len(hex_string)*5-1}_b}".replace('_', ' ')