0%

在 Python 中有很多特殊属性, 例如 __name__, __module__ 等有特殊的意义

__annotations__ 该函数返回对象的注解字典, 更多详情参见官方文档

https://docs.python.org/zh-cn/3/howto/annotations.html

1
2
3
4
class Human(object):

name: str
age: int

在上面的例子中, 我们创建了一个 Human 类, 同时声明了两个类属性name, age

因为我们使用了类型注解, 所以我们可以通过__annotations__方法拿到声明的这两个属性名以及类型

1
2
h = Human()
print(h.__annotations__)

输出

1
{'name': <class 'str'>, 'age': <class 'int'>}

我们得到了一个属性名:类型的键值对

此时我们继续创建一个Human的子类Man

1
2
3
class Man(Human):

name = "Naonao"

注意这里我们的类变量name没有使用类型注解, 仅仅是一个赋值的类变量

1
2
m = Man()
print(m.__annotations__)
1
{'name': <class 'str'>, 'age': <class 'int'>}

查看一下子类的__annotations__注解字典, 我们发现子类继承了父类的注解

但是如果我们这样写

1
2
3
class Man(Human):

name: str

name使用了类型注解, 我们将得到

1
{'name': <class 'str'>}

子类重写了__annotations__

https://docs.python.org/zh-cn/3/howto/annotations.html

查看官方文档发现, 这个是3.9版本之前的一个设计缺陷.

特此记录, 因为本人在此坑里陷了甚久

Cookie 的域无效, 这个大概率是写 Requests 写习惯了, 直接带这 cookie 请求

解决方法也很简单, 先用Selenium打开一下网址, 然后在加上Cookie重新访问即可

具体表现为使用WIndows10自带的输入法输入中文时出现严重卡顿,比如在键盘上按下一个键,至少要反应几秒钟才能出现按的字符,注意,是字符!也就是说字符是一个一个蹦出来的,并不连贯!

解决方法

1
%AppData%\Microsoft\InputMethod\Chs

把上面这个目录删掉!完美解决!

这个目录下有很多小文件

推测是输入法缓存,因为删除了以后,之前输入法记住的常输的句子就木有了。

这个是在网上找的一键清除脚本,没试过不确定是不是有用

1
2
3
4
5
6
7
' Fix Win10 Chinese IME lag
Option Explicit
Dim WSHShell, FSO, Target
Set WSHShell = CreateObject("WScript.Shell")
Set FSO = CreateObject("Scripting.FileSystemObject")
Target = WshShell.ExpandEnvironmentStrings("%AppData%") & "\Microsoft\InputMethod\Chs\"
FSO.DeleteFile Target & "UDP*.tmp"

https://ibug.io/cn/2021/03/win10-microsoft-ime-lag/

事情源于我要给新装的Ubuntu安装Python3.10, 结果在官网居然没有发现安装包

找了半天反正没找到, 只有源码没有发行版

没辙, 从源码编译安装吧

安装依赖

1
2
apt update
apt install build-essential zlib1g-dev libncurses5-dev libgdbm-dev libnss3-dev libssl-dev libreadline-dev libffi-dev libsqlite3-dev wget libbz2-dev

下载对应版本的Python, 例如这里是3.10.8

1
wget https://www.python.org/ftp/python/3.10.6/Python-3.10.8.tgz

解压

1
tar -xf Python-3.10.*.tgz

检查配置

1
2
cd Python-3.10.*/
./configure --enable-optimizations

编译

1
make -j $(nproc)

安装到/usr/bin/python

1
make altinstall

https://computingforgeeks.com/how-to-install-python-on-ubuntu-linux-system/

docker-compose 使用3.7版本,然后加入 deploy,例如:

1
2
3
4
5
6
7
8
9
10
version: "3.7"
services:
redis:
image: redis:alpine
container_name: testredis
deploy:
resources:
limits:
cpus: '0.50'
memory: 500M

限制 CPU 使用率 50%, 500MB 内存

需要注意的是,启动时需增加 –compatibility 选项

1
docker-compose --compatibility up -d

否则会报错

1
WARNING: Some services (mysql, rsnmp) use the 'deploy' key, which will be ignored. Compose does not support 'deploy' configuration - use `docker stack deploy` to deploy to a swarm.

注:

–compatibility是docker-compose 1.20.0加入,主要目的就是用来将deploy中的数据限制、replicas与重启策略直接转译为version 2的语法

https://www.cnblogs.com/yjt1993/p/12402394.htm
https://www.jianshu.com/p/ba5518476057

笔者使用 Docker 部署了一个 Jenkins 镜像,最近升级镜像版本时遇到问题。

现在记录解决方案

java.lang.NoClassDefFoundError: org/jruby/javasupport/proxy/InternalJavaProxy

参考该链接解决

https://community.jenkins.io/t/jenkins-war-update-caused-issue-when-ruby-runtime-plugin-is-installed/3282

There may be deprecated plugins installed on your system that need to be removed.

The Jenkins project stopped distributing the Ruby runtime plugin, the Python runtime plugin, and plugins that depend on those two plugins in January 2022. You can find a detailed list of plugins and more information in the blog post:

大意是旧版本包含了已经弃用的插件。

1
Ruby runtime plugin

这个插件在 2.373 版本已经弃用了,需要卸载之前安装的该插件。

启动旧版本docker镜像后,我们发现在控制台面板无法卸载。

解决方案是,进入到$JENKINS_HOME/plugins目录,然后移除ruby-runtime*

即这两个文件

然后更新镜像版本,一切正常

前言

在计算机科学中, 字符串搜索是一个很常见的任务. 给定两个字符串A和B, 例如

1
2
A -> ABC ABCDAB ABCDABCDABDE
B -> ABCDABD

我们怎么知道 A 是否包含有 B ?

有很多种算法可以完成这个任务, Knuth-Morris-Pratt字符串查找算法(简称为KMP算法)是比较著名的一种.

这个算法由高德纳(Donald Knuth)和沃恩·普拉特(英语:Vaughan Pratt)在1974年构思,同年詹姆斯·H·莫里斯(英语:James H. Morris)也独立地设计出该算法,最终三人于1977年联合发表.

没错! 起头的那个 K 就是高德纳老爷子

该算法的巧妙之处在于, 一个词在不匹配时本身就包含足够的信息来确定下一个匹配可能的开始位置,此算法利用这一特性以避免重新检查先前配對的字符。(来自维基百科)

emmm….

我觉得,上面的话似乎不太好懂,我个人的理解是,该算法设计了一种方法,利用已经匹配过的信息,来减少重新匹配的次数。

以下的文章基于阮一峰的网络日志,特地记录了下学习的过程,以加强理解。

算法流程

如上图,首先字符串“BBC ABCDAB ABCDABCDABDE”的第一个字符与搜索词“ABCDABD”的第一个字符进行比较,因为B与A不匹配,所以我们将搜索词后移一位。

因为B与A不匹配,我们继续后移。

如此直到字符串有一个字符与搜索词的第一个字符匹配到为止。

接着比较字符串和搜索词的下一个字符,还是相同。

直到字符串有一个字符,与搜索词对应的字符不相同为止。

这时,最自然的反应是,将搜索词整个后移一位,再从头逐个比较。这样做虽然可行,但是效率很差,因为你要把”搜索位置”移到已经比较过的位置,重比一遍。

一个基本事实是,当空格与D不匹配时,你其实知道前面六个字符是”ABCDAB”。KMP算法的想法是,设法利用这个已知信息,不要把”搜索位置”移回已经比较过的位置,继续把它向后移,这样就提高了效率。

怎么做到这一点呢?可以针对搜索词,算出一张《部分匹配表》(Partial Match Table)。这张表是如何产生的,后面再介绍,这里只要会用就可以了。

已知空格与D不匹配时,前面六个字符”ABCDAB”是匹配的。查表可知,最后一个匹配字符B对应的”部分匹配值”为2,因此按照下面的公式算出向后移动的位数

1
移动位数 = 已匹配的字符数 - 对应的部分匹配值

因为 6 - 2 等于4,所以将搜索词向后移动4位。

因为空格与C不匹配,搜索词还要继续往后移。这时,已匹配的字符数为2(”AB”),对应的”部分匹配值”为0。所以,移动位数 = 2 - 0,结果为 2,于是将搜索词向后移2位。

因为空格与A不匹配,继续后移一位。

逐位比较,直到发现C与D不匹配。于是,移动位数 = 6 - 2,继续将搜索词向后移动4位。

逐位比较,直到搜索词的最后一位,发现完全匹配,于是搜索完成。如果还要继续搜索(即找出全部匹配),移动位数 = 7 - 0,再将搜索词向后移动7位,这里就不再重复了。

如何计算部分匹配表

首先,要了解两个概念:”前缀”和”后缀”。 “前缀”指除了最后一个字符以外,一个字符串的全部头部组合;”后缀”指除了第一个字符以外,一个字符串的全部尾部组合。

“部分匹配值”就是”前缀”和”后缀”的最长的共有元素的长度。以”ABCDABD”为例,

字符串 前缀 后缀 最长共有元素 最长共有元素的长度
A 空集 空集 0
AB A B 0
ABC A, AB BC, C 0
ABCD A, AB, ABC BCD, CD, D 0
ABCDA A, AB, ABC, ABCD BCDA, CDA, DA, A A 1
ABCDAB A, AB, ABC, ABCD, ABCDA BCDAB, CDAB, DAB, AB, B AB 2
ABCDABD A, AB, ABC, ABCD, ABCDA, ABCDAB BCDABD, CDABD, DABD, ABD, BD, D 0

“部分匹配”的实质是,有时候,字符串头部和尾部会有重复。比如,”ABCDAB”之中有两个”AB”,那么它的”部分匹配值”就是2(”AB”的长度)。搜索词移动的时候,第一个”AB”向后移动4位(搜索串长度-部分匹配值),就可以来到第二个”AB”的位置。

Python 实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
from typing import List


def KMP(text: str, pattern: str, search_all: bool = True) -> List[int]:
"""KMP字符串搜索算法"""

t_len = len(text)
p_len = len(pattern)

# 模式串长度小于待搜索的字符串
if t_len < p_len:
return []

i = 0
result = []
while i < t_len:
is_match = True
# 匹配最后一部分时, 起始坐标加上模式串的
# 长度大于文本长度时, 注定匹配失败, 所以
# 不用多此一举匹配最后部分
if i + p_len > t_len:
break

for j in range(len(pattern)):
if text[i + j] != pattern[j]:
# 第一位就不匹配
if j == 0:
is_match = False
i += 1
break
# 其余位不匹配时, 计算偏移值(对应的部分匹配值)
is_match = False
p_offset = partial_match(pattern[:j])
# 移动位数 = 已匹配的字符数 - 偏移值(对应的部分匹配值)
i += j - p_offset
break

if is_match:
result.append(i)
if not search_all:
break
i += p_len - partial_match(pattern)

return result

def partial_match(p: str) -> int:
"""计算部分匹配值

例如:
in: "ABCDAB"
out: 2

假设我们输入"ABCDAB", 则可知道该字符串的长度为 6, 最大下标(max_index)为 5

字符: A B C D A B
下标: 0 1 2 3 4 5

前缀和后缀的可选范围

Prefix: 0 1 2 3 4
Suffix: 1 2 3 4 5

于是我们知道

i prefix suffix diffrence(前缀和后缀的差值)
-
0 0 5 5
1 0, 1 4, 5 4
2 0, 1, 2 3, 4, 5 3
3 0, 1, 2, 3 2, 3, 4, 5 2
4 0, 1, 2, 3, 4 1, 2, 3, 4, 5 1

我们最多需要进行 5 次循环, i 表示当前循环的次数, prefix 和 suffix 分别
对应了该次循环时每个字符的下标, diffrence 表示了该次循环时, prefix 和
suffix 对应的下标的差值, 显然可知

diffrence = max_index - i

我们需要取最长的共有元素的长度, 很显然我们从大到小搜索效率最高, 我们只需要
搜索到一个有效值, 该值即是最优解. 对应上图即为从下往上搜索.
"""

if len(p) == 1:
return 0

max_macth_length = 0
max_index = len(p) - 1
i = max_index - 1

# 从大到小搜索仅需要搜索到一个即是最优值
while i > -1:
is_match = True
diff = max_index - i
for j in range(i + 1):
if p[j] != p[j + diff]:
is_match = False
break
if is_match:
max_macth_length = i + 1
break
i -= 1
return max_macth_length

测试

1
2
3
text = "ABC ABCDAB ABCDABCDABDEABCDABD"
pattern = "ABCDABD"
print(KMP(text, pattern))

输出

1
[15, 23]

参考链接

https://www.ruanyifeng.com/blog/2013/05/Knuth%E2%80%93Morris%E2%80%93Pratt_algorithm.html

ERROR odoo odoo.modules.loading: Database odoo not initialized

在使用 docker-compose 部署 odoo 时报错

1
ERROR odoo odoo.modules.loading: Database odoo not initialized, you can force it with -i base

解决方案

数据库容器增加环境变量

1
POSTGRES_DB=postgres

例如

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
version: '3.1'

services:

odoo:
image: odoo:15.0
depends_on:
- postgres
ports:
- "8069:8069"
volumes:
- ./odoo_web_data:/var/lib/odoo
- ./config/odoo.conf:/etc/odoo/odoo.conf
- ./addons:/mnt/extra-addons
environment:
- HOST=postgres
- USER=odoo
- PASSWORD=xxxxxxxxx

postgres:
image: postgres:15rc2-alpine3.16
environment:
- POSTGRES_DB=postgres
- POSTGRES_PASSWORD=xxxxxxxxx
- POSTGRES_USER=odoo
- PGDATA=/var/lib/postgresql/data/pgdata
volumes:
- ./odoo_db_data:/var/lib/postgresql/data/pgdata

https://github.com/odoo/odoo/issues/27447

https://github.com/lefilament/ansible_role_odoo_docker/issues/7