
这是来自@pythonetc feed的Python技巧和编程的第十个集合。
以前的选择 。
以字节为单位通过网络存储和发送对象是一个非常大的话题。 为了这些目的,Python通常使用许多工具,让我们讨论它们的优缺点。
例如,我将尝试以特定顺序序列化包含City对象的Cities对象。 可以使用四种方法:
1. JSON。 易于阅读,易于使用,但会占用大量内存。 对于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.泡菜。 这是一个本地Python工具,可自定义,比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
,并且注释为
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',)
但是,在Python 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
目录中:
$ python setup.py sdist bdist_wheel … $ mv dist ~/pypi/pythonetc
我们将开始使用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; } }
现在可以安装该软件包:
$ 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
文字的一种特殊形式(称为“对象文字属性值简写”):
> 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)