Calificación:
  • 0 voto(s) - 0 Media
  • 1
  • 2
  • 3
  • 4
  • 5
obtener el tamaño en bytes de un objeto
#1
hola,

estoy trabajando en envio y recepcion de datos a traves sockets "a pelo" (sin librerias añadidas). Los datos a transmitir pueden ser diccionarios, tablas de datos de Pandas, etc... El tamaño de cada envio `puede ser considerablemente grande o extremadamente pequeño.

No es lo mismo sock.recv(1024) que sock.recv(1024000).

El problema de dividr el objeto en pedazos lo tengo solventado de acuerdo con este articulo

y esta discusion en stackoverflow

en mi codigo lo he resumido asi:
Código:
   """
   definimos el método de conveniencia _send que prefija un mensaje dado con su
   longitud antes de enviarlo a través de una conexión de socket determinada.
   Será utilizado por el servidor para transmitir un mensaje de un cliente a
   todos los demás clientes conectados.
   """
   def _send(self, sock, msg):
       """
       Prefija cada mensaje con una longitud de 4 bytes antes de enviar.

       :param sock: el socket entrante
       :param msg: el mensaje a enviar
       """
       # Empaqueta el mensaje con 4 bytes en cabecera que representan la longitud del mensaje
       msg = struct.pack('>I', len(msg)) + msg
       # Envia el mensaje empaquetado
       sock.send(msg)
       
   """
   Definimos la función _receive que contiene la lógica de implementación de recepción de datos.
   De acuerdo con el protocolo de mensajes que hemos elegido construir a través de TCP / IP,
   cada mensaje del cliente tiene un prefijo de 4 bytes que representa su longitud.
   Entonces, el servidor, a la recepcion de cada mensaje, lo descomprimirá y leerá sus
   primeros 4 bytes para obtener la longitud.
   Una vez que se ha adquirido esta información, el servidor llamará varias veces al
   método recv para obtener el mensaje total.
   """
   def _receive(self, sock):
       """
       Recibe un mensaje entrante del cliente y lo desempaqueta.

       :param sock: el socket entrante
       :return: el mensaje desempaquetado
       """
       data = None
       # Recupera los primeros 4 bytes del mensaje
       tot_len = 0
       while tot_len < self.RECV_MSG_LEN:
           msg_len = sock.recv(self.RECV_MSG_LEN)
           tot_len += len(msg_len)
       # Si el mensaje tiene los 4 bytes que representan la longitud ...
       if msg_len:
           data = ''
           # Desempaqueta el mensaje y obtiene la longitud del mensaje
           msg_len = struct.unpack('>I', msg_len)[0]
           tot_data_len = 0
           while tot_data_len < msg_len:
               # Recupera el fragmento del tamaño máximo de RECV_BUFFER
               chunk = sock.recv(self.RECV_BUFFER)
               # Si no hay el pedazo esperado ...
               if not chunk:
                   data = None
                   break # ... Simplemente sale del bucle
               else:
                   # Une el contenido de los pedazos
                   data += chunk
                   tot_data_len += len(chunk)
       return data

Los datos de, digamos un diccionario, lo serializo con pickle (json tambien valdria) antes de enviarlo (sock.sendall)

¿como obtengo el tamaño en numero de bytes exacto de este objeto serializado?

He probado con sys.getsizeof(objeto), pero parece que no me funciona bien para el esquema mostrado arriba.

¿alguna sugerencia?

Edito:

me contesto a mi mismo. Parece que la solución es serializar a JSON. Esta pequeña librería funciona:

https://github.com/mdebbar/jsonsocket/bl...nsocket.py

La solución está en poder usar len(objeto) en lugar de sys.getsizeof(objeto) para obtener el numero de pytes. Las funciones clave son:


Código:
def _send(socket, data):
 try:
   serialized = json.dumps(data)
 except (TypeError, ValueError), e:
   raise Exception('You can only send JSON-serializable data')
 # send the length of the serialized data first
 socket.send('%d\n' % len(serialized))
 # send the serialized data
 socket.sendall(serialized)

def _recv(socket):
 # read the length of the data, letter by letter until we reach EOL
 length_str = ''
 char = socket.recv(1)
 while char != '\n':
   length_str += char
   char = socket.recv(1)
 total = int(length_str)
 # use a memoryview to receive the data chunk by chunk efficiently
 view = memoryview(bytearray(total))
 next_offset = 0
 while total - next_offset > 0:
   recv_size = socket.recv_into(view[next_offset:], total - next_offset)
   next_offset += recv_size
 try:
   deserialized = json.loads(view.tobytes())
 except (TypeError, ValueError), e:
   raise Exception('Data received was not in JSON format')
  return deserialized
Responder
#2
Hola. Si entendí bien, tu mensaje fue serializado vía pickle, por lo tanto es una secuencia de bytes. ¿Cuál sería el inconveniente en usar len() para calcular tamaño?

Saludos
¡No te pierdas nuestro curso oficial en Udemy para aprender Python, bases de datos SQL, orientación a objetos, tkinter y mucho más!

También ofrecemos consultoría profesional de desarrollo en Python para personas y empresas.
Responder
#3
(30-01-2018, 03:24 AM)Francisco escribió: Hola. Si entendí bien, tu mensaje fue serializado vía pickle, por lo tanto es una secuencia de bytes. ¿Cuál sería el inconveniente en usar len() para calcular tamaño?

Saludos

francisco,

pongo las diferencias:


Código:
>>> import pickle
>>> a = (1,2,3)
>>> b = pickle.dumps(a)
>>> print(b)
b'\x80\x03K\x01K\x02K\x03\x87q\x00.'
>>> print(len(b))
12   ---> me trae algun byte de algun objeto "madre"
>>> import sys
>>> c= sys.getsizeof(b)
>>> print(c)
45
>>>
>>>
>>> import json
>>> d=json.dumps(a)
>>> print(d)
[1, 2, 3]
>>> print(len(d))
9 ------> aqui está la diferencia : los 2 corchetes + 2 espacios + 2 comas + 3 digitos = 9
>>>
>>> print(sys.getsizeof(d))
58
>>>
Responder
#4
En realidad no debería interesarte el tamaño retornado por sys.getsizeof(), dado que probablemente obtenga el tamaño del objeto como un todo (por ejemplo, incluyendo el espacio que ocupa la variable que lleva la cuenta de la cantidad de elementos en una colección) en lugar de su contenido. Esa es la razón por la cual la función calcula dos tamaños diferentes para una lista y una tupla que contienen los mismos elementos.

Por otro lado, pickle y JSON son dos formas de hacer lo mismo: codificar y decodificar datos. Al fin y al cabo json.dumps(a) será convertido a una secuancia de bytes para ser enviado por el socket. Puede que con JSON ahorres un par de bytes en diccionarios, listas o tuplas, pero ciertamente no puede serializar clases definidas por el usuario u otras colecciones de la librería estándar.

Saludos
¡No te pierdas nuestro curso oficial en Udemy para aprender Python, bases de datos SQL, orientación a objetos, tkinter y mucho más!

También ofrecemos consultoría profesional de desarrollo en Python para personas y empresas.
Responder


Salto de foro:


Usuarios navegando en este tema: 1 invitado(s)