Python2 и 3: сравнить str и unicode

Я борюсь с проектом, пытаясь сохранить один и тот же код с Python2.6, Python 2.7 и Python 3.x.

В этом проекте для хранения не-юникодные значения в типе str.

Мне нужно протестировать функцию foo, возвращающую тип str (а не тип unicode); возвращаемое значение заполняется символами, отличными от ascii.

Все, что я хочу, это проверить значение, возвращаемое этой функцией, на моей собственной строке, например:

from __future__ import unicode_literals  # so that "àbcéfg" will be read u"àbcéfg"
bool_test = (foo() == "àbcéfg")

Я застрял, так как «àbcéfg» будет рассматриваться в Python2 как строка unicode, а в Python3 — как строка str.

Например, с Python2 этот код вызывает следующую ошибку:

При сравнении эквивалентности Unicode не удалось преобразовать оба аргумента в Unicode, интерпретируя их как неравные

Есть ли уникальный способ добиться сравнения, общего для Python2 и Python3?

Я попробовал несколько решений (например, преобразование str в байты), но безуспешно.

Любая идея, чтобы помочь мне?


person suizokukan    schedule 24.03.2015    source источник
comment
Итак, в Python 2 это должно возвращать строку байтов, а в Python 3 — строку Unicode? Это... сбивает с толку.   -  person Martijn Pieters    schedule 24.03.2015


Ответы (1)


Вы правильно сравниваете, но foo() не возвращает значение Unicode. Он возвращает строку байтов в Python 2:

>>> def foo():
...     return u"àbcéfg".encode('utf8')
... 
>>> foo() == u"àbcéfg"
__main__:1: UnicodeWarning: Unicode equal comparison failed to convert both arguments to Unicode - interpreting them as being unequal
False

Либо исправьте foo(), либо передайте его функции, которая будет декодировать возвращаемое значение, если оно не является значением Unicode (здесь используется six модуль для объединения двоичных типов в Python 2 и 3):

import six

def ensure_unicode(value, encoding='utf8'):
    if isinstance(value, six.binary_type):
        return value.decode(encoding)
    return value

bool_test = ensure_unicode(foo()) == "àbcéfg"

Если foo() предназначен для возврата строки байтов в Python 2 и строки Unicode в Python 3, то вышеприведенное будет продолжать работать, но не будет специально проверять в Python 2, что это правильный тип; вы можете добавить для этого отдельный тест isinstance():

foo_result = foo()
bool_test = isinstance(foo_result, str) and ensure_unicode(foo_result) == "àbcéfg"
person Martijn Pieters    schedule 24.03.2015
comment
Спасибо. К сожалению, мне не разрешено использовать какую-либо внешнюю библиотеку. foo() возвращает значение str ( -› type(foo()) возвращает str ) : я забыл сказать, что класс, над которым я работаю, изменен python_2_unicode_совместимым декоратор класса. - person suizokukan; 24.03.2015