2017-07-29 50 views
9

Chcę zrobić sprintf na python3, ale z obiektami surowych bajtów, bez konieczności wykonywania ręcznych konwersji dla% s do pracy. Zatem weź obiekt bajtowy jako "szablon", a także dowolną liczbę obiektów dowolnego typu i zwróć obiekt renderowanych bajtów. W ten sposób operator sprintf% Pythona 2 zawsze działał.Jak uzyskać formatowanie w formacie sprintf dla obiektów bajtów w pythonie 3?

b'test %s %s %s' % (5, b'blah','strblah') # python3 ==> error 
Traceback (most recent call last): 
    File "<input>", line 1, in <module> 
TypeError: %b requires bytes, or an object that implements __bytes__, not 'int' 

def to_bytes(arg): 
    if hasattr(arg,'encode'): return arg.encode() 
    if hasattr(arg,'decode'): return arg 
    return repr(arg).encode() 

def render_bytes_template(btemplate : bytes, *args): 
    return btemplate % tuple(map(to_bytes,args)) 

render_bytes_template(b'this is how we have to write raw strings with unknown-typed arguments? %s %s %s',5,b'blah','strblah') 

# output: b'this is how we have to render raw string templates with unknown-typed arguments? 5 blah strblah' 

Ale w Pythonie 2, to jest po prostu zbudowany w:

'example that just works %s %s %s' % (5,b'blah',u'strblah') 
# output: 'example that just works 5 blah strblah' 

Czy istnieje sposób, aby zrobić to w Pythonie 3, ale wciąż osiągnąć taką samą wydajność python 2? Proszę powiedz mi, że czegoś mi brakuje. Zastąpienie tutaj polega na implementacji w cytoncie (lub czy istnieją tam biblioteki dla pythona 3, które pomagają w tym?), Ale nadal nie widzą powodu, dla którego zostały usunięte z biblioteki standardowej innej niż niejawne kodowanie obiektu string. Czy nie możemy po prostu dodać metody bajtów, takich jak format_any()?

Nawiasem mówiąc, to nie jest tak proste, jak to cop-out:

def render_bytes_template(btemplate : bytes, *args): 
    return (btemplate.decode() % args).encode() 

Nie tylko ja nie chcę robić niepotrzebnego kodowanie/dekodowanie, ale args bajty są repr'd zamiast wstrzykiwany na surowo.

+1

Zauważ, że Python 3 chroni cię teraz przed błędami, które zostały ukryte pod linią wodną w Pythonie 2. Spróbuj ''unicode:% s'% (u'Ünîcódæ,)' na przykład na rozmiar. –

Odpowiedz

1

Czy coś takiego działa dla Ciebie? Musisz tylko upewnić się, że kiedy zaczynasz jakąś bytes przedmiot owinąć go w nowym B bajtów podobnego obiektu, która przeciąża % i %= operatory:

class B(bytes): 
    def __init__(self, template): 
     self._template = template 

    @staticmethod 
    def to_bytes(arg): 
     if hasattr(arg,'encode'): return arg.encode() 
     if hasattr(arg,'decode'): return arg 
     return repr(arg).encode() 

    def __mod__(self, other): 
     if hasattr(other, '__iter__') and not isinstance(other, str): 
      ret = self._template % tuple(map(self.to_bytes, other)) 
     else: 
      ret = self._template % self.to_bytes(other) 
     return ret 

    def __imod__(self, other): 
     return self.__mod__(other) 

a = B(b'this %s good') 
b = B(b'this %s %s good string') 
print(a % 'is') 
print(b % ('is', 'a')) 

a = B(b'this %s good') 
a %= 'is' 
b = B(b'this %s %s good string') 
b %= ('is', 'a') 
print(a) 
print(b) 

This wyjścia:

b'this is good' 
b'this is a good string' 
b'this is good' 
b'this is a good string' 
+1

Szczerze mówiąc, nie wiem, czy moje pytanie jest bardziej narzekaniem, czy uczciwym pytaniem o projekt, który staje na drodze do wydajności. Dziękuję za Twój wkład. Jeśli nikt nie odpowie w ciągu tygodnia, dam ci nagrodę. – parity3

+0

Myślę, że to uczciwe pytanie, nie jestem pewien, jakie są koszty wydajności w porównaniu z .format lub f-stringi. – mattjegan

+1

.format i f-stringi wymagają dekodowania(), więc będzie gorzej. Czytałem w innych postach online, że praca z unicode jest w przybliżeniu o połowę szybsza w pracy z bajtami w ogóle. Nie jest to straszne, ale przy wielu obciążeniach boli, gdy wszystko, co chcesz zrobić, to skomponowanie bajtów z innych bajtów, i tak, odpowiedź brzmi, by sparować wszystkie wejścia przed kompozycją, która jest poważnym remontem. A użycie sześciu lub innych pomocników nie rozwiąże żadnego pogorszenia wydajności. Rozumiem, że pragnienie ma być wyraźne, ale zauważ, że polecenie print() akceptuje zarówno bajty, jak i unicode (więc niezupełnie). – parity3

2

Chcę zrobić sprintf na python3, ale z surowymi obiektami bajtów, bez konieczności wykonywania ręcznych konwersji dla% s do pracy.

Aby to działało, wszystkie argumenty związane z formatowaniem muszą już być: bytes.

Zmieniło się to od czasu Py2, co umożliwiło sformatowanie ciągów unicode w ciągu bajtów, ponieważ implementacja Py2 jest podatna na błędy, gdy tylko wprowadzony zostanie ciąg znaków Unicode ze znakami Unicode.

Np na Pythonie 2:

In [1]: '%s' % (u'é',) 
Out[1]: u'\xe9' 

Technicznie że jest poprawna, ale nie to, co deweloper przeznaczeniem. Nie bierze również pod uwagę żadnego użytego kodowania.

w Pythonie 3 OTOH:

In [2]: '%s' % ('é',) 
Out[2]: 'é' 

Dla ciągów bajtów formatowanie, korzystanie bajtowych argumentów typu String (Py3.5 + tylko)

b'%s %s' % (b'blah', 'strblah'.encode('utf-8')) 

Inne typy, takie jak całkowite muszą być przekształcone w ciągi bajtów także.

+0

Dzięki za wzmocnienie moich obserwacji w pytaniu. Istnieją jednak pewne rozbieżności. Pierwszy off print() może zająć obiekt bajtowy, obiekt int oraz kod Unicode. Można więc twierdzić, że w ogóle nie jest to jednoznaczne. Ponadto, zwykłe ciągi znaków Unicode pozwalają% s pracować na czymkolwiek z repr, co również nie jest wyraźne. Więc poszli tylko w połowie drogi. Nie robi nic, tylko wprowadza zamieszanie i redukuje funkcje, ale to tylko moja opinia i oczywiście rzeczy się nie zmienią. Zacznę od obejścia, które próbuje nie zmniejszać wydajności python2 lub po prostu wywoływać zdalne wywołanie python2. – parity3

+0

Aby stwierdzić oczywiste, 'print' służy do drukowania. Drukowanie zakodowanego ciągu unicode i samego ciągu unicode powoduje różne wyniki. To jest wyraźne. Technicznie w obu przypadkach obiekt '__repr__' lub' __str__' jest używany do celów drukowania. "Regularne ciągi Unicode" działają z dowolnym innym ciągiem znaków Unicode, który w Py3 jest domyślny. Zatem repr napisy są unikodami, tak samo jak '__str__' i wszystko, co nie jest jawnie ustawione jako ciąg bajtów. To była decyzja zespołu programistów Pythona i trzeba się do tego przyzwyczaić. – danny