
É uma nova seleção de dicas e truques sobre Python e programação do meu canal Telegram @pythonetc.
Publicações anteriores .
Armazenar e enviar objetos via rede como bytes é um tópico importante. Vamos discutir algumas ferramentas que geralmente são usadas para isso no Python e suas vantagens e desvantagens.
Como exemplo, tentarei serializar o objeto Cities, que contém alguns objetos City e sua ordem. Aqui está quatro métodos que você pode usar:
1. JSON. É legível por humanos, fácil de usar, mas consome muita memória. O mesmo vale para outros formatos como YAML ou 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. Picles. Pickle é nativo para Python, pode ser personalizado e consome menos memória que JSON. A desvantagem é que você precisa usar o Python para remover os dados.
class Cities: def pickle(self): return pickle.dumps(self)
3. Protobuf (e outros serializadores binários, como o msgpack). Consome ainda menos memória, pode ser usado em qualquer outra linguagem de programação, mas requer um esquema personalizado:
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. Manual. Você pode empacotar e descompactar manualmente os dados com o módulo struct. Ele permite consumir a quantidade mínima absoluta de memória, mas o protobuf ainda pode ser uma escolha melhor, pois suporta esquemas de versão e 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 )
Se um argumento de função tem o valor padrão
None
e é anotado como
T
,
mypy
o trata automaticamente como
Optional[T]
(em outras palavras,
Union[T, None]
).
Isso não funciona com outros tipos, então você não pode ter algo como
f(x: A = B())
. Também não funciona com uma atribuição de variável:
a: A = None
causará um erro.
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'
No Python 3, depois que o bloco de
except
é encerrado, as variáveis que armazenam exceções capturadas são removidas de
locals()
mesmo que elas existissem anteriormente:
>>> 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
Se você deseja salvar uma referência à exceção, é necessário usar outra variável:
>>> error = None >>> try: ... 1/0 ... except Exception as e: ... error = e ... >>> error ZeroDivisionError('division by zero',)
Isso não é verdade para o Python 2.
Você pode ter seu próprio repositório
pypi
. Ele permite que você libere pacotes dentro do seu projeto e os instale com o pip como se fossem pacotes regulares.
É notável que você não precisa instalar nenhum software específico, mas pode usar um servidor http normal. Aqui está como isso funciona para mim, por exemplo.
Vamos ter um pacote trivial chamado
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 lançá-lo no diretório ~ / pypi:
$ python setup.py sdist bdist_wheel … $ mv dist ~/pypi/pythonetc
Agora, servidor isso no domínio
pypi.pushtaev.ru
com 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; } }
Agora ele pode ser instalado:
$ 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'
Muitas vezes é necessário declarar um dicionário com todas as chaves iguais às variáveis locais com o mesmo nome. Algo assim:
dict( context=context, mode=mode, action_type=action_type, )
O ECMAScript ainda tem a forma especial de literal de objeto para esses casos (é chamado abreviação de Object Literal Property Value):
> var a = 1; < undefined > var b = 2; < undefined > {a, b} < {a: 1, b: 2}
É possível criar um auxiliar semelhante no Python (infelizmente, ele não parece nem de perto tão bom quanto a notação 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', ])
Você pode se perguntar por que precisamos passar
locals()
como um parâmetro no exemplo anterior. É possível obter os
locals
do chamador no chamado? É verdade, mas você precisa mexer no módulo
inpsect
:
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', ])
Você pode ir ainda mais longe e usar algo assim -
https://github.com/alexmojaki/sorcery :
from sorcery import dict_of dict_of(context, mode, action_type)