
Dies ist die zehnte Sammlung von Python-Tipps und -Programmierungen aus meinem @ pythonetc-Feed.
Vorherige Auswahl .
Das Speichern und Senden von Objekten über das Netzwerk als Bytes ist ein sehr großes Thema. Für diese Zwecke verwendet Python normalerweise eine Reihe von Tools. Lassen Sie uns deren Vor- und Nachteile diskutieren.
Als Beispiel werde ich versuchen, ein Städteobjekt zu serialisieren, das Stadtobjekte in einer bestimmten Reihenfolge enthält. Es können vier Ansätze verwendet werden:
1. JSON. Vom Menschen lesbar, einfach zu bedienen, verbraucht aber viel Speicher. Gleiches gilt für die Formate YAML und 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. Dies ist ein natives Python-Tool, das anpassbar ist und weniger Speicher benötigt als JSON. Nachteil: Python muss zum Abrufen von Daten verwendet werden.
class Cities: def pickle(self): return pickle.dumps(self)
3. Protobuf (und andere binäre Serialisierer, z. B. msgpack). Es verbraucht noch weniger Speicher, kann in jeder Programmiersprache verwendet werden, erfordert jedoch das Schreiben eines expliziten Schemas:
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. Manuell. Sie können Daten manuell mit dem
struct
Modul packen und entpacken. Auf diese Weise können Sie einen möglichst geringen Speicherverbrauch erzielen. Manchmal ist es jedoch besser,
protobuf
zu verwenden, 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 das Funktionsargument den Standardwert
None
und als
T
, betrachtet
mypy
ihn automatisch als
Optional[T]
(
mypy
Union[T, None]
).
Dies funktioniert nicht mit anderen Typen, sodass Sie so etwas wie
f(x: A = B())
nicht schreiben können. Dieser Trick funktioniert auch nicht mit der Variablenzuweisung:
a: A = None
führt zu einem 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'
***.
Wenn Sie in Python 3 den
except
, werden Variablen, in denen abgefangene Ausnahmen gespeichert sind, aus
locals()
, auch wenn sie bereits vorhanden sind:
>>> 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 den Link zur Ausnahme beibehalten 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',)
In Python 2 ist dies jedoch nicht der Fall.
Sie können ganz einfach Ihr eigenes Pypi-Repository erstellen. Sie können Pakete in Ihrem Projekt freigeben und mit
pip
installieren, als wären es reguläre Pakete.
Es ist wichtig zu beachten, dass Sie keine spezielle Software installieren müssen. Sie können einen normalen HTTP-Server verwenden. So funktioniert es bei mir.
Nehmen Sie das primitive
pythonetc
Paket.
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
~/pypi
Verzeichnis
~/pypi
:
$ python setup.py sdist bdist_wheel … $ mv dist ~/pypi/pythonetc
Und wir werden beginnen, das Paket aus der Domain
pypi.pushtaev.ru
mit nginx
pypi.pushtaev.ru
:
$ 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; } }
Jetzt kann das Paket 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'
Oft müssen Sie ein Wörterbuch mit Schlüsseln deklarieren, die denselben Namen wie lokale Variablen haben. Zum Beispiel:
dict( context=context, mode=mode, action_type=action_type, )
In solchen Fällen hat ECMAScript sogar eine spezielle Form des
object
(als
object
Eigenschaftswert-Kurzform bezeichnet):
> var a = 1; < undefined > var b = 2; < undefined > {a, b} < {a: 1, b: 2}
Sie können denselben Helfer in Python erstellen (leider ist er überhaupt nicht so gut wie die Notation in 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', ])
Sie fragen sich vielleicht, warum
locals()
als Parameter übergeben wird? Ist es möglich,
locals
aufrufenden Objekts in den aufgerufenen zu bekommen? Sie können, aber Sie müssen das
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', ])
Sie können noch weiter gehen und eine solche Lösung anwenden -
https://github.com/alexmojaki/sorcery :
from sorcery import dict_of dict_of(context, mode, action_type)