Python: Lazy cadena de decodificación
-
11-09-2019 - |
Pregunta
Estoy escribiendo un programa de análisis, y hay un montón de texto para decodificar, pero la mayoría de mis usuarios sólo se preocupan por unos campos de todos los datos. Así que sólo quiero hacer la decodificación cuando un usuario utiliza realmente algunos de los datos. Es esta una buena manera de hacerlo?
class LazyString(str):
def __init__(self, v) :
self.value = v
def __str__(self) :
r = ""
s = self.value
for i in xrange(0, len(s), 2) :
r += chr(int(s[i:i+2], 16))
return r
def p_buffer(p):
"""buffer : HASH chars"""
p[0] = LazyString(p[2])
Es el único método que necesito para anular?
Solución
No estoy seguro de cómo implementar una subclase de cadena es de gran ventaja aquí. Me parece que si se está procesando una corriente que contiene petabytes de datos, siempre que se haya creado un objeto que no es necesario ya has perdido el juego. Su primera prioridad debe ser hacer caso omiso tanto de entrada como le sea posible.
Por supuesto que podría construir una clase similar a una cuerda que hizo esto:
class mystr(str):
def __init__(self, value):
self.value = value
self._decoded = None
@property
def decoded(self):
if self._decoded == None:
self._decoded = self.value.decode("hex")
return self._decoded
def __repr__(self):
return self.decoded
def __len__(self):
return len(self.decoded)
def __getitem__(self, i):
return self.decoded.__getitem__(i)
def __getslice__(self, i, j):
return self.decoded.__getslice__(i, j)
y así sucesivamente. Una cosa rara de hacer esto es que si subclase str
, todos los métodos que no se implementan de forma explícita será llamado en el valor que se pasa al constructor:
>>> s = mystr('a0a1a2')
>>> s
¡¢
>>> len(s)
3
>>> s.capitalize()
'A0a1a2'
Otros consejos
No veo ningún tipo sobre la evaluación perezosa en su código. El hecho de que se utiliza xrange
sólo significa que la lista de números enteros de 0
a len(s)
se generará en la demanda. Todo el r
cadena será decodificado durante la conversión de cadenas de todos modos.
La mejor manera de poner en práctica la secuencia perezosa en Python está utilizando generadores . Usted podría intentar algo como esto:
def lazy(v):
for i in xrange(0, len(v), 2):
yield int(v[i:i+2], 16)
list(lazy("0a0a0f"))
Out: [10, 10, 15]
Lo que está haciendo está construido en ya:
s = "i am a string!".encode('hex')
# what you do
r = ""
for i in xrange(0, len(s), 2) :
r += chr(int(s[i:i+2], 16))
# but decoding is builtin
print r==s.decode('hex') # => True
Como se puede ver toda su decodificación se s.decode('hex')
.
Pero decodificación "perezosa" suena como la optimización prematura a mí. Se necesitaría gigabytes de datos para siquiera notarlo. Trate de perfiles, la .decode
es 50 veces más rápido que el código antiguo ya.
Tal vez usted quiere algo como esto:
class DB(object): # dunno what data it is ;)
def __init__(self, data):
self.data = data
self.decoded = {} # maybe cache if the field data is long
def __getitem__(self, name):
try:
return self.decoded[name]
except KeyError:
# this copies the fields data
self.decoded[name] = ret = self.data[ self._get_field_slice( name ) ].decode('hex')
return ret
def _get_field_slice(self, name):
# find out what part to decode, return the index in the data
return slice( ... )
db = DB(encoded_data)
print db["some_field"] # find out where the field is, get its data and decode it
Los métodos que necesita para anular realmente dependen de cómo están planeando usar nuevo tipo cadena.
Sin embargo, usted str tipo basado parece un poco sospechoso para mí, ¿has mirado en la implementación de str para comprobar que tiene el atributo value
que se está configurando en su __init__()
? La realización de una dir(str)
no indica que existe tal atributo en str
. Siendo este el caso, los métodos str normales no estarán operando en su dato en absoluto, no creo que ese es el efecto que desee lo contrario de lo que sería la ventaja de sub-classing.
subclasificar los tipos de datos base es un poco extraño de todas formas a menos que tenga necesidades muy específicas. Para la evaluación perezosa desea usted es probablemente mejor de la creación de su clase que contiene una cadena en lugar de sub-clasificar a str y escribir su código de cliente para trabajar con esa clase. A continuación, será libre de añadir la evaluación justo a tiempo que desee en un número de maneras un ejemplo utilizando el protocolo descriptor se puede encontrar en esta presentación: Python modelo de objetos (busque "Jit clase (objeto)" para llegar a la sección correspondiente)
La pregunta es incompleta, en la que la respuesta dependerá de los detalles de la codificación que utiliza.
Es decir, si se codifica una lista de cadenas como cadenas Pascal (es decir, el prefijo de longitud de serie codificado como un número entero de tamaño fijo), y dice que quiere leer la cadena número 100 de la lista, puede buscar () hacia adelante para cada uno de los primeros 99 cuerdas y no leer su contenido en absoluto. Esto le dará alguna ganancia de rendimiento si las cadenas son grandes.
Si, otoh, codificar una lista de cadenas como concatenados 0 terminados en stirngs, que tendría que leer todos los bytes hasta los 100 0.
Además, usted está hablando acerca de algunos "campos", pero su ejemplo se ve completamente diferente.