Python 的协变、逆变与不变

标签:Python

前几天在使用 httpx 时,发现它的代理参数声明的类型是 ProxiesTypes
URLTypes = Union["URL", str]
ProxyTypes = Union[URLTypes, "Proxy"]
ProxiesTypes = Union[ProxyTypes, Dict[URLTypes, Union[None, ProxyTypes]]]
可以看出,dict[str, str] 应该是符合它的参数签名的,然而我传入一个 dict[str, str] 参数后,Pylance 却会报错,这让我大为不解。

于是我又尝试简化了一下这个问题:
from typing import Mapping

a: dict[int, int] = {}
b: dict[int, int | str] = a  # error:
# Expression of type "dict[int, int]" is incompatible with declared type "dict[int, int | str]"
#   "dict[int, int]" is incompatible with "dict[int, int | str]"
#     Type parameter "_VT@dict" is invariant, but "int" is not the same as "int | str"
#     Consider switching from "dict" to "Mapping" which is covariant in the value type
c: Mapping[int, int | str] = a
d: Mapping[int | str, int] = a  # error:
# Expression of type "dict[int, int]" is incompatible with declared type "Mapping[int | str, int]"
#   "dict[int, int]" is incompatible with "Mapping[int | str, int]"
#     Type parameter "_KT@Mapping" is invariant, but "int" is not the same as "int | str"
是不是很奇怪,为啥 dict[int, int]dict[int, int | str]Mapping[int | str, int] 都不兼容,而与 Mapping[int, int | str] 兼容?

如何减小 Python 的 Docker 镜像的大小

标签:Python

虽然我对公司的屎山代码已经见怪不怪了,但是看到一个普通的 Python web 应用的 Docker 镜像大小超过 10 GB,还是让我感叹前人的智慧。
这些巨量的字节会在构建时在中美之间来回传递,运气好可能 5 分钟能构建完毕,运气不好就可能要等 1 小时了。
那么怎样才能减小这些镜像的大小呢?

重新回到 Python 的怀抱

标签:Python

在上一家公司时,我虽然是同时使用 Go 和 Python 进行开发,但 Go 的占比要远大于 Python。
作为一名 6 年的 Gopher 和 15 年的 Pythonista,我其实对这两门语言都很喜欢。虽然 Go 有很多的设计问题,我曾经也认为它设计得很敷衍,甚至现在也没多大进步,但它足够简单,我可以不用费很多心智就写出高性能的代码,且能原生地在各个平台运行。细想起来,似乎没有其他语言能做到。
而与之相对的,有三门语言是我无法提起兴趣的:C++、Ruby 和 Rust,我大概都用了不到半年就放弃了。我知道它们有不少很赞的设计,也不缺少众多的拥趸,可是我感觉在编码和阅读时,大半的精力可能都花在了和语言做斗争上,而不是去处理业务逻辑。这不得不让我想起了那句经典的 "Life is short (You need Python)"。

Python 3.8 支持只接受位置参数了

标签:Python

今天在看 functools.cache 的源码时发现参数列表有个诡异的 /
def cache(user_function, /):
    'Simple lightweight unbounded cache.  Sometimes called "memoize".'
    return lru_cache(maxsize=None)(user_function)
翻了下文档才发现这个叫 Positional-Only Parameters

Python 源码保护的自动化构建方案

标签:Docker, Python

Python 源码保护的方案主要有代码混淆、修改解释器和编译成二进制这三种,其他方式基本没有保护效果。而这三种方案中,最安全的就是用 Cython 来编译 py 文件(但是需要注意兼容性)。

Python 的多进程编程

标签:Python

在并发编程的时候,多线程和多进程是经常会被使用的两种模式(此外还有协程等)。由于 CPython 的 GIL 限制(Jython 和 IronPython 没有 GIL,PyPy 在尝试去掉 GIL),只有获取了 GIL 的线程才能使用 CPU,所以除了需要处理一些可能会阻塞的 IO(读写文件、访问网络等)之外,基本没人会去使用 CPython 的多线程。因此,本文就来说说更有用的 Python 多进程编程。

用 Python 3 的 async / await 做异步编程

标签:Python

前年我曾写过一篇《初探 Python 3 的异步 IO 编程》,当时只是初步接触了一下 yield from 语法和 asyncio 标准库。前些日子我在 V2EX 看到一篇《为什么只有基于生成器的协程可以真正的暂停执行并强制性返回给事件循环?》,激起了我再探 Python 3 异步编程的兴趣。然而看了很多文章和,才发现极少提到 asyncawait 实际意义的,绝大部分仅止步于对 asyncio 库的使用,真正有所帮助的只有《How the heck does async/await work in Python 3.5?》《A tale of event loops》这两篇。

Python 项目的配置管理

标签:Python

每次开始一个新的 Python 项目,我都会为怎么管理配置文件而头疼。不过在迁移我的博客时,终于有空花了点时间,把这件事想清楚。
一年多的时间过去了,一切似乎都很顺利,连我在知乎所做的新项目也沿用了该方案,于是决定把解决方案记录下来。

« 看看还有什么好玩意