
هذه هي المجموعة العاشرة من نصائح Python والبرمجة من خلاصتي @ pythonetc.
التحديدات السابقة .
تخزين وإرسال الكائنات عبر الشبكة كـ بايت هو موضوع كبير جدًا. لهذه الأغراض ، يستخدم Python عادةً عددًا من الأدوات ، دعونا نناقش مزاياها وعيوبها.
كمثال ، سأحاول إجراء تسلسل لكائن مدن يحتوي على كائنات مدينة بترتيب معين. أربعة نهج يمكن استخدامها:
1. جسون. قابل للقراءة ، سهل الاستخدام ، ولكنه يستهلك الكثير من الذاكرة. وينطبق الشيء نفسه على تنسيقات YAML و 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. المخلل. هذه أداة بيثون أصلية ، قابلة للتخصيص ، وتستهلك ذاكرة أقل من JSON. العيب: يجب استخدام Python لاسترداد البيانات.
class Cities: def pickle(self): return pickle.dumps(self)
3. Protobuf (ومسلسلات ثنائية أخرى ، على سبيل المثال ، msgpack). يستهلك ذاكرة أقل ، ويمكن استخدامه من أي لغة برمجة ، لكنه يتطلب كتابة مخطط واضح:
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. يدويا. يمكنك حزم البيانات وتفريغها يدويًا باستخدام الوحدة النمطية
struct
. وبهذه الطريقة يمكنك تحقيق أقل استهلاك ممكن للذاكرة ، ولكن في بعض الأحيان يكون من الأفضل استخدام
protobuf
، لأنه يدعم أنظمة الإصدار والإصدار الصريح.
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 )
إذا كانت الوسيطة الوظيفية لها قيمة افتراضية هي
None
وتم
mypy
أنها
T
، فسوف يعتبر
mypy
تلقائيًا أنها
Optional[T]
(أي ،
Union[T, None]
).
هذا لا يعمل مع الأنواع الأخرى ، لذلك لن تكون قادرًا على كتابة شيء مثل
f(x: A = B())
. أيضًا ، لا تعمل هذه الخدعة مع الواجب المتغير:
a: A = None
سيؤدي إلى حدوث خطأ.
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'
***
في Python 3 ، عند الخروج من الكتلة
except
، تتم إزالة المتغيرات التي تخزن الاستثناءات التي تم اكتشافها من
locals()
، حتى لو كانت موجودة بالفعل:
>>> 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
إذا كنت تريد الاحتفاظ بالرابط الخاص بالاستثناء ، فأنت بحاجة إلى استخدام متغير آخر:
>>> error = None >>> try: ... 1/0 ... except Exception as e: ... error = e ... >>> error ZeroDivisionError('division by zero',)
في بيثون 2 ، هذا لا يحدث.
يمكنك بسهولة إنشاء مستودع pypi الخاص بك. يسمح لك بإصدار حزم داخل مشروعك وتثبيتها مع
pip
، كما لو كانت حزم منتظمة.
من المهم ملاحظة أنك لا تحتاج إلى تثبيت أي برنامج خاص ، يمكنك استخدام خادم HTTP عادي. هذه هي الطريقة التي يعمل بها بالنسبة لي.
خذ حزمة
pythonetc
البدائية.
setup.py: from setuptools import setup, find_packages setup( name='pythonetc', version='1.0', packages=find_packages(), ) pythonetc.py: def ping(): return 'pong'
~/pypi
دليل
~/pypi
:
$ python setup.py sdist bdist_wheel … $ mv dist ~/pypi/pythonetc
وسوف نبدأ في تقديم الحزمة من مجال
pypi.pushtaev.ru
باستخدام 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; } }
الآن يمكن تثبيت الحزمة:
$ 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'
غالبًا ما تحتاج إلى إعلان قاموس يحتوي على مفاتيح بنفس الاسم مثل المتغيرات المحلية. على سبيل المثال:
dict( context=context, mode=mode, action_type=action_type, )
في مثل هذه الحالات ، يحتوي ECMAScript حتى على شكل خاص
object
الحرفي (يُسمى اختصار Object Literal Property Value Shorthand):
> var a = 1; < undefined > var b = 2; < undefined > {a, b} < {a: 1, b: 2}
يمكنك إنشاء نفس المساعد في Python (للأسف ، ليست جيدة على الإطلاق مثل الرموز في 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', ])
قد تسأل ، لماذا تمرير
locals()
كمعلمة؟ هل من الممكن الحصول على
locals
للكائن المتصل
locals
المتصل؟ يمكنك ، ولكن عليك استخدام وحدة
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', ])
يمكنك الذهاب أبعد من ذلك وتطبيق مثل هذا الحل -
https://github.com/alexmojaki/sorcery :
from sorcery import dict_of dict_of(context, mode, action_type)