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

Боря се с проект, който се опитва да поддържа същия код, работещ с Python2.6, Python 2.7 и Python 3.x.

Този проект използва python_2_unicode_compatible class decorator, за да съхранява не-unicode стойности в тип 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_compatible декоратор на клас. - person suizokukan; 24.03.2015