Python __str__ kontra __unicode__


213

Czy istnieje konwencja Pythona, w której należy wdrożyć __str__()versus __unicode__(). Widziałem klasy zastępujące __unicode__()częściej niż, __str__()ale nie wydaje się to spójne. Czy istnieją konkretne zasady, kiedy lepiej wdrożyć jedną z drugą? Czy wdrożenie obu jest konieczne / dobre praktyki?

Odpowiedzi:


257

__str__()to stara metoda - zwraca bajty. __unicode__()jest nową, preferowaną metodą - zwraca znaki. Nazwy są nieco mylące, ale w wersji 2.x utknęliśmy z nimi ze względu na kompatybilność. Ogólnie rzecz biorąc, należy włożyć całe formatowanie ciągów __unicode__()i utworzyć __str__()metodę pośredniczącą:

def __str__(self):
    return unicode(self).encode('utf-8')

W 3.0 strzawiera znaki, więc te same metody są nazwane __bytes__()i __str__(). Zachowują się one zgodnie z oczekiwaniami.


2
masz na myśli tworzenie zarówno metod unicode, jak i str , czy po prostu utrzymywanie ciągów w _ (u "") i tworzenie ciągów (bez metody unicode)?
muntu

12
Czy jest jakaś pułapka we wdrażaniu tylko jednego z nich? Co się stanie, gdy tylko wdrożysz, __unicode__a następnie zrobisz str(obj)?
RickyA,

9
unicodepodnosi a NameErrorna Pythonie 3, czy prosty wzorzec działa zarówno na 2, jak i na 3?
bradley.ayers

1
@ bradley.ayers futurepakiet zapewnia również python_2_unicode_compatiblebez zależności Django.
Monkpit

1
To zależy. Ponieważ python3 nie używa Unicode, ale zamiast str ;) dla Unicode
Eddwin Paz

23

Gdybym specjalnie nie dbał o mikrooptymalizację rygoryzacji dla danej klasy, zawsze wdrażałbym __unicode__tylko, ponieważ jest bardziej ogólna. Kiedy zależy mi na tak drobnych problemach z wydajnością (co jest wyjątkiem, a nie regułą), posiadanie __str__tylko (kiedy mogę udowodnić, że nigdy nie będzie znaków spoza ASCII w łańcuchowym wyjściu) lub obu (gdy oba są możliwe), może Wsparcie.

Sądzę, że są to solidne zasady, ale w praktyce bardzo często WIEDZIEĆ, że nie będzie nic poza znakami ASCII bez wysiłku, aby to udowodnić (np. Forma strunowa ma tylko cyfry, znaki interpunkcyjne i być może krótką nazwę ASCII ;-), w której w przypadku przejścia do __str__podejścia „po prostu ” jest dość typowe (ale jeśli zespół programistów, z którym współpracowałem, zaproponowałby lokalne wytyczne, aby tego uniknąć, dałbym +1 w tej propozycji, ponieważ łatwo jest się mylić w tych sprawach ORAZ „przedwczesna optymalizacja jest źródłem wszelkiego zła w programowaniu” ;-).


2
W Pythonie 2.6.2 ostatnio się potknąłem, ponieważ instancje konkretnej wbudowanej podklasy wyjątków dawały różne wyniki dla str (e) i unicode (e). str (e) dało wynik przyjazny dla użytkownika; unicode (e) dawał różne, nieprzyjazne dla użytkownika dane wyjściowe. Czy to zachowanie jest uważane za błędne? Ta klasa to UnicodeDecodeError; Nie wymieniłem tego z góry, aby uniknąć nieporozumień - fakt, że wyjątek jest związany z Unicode, nie jest szczególnie istotny.
Paul Du Bois

13

Świat staje się coraz mniejszy, więc istnieje prawdopodobieństwo, że każdy napotkany ciąg będzie zawierał Unicode. Dlatego w przypadku każdej nowej aplikacji powinieneś przynajmniej ją podać __unicode__(). To, czy ty też przeskoczysz, __str__()to tylko kwestia gustu.


8

Jeśli pracujesz zarówno w Python2, jak i Python3 w Django, polecam dekorator kompatybilny z python_2_unicode_compliance:

Django zapewnia prosty sposób definiowania metod str () i unicode (), które działają w Pythonie 2 i 3: musisz zdefiniować metodę str () zwracającą tekst i zastosować dekorator python_2_unicode_compatible ().

Jak zauważono we wcześniejszych komentarzach do innej odpowiedzi, niektóre wersje future.utils również obsługują ten dekorator. W moim systemie musiałem zainstalować nowszy moduł przyszłości dla python2 i zainstalować przyszłość dla python3. Następnie jest funkcjonalny przykład:

#! /usr/bin/env python

from future.utils import python_2_unicode_compatible
from sys import version_info

@python_2_unicode_compatible
class SomeClass():
    def __str__(self):
        return "Called __str__"


if __name__ == "__main__":
    some_inst = SomeClass()
    print(some_inst)
    if (version_info > (3,0)):
        print("Python 3 does not support unicode()")
    else:
        print(unicode(some_inst))

Oto przykładowe dane wyjściowe (gdzie venv2 / venv3 są instancjami virtualenv):

~/tmp$ ./venv3/bin/python3 demo_python_2_unicode_compatible.py 
Called __str__
Python 3 does not support unicode()

~/tmp$ ./venv2/bin/python2 demo_python_2_unicode_compatible.py 
Called __str__
Called __str__

3

Python 2: Zaimplementuj tylko __str __ () i zwróć Unicode.

Kiedy __unicode__()jest pominięty, a ktoś dzwoni unicode(o)lub u"%s"%o, Python wywołuje o.__str__()i konwertuje na Unicode przy użyciu kodowania systemowego. (Zobacz dokumentację__unicode__() .)

Przeciwnie nie jest prawdą. Jeśli zaimplementujesz, __unicode__()ale nie __str__(), to kiedy ktoś zadzwoni str(o)lub "%s"%o, Python powróci repr(o).


Racjonalne uzasadnienie

Dlaczego to działa na zwracają unicodeod __str__()?
Jeśli __str__()zwraca kod Unicode, Python automatycznie konwertuje go na strkodowanie systemowe.

Jaka jest korzyść?
① Uwalnia od martwienia się o to, czym jest kodowanie systemowe (tj locale.getpreferredencoeding(…).). Osobiście jest to nie tylko niechlujne, ale myślę, że i tak powinien się tym zająć. ② Jeśli jesteś ostrożny, twój kod może wyjść kompatybilny z Pythonem 3, w którym __str__()zwraca Unicode.

Czy zwodnicze jest zwrócenie kodu Unicode z funkcji o nazwie __str__()?
Trochę. Być może już to robisz. Jeśli masz from __future__ import unicode_literalsna górze pliku, istnieje duża szansa, że ​​zwrócisz Unicode, nawet o tym nie wiedząc.

Co z Python 3?
Python 3 nie używa __unicode__(). Jeśli jednak zaimplementujesz, __str__()aby zwracał Unicode w Pythonie 2 lub Pythonie 3, to ta część kodu będzie kompatybilna krzyżowo.

Co jeśli chcę unicode(o)się zasadniczo różnić od str()?
Zaimplementuj zarówno __str__()(możliwe zwrot str) i __unicode__(). Wyobrażam sobie, że byłoby to rzadkie, ale możesz chcieć mieć zasadniczo różne dane wyjściowe (np. Wersje znaków specjalnych ASCII, jak ":)"dla u"☺").

Zdaję sobie sprawę, że niektórzy mogą uznać to za kontrowersyjne.


1

Warto zwrócić uwagę na osoby nieznające tej __unicode__funkcji niektóre z domyślnych zachowań, które ją otaczają w Pythonie 2.x, zwłaszcza gdy są zdefiniowane obok siebie __str__.

class A :
    def __init__(self) :
        self.x = 123
        self.y = 23.3

    #def __str__(self) :
    #    return "STR      {}      {}".format( self.x , self.y)
    def __unicode__(self) :
        return u"UNICODE  {}      {}".format( self.x , self.y)

a1 = A()
a2 = A()

print( "__repr__ checks")
print( a1 )
print( a2 )

print( "\n__str__ vs __unicode__ checks")
print( str( a1 ))
print( unicode(a1))
print( "{}".format( a1 ))
print( u"{}".format( a1 ))

daje następujące dane wyjściowe konsoli ...

__repr__ checks
<__main__.A instance at 0x103f063f8>
<__main__.A instance at 0x103f06440>

__str__ vs __unicode__ checks
<__main__.A instance at 0x103f063f8>
UNICODE 123      23.3
<__main__.A instance at 0x103f063f8>
UNICODE 123      23.3

Teraz, kiedy odkomentuję tę __str__metodę

__repr__ checks
STR      123      23.3
STR      123      23.3

__str__ vs __unicode__ checks
STR      123      23.3
UNICODE  123      23.3
STR      123      23.3
UNICODE  123      23.3
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.