#933 Python 自带小工具
Python 2023-08-06相信做 Python 的都知道 Python 有一个内置的 HTTP Server,可能经常临时性使用一下。
今天,我在 https://til.simonwillison.net/python/stdlib-cli-tools 上了解到 Python 其实内置了不少小工具,可以了解一下,说不定可以用到。
coding in a complicated world
相信做 Python 的都知道 Python 有一个内置的 HTTP Server,可能经常临时性使用一下。
今天,我在 https://til.simonwillison.net/python/stdlib-cli-tools 上了解到 Python 其实内置了不少小工具,可以了解一下,说不定可以用到。
阮一峰博客上看到这么一个数学题(原文是 maths.org 2014 年的一篇文章):
已知:小圆的半径为 $r$,大圆的半径为 $4r$
问:小圆自身滚动了几周?
我们生活在这样一个社会:小孩子像成年人一样老成,而成年人像小孩子一样幼稚。
现在的孩子们比以前更容易接触到成年人的世界,因此他们更早成人化。
从很小的年龄起,他们就在视频网站观看暴力和战争,在社交网络上看到性感和暴露的照片和视频。
然而,当孩子们成年以后,他们往往无法实现经济独立,也没有机会承担足够的责任。
结果,整个社会的文化就变得很幼稚,成年人感到无法做出承诺,即使承诺了也缺乏信心,对以后的生活感到难以把握。
他们的行事方式和处事态度,就像还在青少年时期。
mime-type:image/png larger-than:1K
# Edge 官方示例,表示过滤出大于 1K 的 PNG 图片
domain:*.csdn.net method:POST
# 过滤出 CSDN 域的 POST 请求。
has-response-header:Content-Type -domain:*.baidu.com
# 过滤出带 Content-Type 头的请求,排除百度域。
属性 | 详情 |
---|---|
domain |
仅显示来自指定域的资源。您可以使用通配符(* )来包含多个域。例如, *.com 会显示所有以 .com 结尾的域名资源。DevTools 会将所有找到的域名填充到自动完成下拉菜单中。 |
has-response-header |
显示包含指定 HTTP 响应头的资源。 DevTools 会将所有找到的响应头填充到自动完成下拉菜单中。 |
is |
使用 is:running 查找 WebSocket 资源。 |
larger-than |
显示大于指定大小的资源,单位为字节。 设置值为 1000 相当于设置值为 1k 。 |
method |
显示通过指定的 HTTP 方法类型检索的资源。 DevTools 会将所有找到的 HTTP 方法填充到下拉菜单中。 |
mime-type |
显示指定 MIME 类型的资源。 DevTools 会将所有找到的 MIME 类型填充到下拉菜单中。 |
mixed-content |
显示所有混合内容资源(mixed-content:all ),或仅显示当前显示的资源(mixed-content:displayed )。 |
scheme |
显示通过不安全的 HTTP(scheme:http )或安全的 HTTPS(scheme:https )检索的资源。 |
set-cookie-domain |
显示具有与指定值匹配的 Set-Cookie 标头中的 Domain 属性的资源。DevTools 会将所有找到的 Cookie 域填充到自动完成中。 |
set-cookie-name |
显示具有与指定值匹配的 Set-Cookie 标头中的名称的资源。DevTools 会将所有找到的 Cookie 名称填充到自动完成中。 |
set-cookie-value |
显示具有与指定值匹配的 Set-Cookie 标头中的值的资源。DevTools 会将所有找到的 Cookie 值填充到自动完成中。 |
status-code |
显示与特定 HTTP 状态码匹配的资源。 DevTools 会将所有找到的状态码填充到自动完成下拉菜单中。 |
-
表示取反,小技巧:可以通过输入 -
之后的自动补全查看所有支持的选项。类比到河流,上游和下游概念就非常明确,水从上游往下游流动。
在工业生产车间中,不同工序就组成一条河流,流动的产品,前面的工序是上游,后面的工序是下游。
下游依赖上游。
在软件设计中,不同服务(SOA、微服务)组成一条河流,流动的是数据,先处理数据的是上游,后处理数据的是下游。
下游依赖上游。
注意:数据是怎么个流动方式的不重要,可能是上游推的,可能是下游拉的。上游和下游之间可能会互相调用 API,这个也不影响数据的依赖关系。
总之,上游靠近入口网关,下游靠近出口网关。
开源项目中,fork 关系组成一条河流,流动的是代码,被 fork 的项目是上游,fork 出来的项目是下游。
下游依赖上游。
当然,下游的代码更新也可能会反馈到上游。
两周前,美国社会向来最具争议的政策之一——自上世纪 60 年代开始践行的“平权法案”(Affirmative Action)被推翻。
美国最高法院针对涉及哈佛大学和北卡罗莱纳大学被控在招生时考虑种族比例的案件裁定,宪法禁止大学在招生过程中考虑申请人的种族因素。这一判决被视作自推翻堕胎权判例后又一次历史性裁决,也有观点指出,这一裁决更多体现美国最高院保守派压倒性势力以及保守主义的全面回归。
看理想两位节目主讲人庞颖、詹青云,关于此次平权法案被推翻特别录制了一期番外,延续《思辨力 35 讲》的节目形式,庞颖和詹青云分别从支持者、反对者的两方立场就平权法案的争议与利弊进行了一场辩论。问题不仅关乎种族正义、教育平等,也关乎社会的公正与良善。
Python 内部的 GIL(全局解释器锁)使 Python 的多线程开发变得更加简单,避免了大部分竞争条件,但是限制了 Python 进程使用多核心处理器,所有需要并行的场景全部受限。
官方建议,如果需要用到多核,那就选择多进程编程。
Python 出现的时候还没有多核 CPU,而现在多核心早就成为了主流,4 核、8 核太常见了。
因此从来不乏移除 GIL 的声音,不过没有一次成功。要想不对现有的项目造成太大的影响简直难于登天。
PS:几年前有一个叫做 Gilectomy 的项目(GIL Ectomy,GIL 切除术)因为导致 Python 性能下滑严重而失败。
最大的挑战是要保持对现有代码的兼容性,保住基本盘,也就是容易上手,容易开发。
2021 年,香农计划成员 Eric Snow 提交 PEP 684 A Per-Interpreter GIL(每个解释器一个 GIL)。
这应该是一个比较稳妥的方案,但我怀疑性价比是否足够。
PS:和下面提到的 nogil 方案不冲突。
PEP 683 – Immortal Objects, Using a Fixed Refcount
PEP 554 – Multiple Interpreters in the Stdlib
2021 年,Meta(Facebook)开发者 Sam Gross 基于 Python 3.9 创建了一个 nogil 的分支,最终证明了这个方案技术上可行,相较于 Gilectomy 单核性能没有收到太大的影响,拓展性也不错。
现在是最接近移除 GIL 的时刻。
如果顺利通过,几年之后,我们就能用上没有 GIL 的 Python。
--disable-gil
来创建一个没有 GIL 的 Python 版本。for
语义变更
仔细观察下面的例子就能知道问题在哪里了:
package main
import "fmt"
func main() {
items := []int{1, 2, 3}
{
var all []*int
for _, item := range items {
all = append(all, &item)
}
fmt.Printf("%+v\n", all)
// [0xc00008c018 0xc00008c018 0xc00008c018]
// 输出的都是最后一个值!!!
for _, i := range all {
fmt.Printf("%+v, %+v\n", i, *i)
}
}
// fix it:
{
var all []*int
for _, item := range items {
item := item // 重点
all = append(all, &item)
}
for _, i := range all {
fmt.Printf("%+v, %+v\n", i, *i)
}
}
}
package main
import "fmt"
func main() {
{
var prints []func()
for _, v := range []int{1, 2, 3} {
prints = append(prints, func() { fmt.Println(v) })
}
for _, print := range prints {
print()
}
}
// 输出的是 3 3 3,而不是 1,2,3,Why?
// fix it:
{
var prints []func()
for _, v := range []int{1, 2, 3} {
v := v // 重点
prints = append(prints, func() { fmt.Println(v) })
}
for _, print := range prints {
print()
}
}
}
package main
import (
"fmt"
"sync"
)
func main() {
items := []int{1, 2, 3}
{
wg := sync.WaitGroup{}
for _, v := range items {
wg.Add(1)
go func() {
// 会提示:loop variable v captured by func literal
fmt.Println(v)
wg.Done()
}()
}
wg.Wait()
}
// fix it:
{
wg := sync.WaitGroup{}
for _, v := range items {
wg.Add(1)
v := v // 重点
go func() {
fmt.Println(v)
wg.Done()
}()
}
wg.Wait()
}
}
这个例子可以改写成:
package main
import (
"fmt"
)
func main() {
items := []int{1, 2, 3}
done := make(chan bool)
{
for _, v := range items {
go func() {
// 会提示:loop variable v captured by func literal
fmt.Println(v)
done <- true
}()
}
for _ = range items {
<-done
}
}
// fix it:
{
for _, v := range items {
v := v // 重点
go func() {
fmt.Println(v)
done <- true
}()
}
for _ = range items {
<-done
}
}
}
根据 Go 的设计思想,花括号内是一个独立的作用域,for 循环每一次也应该是独立的。
当前这次循环中的变量和上一次循环的变量应该是不一样的。
但实际上,根据运行结果来看,他们的地址是一样的。
闭包函数应该也是这样的,去原来的位置读相关变量,但是之前的位置写入了新的值。
这个设计是一个大坑,对于新人非常不友好。
从 1.21 开始支持根据环境变量 GOEXPERIMENT=loopvar
来启用新的订正版语义。
从 1.22 开始正式修改 for 语义。
Russ Cox 认为当前语义的代价很大,出错的频率高于正确的频率。
但是这次变更 for 语句语义的决定和之前的承诺有冲突(保证兼容性的基础就是语义不会被重新定义)。
但是好在 Go 已经为这种情况做好了足够的准备,即,go 支持在同一批编译中,按照不同包的 mod 文件中声明的 go 版本来对体现不同的语法特性。
就比如 A 包需要 1.22,然后依赖 B 包,需要 1.18,那么 A 包中的代码按照新的定义来,B 包中的代码按照旧的定义来。
《卡拉马佐夫兄弟》(俄语:Бра́тья Карама́зовы、英语:The Brothers Karamazov)是俄罗斯作家陀思妥耶夫斯基创作的最后一部长篇小说,通常也被认为是他一生文学创作的巅峰之作。这部宏篇巨制在经历了《俄国导报》上两年的连载后,于 1880 年完成。他曾构想将其作为他的一部更宏大的作品《一个伟大罪人的一生》(The Life of a Great Sinner)的第一部分,然而未能如愿,他在《卡拉马佐夫兄弟》完成后仅四个月就辞世了。
我没有看过这本书,但是看到这句话还是挺触动的:
要爱具体的人,不要爱抽象的人。 > 要爱生活本身,不要爱生活的意义。
知识分子的一个经常性的倾向,就是我们喜欢抽象概念,我们胜过具象的事物,
但是一个越爱抽象人的人往往难对具象的人表现关爱。
因为抽象的人是美好的,抽象的人存在于理念之间,而具体的人都是有缺陷的。所以这就是为什么你越是感到抽象人的美好,你越会发现具体人,你身边人的可恶,可鄙,可耻。
记得我小的时候看过一个小故事,讲的是一个小孩,清晨起床之后,安安静静的坐着思考今天要为爸爸、妈妈、弟弟做点什么,但是爸爸、妈妈、弟弟逐个过来找他帮忙的时候,他每次都不耐烦的说:我在思考呢!