Как получить совместимое поведение type() в python 2 и 3 с помощью unicode_literals?

Этот вопрос поразительно похож на этот, однако предложение в комментариях не работает (больше?), как показано ниже.

Я пытаюсь написать пакет, совместимый с python2-3, и в одном из моих методов есть генератор классов, а type() вызывает у меня проблемы в тестах python-2.7:

Python 2.7.13 (default, Mar 18 2017, 17:03:32) 
[GCC 4.2.1 Compatible Apple LLVM 7.0.2 (clang-700.1.81)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> from __future__ import unicode_literals
>>> from builtins import str
>>> type('MyClass', (object,), {})
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: type() argument 1 must be string, not unicode
>>> type(str('MyClass'), (object,), {})
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: type() argument 1 must be string, not newstr

На странице обзора Python-Future говорится:

# Compatible output from isinstance() across Py2/3:
assert isinstance(2**64, int)        # long integers
assert isinstance(u'blah', str)
assert isinstance('blah', str)       # only if unicode_literals is in effect

Я ожидал, что это даст мне последовательное поведение везде, где требуются строки, но, по-видимому, нет.

Каков правильный, независимый от версии способ сделать это? Другой вопрос, на который я ссылался, был задан в эпоху python-2.6, и похоже, что с тех пор поведение изменилось. Я не думаю, что могу просто сбросить unicode_literals, так как я сталкиваюсь с проблемами переносимости (в другом месте) с вызовами hashlib, если у меня его нет.


person mpounsett    schedule 22.03.2017    source источник
comment
почему вы импортируете unicode_literals? кажется, что это больше проблем, чем решений, для этого конкретного примера вам не нужно, чтобы он работал как в py2, так и в py3   -  person Copperfield    schedule 22.03.2017
comment
если вы не выполняете какие-либо манипуляции со строками, требующие различия между строкой байтов и строкой юникода, и/или вы выполняете декодирование и кодирование и т. д., тогда вам, скорее всего, не понадобится unicode_literal, оставьте его как есть, и он будет работать нормально в py2 и py3   -  person Copperfield    schedule 22.03.2017
comment
Я не могу гарантировать, что строки, переданные в эту библиотеку, никогда не будут Unicode. И есть некоторые методы, которые явно требуют строк Unicode. Кажется, что рекомендуемый способ справиться с этим в коде 2-3 — использовать unicode_literals.   -  person mpounsett    schedule 22.03.2017


Ответы (1)


Не используйте builtins.str(), используйте обычный str, который поставляется с вашей версией Python:

>>> from __future__ import unicode_literals
>>> type(str('MyClass'), (object,), {})
<class '__main__.MyClass'>

Это работает как в Python 2, так и в 3. Если модуль future.builtins заменяет встроенный тип str по умолчанию, используйте модуль __builtin__:

try:
    # Python 2
    from __builtin__ import str as builtin_str
except ImportError:
    # Python 3
    from builtins import str as builtin_str

MyClass = type(builtin_str('MyClass'), (object,), {})
person Martijn Pieters    schedule 10.04.2017
comment
Похоже, мне действительно нужно использовать смесь этого и встроенного.str() ... в большинстве мест в моем коде мне нужно поведение встроенного.str(), совместимое с Python3 (например, ожидая, что issubclass(x, str) будет возвращать True для литералов Unicode), а затем для некоторых очень немногих конкретных случаев (например, type()) мне нужен __builtin__.str(). Это кажется правильным? - person mpounsett; 11.04.2017
comment
@mpounsett: конечно, и все, что вам нужно сделать, это убедиться, что вы разделяете их, назначая им разные имена. Поскольку модуль future.builtins предназначен для вставной прокладки для модуля builtins Python 3, просто продолжайте использовать from builtins import str, но используйте трюк try..except ImportError с import .. as ..., чтобы сохранить доступ к объекту str Python 2 под другим именем. - person Martijn Pieters; 11.04.2017