
Es ist eine neue Auswahl von Tipps und Tricks zu Python und Programmierung von meinem Telegramm-Kanal @pythonetc.
Frühere Veröffentlichungen .
Das Speichern und Senden von Objekten über das Netzwerk als Bytes ist ein großes Thema. Lassen Sie uns einige Tools diskutieren, die normalerweise in Python dafür verwendet werden, sowie deren Vor- und Nachteile.
Als Beispiel werde ich versuchen, das Cities-Objekt zu serialisieren, das einige City-Objekte sowie deren Reihenfolge enthält. Hier sind vier Methoden, die Sie verwenden können:
1. JSON. Es ist für Menschen lesbar, einfach zu bedienen, verbraucht aber viel Speicher. Gleiches gilt für andere Formate wie YAML oder 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. Gurke. Pickle ist nativ für Python, kann angepasst werden und verbraucht weniger Speicher als JSON. Der Nachteil ist, dass Sie Python verwenden müssen, um die Daten zu entfernen.
class Cities: def pickle(self): return pickle.dumps(self)
3. Protobuf (und andere binäre Serialisierer wie msgpack). Verbraucht noch weniger Speicher, kann aus allen anderen Programmiersprachen verwendet werden, erfordert jedoch ein benutzerdefiniertes Schema:
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. Handbuch. Sie können Daten mit dem struct-Modul manuell packen und entpacken. Es ermöglicht Ihnen, die absolut minimale Speichermenge zu verbrauchen, aber protobuf kann immer noch eine bessere Wahl sein, da es Versionierung und explizite Schemata unterstützt.
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 )
Wenn ein Funktionsargument den Standardwert
None
und als
T
, behandelt
mypy
automatisch als
Optional[T]
(mit anderen Worten
Union[T, None]
).
Das funktioniert bei anderen Typen nicht, daher können Sie so etwas wie
f(x: A = B())
. Es funktioniert auch nicht mit einer Variablenzuweisung:
a: A = None
verursacht einen Fehler.
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'
In Python 3 werden nach dem Beenden des
except
die Variablen, in denen abgefangene Ausnahmen gespeichert sind, aus
locals()
auch wenn sie zuvor vorhanden waren:
>>> 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
Wenn Sie einen Verweis auf die Ausnahme speichern möchten, müssen Sie eine andere Variable verwenden:
>>> error = None >>> try: ... 1/0 ... except Exception as e: ... error = e ... >>> error ZeroDivisionError('division by zero',)
Dies gilt nicht für Python 2.
Möglicherweise haben Sie ein eigenes
pypi
Repository. Sie können damit Pakete in Ihrem Projekt freigeben und mit pip installieren, als wären sie reguläre Pakete.
Es ist bemerkenswert, dass Sie keine bestimmte Software installieren müssen, sondern stattdessen einen normalen http-Server verwenden können. So funktioniert es zum Beispiel bei mir.
Lassen Sie uns ein triviales Paket namens
pythonetc
.
setup.py: from setuptools import setup, find_packages setup( name='pythonetc', version='1.0', packages=find_packages(), ) pythonetc.py: def ping(): return 'pong'
Lassen Sie es uns im Verzeichnis ~ / pypi freigeben:
$ python setup.py sdist bdist_wheel … $ mv dist ~/pypi/pythonetc
Server dies nun auf der Domain
pypi.pushtaev.ru
mit 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; } }
Es kann jetzt installiert werden:
$ 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'
Es kommt häufig vor, dass Sie ein Wörterbuch deklarieren müssen, bei dem alle Schlüssel den lokalen Variablen mit demselben Namen entsprechen. So etwas wie das:
dict( context=context, mode=mode, action_type=action_type, )
ECMAScript hat sogar die spezielle Form des Objektliteral für solche Fälle (es heißt Objektliteral Property Value Shorthand):
> var a = 1; < undefined > var b = 2; < undefined > {a, b} < {a: 1, b: 2}
Es ist möglich, einen ähnlichen Helfer in Python zu erstellen (leider sieht er nicht einmal so gut aus wie die ECMAScript-Notation):
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', ])
Sie fragen sich vielleicht, warum wir im vorherigen Beispiel
locals()
als Parameter übergeben müssen. Ist es möglich, die
locals
des Anrufers in den Angerufenen zu bringen? Es ist zwar so, aber Sie müssen sich mit dem
inpsect
Modul
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', ])
Sie können noch weiter gehen und so etwas verwenden -
https://github.com/alexmojaki/sorcery :
from sorcery import dict_of dict_of(context, mode, action_type)