
Ini adalah pilihan baru tips dan trik tentang Python dan pemrograman dari saluran-Telegram saya @pythonetc.
Publikasi sebelumnya .
Menyimpan dan mengirim objek melalui jaringan sebagai byte adalah topik yang sangat besar. Mari kita bahas beberapa alat yang biasanya digunakan untuk itu dalam Python dan kelebihan dan kekurangannya.
Sebagai contoh, saya akan mencoba membuat serial objek City yang berisi beberapa objek City beserta urutannya. Berikut adalah empat metode yang dapat Anda gunakan:
1. JSON. Dapat dibaca oleh manusia, mudah digunakan, tetapi menghabiskan banyak memori. Hal yang sama berlaku untuk format lain seperti YAML atau 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. Acar. Acar adalah asli untuk Python, dapat disesuaikan dan mengkonsumsi lebih sedikit memori daripada JSON. Kelemahannya adalah Anda harus menggunakan Python untuk menghapus data.
class Cities: def pickle(self): return pickle.dumps(self)
3. Protobuf (dan serialiser biner lainnya seperti msgpack). Mengkonsumsi lebih sedikit memori, dapat digunakan dari bahasa pemrograman lain, tetapi membutuhkan skema khusus:
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. Anda dapat mengemas dan membongkar data secara manual dengan modul struct. Ini memungkinkan Anda untuk mengkonsumsi jumlah minimum memori absolut, tetapi protobuf masih bisa menjadi pilihan yang lebih baik karena mendukung skema versi dan eksplisit.
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 )
Jika argumen fungsi memiliki nilai default
None
dan dijelaskan sebagai
T
,
mypy
secara otomatis memperlakukannya sebagai
Optional[T]
(dengan kata lain,
Union[T, None]
).
Itu tidak bekerja dengan tipe lain, jadi Anda tidak dapat memiliki sesuatu seperti
f(x: A = B())
. Itu juga tidak berfungsi dengan tugas variabel:
a: A = None
akan menyebabkan kesalahan.
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'
Di Python 3, setelah blok
except
keluar, variabel yang menyimpan pengecualian yang ditangkap dihapus dari
locals()
bahkan jika sebelumnya ada:
>>> 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
Jika Anda ingin menyimpan referensi ke pengecualian, Anda harus menggunakan variabel lain:
>>> error = None >>> try: ... 1/0 ... except Exception as e: ... error = e ... >>> error ZeroDivisionError('division by zero',)
Ini tidak benar untuk Python 2.
Anda mungkin memiliki repositori
pypi
Anda sendiri. Ini memungkinkan Anda merilis paket di dalam proyek Anda dan menginstalnya dengan pip seolah-olah itu adalah paket biasa.
Sungguh luar biasa bahwa Anda tidak harus menginstal perangkat lunak tertentu, tetapi dapat menggunakan server http biasa. Inilah cara kerjanya untuk saya, misalnya.
Mari kita memiliki paket sepele bernama
pythonetc
.
setup.py: from setuptools import setup, find_packages setup( name='pythonetc', version='1.0', packages=find_packages(), ) pythonetc.py: def ping(): return 'pong'
Mari kita lepaskan ke direktori ~ / pypi:
$ python setup.py sdist bdist_wheel … $ mv dist ~/pypi/pythonetc
Sekarang
pypi.pushtaev.ru
domain
pypi.pushtaev.ru
dengan 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; } }
Sekarang dapat diinstal:
$ 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'
Cukup sering ketika Anda harus mendeklarasikan kamus dengan semua kunci sama dengan variabel lokal dengan nama yang sama. Sesuatu seperti ini:
dict( context=context, mode=mode, action_type=action_type, )
ECMAScript bahkan memiliki bentuk khusus objek literal untuk kasus-kasus seperti itu (ini disebut Steno Nilai Properti Objek Literal):
> var a = 1; < undefined > var b = 2; < undefined > {a, b} < {a: 1, b: 2}
Dimungkinkan untuk membuat helper serupa di Python (sayangnya, tampilannya bahkan tidak sebagus notasi 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', ])
Anda mungkin bertanya-tanya mengapa kami harus melewatkan
locals()
sebagai parameter pada contoh sebelumnya. Apakah mungkin untuk mendapatkan
locals
dari penelepon di callee? Memang, tetapi Anda harus mengacaukan 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', ])
Anda dapat melangkah lebih jauh dan menggunakan sesuatu seperti ini -
https://github.com/alexmojaki/sorcery :
from sorcery import dict_of dict_of(context, mode, action_type)