@Pythonetc Abril 2019



Esta es la décima colección de consejos y programación de Python de mi feed @pythonetc.

Selecciones anteriores





Almacenar y enviar objetos a través de la red como bytes es un tema muy importante. Para estos fines, Python generalmente usa una serie de herramientas, discutamos sus ventajas y desventajas.

Como ejemplo, intentaré serializar un objeto Ciudades que contenga objetos Ciudad en un orden específico. Se pueden usar cuatro enfoques:

1. JSON. Legible por humanos, fácil de usar, pero consume mucha memoria. Lo mismo es cierto para los formatos YAML y XML.

class City: def to_dict(self): return dict( name=self._name, country=self._country, lon=self._lon, lat=self._lat, ) class Cities: def __init__(self, cities): self._cities = cities def to_json(self): return json.dumps([ c.to_dict() for c in self._cities ]).encode('utf8') 

2. Pickle. Esta es una herramienta nativa de Python, personalizable, consume menos memoria que JSON. Desventaja: Python debe usarse para recuperar datos.

 class Cities: def pickle(self): return pickle.dumps(self) 

3. Protobuf (y otros serializadores binarios, por ejemplo, msgpack). Consume incluso menos memoria, se puede usar desde cualquier lenguaje de programación, pero requiere escribir un esquema explícito:

 syntax = "proto2"; message City { required string name = 1; required string country = 2; required float lon = 3; required float lat = 4; } message Cities { repeated City cities = 1; } class City: def to_protobuf(self): result = city_pb2.City() result.name = self._name result.country = self._country result.lon = self._lon result.lat = self._lat return result class Cities: def to_protobuf(self): result = city_pb2.Cities() result.cities.extend([ c.to_protobuf() for c in self._cities ]) return result 

4. Manualmente. Puede empaquetar y desempaquetar datos manualmente utilizando el módulo struct . De esta forma, puede lograr el menor consumo de memoria posible, pero a veces es mejor usar protobuf , ya que admite versiones y esquemas explícitos.

 class City: def to_bytes(self): name_encoded = self._name.encode('utf8') name_length = len(name_encoded) country_encoded = self._country.encode('utf8') country_length = len(country_encoded) return struct.pack( 'BsBsff', name_length, name_encoded, country_length, country_encoded, self._lon, self._lat, ) class Cities: def to_bytes(self): return b''.join( c.to_bytes() for c in self._cities ) 





Si el argumento de la función tiene un valor predeterminado de None y se anota como T , entonces mypy lo considerará Optional[T] (es decir, Union[T, None] ).

Esto no funciona con otros tipos, por lo que no podrá escribir algo como f(x: A = B()) . Además, este truco no funciona con la asignación de variables: a: A = None dará lugar a un error.

 def f(x: int = None): reveal_type(x) def g(y: int = 'x'): reveal_type(y) z: int = None reveal_type(z) $ mypy test.py test.py:2: error: Revealed type is 'Union[builtins.int, None]' test.py:4: error: Incompatible default for argument "y" (default has type "str", argument has type "int") test.py:5: error: Revealed type is 'builtins.int' test.py:7: error: Incompatible types in assignment (expression has type "None", variable has type "int") test.py:8: error: Revealed type is 'builtins.int' 

***

En Python 3, cuando sale del bloque except , las variables que almacenan excepciones capturadas se eliminan de los locals() , incluso si ya existían:

 >>> e = 2 >>> try: ... 1/0 ... except Exception as e: ... pass ... >>> e Traceback (most recent call last): File "<stdin>", line 1, in <module> NameError: name 'e' is not defined 

Si desea mantener el enlace a la excepción, debe usar otra variable:

 >>> error = None >>> try: ... 1/0 ... except Exception as e: ... error = e ... >>> error ZeroDivisionError('division by zero',) 

En Python 2, sin embargo, esto no sucede.




Puede crear fácilmente su propio repositorio de pypi. Le permite lanzar paquetes dentro de su proyecto e instalarlos con pip , como si fueran paquetes regulares.

Es importante tener en cuenta que no necesita instalar ningún software especial, puede usar un servidor HTTP normal. Así es como funciona para mí.

Tome el paquete primitivo pythonetc .

 setup.py: from setuptools import setup, find_packages setup( name='pythonetc', version='1.0', packages=find_packages(), ) pythonetc.py: def ping(): return 'pong' 

Vamos a liberarlo en el directorio ~/pypi :

 $ python setup.py sdist bdist_wheel … $ mv dist ~/pypi/pythonetc 

Y comenzaremos a proporcionar el paquete desde el dominio pypi.pushtaev.ru usando nginx:

 $ cat /etc/nginx/sites-enabled/pypi server { listen 80; server_name pypi.pushtaev.ru; root /home/vadim/pypi; index index.html index.htm index.nginx-debian.html; location / { autoindex on; try_files $uri $uri/ =404; } } 

Ahora el paquete se puede instalar:

 $ pip install -i http://pypi.pushtaev.ru --trusted-host pypi.pushtaev.ru pythonetc … Collecting pythonetc Downloading http://pypi.pushtaev.ru/pythonetc/pythonetc-1.0-py3-none-any.whl Installing collected packages: pythonetc Successfully installed pythonetc-1.0 $ python Python 3.7.0+ (heads/3.7:0964aac, Mar 29 2019, 00:40:55) [GCC 4.9.2] on linux Type "help", "copyright", "credits" or "license" for more information. >>> import pythonetc >>> pythonetc.ping() 'pong' 





A menudo debe declarar un diccionario con claves del mismo nombre que las variables locales. Por ejemplo:

 dict( context=context, mode=mode, action_type=action_type, ) 

En tales casos, ECMAScript incluso tiene una forma especial del literal del object (llamado Abreviatura del valor de la propiedad literal del objeto):

 > var a = 1; < undefined > var b = 2; < undefined > {a, b} < {a: 1, b: 2} 

Puede crear el mismo ayudante en Python (por desgracia, no es tan bueno como la notación en ECMAScript):

 def shorthand_dict(lcls, names): return {k: lcls[k] for k in names} context = dict(user_id=42, user_ip='1.2.3.4') mode = 'force' action_type = 7 shorthand_dict(locals(), [ 'context', 'mode', 'action_type', ]) 

Puede preguntar, ¿por qué pasar locals() como parámetro? ¿Es posible obtener locals objeto que llama en el llamado? Puede, pero debe usar el módulo de inspect :

 import inspect def shorthand_dict(names): lcls = inspect.currentframe().f_back.f_locals return {k: lcls[k] for k in names} context = dict(user_id=42, user_ip='1.2.3.4') mode = 'force' action_type = 7 shorthand_dict([ 'context', 'mode', 'action_type', ]) 

Puede ir aún más lejos y aplicar dicha solución: https://github.com/alexmojaki/sorcery :

 from sorcery import dict_of dict_of(context, mode, action_type) 

Source: https://habr.com/ru/post/450862/


All Articles