
Ini adalah kumpulan kesepuluh tips Python dan pemrograman dari umpan @pythonetc saya.
Pilihan sebelumnya .
Menyimpan dan mengirim objek melalui jaringan sebagai byte adalah topik yang sangat besar. Untuk keperluan ini, Python biasanya menggunakan sejumlah alat, mari kita bahas kelebihan dan kekurangannya.
Sebagai contoh, saya akan mencoba membuat serial objek City yang berisi objek City dalam urutan tertentu. Empat pendekatan dapat digunakan:
1. JSON. Dapat dibaca oleh manusia, mudah digunakan, tetapi menghabiskan banyak memori. Hal yang sama berlaku untuk format YAML dan 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. Ini adalah alat Python asli, dapat disesuaikan, mengkonsumsi lebih sedikit memori daripada JSON. Kerugian: Python harus digunakan untuk mengambil data.
class Cities: def pickle(self): return pickle.dumps(self)
3. Protobuf (dan serializer biner lainnya, misalnya, msgpack). Ini mengkonsumsi lebih sedikit memori, dapat digunakan dari bahasa pemrograman apa pun, tetapi membutuhkan penulisan skema eksplisit:
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. Secara manual. Anda dapat mengemas dan membongkar data secara manual menggunakan modul
struct
. Dengan cara ini Anda dapat mencapai konsumsi memori serendah mungkin, tetapi kadang-kadang lebih baik menggunakan
protobuf
, 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 dianotasi sebagai
T
, maka
mypy
akan secara otomatis menganggapnya
Optional[T]
(yaitu,
Union[T, None]
).
Ini tidak berfungsi dengan tipe lain, jadi Anda tidak akan bisa menulis sesuatu seperti
f(x: A = B())
. Selain itu, trik ini 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'
***
Dalam Python 3, ketika keluar dari blok
except
, variabel yang menahan pengecualian yang tertangkap dihapus dari
locals()
, bahkan jika mereka sudah 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 tautan ke pengecualian, Anda perlu menggunakan variabel lain:
>>> error = None >>> try: ... 1/0 ... except Exception as e: ... error = e ... >>> error ZeroDivisionError('division by zero',)
Dalam Python 2, bagaimanapun, ini tidak terjadi.
Anda dapat dengan mudah membuat repositori pypi Anda sendiri. Ini memungkinkan Anda untuk merilis paket di dalam proyek Anda dan menginstalnya dengan
pip
, seolah-olah itu adalah paket biasa.
Penting untuk dicatat bahwa Anda tidak perlu menginstal perangkat lunak khusus, Anda dapat menggunakan server HTTP biasa. Beginilah cara kerjanya untuk saya.
Ambil paket
pythonetc
primitif.
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
Dan kami akan mulai menyediakan paket dari domain
pypi.pushtaev.ru
menggunakan 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 paket 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'
Seringkali Anda perlu mendeklarasikan kamus dengan kunci dengan nama yang sama dengan variabel lokal. Sebagai contoh:
dict( context=context, mode=mode, action_type=action_type, )
Dalam kasus-kasus seperti itu, ECMAScript bahkan memiliki bentuk khusus dari
object
literal (disebut Object Value Properties Nilai Singkat):
> var a = 1; < undefined > var b = 2; < undefined > {a, b} < {a: 1, b: 2}
Anda dapat membuat helper yang sama dengan Python (sayangnya, itu sama sekali tidak sebagus notasi dalam 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, mengapa melewati
locals()
sebagai parameter? Apakah mungkin untuk mendapatkan
locals
objek panggilan di tempat yang dipanggil? Anda bisa, tetapi Anda harus menggunakan modul
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', ])
Anda dapat melangkah lebih jauh dan menerapkan solusi seperti itu -
https://github.com/alexmojaki/sorcery :
from sorcery import dict_of dict_of(context, mode, action_type)