0%

python技法总结

总结一些python基础技法和常用库

前言

本文的内容按照下面的思路组织

python作为一门现代语言,有着非常好的包管理工具与完美的生态

语言基础

了解python中如何组织变量的是非常重要的,是明确参数传递,变量作用域,lambda表达式的关键

多线程是python中非常重要的一环,尤其是在爬虫的时候

面对对象编程

面对对象是非常优秀的设计模式,python中涉及到面对对象的小细节

生态

为了防止重复造轮子,一门现代的语言基本上都有包管理工具,如java的maven,gradle,js的npm等

IDE

变量

变量与引用

标记赋值

标记赋值:参考文献

一个有趣的例子

为什么values=[0, [...], 2]
1
2
3
4
5
>>> values = [0, 1, 2]
>>> values[1] = values
>>> values
[0, [...], 2]

参考文献

参考文献

参数传递

感觉cpp中这样写是对的,如何理解python中的这种现象,java中也是一样的

原因在于不存在二阶指针!!!

无法修改地址所指的方法

78行创建了一个新的变量root

图片详情找不到图片(Image not found)

值传递和参数传递

参考文献

可变类型与不可变类型

类和对象

父类的初始化

方法一: 调用未绑定的父类init方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class A(object):
def __init__(self):
self.a = 5

def function_a(self):
print('I am from A, my value is %d' % self.a)


class B(A):
def __init__(self):
A.__init__(self) # 此处修改了。如果类A的__init__方法需要传参,也需要传入对应的参数
self.b = 10

def function_b(self):
print('I am from B, my value is %d' % self.b)
self.function_a()



if __name__ == '__main__':
b = B()
b.function_b()


方法二:调用super函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class A(object):
def __init__(self):
self.a = 5

def function_a(self):
print('I am from A, my value is %d' % self.a)


class B(A):
def __init__(self):
super(B, self).__init__() # 此处修改了
self.b = 10

def function_b(self):
print('I am from B, my value is %d' % self.b)
self.function_a()


if __name__ == '__main__':
b = B()
b.function_b()

super方法的参数

带不带参数理论上是一样的

参考文献

参考文献
super

设计模式

单例模式

关键字与内置包

set.discard

该方法不同于 remove() 方法,因为 remove() 方法在移除一个不存在的元素时会发生错误,而 discard() 方法不会。

例子
1
2
3
4
5
6
7

fruits = {"apple", "banana", "cherry"}

fruits.discard("banana")

print(fruits)

参考文献

assert

sorted

字母表排序

int

实验
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
>>> int(1.2)
1
>>> int(1.6)
1
>>> int(1.25)
1
>>> int(1.05)
1
>>> int(3.05)
3
>>> int("1.2")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: invalid literal for int() with base 10: '1.2'
>>> type(1.2)
<class 'float'>
>>> type("1.2")
<class 'str'>
>>> int(1.2)
1
>>> int("1")
1
>>> float("1.2")
1.2
>>> int("0.00")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: invalid literal for int() with base 10: '0.00'
>>>

int的总结

  • 当用int对浮点数进行转型的时候,会截断(truncate)小数部分
  • 如果对小数字符串进行int转型会报错

数据结构

序列(sequence)

查看一个对象是不是序列

1
isinstance([], collections.Sequence)

list

list是一个动态数组

cpython中的实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
typedef struct {
PyObject_VAR_HEAD
/* Vector of pointers to list elements. list[0] is ob_item[0], etc. */
PyObject **ob_item;

/* ob_item contains space for 'allocated' elements. The number
* currently in use is ob_size.
* Invariants:
* 0 <= ob_size <= allocated
* len(list) == ob_size
* ob_item == NULL implies ob_size == allocated == 0
* list.sort() temporarily sets allocated to -1 to detect mutations.
*
* Items must normally not be NULL, except during construction when
* the list is not yet visible outside the function that builds it.
*/
Py_ssize_t allocated;
} PyListObject;

参考文献(参考译文)
stackoverflow参考文献

insert

insert的时候需要移动数组的指针,平均之间复杂度为O(n)

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
static int
ins1(PyListObject *self, Py_ssize_t where, PyObject *v)
{
Py_ssize_t i, n = Py_SIZE(self);
PyObject **items;
if (v == NULL) {
PyErr_BadInternalCall();
return -1;
}
if (n == PY_SSIZE_T_MAX) {
PyErr_SetString(PyExc_OverflowError,
"cannot add more objects to list");
return -1;
}

if (list_resize(self, n+1) < 0)
return -1;

if (where < 0) {
where += n;
if (where < 0)
where = 0;
}
if (where > n)
where = n;
items = self->ob_item;
for (i = n; --i >= where; )
items[i+1] = items[i];
Py_INCREF(v);
items[where] = v;
return 0;
}

set

集合的交并补

参考文献

str

字符串的reverse

@property

参考文献

filter

选出满足条件的对象

函数原型

1
filter(function, iterable)

例子

基本应用

从列表中选出满足特定条件的元素

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
# 新建一个list
exampleList = ["aaa.jpg", "bbb", "ccc"]
# 得到的是一个filter对象
filter(lambda x: ".jpg" in x, exampleList)
# <filter at 0x1875c456dd8>
dir(filter)
# ['__class__',
# '__delattr__',
# '__dir__',
# '__doc__',
# '__eq__',
# '__format__',
# '__ge__',
# '__getattribute__',
# '__gt__',
# '__hash__',
# '__init__',
# '__init_subclass__',
# '__iter__', # 实现了iter魔法函数,可以当作迭代器来用
# '__le__',
# '__lt__',
# '__ne__',
# '__new__',
# '__next__',
# '__reduce__',
# '__reduce_ex__',
# '__repr__',
# '__setattr__',
# '__sizeof__',
# '__str__',
# '__subclasshook__']
[i for i in filter(lambda x: ".jpg" in x, exampleList)]
# ['aaa.jpg']

综合应用举例

连续爬取图片的时候,下载图片合并成pdf之后,删除上一次爬虫生成的图片
删掉”resources/2020/comments/*.jpg”,”resources/2020/comments/*.jpg”

1
2
3
4
5
6
7
8
9
10
11
import os

if __name__ == '__main__':
# 删掉多余的jpg
for savePath in ["comments", "papers"]:
# 此时f是文件名
for f in filter(lambda x: '.jpg' in x, os.listdir(os.path.join("resources", "2020", savePath))):
# 合并成相对路径
file_path = os.path.join("resources", "2020", savePath, f)
# 删掉文件
os.remove(file_path)

print

  • %s的妙用
    有的时候需要原字符串(即带转义字符\的)

    保存为文件的时候

logging

常用日志设置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 得到一个logger
# __name__可以避免一些奇奇怪怪的bug
rootLogger = logging.getLogger(__name__)
# 设置日志级别
LOGGINGLEVEL = logging.DEBUG
rootLogger.setLevel(LOGGINGLEVEL)
# 设置字符串格式
logFormatter = logging.Formatter(' [%(levelname)s] %(asctime)s - %(pathname)s[line:%(lineno)d]: %(message)s')

if not os.path.exists(os.path.join(RESOURCESPATH,'log')):
# 如果不存在创建log文件存放日志
os.makedirs(os.path.join(RESOURCESPATH,'log'))
# 设置logger将日志信息输出到文件
fileHandler = logging.FileHandler(os.path.join(RESOURCESPATH,'log', 'mqRun.log'), encoding='utf-8')
fileHandler.setFormatter(logFormatter)
fileHandler.setLevel(LOGGINGLEVEL)
rootLogger.addHandler(fileHandler)
# 设置logger将日志信息输出到控制台
consoleHandler = logging.StreamHandler()
consoleHandler.setFormatter(logFormatter)
consoleHandler.setLevel(LOGGINGLEVEL)
rootLogger.addHandler(consoleHandler)

datetime,time

pickle-序列化与反序列化

导入

1
2
with open(os.path.join(DATAPATH,"full_music_data.pkl"),"rb") as f:
fullMusicData = pickle.load(f)

导出

TODO

unitest

非常好用的自动化测试框架

json

json字段和python中字段的对应关系

参考文献

dict

dict多属性排序

1
2
3
4
5
>>> a = {1:[1,2,3],2:[1,1,3],3:[1,2,3],4:[1,2,1]}
>>> dict(sorted(a.items(), key=lambda x: (x[1][0], x[1][1], x[1][2])))
{2: [1, 1, 3], 4: [1, 2, 1], 1: [1, 2, 3], 3: [1, 2, 3]}

# 列表中第一个元素相同的话,按照第二个元素排序,反之按照第三个元素排序

dict

list

并且得到最小的id

1
2
from operator import itemgetter
min(enumerate(a), key=itemgetter(1))[0]

参考文献

得到key的index

1
2
3
4
keys=i.keys()
keys.index(key)

# AttributeError: 'dict_keys' object has no attribute 'index'

参考文献

多线程

线程锁

lock和rlock的区别

lock可以在一个线程上锁,在另一个线程解锁

rlock只能在上锁的线程解锁,并且可重入
参考文献

sleep

只会阻塞该线程

参考文献
参考文献

re

正则表达式测试网址

开启.匹配换行符

添加参数re.DOTALL

1
pattern = re.compile(r".*?---(.*)---",re.DOTALL)

关于re.DOTALL和re.M混合使用导致前者失效

$\mho$(TODO)

替换的时候使用匹配到的字符串

bug

当要匹配的内容中存在\U的内容时,会出现问题

比如

1
"C:\Users\%username%\AppData\Roaming\Code\User"

会报错
1
re.error: bad escape \U at position 374 (line 27, column 7)

解决方法

TODO

问号的用处

非贪婪匹配

前瞻后顾

前瞻: exp1(?=exp2) 查找exp2前面的exp1

后顾: (?<=exp2)exp1 查找exp2后面的exp1

负前瞻: exp1(?!exp2) 查找后面不是exp2的exp1

负后顾: (?<!exp2)exp1 查找前面不是exp2的exp1

举个负后顾的例子

在做笔记的时候,把所有的「奈奎斯特」都少打了「奈」变成了「奎斯特」,这时候需要将所有的「奎斯特」替换成「奈奎斯特」该怎么做呢?

被替换处填入替换处填入
(?<!奈)奎斯特奈奎斯特

如在vscode中

图片详情找不到图片(Image not found)
修改批改符号

\checkmark改为\color{green}{\checkmark}

(?<!\\color\{green\}\{)\\checkmark

\color{green}{\checkmark}

不捕获分组

参考文献

举个例子

图片详情找不到图片(Image not found)
图片详情找不到图片(Image not found)

找到第一个不为某个值的位置在字符串中

参考文献

search 和 match的区别

pattern = re.compile(r"^[-]?[1]?\d{1,3}(.\d{0,2})?$")

$\color{red}{\text{Q}}$: 像上面那样写还有区别吗

参考文献

限定符

符号含义
?出现0次或者多次

使用前面匹配到的结果

$1 -> \1

参考文献

包管理

pip

查看pip版本
pip show pip

annaconda

pymongo

mongodb是非常常用的非关系型数据库之一,python中常用pymongo对mongdb数据库进行操作

数据库连接

数据库库连接的url和官方的mongodb compass可视化软件一致

1
dbs = pymongo.MongoClient("mongodb://user:pass@ip/database")

创建db和集合

docker容器数据的备份

参考文献

参考文献

flask

flask的第一份源码

flask的源码解读

参考文献

flask中的蓝图

参考文献

pycharm

快捷键

快捷键作用
shift + F6改变量名

run unitest 而不是 run main

参考文献

断点异常

debug的时候明明没有断点的地方确断点了,或者断点没有阻塞运行:重启pycharm

Details![](https://raw.githubusercontent.com/ednow/cloudimg/main/githubio/20210704232404.png)

关闭和开启兼容性检查

Details![](https://raw.githubusercontent.com/ednow/cloudimg/main/githubio/20210707214745.png)

性能分析工具profile

参考文献

memory_profile

memory_profile

Q & A

得到当前路径

os.getcwd()

参考文献

进制转换

进制转换找不到图片(Image not found)
[参考文献](https://blog.csdn.net/weixin_43353539/article/details/89444838) ## 只格式化部分字符串 [参考文献](https://stackoverflow.com/questions/11283961/partial-string-formatting) ## Traceback ## 一次读多个string
代码详情
1
'\n'.join(iter(input, sentinel))

参考文献

好像不行

图片详情找不到图片(Image not found)

自定义Exception

参考文献

内部typing

参考文献

append 和 +的区别

参考文献

重定向输入输出

重定向输入

将输入重定向为字符串

1
2
3
4
5
6
7
8
from io import StringIO
import sys
f = StringIO('1111\n22')
sys.stdin = f
string = input()
print(string)

# 1111

参考文献

重定向输出

1
2
3
4
5
6
7
from io import StringIO
import sys
f = StringIO()
sys.stdout = f
print("1111")

f.getvalue() # '1111\n'

zen of python

为什么要加密

参考文献

一行有趣的lambda代码

nb_children = lambda node: sum(nb_children(child) for child in children(node)) + 1

用lambda函数进行递归,求一颗树中节点的个数

参考文献

一份合理的工程结构

这种工程结构在pythcharm中是能够在同级目录下导入模块

但下面这个不行

字符串的split函数

如果不带参数的话,默认会把非空字符当作sep

统一改变list的数据类型

map(int, list)可以将字符串统一改成int类型

sort默认是升序

交替穿插两个等长的list

法1

1
2
sum(zip(l1, l2), ())
# sum(zip(list1, list2))

解释:参考文献

法2

1
2
3
4
5
6
7
8
9
10
11
12
13
>>> list1 = ['f', 'o', 'o']

>>> list2 = ['hello', 'world']

>>> result = [None]*(len(list1)+len(list2))

>>> result[::2] = list1

>>> result[1::2] = list2

>>> result

['f', 'hello', 'o', 'world', 'o']

参考文献

reduce和sum的区别

参考文献

字符串的split函数

如果不带参数的话,默认会把非空字符当作sep

统一改变list的数据类型

map(int, list)可以将字符串统一改成int类型

sort默认是升序

交替穿插两个等长的list

法1

1
2
sum(zip(l1, l2), ())
# sum(zip(list1, list2))

解释:参考文献

法2

1
2
3
4
5
6
7
8
9
10
11
12
13
>>> list1 = ['f', 'o', 'o']

>>> list2 = ['hello', 'world']

>>> result = [None]*(len(list1)+len(list2))

>>> result[::2] = list1

>>> result[1::2] = list2

>>> result

['f', 'hello', 'o', 'world', 'o']

参考文献

reduce和sum的区别

参考文献

misc

python包去重

Details
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
string = """#include <iostream>
#include <cstdio>
#include <string>
#include <vector>
#include <deque>
#include <list>
#include <map>
#include <iostream>
#include <cstdio>
#include <string>
#include <vector>
#include <deque>
#include <list>
#include <map>
#include <iostream>
#include <cstdio>
#include <string>
#include <vector>
#include <deque>
#include <list>
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <vector>
#include <deque>
#include <list>
#include <algorithm>
#include <string.h>
#include <stack>
#include <stdio.h>
#include <iostream>
#include <cstdio>
#include <string>
#include <vector>
#include <deque>
#include <cstring>
#include <list>"""

package = string.split("\n")

print("\n".join(list(set(package))))

通过内存地址访问变量

参考文献

参考文献

sys.stderr 比 sys.stdin输出的优先级更高?

奇异的bug

UnboundLocalError: local variable 'sortedAppendix' referenced before assignment

只改了一个元素,一列都变了?

bug代码

[[0] * cities] * cities 会创建索引相同的五个list

参考文献

raw string

参考文献

小括号的妙用

简单应用

如果直接运行

1
2
1 >
2

会报错

但如果这样子写
1
2
3
4
5
a = (
1 >
2
)
a # True


python解释器就能正确解释

综合应用举例

使用pyecharts画图的时候,灵活运用小括号,可以是代码简洁,增强阅读性

取组合排列

1
2
3
list(combinations([1,2,3],2))
# [(1, 2), (1, 3), (2, 3)]

stringIO

参考文献

通过字符串获取方法

getattr(object, name[, default])

参考文献

通过字符串导包

参考文献

exec表达式

参考文献

python 每n个字符串为一组

参考文献

在list中找出第一个不为0的下表

参考文献

python中的等于赋值

参考文献

打印错误日志

参考文献1
参考文献2

打包自己的库并安装

官方文档

list是线程安全的

参考文献

线程池

参考文献

python读取截切板中的图片为二进制

Python可以通过第三方库来读取剪贴板中的内容,但是需要注意的是,不同的操作系统可能需要使用不同的库。对于图片,我们可以使用Pillow库来处理图片,然后将其转换为二进制。

如果你在Windows系统中,可以使用clipboardPillow库来读取剪贴板中的图片并转换为二进制。首先,你需要安装这些库,可以使用pip来安装:

1
pip install clipboard Pillow

然后,你可以使用以下代码来读取剪贴板中的图片并转换为二进制:

1
2
3
4
5
6
7
8
9
10
11
import io
import clipboard
from PIL import Image

# 从剪贴板获取图片
image = clipboard.paste_as_image()

# 转换为二进制
binary_image = io.BytesIO()
image.save(binary_image, format='PNG')
binary_image = binary_image.getvalue()

如果你在Mac或Linux系统中,可以使用xclipPillow库来读取剪贴板中的图片并转换为二进制。首先,你需要安装这些库,可以使用pip和apt来安装:

1
2
pip install Pillow
sudo apt-get install xclip

然后,你可以使用以下代码来读取剪贴板中的图片并转换为二进制:

1
2
3
4
5
6
7
8
9
10
11
12
13
import io
import subprocess
from PIL import Image

# 从剪贴板获取图片
command = "xclip -selection clipboard -t image/png -o"
image = subprocess.check_output(command.split(), shell=False)

# 转换为二进制
image = Image.open(io.BytesIO(image))
binary_image = io.BytesIO()
image.save(binary_image, format='PNG')
binary_image = binary_image.getvalue()

注意:这些代码可能不会在所有环境中都能正常工作,因为它们依赖于特定的操作系统功能和第三方库。如果你遇到问题,可能需要根据你的具体环境进行调整。

ORM 框架:SQLAlchemy

选择最大值,并且附带其他条件

代码详情
1
2
3
4
5
from sqlalchemy import desc

qry = session.query(Data).filter(
Data.user_id == user_id).order_by(
desc(Data.counter).limit(1)

参考文献

python文件

pyi

Python 的存根文件,用于代码检查时的类型提示。

常用的 IDE 都会有类型检查提示功能,比如在 PyCharm 中,当我们给一个函数传入一个错误的类型时会给出对应的提示,这其实不是 IDE 的特殊开发的功能,它只是集成了PEP484的规定,利用了已经预先生成好的 pyi文件。

参考文献

详解Python相关文件常见的后缀名

详解Python文件: .py、.ipynb、.pyi、.pyc、​.pyd