@Pythonetc Abril de 2019



Esta é a décima coleção de dicas e programação em Python do meu feed @pythonetc.

Seleções anteriores .





Armazenar e enviar objetos pela rede como bytes é um tópico muito importante. Para esses fins, o Python geralmente usa várias ferramentas, vamos discutir suas vantagens e desvantagens.

Como exemplo, tentarei serializar um objeto Cidades que contenha objetos Cidade em uma ordem específica. Quatro abordagens podem ser usadas:

1. JSON. Legível por humanos, fácil de usar, mas consome muita memória. O mesmo vale para os formatos YAML e 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. Essa é uma ferramenta nativa do Python, personalizável, que consome menos memória que o JSON. Desvantagem: Python deve ser usado para recuperar dados.

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

3. Protobuf (e outros serializadores binários, por exemplo, msgpack). Consome ainda menos memória, pode ser usado em qualquer linguagem de programação, mas requer a criação de um 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. Você pode empacotar e descompactar manualmente os dados usando o módulo struct . Dessa forma, você pode obter o menor consumo de memória possível, mas às vezes é melhor usar o protobuf , pois ele 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 o argumento da função tiver um valor padrão None e for anotado como T , mypy o considerará automaticamente Optional[T] (ou seja, Union[T, None] ).

Como não funciona com outros tipos, você não poderá escrever algo como f(x: A = B()) . Além disso, esse truque não funciona com a atribuição de variáveis: a: A = None levará a 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, quando você sai do bloco de except , as variáveis ​​que armazenam exceções capturadas são removidas de locals() , mesmo que já existam:

 >>> 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 manter o link para a exceção, precisa usar outra variável:

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

No Python 2, no entanto, isso não acontece.




Você pode facilmente criar 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.

É importante observar que você não precisa instalar nenhum software especial; pode usar um servidor HTTP comum. É assim que funciona para mim.

Pegue o pacote pythonetc primitivo.

 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 

E começaremos a fornecer o pacote do domínio pypi.pushtaev.ru usando o 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 o pacote 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' 





Geralmente, você precisa declarar um dicionário com chaves com o mesmo nome que as variáveis ​​locais. Por exemplo:

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

Nesses casos, o ECMAScript ainda possui uma forma especial do literal do object (chamado Atalho Literal do Valor da Propriedade Literal do Objeto):

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

Você pode criar o mesmo auxiliar no Python (infelizmente, não é tão bom quanto a notação no 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 perguntar: por que passar locals() como parâmetro? É possível obter locals objeto de chamada no chamado? Você pode, mas precisa usar o 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', ]) 

Você pode ir ainda mais longe e aplicar essa solução - https://github.com/alexmojaki/sorcery :

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

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


All Articles