Я пишу простой код для связи с несколькими идентичными устройствами RS-232 с помощью pyserial. Отправляю команду и получаю ответ. 6-й элемент в ответе — это мой ID. Этот идентификатор используется для определения того, с каким устройством я разговариваю.
Я хочу иметь более элегантный синтаксис, который извлекает 6-й элемент как целое число как в Python 3, так и в Python 2. Есть ли более элегантный способ, чем просто написать две разные функции, которые я вызываю в зависимости от того, какой Python используется.
В случае Python 3
>>> port = Serial('/dev/cu.usbserial4')
>>> port.baudrate = 9600
>>> port.timeout = 0.4
>>> port.write(b"/1?80\r")
6
>>> reply = port.readline()
>>> reply
b'\xff/0`ZA4\x03\r\n'
>>> reply[6]
52
>>> chr(reply[6])
'4'
>>> int(chr(reply[6]))
4
В случае Python 2
>>> port = Serial('/dev/cu.usbserial4')
>>> port.baudrate = 9600
>>> port.timeout = 0.4
>>> port.write(b"/1?80\r")
6
>>> reply = port.readline()
>>> reply
'\xff/0`ZA4\x03\r\n'
>>> reply[6]
'4'
>>> int(reply[6])
4
Результатом port.readline()
являются двоичные данные (называемые str
в Python 2, bytes
в Python 3). Таким образом, вопрос заключается в том, как обрабатывать двоичные данные совместимым с Python 2 и Python 3 способом. Есть несколько способов сделать это. Следующие три решения дают одинаковый результат (число = 52) для Python 2 и 3.
ДЛЯ ДВОИЧНЫХ ДАННЫХ
Решение 1
Если вы знаете, как кодируются данные, вы можете правильно их раскодировать. Это приведет к «текстовой строке Unicode» (называется unicode
в Python 2, str
в Python 3).
reply = b'\xff/0`ZA4\x03\r\n'
decoded_reply = reply.decode('latin-1')
number = ord(decoded_reply[6])
Решение 2
Более общим решением было бы использование модуля struct
для декодирования двоичных данных:
import struct
reply = b'\xff/0`ZA4\x03\r\n'
number = struct.unpack('B', reply[6:7])[0]
Решение 3
Вы также можете использовать модуль six
:
Six is a Python 2 and 3 compatibility library. It provides utility functions for smoothing over the differences between the Python versions with the goal of writing Python code that is compatible on both Python versions. See the documentation for more information on what is provided.
Например:
import six
reply = b'\xff/0`ZA4\x03\r\n'
number = six.byte2int(reply[6:7])
Обратите внимание, что эта библиотека также предоставляет решения для многих других проблем совместимости. Поэтому, если вы пишете более крупное приложение, которое должно быть совместимо с Python 2 и 3, на него, безусловно, стоит обратить внимание.
ДЛЯ ДАННЫХ ASCII
Если ваш идентификатор представляет собой число в кодировке ASCII в диапазоне от 0 до 9, лучшим решением будет следующее решение. Использование struct
в этом случае не имеет смысла.
reply = '\xff/0`ZA4\x03\r\n' # input in Python 2
reply = b'\xff/0`ZA4\x03\r\n' # input in Python 3
number = int(reply.decode('latin-1')[6])
# or if your reply is mixed binary and ASCII and you're only interested in byte 6:
number = int(reply[6:7].decode('ascii'))
Число будет равно 4 как в Python 2, так и в 3.
В качестве альтернативы вы можете использовать одно из «двоичных» решений и вычесть из результата 48 (= символ ASCII «0»).
(Все приведенные выше примеры были протестированы с Python 2.7 и 3.7)
Отредактировано позже: так что все 3 будут работать, но это не выглядит красиво. Например, решение № 3 выглядит так: Python 3: In [31]: int(chr(six.byte2int(b[6:7])) )) Out[31]: 4 и Python 2: In [13]: int (chr(six.byte2int('\xff/0`ZA4\x03\r\n'[6:7]))) Выход[13]: 4
@ Валентин, я не понимаю, что ты имеешь в виду. Я протестировал все три приведенных выше примера с Python 2.7 и Python 3.7 (в Python 2.7 вы могли написать '\xff..'
вместо b'\xff..'
, чтобы точно соответствовать полученному ответу, но результат тот же). В вашем последнем примере вы фактически делаете int(chr(52))
, что равно 4. Почему вы это делаете? Я не могу протестировать код с реальным модулем pyserial
, но, учитывая reply
, как указано в вашем вопросе, во всех трех решениях переменная number
получит значение 52 в Python 2 и 3. Разве это не ожидаемый результат? Вы хотите, чтобы он вместо этого возвращал 4?
@ Валентин, я обновил свой ответ. Пожалуйста, дайте мне знать, если это то, что вы ожидали.
Спасибо за 3 возможных решения. Однако только первый работает как в Python 2, где readline() возвращает строку, так и в Python 3, где ответом являются байты. Решение 3 в Python 3: IIn [25]: six.byte2int(b'\xff/0
ZA4\x03\r\n'[6:7]) Out[25]: 52; Solution 2 in Python2: In [8]: struct.unpack('B','\xff/0
ZA4\x03\r\n'[6:7]) Out[8]: (52,) ; Решение 1 в Python 2: В [9]: '\xff/0ZA4\x03\r\n'.decode('latin-1')[6] Out[9]: u'4' Solution 1 in Python 3:In [26]: b'\xff/0
ZA4\x03\r\n'.decode('latin-1')[6] Out[26]: '4'