独到科技


推荐阅读

mongodb官网手册|官网

kafaka基本操作|知乎

如何高效操作pycharm|个人

第三方库

  • xlrd
  • Asyncio
  • pandas

Target

  • celery
  • Mongoldb
  • Nginx
  • Kibana
  • ElasticSearch
  • Rabitmq
  • redis
  • git
  • docker
  • kafka
  • tornado

Advance

  • docker

Day41

str转dict

  1. 使用json转化

需要””才可以转化

1
2
3
4
5
6
7
import json
# 原始数据的key和value需要是""
# 如果内部是''需要使用eval
# 但是使用eval可能导致
a = '{"a":"1", "b":"1"}'
c = json.loads(a)
print(c, type(c))
  1. 使用eval转化

对引号没有要求,但是具有一定的安全性问题

1
2
3
4
5
6
7
user_info = "{'name' : 'john', 'gender' : 'male', 'age': 28}"
user_dict =json.loads(user_info )
print(user_dict , type(user_dict ))

a = '{"a":"1", "b":"1"}'
b = eval(a)
print(b, type(b))
  • 安全性问题

    1
    2
    # 如果,当前目录中恰好有一个文件,名为data.py,则恶意用户变读取到了文件中的内容。
    please input:open('data.py').read()
  1. 使用 literal_eval进行转换

综合了方法一和方法二的优点

1
2
3
4
import ast
user = '{"name" : "john", "gender" : "male", "age": 28}'
user_dict = ast.literal_eval(user)
print(user_dict,type(user_dict))

Day40

bytes图片转jpg格式

1
2
3
4
5
6
7
8
9
10
import urllib3
import os
# PIL图像处理标准库
from PIL import Image
from io import BytesIO

# 将bytes转化成字节流
bytes_stream = BytesIO(qrcode)
# 读取图片
roiimg = Image.open(bytes_stream)
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
import urllib3
import os
# PIL图像处理标准库
from PIL import Image
from io import BytesIO

http = urllib3.PoolManager()
response = http.request('GET','f.hiphotos.baidu.com/image/pic/item/8d5494eef01f3a29f863534d9725bc315d607c8e.jpg')
result = response.data

# 将bytes转化成字节流是关键
# 将bytes结果转化为字节流
bytes_stream = BytesIO(result)
# 读取到图片
roiimg = Image.open(bytes_stream)

# roiimg.show() #展示图片
# print(type(result))
# print(response.status)

imgByteArr = BytesIO() # 初始化一个空字节流
roiimg.save(imgByteArr,format('PNG')) # 把我们得图片以‘PNG’保存到空字节流
imgByteArr = imgByteArr.getvalue() # 无视指针,获取全部内容,类型由io流变成bytes。
# dir_name = os.mkdir('baiduimg')
img_name = '1.jpg'
with open(os.path.join('baiduimg',img_name),'wb') as f:
f.write(imgByteArr)

Day39

merge特定提交

  1. 使用git log或者git reflog查看提交信息
  2. 回到要merge的分支
  3. git cherry-pick <hash_version_num>

Day38

git 撤销commit

  1. 执行commit后,还没执行push时,想要撤销这次的commit
1
2
# soft撤销不会撤销add的文件, 同样也撤销不了merge后有conflict的文件
git reset --soft HEAD^
  1. 详细命令
1
2
3
4
5
6
7
8
9
10
11
# 如果想要连着add也撤销的话,–soft改为–hard(删除工作空间的改动代码)
git reset --hard HEAD^

# HEAD^ 表示上一个版本,即上一次的commit,也可以写成HEAD~1
# 如果进行两次的commit,想要都撤回,可以使用HEAD~2

–-soft
不删除工作空间的改动代码 ,撤销commit,不撤销git add file

–hard
删除工作空间的改动代码,撤销commit且撤销add

git stash指定文件

1
git stash push <file1> <file2> <file3> [file4 ...]

Day37

vim编辑tab

编辑vim tab空四个格

1
2
3
4
5
6
7
8
# 编辑文件
vi ~/.vimrc

# 输入order
syntax on
set ts=4 # 空格数
set expandtab # 设置 Tab 键宽度为 4 个空格。
set autoindent # 设置自动缩进,即每行的缩进同上一节相同。

Day36

Datetime-String-Unix

datetime <–> string <–> unix 相互转化

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
import datetime
import time

# 日期时间字符串
st = "2017-11-23 16:10:10"
# 当前日期时间
dt = datetime.datetime.now()
# 当前时间戳
sp = time.time()

# 1.把datetime转成字符串
def datetime_toString(dt):
print("1.把datetime转成字符串: ", dt.strftime("%Y-%m-%d %H:%M:%S"))


# 2.把字符串转成datetime
def string_toDatetime(st):
print("2.把字符串转成datetime: ", datetime.datetime.strptime(st, "%Y-%m-%d %H:%M:%S"))


# 3.把字符串转成时间戳形式
def string_toTimestamp(st):
print("3.把字符串转成时间戳形式:", time.mktime(time.strptime(st, "%Y-%m-%d %H:%M:%S")))


# 4.把时间戳转成字符串形式
def timestamp_toString(sp):
print("4.把时间戳转成字符串形式: ", time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(sp)))


# 5.把datetime类型转外时间戳形式
def datetime_toTimestamp(dt):
print("5.把datetime类型转外时间戳形式:", time.mktime(dt.timetuple()))

Day35

Linux修改文件名

文件重命名

单个文件命名

1
sudo mv old_file_name new_file_name

文件重命名[批量]

多个文件命名

匹配正则表达式

docker进阶

背景: 使用官方的docker-compose文档联系docker-compose

docker-compose: 可以理解为一个官方插件, 需要下载, 用于维护项目, 可以快速启动多个微服务, 需要docker-composeyml文件配置各个微服务间的依赖关系, 并使用docker-compose build docker-compose up启动服务

1
curl -L https://get.daocloud.io/docker/compose/releases/download/1.25.5/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose

过程如下:

  1. 根据yml文件目录(即项目目录名)创建一个默认的网络, project-compose_default, 会将所涉及的所有微服务共同至于一个网络下, 这样可以使用域名而非ip进行访问
  2. 根据yml文件创建对应的container容器
  3. 启动服务, 开启项目

官网docker-compose

创建目录composetest, 所有的文件均创建在此目录下

  1. 创建应用(app.py)
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
import time
from flask import Flask
import redis


app = Flask(__name__)
cache = redis.Redis(host="redis", port=6379)


def get_hit_count():
retries = 5
while True:
try:
return cache.incr("hits")
except redis.exceptions.ConnectionError as exc:
if retries == 0:
raise exc
retries -= 1
time.sleep(0.5)


@app.route("/")
def hello():
count = get_hit_count()
return f"hello world!, i have been seen {count} times."


if __name__ == "__main__":
app.run(host="0.0.0.0", debug=True)
  1. 创建Dockerfile构建imagse(打包服务和环境)
1
2
3
4
5
FROM python:3.6-alpine
ADD . /code
WORKDIR /code
RUN pip install -r requirements.txt
CMD ["python", "app.py"]
  1. 创建docer-compose.yml (启动项目, 构建各个微服务之间的依赖)
1
2
3
4
5
6
7
8
version: "3"
services:
web:
build: .
ports:
- "5000:5000"
redis:
image: "redis:alpine"
  1. 创建requirements.txt(依赖环境)
1
2
redis
flask

Review

  • docker-compose build出现error时, 需要执行docker rm $(docker ps -aq)删除所有存在且不可用容器

Day34

Dal[数据交换层]

DAL是[数据访问层]英文缩写,即为数据访问层(Data Access Layer)。其功能主要是负责数据库的访问。简单地说就是实现对数据表的Select(查询)、Insert(插入)、Update(更新)、Delete(删除)等操作。

Dal一般有自己的服务器IP, 可以进入查看相应的log, 如果Dal做了负载均衡, 最好的方式是通过script查看对数据的访问

Day33

asyncio[异步]

1
2
3
4
5
6
7
8
9
10
11
12
async def main():
task = asyncio.Task(update_username())
done, pending = await asyncio.wait({task})
if task in done:
print(task)


if __name__ == "__main__":
BaseModel.init_model_dict()
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
loop.close()

Day32

vim命令

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
h Move left
j Move down
k Move up
l Move right
w Move to next word
W Move to next blank delimited word
b Move to the beginning of the word
B Move to the beginning of blank delimted word
e Move to the end of the word
E Move to the end of Blank delimited word
( Move a sentence back
) Move a sentence forward
{ Move a paragraph back
} Move a paragraph forward
# =================================
0 Move to the begining of the line
$ Move to the end of the line
# =================================
1G Move to the first line of the file
G Move to the last line of the file
nG Move to nth line of the file
:n Move to nth line of the file
fc Move forward to c
Fc Move back to c
H Move to top of screen
M Move to middle of screen
L Move to botton of screen
% Move to associated ( ), { }, [ ]

Day31

执行order

1
2
3
4
5
6
7
8
9
# 查看linux所有history order
history
>>
1 ls
2 ll
3 docker ps -a

# 执行history order
! <order-rank>

Day30

判断bool

1
bool(var)

Day29

服务器之间传递文件

两台Linux服务器之间传输文件的四种方法 - 知乎 (zhihu.com)

1
2
3
4
5
6
7
8
9
10
11
# 从本地复制到远程
scp local_file remote_username@remote_ip:remote_folder

scp local_file remote_username@remote_ip:remote_file

scp local_file remote_ip:remote_folder

scp local_file remote_ip:remote_file

# 从远程复制到本
# 只要将从本地复制到远程的命令的后2个参数调换顺序即可地
  • 如果没有权限使用root用户(默认通过ssh连接)

scp /Users/edy/PycharmProjects/TXM/component/tasks/order_tasks.py root@59.110.228.229:/root/

在容器内修改项目

  • 砍掉上线这一步骤, 节约时间
  • 在测试服进行debug
1
2
3
4
在宿主机与container中copy文件

docker cp local_system/app.py container_id:/Project/demo
docker cp [宿主机目录|文件] [容器ID]:[目标文件|目录]

1
2
3
4
5
6
7
8
9
10
11
# modify local file
...

# copy file to remote-server
scp /Users/edy/PycharmProjects/TXM/component/tasks/order_tasks.py root@59.110.228.229:/root/

# copy file from server to container
docker cp order_tasks.py txm-task-order:/TXM/component/tasks/

# restart target docker
docker restart <docker_name>

Day28

连接本地服务[wait]

Day27

For-loop

1
2
3
4
5
6
7
8
9
tmp = []

for item in temp:
print('something~')

"""
由于tmp为空, 此时for-in-loop-structure并不会
运行for-block-code
"""

集合取交集

用法: set().intersection(set_1, set_2|iterable)

  • intersection()可以任何可迭代集合
  • 返回的是set

intersection() 方法返回包含两个或更多集合之间相似性的集合。

含义:返回的集合仅包含两个集合中都存在的项目,或者如果使用两个以上的集合进行比较,则在所有集合中都存在。

1
2
3
4
5
module_list = ['assistant', 'moments', 'friend-welcome', 'xxxx']
modules = ['xxxx', '12371283']
ans = list(set(modules).intersection(module_list))

print(ans)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
x = {"a", "b", "c"}
y = {"c", "d", "e"}
z = {"f", "g", "c"}

result = x.intersection(y, z)

print(result)
>>
{"c"}

a = {"a", "b", "c"}
b = ["c", "f"]
c = {"a", "d", "c"}
d = "cccddaqasda"

ans = a.intersection(b, c, d)
print(ans)

Day26

Shell[命令]

查看磁盘

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
1.命令格式:

df [选项] [文件]

2.命令功能:

显示指定磁盘文件的可用空间。如果没有文件名被指定,则所有当前被挂载的文件系统的可用空间将被显示。默认情况下,磁盘空间将以 1KB 为单位进行显示,除非环境变量 POSIXLY_CORRECT 被指定,那样将以512字节为单位进行显示

3.命令参数:

必要参数:

-a 全部文件系统列表

-h 方便阅读方式显示

-H 等于“-h”,但是计算式,1K=1000,而不是1K=1024

-i 显示inode信息

-k 区块为1024字节

-l 只显示本地文件系统

-m 区块为1048576字节

--no-sync 忽略 sync 命令

-P 输出格式为POSIX

--sync 在取得磁盘信息前,先执行sync命令

-T 文件系统类型
1
2
3
4
5
# 搜索执行的命令
control r

# 查看存储情况
df -h

mongodb模糊查询

1
2
3
4
# / 是mongodb的通配符
# 以下命令使用正则表达式查找包含 runoob 字符串的文章:
db.posts.find({post_text:/runoob/})
db.posts.find({post_text:{$regex:"runoob"}})

Day25

环境

  • DEV|自测

[Development environment]开发环境,外部用户无法访问,开发人员使用,版本变动很大。

  • TEST

[Test environment]测试环境,外部用户无法访问,专门给测试人员使用的,版本相对稳定。

  • SIT

[System Integration Test]系统集成测试,开发人员自己测试流程是否走通。

  • UAT

[User Acceptance Test environment]用户验收测试环境,用于生产环境下的软件测试者测试使用。

  • PRE

[preparatory environment]灰度环境,外部用户可以访问,但是服务器配置相对低,其它和生产一样,外部用户可以访问,版本发布初期,正式版本发布前。

  • PRD|PRO

[Production environment]生产环境,面向外部用户的环境,正式环境,连接上互联网即可访问。

  • FAT

[Feature Acceptance Test environment]功能验收测试环境,用于软件测试者测试使用

Day24

线上script

获取线上数据库时使用script, 其中需要登陆线上的服务器中的docker

写法:

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
# 头文件
# 用户端输出print
from config.config import Table
from model import BaseModel
import pandas as pd

data = pd.read_excel("/Users/edy/Desktop/test.xlsx", usecols=[1]) # 读取项目名称列,不要列名
data_list = data.values.tolist()
phone_list = []
for item in data_list:
phone_list.extend(item)

phones = map(str, phone_list)

res = []

for item in phones:
device_info = await BaseModel.fetch_one(Table.UDevice, "*",
BaseModel.where_dict({"sim": item}))
if device_info:
sim = device_info.get("sim")
uniques = device_info.get("unique_id")
bot_usernames = (device_info.get("bot_username"))
res.append(
{
"phone": sim,
"设备id": uniques,
"微信号": bot_usernames
}
)

print(res)

Day23

分支关联

本地分支与远程分支关联(隐式关联)

1
2
3
4
5
6
git branch --set-upstream-to=origin/<branch> dev_lyh_private_send

# 优先选择此方法
git push --set-upstream origin local_branch

git push -u origin local_branch

sheet_name[规范]

Excel工作表表名不能用下列字符:
冒号——:
斜杠——/
问号——?
反斜杠——
星号——*
方符号——[ 或 ]

构成工作表的表名字符个数不得超过31个。

Day22

Python字符串编码格式

unicode编码

Python中的编码格式

字符串的编码一开始是 ascii,只支持英文,由于多种语言的存在,出现万国码 unicode,但 unicode 不兼容 ascii,而且对存储空间造成浪费,所以出现 utf-8 编码,一种针对 unicode 的可变长度字符编码。

在 Python3 中共有两种字符序列。一种是 str 序列,默认对字符串编码;一种是 bytes 序列,操作二进制数据流,如代码段一中的 hi,通过在字符串前的 b,即表示 bytes 。这两种序列可通过 decode 和 encode 相互转换

在网络传输中,如 urllib、request 等获取数据的库,通常返回 bytes 序列,这时可通过 decode 指定相应的格式经行解码,获取中文字符。

1
2
3
# 以b开头 --> bytes序列

# 以u开头 --> unicode序列

方法一:使用unicode_escape 解码

1
2
3
4
5
6
# 
unicode = b'\\u4f60\\u597d'
re = unicode.decode("unicode_escape")
print(re)

返回:你好

方法二:使用encode()方法转换,再调用bytes.decode()转换为字符串形式

1
2
s = r'\u4f60\u597d'
print(s.encode().decode("unicode_escape"))

方法三: 使用json.loads 解码(为json 格式)

1
2
str = '\u4eac\u4e1c\u653e\u517b\u7684\u722c\u866b'
print json.loads('"%s"' %str)

方法四:使用eval(遇到Unicode是通过requests在网上爬取的时候)

1
2
3
response = requests.get(url,headers=headers)
re = eval("u"+"\'"+response.text+"\'")
print(re)

Day21

调试API

  • 本地调试http, 不用https

[1, 2]转化成[“1”, “2”]

使用map函数

描述

map() 会根据提供的函数对指定序列做映射。

第一个参数 function 以参数序列中的每一个元素调用 function 函数,返回包含每次 function 函数返回值的新列表。

语法

map() 函数语法:

1
map(function, iterable, ...)

Day20

获取维度数据

operator中的itemgetter函数

from operator import itemgetter

该函数的作用是获取对象的某个/些维度的数据, 默认将对象的维度看为1维 该函数的使用方法:

  1. 定义一个itemgetter函数,并指定获取某个对象中哪些维度的值
  2. 用定义好的函数作用于对象

example

1
2
3
4
5
6
7
8
9
10
11
12
from operator import itemgetter

# define 1*1 1*2 array
a = [1, 2, 3]
b = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]

# define itemgetter-fun fetch a | b dimension data
get = itemgetter(0)
print(get(a), get(b), sep="\n")
>>
1
[1, 2, 3]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
from operator import itemgetter

# define 1*1 1*2 array
a = [1, 2, 3]
b = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]

# 获取a b第0维和2维的数据
# 获取多维数据 return: tuple
get = itemgetter(0, 2)

print(get(a), get(b), sep="\n")
>>
(1, 3)
([1, 2, 3], [7, 8, 9])

字典列表排序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from operator import itemgetter

list_to_be_sorted = [{'lv': 44, 'age': 10},
{'lv': 12, 'age': 39},
{'lv': 88, 'age': 22},
{'lv': 6, 'age': 2},
{'lv': 3, 'age': 4}]

new_list = sorted(list_to_be_sorted, key=itemgetter('age'))
print(new_list)

>>
[
{'lv': 6, 'age': 2},
{'lv': 3, 'age': 4},
{'lv': 44, 'age': 10},
{'lv': 88, 'age': 22},
{'lv': 12, 'age': 39}
]

python封装「,」

1
2
3
4
5
6
7
8
# 联系使用多个变量并用,进行连接时 python会将其自动封装成tuple
a = 1
a = a, a, a, a
print(type(a), a, sep='\n')

>>
<class 'tuple'>
(1, 1, 1, 1)

Day19

本地远程测试

  • mac terminalifconfig找到en0>> inetip地址, 将localhost替换成inet地址即可远程连调

本地测试

开展本地测试首先在config中配置本地环境

http://127.0.0.1:9000/controllername/api_name

TXM上线

团小蜜测试服打完release-v_test_0.26.97tag后, 直接push到远程分支, 不用运行sh脚本直接可以上线

Git|删除Tag

git 删除本地标签:

1
2
3
git tag -d 标签名  

例如:git tag -d v3.1.0

git 删除远程标签:

1
2
3
git push origin :refs/tags/标签名  

例如:git push origin :refs/tags/v3.1.0

git 删除本地模块标签的操作

1
2
3
4
5
6
7
git submodule foreach git tag -d 标签名

例如:git submodule foreach git tag -d v3.1.0

删除成功提示:
To http://XXXXXXXX_Master.git
- [deleted] v3.1.0

git 删除远程模块标签的操作

1
2
3
4
5
6
7
8
9
10
11
git submodule foreach git push origin :refs/tags/标签名

例如:git submodule foreach git push origin :refs/tags/v3.1.0

删除成功的提示:
Entering 'XXXXXXXX'
To http://XXXXXXXX.git
- [deleted] v3.1.0
Entering 'XXXXXXXX'
To http://XXXXXXXX.git
- [deleted] v3.1.0

Day18

mongo聚合查询

聚合查询

1
2
3
4
5
# aggregation
[
{$match: {member_count:{$gt:100}}},
{$group: {"_id": {}, "total": {$sum: "$member_count"}}}
]

Day17

时间戳

time.time()

格林威治时间1970年01月01日00时00分00秒起至现在的总秒数

db.collection.findOne()._id.getTimestamp()

世界标准时间

ISODate时间ISODate(“2013-03-01T00:00:00Z”) + 你的时区

北京+8h

查看log

  1. 进入服务器
1
ssh root@ip
  1. 查看镜像
1
docker ps
  1. 根据kibana-index进入对应的镜像内
1
2
3
4
5
# 静态查询
docker logs --tail 100 celery_newmsg

# 动态查询
docker logs -f --tail 100 image_name

查询技巧

  • 显示所有log
1
2
3
4
5
# 显示某个指定容器log
docker logs [options] 容器ID or 容器name

# 显示docker compose启动的所有容器log
docker-compose logs
  • 显示实时log
1
docker logs -f [docker_id] or [docker_name]
  • 动态显示log尾部
1
docker logs -f --tail 100 [docker_id] or [docker_name]
  • 使用grep过滤log
1
2
# 查找所有包含"error"log
docker logs | grep error
  • 根据时间进行log的查找
1
docker logs --since 2021-08-25T12:01:46.452616Z [容器Id] or [容器name]
  • 组合查询
1
docker logs -f --tail 100 容器id | grep info

Day16

使用GIT进行版本控制

以Mac为例

  1. 查看git version
1
git --version
  1. 安装git
1
sudo apt-get install git
  1. 配置git

git跟踪谁修改了项目, git必须知道你的用户名, 但你可以使用一个虚假的emial

1
2
git config --global user.name "username"
git config --global user.email "email"
  1. 创建项目|以下操作非必要说明均在sub_dir中使用命令
1
2
3
4
cd super_directory
mkdir sub_dir
cd sub_dir
touch project.py test.py
  1. 初始化仓库

git在对应的目录中初始化一个空的仓库, 仓库是指程序中被git主动追踪的一组文件. git用来管理仓库的文件都存储在.git/这个隐藏目录中, 包括index/ local git, .git存放着项目的所有历史记录

1
2
3
4
5
git init

# 添加忽略追踪文件
.gitignore
text.py
  1. 检查状态

git中一个分支代表一个版本对应一般开发一个功能

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
git status
# on branch master
#
# initial commit
#
# untracked files
#
# .gitignore
# test.py

git add .
"""
将项目中未被跟踪的所有文件全部加入到仓库中, 但是并不提交他们, 只是让git开始关注它们
"""

git commit -m "info"
"""
将被追踪的文件提交到本地仓库
"""
  1. 查看历史提交

每次提交时, git都会生成一个包含40字符的UID, 记录提交是谁执行的|提交的时间以及提交的指定信息

1
2
3
4
git log

git log --pretty=oneline
# 只显示提交引用的UID和提交时的info
  1. 撤销修改

git checkout命令用于切换分支或恢复工作树文件。git checkout是git最常用的命令之一,同时也是一个很危险的命令,因为这条命令会重写工作区。

放弃修改, 恢复到前一个可执行状态

将working的修改放弃, 回到最近一次commit代码, 如果已经通过add进入indexing则无法撤回

注意:在checkout时会影响到working code, 因此必须在checkout时先add -commit或者stash push

1
2
3
4
5
6
7
8
9
10
11
12
# 撤销当前working代码, 回到前一次commit
git checkout .

# 切换分支
git checkout taget_branch

# 切换工作树
git checkout UID的前六位
>>
提示目前在历史分支树上
detached: 分离的
HEAD detached at 81ba859
  1. 回退分支

版本–分支–code

git rest --hard HEAD^:回退到上一版;
git rest --hard HEAD^^:回退到倒数第二版;
git rest --hard 3628164:回退到commit id为3628164的版本;

lunix

Apt: advanced packaging tool 高级封装工具

  • 下载软件
1
sudo apt-get install package_name
  • 更新软件
1
sudo apt package_name
  • 删除软件
1
sudo apt remove package_name
  • 更新所有可升级软件
1
2
3
4
5
6
7
列出所有可更新的软件清单命令:sudo apt update

升级软件包:sudo apt upgrade

列出可更新的软件包及版本信息:apt list --upgradeable

升级软件包,升级前先删除需要更新软件包:sudo apt full-upgrade

Day15

体系

  • uid与wxid打通==>生态闭环
  • 分销==>打通ERP
  • 目前主打社群沉淀与私域流量维护
  • to b和to c两种用户画像
  • to b 注重流量维护|社群沉淀
  • to c 注重营销

git常用命令

  • 初始化仓库

git init repo_name

  • 将远程仓库clone到本地

git clone https://gitee.com/cr_rui/drugstore.git

  • 添加远程仓库到本地

    git remote add origin https://gitee.com/cr_rui/drugstore.git

  • 本地分支连接远程分支

git branch -u origin/name

  • 将当前分支推送到远程上的同名的方便方法

git push origin HEAD

  • 删除远程分支

git push origin --delete 'branch_name'

  • 删除本地分支

git branch -d 会在删除前检查merge状态(其与上游分支或者与head)。
git branch -D 是git branch –delete –force的简写,它会直接删除。

git branch -d|D 'branch_name'

都是删除本地分支的方法(与删除远程分支命令相独立,要想本地和远程都删除,必须得运行两个命令)

chain

1
2
chain(*iterables) --> chain object
Return a chain object whose .__next__() method returns elements from the first iterable until it is exhausted, then elements from the next iterable, until all of the iterables are exhausted.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import itertools
a = [[1], [2, 3], [4, 5, 6]]
b = [[1], [2, 2]]
c = itertools.chain(a, b)
d = itertools.chain(*a, b)
e = itertools.chain(a, *b)
f = itertools.chain(*a, *b)
g = itertools.chain(*(a, b))

print(list(c))
print(list(d))
print(list(e))
print(list(f))
print(list(g))
>>
[[1], [2, 3], [4, 5, 6], [1], [2, 2]]
[1, 2, 3, 4, 5, 6, [1], [2, 2]]
[[1], [2, 3], [4, 5, 6], 1, 2, 2]
[1, 2, 3, 4, 5, 6, 1, 2, 2]
[[1], [2, 3], [4, 5, 6], [1], [2, 2]]

string:可迭代对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import itertools
a = ['a', 'aa', 'aaa']
b = itertools.chain(a)
c = itertools.chain(*a)
print(a)
print(list(b))
print(list(c))
>>
['a', 'aa', 'aaa']
['a', 'aa', 'aaa']
['a', 'a', 'a', 'a', 'a', 'a']

# ------split-------
import itertools
a = [[1], [2, 3], [4, 5, 6]]
b = itertools.chain(a)
c = itertools.chain(*a)
print(a)
print(list(b))
print(list(c))
>>
[[1], [2, 3], [4, 5, 6]]
[[1], [2, 3], [4, 5, 6]]
[1, 2, 3, 4, 5, 6]

Day14

filter

filter() 函数用于过滤序列,过滤掉不符合条件的元素,返回由符合条件元素组成的新列表。

该接收两个参数,第一个为函数,第二个为序列,序列的每个元素作为参数传递给函数进行判断,然后返回 True 或 False,最后将返回 True 的元素放到新列表中。

**注意:** *Python2.7 返回列表,Python3.x 返回迭代器对象*

使用方法

1
filter(function, iterable)

example:

1
2
3
a = [i for i in range(10)]
b = filter(lambda x: x%2==0, a)
print(list(b))

Day13

Git|stash

在add之后进入index, 此时可以使用git stash save “info”将改动的code放在stash库中, 地址.git/refs/stash

stash库没有branch的限制, 可以在任意的branch将code拿出来, 切换到对应的branch

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# push stash
git stash push -m "info"
-----or-----
git stash save "info"

# view all stash
git stash list

# view info stash@{id}
git stash show -p stash@{id}

# load specify stash
git stash pop
------or------
git stash apply stash@{id}

# delete
git stash drop stash@{id}

Day12

手机|API传视频参数

**手机端:**只需要response的值一样即可, 用户在手机端只需要点击视频即可, 不需要request

**API调用:**不仅需要传入的参数request一样, 而且也需要response的值一样

任务传播流程

![image-20210817180655004](/Users/edy/Library/Application Support/typora-user-images/image-20210817180655004.png)

在传递过程中, 尤其是API的调用存在参数的校验

对外的接口可以在一级传参中进行封装

1
2
3
4
5
6
7
8
9
10
a = input('a=')
b = input('b=')

params = {
'param_a': a,
'param_b': b
}

# 对外参数显示a, b
# 而实际传递过程中是param_a, param_b

回调

For a client-side API I could guess something like this:

  1. You make an API call to some url.
  2. You receive a response
  3. A callback function is called with the response as its arguments

illustration/·ai-lou-s·-·zhui·-·shen·/

![image-20210818112915134](/Users/edy/Library/Application Support/typora-user-images/image-20210818112915134.png)

thumb_url

缓存文件

Thumbs.db只是一个图片索引文件,可以加速你用缩略图方式查看图片,会随着文件夹的图片的增多也体积增大。

Day11

vim编辑器

1
2
3
4
5
6
7
vim file  # 查看文件不编辑
i # 进入编辑模式
esc # 退出编辑模式 输入:一下指令
:q 即可退出vim编辑器
:q!(强制退出不保存)
:wq(保存后退出)
:wq!(强制保存后退出)

回调API设置

  1. 首先启动API, 新起一个API_test, 对照API开发文档将所需参数填入, 运行后查看response值

  2. 之后在database中查找对应的module_id

  3. 在源码中查看所有打出response的情况, 并在log中寻找

  • 在log中查找关键词调用客户info中打印

Day10

debug后的版本提交流程

「整体上分为三个部分」

  • 进入项目目录查看当前修改的内容, 确认是否要将修改的文件推上测试服
  • 对文件根据命名规则打上tag
  • 进入docker修改指定项目的deploy-debug.sh

0.查看状态

1
2
3
# 对当前分支状态进行查看,使用的最频繁的命令
# modified-->add->commit
git status

1.进入项目目录

1
cd Priject_Debug

2.查看目录状态

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
git status

>>
return info:
on branch master # 目前所在branch

changes to be commited: # 那些文件更改了并提交了,从缓存区add到了暂存区
(color: green)
new file: xxxx.py
new file: xxxx.py

changes not staged for commit: # 那些文件更改了还没提交,等待add添加
(color: red)
modified: xxx.py
modified: xxx.py

3.暂存分支

1
2
3
4
5
6
git stash  # 暂存分支

git status # 查看当前分支状态

# 抛出暂存分支
git stash pop

保存当前的修改,但是想回到之前最后一次提交的干净的工作仓库时进行的操作git stash将本地的修改保存起来,并且将当前代码切换到HEAD提交上.

4.创建|切换分支

1
2
3
4
5
# 切换已经存在的分支
git checkout new_created_branch

# 切换分支,如果分支不存在则创建分支
git checkout -b new_uncreated_branch

5.查看log

1
git log

6.查看改动|提交指定文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 查看xxx.py文件哪里出现了改动
git diff dir/xxx.py

# 改动文件中包含不想提交的
# 使用git reset HEAD
# 把add到index的文件撤回到working中
git reset HEAD

# add指定修改文件
git add dir/xx.py

git status

# 提交修改的文件
git commit -m "提交信息说明|修复具体的bug|增添的功能"

7.打上tag|push tag

1
2
3
4
5
6
7
# 在阿里云服务中查看命名规则
git tag release-vtest_0.25.47
“”“
阿里云会根据明明规则检测tag
如果符合则会创建相应的镜像
”“”
git push --tags

8.将分支关联到远程分支

1
2
3
# origin 是默认的远程版本库名称
# 将本地分支关联到远程分支并提交
git push --set-upstream origin cur_branch

9.容器|上线测试服

使用到7中的tag版本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
ssh root@测试环境ip

docker ps # 查看容器
ls # 查看测试环境内的文件
pwd # 查看当前路径

cd ZidouBiz # 切换到zidoubiz文件
ls # 查看zidoubiz文件内的文件

# 找到deploy-debug.sh文件并修改
# vi deploy-debug.sh
sh deploy-debug.sh test_0.25.47

# 查看版本状态
docker ps

10.拉version-tag

1
2
3
4
5
6
7
8
# 1.将远程代码clone到本地
git clone xxxx.xxx

# 2.查看tag
git tag

# 3.检出tag
git checkout tagName

script调试

在docker中对写死的脚本进行调试, 尽量加上所有的import, 如果出现“NoneType” object is not callable, 检查是否初始化了basemode父类

调用格式:

1
BaseModel.init_model_dict()

Day9

Python内置装饰器

阅读推荐

@property|属性方法

1.@property的使用

@property是python的一种装饰器,用来修饰方法

@property是把类内方法当成属性来使用,使用@property装饰器来创建只读属性

@property装饰器会将方法转换为相同名称的只读属性,必须要有返回值,相当于getter

2.@property

在类中使用, 对类中的方法进行装饰, 将方法装饰成只读属性, 在本质上相当于getter, 并且必须有返回值

@staticmethod

  • 在类中声明一个静态的方法
  • 这个方法不接收 clsself 参数
  • 这个静态方法并不能访问类属性或者实例属性
  • 可以使用 ClassName.MethodName()object.MethodName()调用
  • 它可以返回类的对象
1
2
3
4
5
6
7
8
9
class Student:
name = 'unknown' # class attribute

def __init__(self):
self.age = 20 # instance attribute

@staticmethod
def tostring():
print('Student Class')
1
2
3
4
5
6
7
>>> Student.tostring() # 调用class的statcimethod
'Student Class'
>>> Student().tostring() # 调用
'Student Class'
>>> std = Student() # 调用instance
>>> std.tostring()
'Student Class'

The static method cannot access the class attributes or instance attributes. It will raise an error if try to do so.

1
2
3
4
5
6
7
8
9
class Student:
name = 'unknown' # class attribute

def __init__(self):
self.age = 20 # instance attribute

@staticmethod # 不能在staticmethod装饰的函数中访问class属性或instance属性
def tostring():
print('name=',name,'age=',self.age)
1
2
3
4
5
6
7
>>> Student.tostring()
Traceback (most recent call last):
File "<pyshell#22>", line 1, in <module>
Student.tostring()
File "<pyshell#21>", line 7, in display
print('name=',name,'age=',self.age)
NameError: name 'name' is not defined

@calssmethod

classmethod阅读

0.用于预处理实例化对象时所接受的参数,并且返回一个初始化的类

1.作用

将类中的实例方法改为类方法, 必须接受一个cls参数, 并且返回一个初始化的类

  • 在创建一个实例化对象时使用
  • 不加装饰器的类中方法, 均是实例化方法, 只能对实例化对象进行操作, 无法对类进行操作

2.使用场景

在创建一个实例化对象时, 对接受的参数格式进行预先调整, 并将调整好的格式付给类, 相当于在创建实例化对象时, 现根据参数创建一个自适应的初始类

3.好处

  • 将处理函数封装在类中, 代码更加简洁
  • 使代码更加容易维护

4.缺点

  • 在实例化时需要调用classmethod
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
class A:

def __init__(self, year, month, day):
self.year = year
self.month = month
self.day = day

@classmethod
def modify_str(cls, str):
# tmp = str.split('-')
# year = tmp[0]
# month = tmp[1]
# day = tmp[2]
year, month, day = str.split('-')
init_cls = cls(year, month, day)
return init_cls

def out_date(self):
year = "year: {}".format(self.year)
month = "month: {}".format(self.month)
day = "day: {}".format(self.day)
print(year, month, day, sep='\n')

time = A.modify_str("2021-8-11")
time.out_date()
>>
error 0

>>
# 在创建实例化对象时没有使用classmethod
time = A("2021-8-11")
time.out_date()

TypeError: __init__() missing 2 required positional arguments: 'month' and 'day'

Day8

API调用

⚠️:如果API在目录controller中不存在controller/api_controller,即不存在action_api_controller那么此api在ZidouBiz/api_controller里,并且命名规则为api_action[set|get|del]

action_api:可以被外界调用

api_action:内部api

使用apipost调用简单的api时[在api_controller中存在]

一般在apipost可以测试的都是action_api_name

端口需要在源文件中查询

http://127.0.0.1:8000|9000/class_name/api_name

1
2
3
4
5
# 文件结构如下
controller||directory
>api_name_controller||api_file
>class API_name:||class_name
>def action_api||api_name

⚠️:紫豆所有api调用request都是post请求,在测试调用API时使用使用pycharm中的本地文件进行调用

调用一次api需要获得sign

function:调用的api==>本地存住在api_controllert.py

action:增删改

⚠️:tokrn不是secret, secert的获取开通API接口

需要修改的参数有:

1
2
3
4
5
phone<-->secret==>紫豆后台获取
params={
"key": value
}
url="http://api.zidouzone.com/api/bup"

Counter

计数器:返回一个实力化对象「dict」,对象本身iterable

可以使用list(Counter.items|keys(default)|value()) 查看对应的返回值

1
2
3
4
5
6
7
8
9
10
11
12
from collections import Counter

c1 = Counter() # a new, empty counter
c2 = Counter('gallahad') # a new counter from an iterable
c3 = Counter({'a': 4, 'b': 2}) # a new counter from a mapping
c4 = Counter(a=4, b=2) # a new counter from keyword args
print(c1, c2, c3, c4, sep='\n')

Counter()
Counter({'a': 3, 'l': 2, 'g': 1, 'h': 1, 'd': 1})
Counter({'a': 4, 'b': 2})
Counter({'a': 4, 'b': 2})

Day7

熟悉项目

  • 通过script运行部分函数加深了解

紫豆助手

项目介绍

  • 本项目是一个微信社群管理工具,可以通过微信机器人实现一些自动化操作,并统计微信群的各种信息。
  • 本项目使用了类似MVC的设计模式,Model将MongoDB的数据封装为统一的model类以便使用,Controller接受前端的接口调用并实现所需功能。
  • 本项目有两种方式处理外部信息,第一种是接受接口调用,url格式为 domin/controller_name/interface_name,Controller中可被外界调用的接口,函数名必须加上”action_”前缀, 接口返回值必须用make_response方法。第二种是处理Android_Server通过RabbitMQ发来的微信实时消息,消息于celery的celery_newmsg队列中,项目中处理消息的方法在component/msg_handler中。

目录结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
├── celery_app.py           初始化celery配置
├── celery_config.py celery配置文件,包括队列配置和beat配置等
├── component 各种工具函数,处理微信消息的组件和celery任务函数等
├── config 各种配置数据和python依赖包文件等
├── controller 接口文件
├── doc 文档
├── error-config 错误码信息,git子模块,和前端共同维护
├── model 数据model
├── rabbitmq rabbitMQ的配置和一些直接发给MQ的函数
├── static 一些静态数据文件
├── supervisord_conf supervisor配置文件
├── tests 单元测试
├── upgrade.sh 上线脚本
├── ZidouBiz.py 项目启动入口
└── ZidouLocal.py 本地调试启动入口

环境配置

安装python 3.6.4以上, 运行pip install -r config/requirements.txt安装依赖包 安装Redis 如果想在服务器运行还需要: MongoDB, ElasticSearch, RabbitMQ, Nginx, Supervisor 如在生产环境运行,务必在主目录下创建名为”online_config”的文件,用来区分生产环境与正式环境,切记。

Docker

【推荐阅读】

1
2
3
4
5
ssh root@test_env_ip
docker ps
docker exec -it name /bin/bash
pip3 install ipython -i 清华园(mirrors_url)
ipython

cls

一、cls含义

python中cls代表的是类的本身,相对应的self则是类的一个实例对象。

二、cls用法

cls可以在静态方法中使用,并通过cls()方法来实例化一个对象。

mongo

mongodb聚合操作 SQL 操作/函数
$match where
$group group by
$match having
$project select
$sort order by
$limit limit
$sum sum()
$sum count()
$lookup (v3.2 新增) join

Day6

变量

类对象

指在对class进行实例化时所创建的对象object,在class中如果定义了一个_attribute,虽然在instance.attribute无法访问,但可以使用instance._attribute进行访问

1
2
3
4
5
6
7
8
9
10
11
12
13
14
xx:公有变量

_xx:前置单下划线,私有化属性或方法,一般来讲,变量_xx被看作是“私有的”,在模块或类外不可以使用。当变量是私有的时候,用_xx 来表示变量是很好的习惯。类对象和子类可以访问,这并不能完全做到真正的私有,只是约定俗成的而已,这样写表示不希望这个变量在外部被直接调用

==>模块和类外不可以使用|但类对象和子类可以访问|并不是真正的私有

__xx:前置双下划线,私有化属性或方法,无法在外部直接访问(名字重整所以访问不到,只能是允许这个类本身进行访问了。连子类也不可以)

==>真正的私有化属性,只允许在calss本身进行调用,子类和类对象访问不到


__xx__:前后双下划线,系统定义名字(这就是在python中强大的魔法方法),因为变量名__xxx__对Python 来说有特殊含义,对于普通的变量应当避免这种命名风格。

xx_:后置单下划线,用于避免与Python关键词的冲突

Day5

缺省

缺省=default

Head|Body

head定义文档的头部,是所有头部元素的容器。

<head>中的元素可以引用脚本、文档的各种属性和信息,包括文档的标题、在web中的位置以及和其它文档的关系等。绝大多数文档头部包含的数据都不会真正作为内容显示给读者。

<title>、<meta>、<link>、<style>、<script>

应该把 <head> 标签放在文档的开始处,紧跟着<html> 后面,并处于 <body> 标签之前。

文档的头部经常会包含一些 <meta> 标签,用来告诉浏览器关于文档的附加信息。

Body

1
一个页面的信息,也就是浏览器呈现出来的,用户看到的页面效果。也就是说这里是网页的主体

DOM

文档对象模型(Document Object Model,简称DOM),是W3C组织推荐的处理可扩展置标语言的标准编程接口。它是一种与平台和语言无关的应用程序接口(API),它可以动态地访问程序和脚本,更新其内容、结构和www文档的风格(目前,HTMl和XML文档是通过说明部分定义的)。文档可以进一步被处理,处理的结果可以加入到当前的页面。DOM是一种基于树的API文档,它要求在处理过程中整个文档都表示在存储器中。另外一种简单的API是基于事件的SAX,它可以用于处理很大的XML文档,由于大,所以不适合全部放在存储器中处理。

XHR

1、概念

xhr,全称为XMLHttpRequest,用于与服务器交互数据,是ajax功能实现所依赖的对象,jquery中的ajax就是对 xhr的封装。

2、xhr

XMLHttpRequest 对象提供了对 HTTP 协议的完全的访问,包括做出 POST 和 HEAD 请求以及普通的 GET 请求的能力。XMLHttpRequest 可以同步或异步地返回 Web 服务器的响应,并且能够以文本或者一个 DOM 文档的形式返回内容。

xhr 接口强制要求每个请求都具备严格的HTTP语义–应用提供数据和URL,浏览器格式化请求并管理每个连接的完整生命周期,所以XHR仅仅允许应用自定义一些HTTP首部,但更多的首部是不能自己设定的。

浏览器会拒绝绝对不安全的首部重写,以保证应用不能假扮用户代理、用户或请求来源,如Origin由浏览器自动设置,Access-Control-Allow-Origin由服务器设置,如果接受该请求,不包含该字段即可,浏览器发出的请求将作废。

如果想要启用cookie和HTTP认证,客户端必须在发送请求时通过XHR对象发送额外的属性(withCredentials),而服务器也需要以Access-Control-Allow-Credentials响应,表示允许应用发送隐私数据。同样,如果客户需要写入或读取自定义HTTP标头或想要使用“非简单的方法”的请求,那么它必须首先通过发出一个预备请求,以获取第三方服务器的许可!

AJAX

AJAX = 异步 JavaScript 和 XML。

AJAX 是一种用于创建快速动态网页的技术。

通过在后台与服务器进行少量数据交换,AJAX 可以使网页实现异步更新。这意味着可以在不重新加载整个网页的情况下,对网页的某部分进行更新。

传统的网页(不使用 AJAX)如果需要更新内容,必需重载整个网页面。

Day4

API测试

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
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
import requests
import json
import time
import random
import hashlib

"""
前置函数:得到一个编号|签名|获取response
"""
def get_nonce():
"""
generate a random string which length -> [1,8]
Returns:

"""
base_str = list('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789')

# random.randint(a, b) ==> [a, b]
length = random.randint(1, 8)

samples = random.sample(base_str, length)
return ''.join(samples)


def get_sign(secret, data):
"""
calculate the sign
Args:
secret:
data:

Returns:

"""
temp_json = dict()

# dict.update(sub_dict): add sub_dict to dict and if key conflict
# update the 'value'
temp_json.update(data)

pre_list = [str(k) + "=" + str(v) for k, v in temp_json.items()]
print('pre_list is :', pre_list, '\n')
pre_list.sort()
pre_str = '&'.join(pre_list)
pre_str = pre_str + secret
print(pre_str)
m = hashlib.md5()

# string.encode() --> 对字符串进行编码,default=utf-8
m.update(pre_str.encode())

return m.hexdigest()


def get_request_data(phone, function, action, params, secret):
data = {
"phone": phone,

# int(time.time()) ==> remainder the integer|
"timestamp": time.strftime("%Y%m%d%H%M%S", time.localtime(int(time.time()))),
"nonce": get_nonce(),
"function": function,
"action": action,
"params": json.dumps(params)
}
sign = get_sign(secret, data)
data.update({"sign": sign})
return data


# 先有一个
url = "http://api.zidouzone.com/api/pub" # test env
# url = "http://api.zidouchat.com/api/pub" # release env
# url = "http://127.0.0.1:8000/api/pub" # local env

# default parameters
phone = "15714358139"

function = "send_msg" # select the module

# can select from 'set|get|del'
action = "set"

a = {'description': '秀儿你好。。', 'title': '程序员小哥公开了他的上班摸鱼神器!网友:秀儿你好~', 'imageUrl': '',
'linkUrl': 'http://mp.weixin.qq.com/..'}

# the format | wechat`s information
xml = {
"appName": "美团团购丨优选外卖单车美食酒店",
"appid": "wxde8ac0a21135c07d",
"appservicetype": 0,
"desc": "",
"pagepath": "index/pages/mt/mt.html",
"pkginfoType": 0,
"thumbAESKey": "1",
"thumbFileId": "1",
"thumbHeight": 576,
"thumbMD5": "1",
"thumbSize": 52529,
"thumbUrl": "1",
"thumbWidth": 720,
"title": "吃喝玩乐 尽在美团",
"type": 2,
"username": "gh_870576f3c6f9@app",
"version": 781,
"weappIconUrl": "http://wx.qlogo.cn/.."
}

link = {
'linkUrl': 'http://mp.weixin.qq.com/s?',
'title': '程序员小哥公开了他的上班摸鱼神器!网友:秀儿你好~',
'description': '秀儿你好。。',
'imageUrl': ''}

"""
bot在chatroom中发送message时,发送的message均已card的形式存在,
我们需要在发送前set下title【type:文本】|description【type:文本】|image【type:url】
"""
params = {
"msg": [
{
"type": "link",
"linkUrl": "https://news.online.sh.cn/..",
"title": "test", # 在chatroom中展示的卡片链接标题
"description": "123456", # 在card中显示描述信息
# set在card中的photo
"imageUrl": "https://cdn.jsdelivr.net/gh/.."
}
],
# 选择chatroom,可以contains multiple rooms
"chatroom_list": ["10696050004037900"]
# "callback": "http://txmtest.zidouchat.com/groupbuy/get_detail"
}

# question: who get the phone | chatroom?
'''
from mongodb --table|client_chatroom_r|
'''

# get the 'secret' from ziDou
secret = "afd24967eb9e469239c07652f7c9f6ef"

# the scripts of calculating the sign
data = get_request_data(phone, function, action, params, secret)

# use 'get or post'
res = requests.post(url, headers={"content-type": "application/json"},
data=json.dumps(data)).content.decode("utf-8")

# print the responses
print(res)
print(json.loads(res))
print(json.dumps(a))

Api请求

get|post区别

  1. 最直观的区别就是GET把参数包含在URL中,POST通过request body传递参数
get post
回退 无害 再次提交
产生url被bookmark 可以 不可以
主动被浏览器cache 不会
request编码 url 多种
请求参数在浏览器的历史 不会
url传递的参数 长度受限 无限制
对参数的数据类型 ascii 无限制
安全性 参数暴露在url中 安全
参数传递方式 url request body

Str.encode

Python encode() 方法以 encoding 指定的编码格式编码字符串。errors参数可以指定不同的错误处理方案。

参数 描述
encoding 可选。字符串。规定要使用的编码。默认是 UTF-8。
errors 可选。字符串。规定错误方法。合法值是: ‘backslashreplace’ - 使用反斜杠代替无法编码的字符 ‘ignore’ - 忽略无法编码的字符 ‘namereplace’ - 用解释字符的文本替换字符 ‘strict’ - 默认值,失败时引发错误 ‘replace’ - 用问号替换字符 ‘xmlcharrefreplace’ - 用 xml 字符替换字符

dict.update

1
2
3
4
# 将dict中的key-value更新到supdict中,其中如果碰到key-conflict,则更新key-value

# 使用方法
super_dict.update(sub_dict)

json基本操作

【辅助阅读】

一般是python对象「str|dict|tuple|dict」与json(可以理解为dict+)

json => {“key”: “value”} 双引号

dict =>{‘key’: ‘value’}|双引号|单引号都可以

  1. dumps

将python对象格式化成json字符

  1. loads

将字符串解码成python对象

  1. dump

将python对象写入json文件中

  1. load

加载json格式文件,返回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
import json

str={'article':'中华人民共和国共和国万岁','tag':'中国共产党万岁'}
# 将python对象格式化成json字符串
encoded_json=json.dumps(str,ensure_ascii=False)
print(encoded_json,type(encoded_json))
>>
{"article": "xxx", "tag": "xxx"} <class 'str'>


# 将json字符串解码成python对象
decode_json=json.loads(encoded_json)
print(decode_json,type(decode_json))
>>
{"article": "xxx", "tag": "xxxx"} <class 'dict'>

# json.dump主要用来将python对象写入json文件
f = open('demo.json','w',encoding='utf-8')
json.dump(decode_json,f,ensure_ascii=False)
f.close()
>>
|demo.json|
|{xxx: xx, xxx: xxx}

# json.load加载json格式文件,返回python对象
f = open('demo.json','r',encoding='utf-8')
data = json.load(f)
print(data,type(data))
f.close()
>>
{"xxx": xxx, "xxx":xxx} <calss 'dict'>

logging|查看log

什么是log?

记录某些软件运行时所发生事件的方法

日志的作用

  1. 了解软件|系统|应用运行情况
  2. 分析用户操作行为|类型
  3. 发现bug解决问题

日志的等级

通常只需要记录应用程序的异常信息、错误信息等,这样既可以减小服务器的I/O压力

  • DEBUG
  • INFO
  • NOTICE
  • WARNING
  • ERROR
  • CRITICAL
  • ALERT
  • EMERGENCY

日志的格式

一条日志信息对应的是一个事件的发生

  • 事件发生时间
  • 事件发生位置
  • 事件的严重程度–日志级别
  • 事件内容

日志的格式可以自定义

log的实现【logging】

标准库函数

logging模块默认定义了以下几个日志等级,它允许开发人员自定义其他日志级别,但是这是不被推荐的,尤其是在开发供别人使用的库时,因为这会导致日志级别的混乱。

level 描述
DEBUG 最详细的日志信息,典型应用场景是 问题诊断
INFO 信息详细程度仅次于DEBUG,通常只记录关键节点信息,用于确认一切都是按照我们预期的那样进行工作
WARNING 当某些不期望的事情发生时记录的信息(如,磁盘可用空间较低),但是此时应用程序还是正常运行的
ERROR 由于一个更严重的问题导致某些功能不能正常运行时记录的信息
CRITICAL 当发生严重错误,导致应用程序不能继续运行时记录的信息
  • 开发时,使用debug或info来获取详细的日志信息,进行来发部署
  • 上线时,使用warning|error|critical级别的日志来降低I/O提高效率

「上面列表中的日志等级是从上到下依次升高的,即:DEBUG < INFO < WARNING < ERROR < CRITICAL,而日志的信息量是依次减少的」

「当为一个应用程序指定log level时,只会记录大于等于次level的信息」

logging使用方法

  • 使用logging提供的模块级别的函数
  • 使用Logging日志系统的四大组件【module级别的封装】
函数 说明
logging.debug(msg, *args, **kwargs) 创建一条严重级别为DEBUG的日志记录
logging.info(msg, *args, **kwargs) 创建一条严重级别为INFO的日志记录
logging.warning(msg, *args, **kwargs) 创建一条严重级别为WARNING的日志记录
logging.error(msg, *args, **kwargs) 创建一条严重级别为ERROR的日志记录
logging.critical(msg, *args, **kwargs) 创建一条严重级别为CRITICAL的日志记录
logging.log(level, *args, **kwargs) 创建一条严重级别为level的日志记录
logging.basicConfig(**kwargs) 对root logger进行一次性配置

logging.basicConfig(**kwargs)函数用于指定“要记录的日志级别”、“日志格式”、“日志输出位置”、“日志文件的打开模式”等信息,其他几个都是用于记录各个级别日志的函数。

组件 说明
loggers 提供应用程序代码直接使用的接口
handlers 用于将日志记录发送到指定的目的位置
filters 提供更细粒度的日志过滤功能,用于决定哪些日志记录将会被输出(其它的日志记录将会被忽略)
formatters 用于控制日志信息的最终输出格式
1
2
3
4
5
6
7
8
9
10
import logging
logging.debug('debug log')
logging.info('info log')
logging.warning('warning log')
logging.error('error log')
logging.critical('critical log')
>>
WARNING:root:This is a warning log.
ERROR:root:This is a error log.
CRITICAL:root:This is a critical log.

Q1为什么debug和info没有打印?

logging函数级别是WARNING

Q2打印的字段含义

日志级别:日志器名称:日志内容

因为logging的日志器格式basic_format为

1
"%(levelname)s:%(name)s:(message)%s"

hashlib.md5|hexdigest

用途简介

对二进制信息进行加密,形成一个长度固定32的数据块【摘要加密算法】,生成一个object,使用.hedigest()获取可读性信息

使用方法

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

# create an instance object
md = hashlib.md5()

# use update-->add information(only binary data type)
# use str.encode('utf-8') translate the 'string' to 'binary'
md.update('string'.encode('utf-8'))

# gengerate a memory addression
# use hexdigesmdt() to access the coed-info
print(md.hexdigest()) --> len=32

参数

python中所有数据结构都是对象

位置参数

最基本的参数,按照位置一一对应「等同于其他语言基本参数」

def fun(para1, para2)

默认参数

等同于c++,在某些情况下提供方便

def fun(para1, para2=val)

⚠️默认参数必须在最右端

python解释器会将默认参数作为一个公共对象来对待,多次调用含有默认参数的函数,就会进行多次修改。 因此定义默认参数时一定要使用不可变对象(int、float、str、tuple)。使用可变对象语法上没错,但在逻辑上是不安全的,代码量非常大时,容易产生很难查找的bug。

可变参数

目的:「0|+输入参数个数未知时,方便函数调用,可变参数将以tuple的形式传递

格式:*参数

Input:[]|()

关键字参数

目的:「0|+输入参数以dict的形式传递」

格式:**kw

命名关键字参数

目的:「在关键字参数前增加一个“*”」

区别:

关键字参数 命名关键字参数
参数名字 任何名字都可以’key’ 只能是*最后的key

⚠️:如果在使用关键字参数前使用了可变参数,则关键字参数不在添加“*” ,分隔


数据流程节点

phone「client」–设备「bot」–服务器「As服务器(有八个容器)」【消息type–a_message】

Day3

CubicWeb

RabbitMP

kibana

查看日志log

release:线上端

debug:测试端

Docker

1
2
# 查看正在运行的容器
docker ps -a

Docker create|创建容器

我们可以从任何镜像创建容器,包括您创建的自定义镜像。

输入以下命令

1
docker create -p 3000:80 --name exampleApp3000 yoyomooc/exampleapp

说明:

  • docker create命令用于创建一个新的镜像。
  • -p参数告诉 Docker 如何在容器中映射端口 80到主机操作系统。我指定容器内的端口80映射到主机操作系统中的端口3000。这与Docker中的EXPOSE命令相对应。
  • --name 参数为容器指定了一个名字,这样一来,一旦容器的已经创建了。本例中的名称是 exampleApp3000,表示这个容器将响应于请求发送至主机操作系统中的端口3000。
  • 最后一个参数告诉Docker要使用哪个镜像作为新容器的模板。这个命令指定了yoyomooc/exampleapp镜像,这是docker build中使用的名称。

Docker exec|进入交互

  • **-d :**分离模式: 在后台运行
  • **-i :**即使没有附加也保持STDIN(标准输入:client–>index) 打开
  • **-t :**分配一个伪终端

docker -compose

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 使用ssh链接到服务器
ssh username@ip

# 查看镜像
docker images

# 查看进程
docker ps

# 在容器中开启一个交互模式的终端
docker exec -it 容器名|容器id /bin/bash(/bin/sh)

# 删除一个容器
docker rm 容器名/容器id

# 查看容器的信息,tail查看后100行内容
tail -100f

# 查看日志信息,tail查看后100行内容
docker logs --tail 100 -f 容器名

# 通过ssh链接到服务器
ssh username@ip -i /Users/edy/.ssh/id_rsa

异步函数

  • 同步:是指完成事务的逻辑,先执行第一个事务,如果阻塞了,会一直等待,直到这个事务完成,再执行第二个事务,顺序执行
  • 异步:是和同步相对的,异步是指在处理调用这个事务的之后,不会等待这个事务的处理结果,直接处理第二个事务去了,通过状态、通知、回调来通知调用者处理结果

使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import asyncio

async def fun():
for i in range(10):
print(i)
await asyncio.sleep(0.5)
print('the task is over.')

# 函数+async=协程函数
# fun()并不会执行协程函数,只会生成一个协程对象,函数内部的code执行需要使用事件循环处理

# 创建一个实例化对象,调用run_util_complete的方法
loop = asyncio.get_event_loop()
loop.run_util_complete(协程对象)
# 协程对象=协程函数+()

# await + 可等待的io对象(协程对象|future|task)

DB|index|join

路由|api|token|om框架

web框架【io】

redis进行缓存

redis启动服务

1
redis-server

mongodb插入数据时进行校验

keyword 【await】

缺省参数|缺省

mongog封装如何实现?|语法的封装

vi 修改host|wrapper

python定时任务

信息的传递

  • 企业微信:josn

  • 微信:xml

python时间戳

签名算法

Day2

git基础

逻辑模式

photos

![image-20210803094748281](/Users/edy/Library/Application Support/typora-user-images/image-20210803094748281.png)

【working】 – 【index】 – 【master|local repository】

工作区(本地dir)–add–index(缓存区)–commit – git repo|local repo【在.git文件夹里】–push–remot repo【github】

git将文件存储的位置

【创建|查看用户】

查看邮箱|名字

git config –global –list

【修改】

git config –global user.name “username”

git config –global user.email “email”

邮箱地址是本地git客户端的一个变量,不随git库而改变。而不同项目,设置的用户名和密码不同


git config user.name

git config user.password

git config user.email


git分为三个区,working directory–modify【add】–>index「缓存区」–commit–>改动的内容在local repository中生效,之后使用push到远程仓库中remote repository

添加文件/夹|撤销

【1.create new repository】

    git init | git init new_repo[sub dir]

【2.create new file】

    touch file_name

    [使用git status发现此时的file_name并没有被追踪]:git status可以查看branch和提交信息以及dir中的文件是否被追踪

*【3.git add . |

    `只用dir中touch了一个file,才可以使用git add file,将文件追踪以备commit`

    使用git . | *进行commit时默认提交所有【修改】和【未被追踪】的文件,添加到git system的index区(缓存区)⚠️并不包括【删除文件】

命令选项:

-u

表示将已跟踪文件中的修改和删除的文件添加到暂存区,不包括新增加的文件,注意这些被删除的文件被加入到暂存区再被提交并推送到服务器的版本库之后这个文件就会从git系统中消失了。

-A

-A 表示将所有的已跟踪的文件的修改与删除和新增的未跟踪的文件都添加到暂存区。

*.html

添加某个文件类型到暂存区,比如所有的 .html 文件。

index/

添加整个文件夹到暂存区,比如根目录的 index 文件夹。

index/index.html

添加某个文件或者某个文件夹中的某个文件到暂存区 ,比如 index 下的 index.html 文件。


【一些自动生成的,比如日志文件,或者编译过程中创建的临时文件等,不需要纳入 Git 的管理,在这种情况下,我们可以创建一个名为 .gitignore 的文件,来跳过上传。】

【0取消|撤销文件】

git reset HEAD file_name

注意并不是在dir中del了file,而是将file从git库中删除,在working中依然存在,并且status变成了touch状态「untracked」

commit

【save changes(index) to the local repository [git **repository]**】

【1.git commit -m‘message’】

commit to local repository with message[annontation]

【git commit详细解读】

当我们修改了很多文件,而不想每一个都add,想commit自动来提交本地修改,我们可以使用-a标识

git commit -a -m‘changed some files’

git commit 命令的-a选项可将所有被修改或者已删除的且已经被git管理的文档提交到仓库中。

千万注意,-a不会造成新文件被提交,只能修改。

Project address

1
2
3
git remote add origin + address
address:
git@gitlab.zidouchat.com:liuyuhao/test.git

新建repo使用git

Git global setup
1
2
git config --global user.name "liuyuhao"
git config --global user.email "liuyuhao@doodod.com"
Create a new repository
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
git clone git@gitlab.zidouchat.com:liuyuhao/test.git

cd repo_name

# working创建
touch README.md

# 将file添加到index
git add README.md

# 将file转移到local repo
git commit -m "add README"

# push到remote repo
git push -u origin master
Push an existing folder
1
2
3
4
5
6
cd existing_folder
git init
git remote add origin git@gitlab.zidouchat.com:liuyuhao/test.git
git add .
git commit -m "Initial commit"
git push -u origin master
Push an existing Git repository
1
2
3
4
5
cd existing_repo
git remote rename origin old-origin
git remote add origin git@gitlab.zidouchat.com:liuyuhao/test.git
git push -u origin --all
git push -u origin --tags

Git push报错

【origin报错】

当使用Git进行代码push提交时,出现报错信息“fatal: 'origin' does not appear to be a git repository...”,

$ git push -u origin master
fatal: 'origin' does not appear to be a git repository
fatal: Could not read from remote repository.

1
2
3
4
5
6
7
8
9
是因为远程不存在origin这个仓库名称,可以使用如下操作方法,查看远程仓库名称以及路径相关信息,可以删除错误的远程仓库名称,重新添加新的远程仓库;

git remote -v:查看远程仓库详细信息,可以看到仓库名称

git remote remove orign:删除orign仓库(如果把origin拼写成orign,删除错误名称仓库)

git remote add origin 仓库地址:重新添加远程仓库地址

git push -u origin master:提交到远程仓库的master主干

【! [rejected] master -> master (fetch first)】

报错原因:

remote repository与local repository在version方面不一样

解决方法:

【温柔型】

使用git pull将local与remote的version更新到一样

⚠️:local中的modify可能会被覆盖,最好使用git fetch(不会自动合并)

【暴力型】

git push -f

⚠️忽略版本不一致,将remote repo覆盖

1
2
3
同步remote的master
git pull origin master
git push origin master

【! [rejected] master -> master (pre-receive hook declined)】

【问题原因】

git push上不去的原因在于push的分支权限为protected,只用管理员或者项目的管理员指派具有权限的人才可以操作

【解决方法】

1.将所要push的内容所在的分支protected全县关闭

  • 进入project的settings
  • 点击protected branches,点击unprotected,将master分支权限关闭

2.新建其他分支,将project push到新的分支上,在进行merge

1
2
3
4
5
6
7
8
9
10
11
# create new branch
git branch new_branch

# exchange branch
git checkout new_branch

# push project
git add .
git commit -m 'push_mes'
git remote add origin repo_address
git push -u origin new_branch

合并分支

分支指令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 查看已有的分支
git branch -a
# 创建分支
git branch newbranch
# 切换分支
git checkout targetbranch
# 合并分支
git checkout master
git merge subbranch
# 删除分支
git branch -D subbranch

# 删除远程分支
git push origin --delete [branch_name]

# 恢复误删分支
git log --查找分支号
git branch delbranch分支号
# 查看分支图
git log --graph
# 重新命名分支
git branch -m <cur_branch> <new_branch>

【如何合并分支】

【分支的介绍】

DevOps

DevOps(Development OPerations) 是一组过程|方法|系统|的统称,用于促进开发(应用程序|软件工程)、技术运营和质量保障部门之间的沟通、协作与整合

Pip 报错

1
2
3
4
5
6
7
8
“”“
0.首先check在python dir是否存在pip
which pythd on
cd /Library/Frameworks/Python.framework/Versions/3.6/bin/
ls
1.使用pip3|pip2
2.sudo easy_install pip[python2.x]
”“”

Docker

简介

docker是一个开源的应用容器引擎,可以让开发者打包他们的应用以及依赖包到一个轻量级、可移植的容器中,然后发布到任何流行的 Linux 机器上,也可以实现虚拟化。

容器是完全使用沙箱机制,相互之间不会有任何接口(类似 iPhone 的 app),更重要的是容器性能开销极低。

Docker 使用客户端-服务器 (C/S) 架构模式,使用远程API来管理和创建Docker容器。

Docker 容器通过 Docker 镜像来创建。

容器与镜像的关系类似于面向对象编程中的对象与类。

Docker 面向对象
容器 对象
镜像

常用命令

Flask框架

什么是Flask框架

Flask是一个用Python编写的Web应用程序框架。 它由 Armin Ronacher 开发,他领导一个名为Pocco的国际Python爱好者团队。 Flask基于Werkzeug WSGI工具包和Jinja2模板引擎。两者都是Pocco项目。

WSGI

Web Server Gateway Interface(Web服务器网关接口,WSGI)已被用作Python Web应用程序开发的标准。 WSGI是Web服务器和Web应用程序之间通用接口的规范。

Werkzeug

它是一个WSGI工具包,它实现了请求,响应对象和实用函数。 这使得能够在其上构建web框架。 Flask框架使用Werkzeug作为其基础之一。

jinja2

jinja2是Python的一个流行的模板引擎。Web模板系统将模板与特定数据源组合以呈现动态网页。

Flask通常被称为微框架。 它旨在保持应用程序的核心简单且可扩展。Flask没有用于数据库处理的内置抽象层,也没有形成验证支持。相反,Flask支持扩展以向应用程序添加此类功能。一些受欢迎的Flask扩展将在本教程后续章节进行讨论。

Day1

框架

紫豆助手|团小蜜

1
flask

venv

安装module

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 此命令需要管理员权限,在linux或macos
sudo pip install virtualenv
pip install virtualenv

# 创建新的虚拟环境-创建所需目录
mkdir newvenv
cd newvenv

# 配置环境
virtualenv venv

# 激活linux|macos
cd venv/bin
source activate

# windows
venv/scripts/activate

设置venv

1
python -m venv my_venv_name

激活venv

1
2
cd + myvenv/bin
Source + activate

退出venv

1
deactivate

ssh

【详细了解ssh】

查看ssh

进入ssh路径cd ~/.ssh

generate ssh

ssh-keygen -t rsa -C'email@email.com'

查看是否链接成功

ssh -T git@email@email.com

什么是ssh?

·SSH是一种网络协议,用于计算机之间的加密登录

·SSH的默认端口是22,也就是说,你的登录请求会送进远程主机的22端口。使用p参数,可以修改这个端口。

ssh配置完成后有一下文件

1
2
3
Id_rsa: private key
id_rsa.pub: public key
know_hosts:ssh会把你每个你访问过计算机的公钥(public key)都记录在~/.ssh/known_hosts。当下次访问相同计算机时,OpenSSH会核对公钥。如果公钥不同,OpenSSH会发出警告, 避免你受到DNS Hijack之类的攻击。

【什么是know_hosts】

ssh登陆远程服务器

1
2
3
4
5
6
7
8
# 默认端口进入(22)
ssh username@ip

# 指定端口进入
ssh -p port username@ip

# 调用图像界面程序
ssh -X username@ip

git

任务阅读

详细了解什么是git

什么是git?

Git 是一个开源的分布式版本控制系统,用于敏捷高效地处理任何或小或大的项目。

Git init:初始化git

修改host

terminal中输入sudo vi /etc/hosts

1.输入本机密码后,打开hosts文件,键盘输入 i (插入),修改hosts文件后,按 esc 键退出,再按shift+:键,再输入w和q,保存退出

2.不保存退出,则按q和!键

mongohub【数据库】

参见阅读

MongoDB 是什么 ? 为什么网页用它?

MongoDB 是一个基于分布式文件存储的数据库。由 C++ 语言编写。旨在为 WEB 应用提供可扩展的高性能数据存储解决方案。

MongoDB 是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系数据库的。

homebrew【包insatll】

参见阅读Homebrew介绍和使用

Homebrew是一款Mac OS平台下的软件包管理工具,拥有安装、卸载、更新、查看、搜索等很多实用的功能。简单的一条指令,就可以实现包管理,而不用你关心各种依赖和文件路径的情况,十分方便快捷。

redis【数据库】

参见阅读Redis中文教程

Redis 是一个开源(BSD许可)的,内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件。 它支持多种类型的数据结构

1
2
3
4
字符串(strings) 散列(hashes)列表(lists) 集合(sets)有序集合(sorted sets)
范围查询 bitmaps hyperloglogs
地理空间(geospatial) 索引半径查询。
Redis 内置了 复制(replication),LUA脚本(Lua scripting), LRU驱动事件(LRU eviction),事务(transactions) 和不同级别的磁盘持久化(persistence), 并通过 Redis哨兵(Sentinel)和自动分区(Cluster)提供高可用性(high availability)。

新拉的项目没有环境

新拉的project没有对应的环境,也没有对应的terminal

1.首先先在setting中配置虚拟环境,增加新的环境venv

2.在全局路径中寻找requirements.txt文件,查看需要安装的包

3.在terminal中输入

pip install -r requirements.txt

安装文件的使用

如果requirements.txt不再dir里

使用:

1
pip install -r config/requirements.txt

每日复盘

Q9Mongodb保存|插入字段失败

  • 1.查看Table定义==>fiedls = fiedls.IntFields() ==>需要加()

Q8虚拟环境配置

  • 拉取项目后如何配置虚拟环境?
  • 如何激活虚拟环境, 并每次进入pycharm的terminal都保证是venv?
  1. venv的目录和interpreter的目录要配置相同

![image-20211018164551110](/Users/edy/Library/Application Support/typora-user-images/image-20211018164551110.png)

![image-20211018164714977](/Users/edy/Library/Application Support/typora-user-images/image-20211018164714977.png)

  1. terminal的配置如下

![image-20211018164922890](/Users/edy/Library/Application Support/typora-user-images/image-20211018164922890.png)

Q7Nginx Timeout 504

-Info

在前端导出excel时, 导出失败并返回GateWay TimeOut 504

-Analysis

  • 重新导出查看log
1
2
url: /channel/get_excel?token=ab5c40fa9da40d74f5a2ba17d84dcf63
GET /channel/get_excel?token=ab5c40fa9da40d74f5a2ba17d84dcf63 (183.81.181.194) 67000.83ms
  • 说明导出正常, 其中导出时间大于60s
  • nginx做反向代理,默认请求是有一个60秒的超时,如果http请求超过了60秒,再返回,连接就会被nginx中断,前端就会得到504的错误:gateway time-out。

-Solution

修改nginx的配置, 将超时设置延长至300s

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
docker ps 

docker exec -it nginx_container_id bash

whereis nginx.conf

cd /etc/config

vim nginx.conf

{
...
keepalive_timeout 300;
proxy_connect_timeout 300;
proxy_send_timeout 300;
proxy_read_timeout 300;
send_timeout 300
...
}

-Review

  • 如果程序正常, log也没有报错, 很可能是配置的原因

Q6mongodb插入数据报错

-Info

在mongodb中插入数据报错{'code': -1, 'msg': 'exception'}

-Analysis

经过排查不是以下原因:

  • 插入字段类型与定义不符
  • 缺少必要字段

-Solution

在mongodb表中定义字段如下:

1
2
3
4
5
6
7
# 群发任务
class AssistantBatchSend(BaseModel):
__table__ = 'assistant_batch_send'
client_id = fields.IntField() # 创建人
task_id = fields.IntField() # 任务标识
message_list = fields.JsonField() # 发送内容
bot_username_list = fields.JsonField() # 机器人id

在插入的数据中

1
2
3
task_identification = int(str(self.user_info.client_id) + str(int(time.time()*1000000))) # 任务标识
immediate_info = CM(Table.AssistantBatchSend)
immediate_info.task_id = task_identification

其中task_id定义为intfiled长度最大

为:

![image-20211011134300296](/Users/edy/Library/Application Support/typora-user-images/image-20211011134300296.png)

但是传入的长度为22, 应该把此字段定义为StringField

Q5SSH连接VCM断开

-Question

报错信息

info1

client_loop: send disconnect: Broken pipe

info2

yum-config-manager client_loop: send disconnect: Broken pipe

-Analysis

info1

终端连接服务器无响应且没用自动间隔连接配置VCM的问题

-Solution

info1

修改/etc/ssh/sshd_config文件, 修改ClientAliveInterval 120

1
2
3
4
5
6
# 通过每 300 秒(每 5 分钟)进行响应确认来保持连接。 我认为这个部分的数字可以是60或120。
>/etc/ssh/sshd_config
#PermitUserEnvironment no
#Compression delayed
ClientAliveInterval 60
#ClientAliveCountMax 3

重新启动ssh服务

1
systemctl restart sshd

或者在客户端进行更改/etc/ssh/ssh_config

1
2
3
# 添加参数
TCPKeepAlive yes
ServerAliveInterval 60

info2

配置/etc/ssh/ssh_config文件,增加以下内容即可:

1
2
3
4
5
6
7
Host *
IPQoS=throughput
# 断开时重试连接的次数
ServerAliveCountMax 5

# 每隔5秒自动发送一个空的请求以保持连接
ServerAliveInterval 5

重新启动ssh服务

1
systemctl restart sshd

-Review

1.在服务端进行修改

在服务器端, 可以让服务器发送“心跳”信号测试提醒客户端进行保持连接

通过修改 sshd 的配置文件,能够让 SSH Server 发送“心跳”信号来维持持续连接,下面是设置的内容

打开服务器 /etc/ssh/sshd_config,我在最后增加一行

1
2
ClientAliveInterval 60
ClientAliveCountMax 1

这样,SSH Server 每 60 秒就会自动发送一个信号给 Client,而等待 Client 回应,(注意:是服务器发心跳信号,不是客户端,这个有别于一些 FTP Client 发送的 KeepAlives 信号),如果客户端没有回应,会记录下来直到记录数超过 ClientAliveCountMax 的值时,才会断开连接。

2.在客户端进行修改

只要在/etc/ssh/ssh_config文件里加两个参数

1
2
TCPKeepAlive yes
ServerAliveInterval 60

前一个参数是说要保持连接,后一个参数表示每过1分钟发一个数据包到服务器表示“我还活着”

如果你没有root权限,修改或者创建~/.ssh/ssh_config也是可以的

Q4vim安装失败

-Question

  1. 在使用vim时发现报错bash: vim: command not found

-Analysis

使用cat /proc/version查看系统版本

![image-20210928202753365](/Users/edy/Library/Application Support/typora-user-images/image-20210928202753365.png)

查看系统镜像中是否有vim, 发现red hat 5才有vim

-Solution

  • 使用apt install vim 安装vim
  • 发现Unable to locate package vim

![image-20210928203059115](/Users/edy/Library/Application Support/typora-user-images/image-20210928203059115.png)

  • 需要输入:apt-get update(作用是:同步 /etc/apt/sources.list 和 /etc/apt/sources.list.d 中列出的源的索引,这样才能获取到最新的软件包)
  • 之后再输入apt-get install vim安装(非root用户登录,root apt-get intall vim

-Review

docker常用命令

1
2
3
4
5
6
7
8
9
10
11
# 修改了image内的文件后重启image
docker restart image_id

# 启动container中的所有image
docker start $(docker ps -a | awk '{ print $1}' | tail -n +2)

# 停止container中的所有image
docker stop $(docker ps -a | awk '{ print $1}' | tail -n +2)

# 删除container中的所有image
docker rm $(docker ps -a | awk '{ print $1}' | tail -n +2)

Q3镜像部署失败

-Question

  1. 部署对应的tag的时候出现error

![image-20210928155639667](/Users/edy/Library/Application Support/typora-user-images/image-20210928155639667.png)

  1. 有正在运行的container, 无法部署

-Analysis

  • 在部署的过程中存在正在运行的container
  • 需要将所有的正在运行的container停下来或者删除所有正在运行的container中的image

-Solution

1
2
3
4
5
6
7
8
9
10
11
# 启动所有image
docker start $(docker ps -a | awk '{ print $1}' | tail -n +2)

# 停止运行所有image
docker stop $(docker ps -a | awk '{ print $1}' | tail -n +2)

# 删除container中的所有image
docker rm $(docker ps -a | awk '{ print $1}' | tail -n +2)

# 重新部署
sh deploy-debug.sh 1.0.0.65

-Review

  • 使用docker ps -acontainer中的所有image包括正在运行和未运行的
  • docker rmi -f 'docker iamges' 删除的已经存在但未运行的image
  • docker rm image_id删除正在运行的image

Q2镜像重启

-Question

  1. Docker ps 查看镜像状态显示restart

![image-20210928095953991](/Users/edy/Library/Application Support/typora-user-images/image-20210928095953991.png)

  1. RabbitMQ只有生产者没有消费者

-Analysis

  1. 部分服务器没起来

-Solution

  1. docker logs -f image_name 查看对应的image日志
  2. 找到报错信息

![image-20210928095931103](/Users/edy/Library/Application Support/typora-user-images/image-20210928095931103.png)

cerely can't find module name 'component.task.member_change_task.component'

cerely配置错误

-Review

  • docker logs -f
  • command r 搜索已经使用的order

Q1磁盘溢出

-Question

  1. mongodb连接失败报错
    • failed to read 4 bytes from socket within 300000 milliseconds
  2. 紫豆超管后台登陆失败
    • login 无响应
  3. 机器人正常但不可用
    • 机器人上报heartbeat给服务器但服务器不callback

-Analysis

  1. 登陆紫豆服务器ssh root@ip
    • 报错显示disk overflower
  2. df -h查看disk存储情况

image-20210926170428181

-Solution

  1. 删除不用的container|容器修剪

    • PRO环境具有一定的风险
    • docker system prune docker container prune
    1
    2
    3
    4
    5
    6
    7
    8
    docker container prune
    WARNING! This will remove all stopped containers.
    Are you sure you want to continue? [y/N] y
    Deleted Containers:
    4a7f7eebae0f63178aff7eb0aa39cd3f0627a203ab2df258c1a00b456cf20063
    f98f9c2aa1eaf727e4ec9c0283bc7d4aa4762fbdba7f26191f26c97f64090360

    Total reclaimed space: 212 B
  2. 删除镜像[优先选择]

    • docker rmi -f `docker images`
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11

      - docker image需`

      - **删除已经存在的镜像「不包括正在运行的」**

      ### -Review

      **docker rmi :** 删除本地一个或多个镜像。

      ```docker
      docker rmi [OPTIONS] IMAGE [IMAGE...]

OPTIONS说明:

  • **-f :**强制删除;
  • **–no-prune :**不移除该镜像的过程镜像,默认移除;

docker prune 命令

prune 命令用来删除不再使用的 docker 对象。

删除所有未被 tag 标记和未被容器使用的镜像:

1
2
3
$ docker image prune
WARNING! This will remove all dangling images.
Are you sure you want to continue? [y/N] y

删除所有未被容器使用的镜像:

1
$ docker image prune -a

删除所有停止运行的容器:

1
$ docker container prune

删除所有未被挂载的卷:

1
$ docker volume prune

删除所有网络:

1
$ docker network prune

删除 docker 所有资源:

1
$ docker system prune

docker system prune 命令:

1
2
3
4
5
This will remove:
- all stopped containers
- all networks not used by at least one container
- all dangling images
- all dangling build cache

删除停止的容器、删除所有未被容器使用的网络、删除所有none的镜像。

Pythonon_Source_Code

1Python列表【底层|原理|剖析】

1.创建列表

结构体

1
2
3
4
5
6
7
8
typedef struct{
struct _object *_ob_next;
struct _object *_ob_prev; // python内部将对象放在链表进行内存管理
Py_ssize_t ob_refcnt; // 引用计数器,就是多少变量用了它
PyObject **ob_item; // 指针的指针,存列表的元素
Py_ssize_t ob_size; // 已有元素个数
Py_ssize_t allocated; // 列表容量,可容纳个数
} PyListObject;

创建list

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
PyObject *
PyList_New(Py_ssize_t size)
{
PyListObject *op;
size_t nbytes;
#ifdef SHOW_ALLOC_COUNT
static int initialized = 0;
if (!initialized) {
Py_AtExit(show_alloc);
initialized = 1;
}
#endif
// 缓存机制
if (size < 0) {
PyErr_BadInternalCall();
return NULL;
}
/* Check for overflow without an actual overflow,
* which can cause compiler to optimise out */
if ((size_t)size > PY_SIZE_MAX / sizeof(PyObject *))
return PyErr_NoMemory();
nbytes = size * sizeof(PyObject *);
if (numfree) {
numfree--;
op = free_list[numfree];
_Py_NewReference((PyObject *)op);
#ifdef SHOW_ALLOC_COUNT
count_reuse++;
#endif
} else {
op = PyObject_GC_New(PyListObject, &PyList_Type);
if (op == NULL)
return NULL;Py
#ifdef SHOW_ALLOC_COUNT
count_alloc++;
#endif
}

if (size <= 0)
op->ob_item = NULL;
else {
op->ob_item = (PyObject **) PyMem_MALLOC(nbytes);
if (op->ob_item == NULL) {
Py_DECREF(op);
return PyErr_NoMemory();
}
memset(op->ob_item, 0, nbytes);
}
Py_SIZE(op) = size; // 元素个数
op->allocated = size; // 容量
_PyObject_GC_TRACK(op); //放到双向链表进行维护
return (PyObject *) op; //返回列表的指针
}

2.添加元素

list中插入一个元素时,扩容连续的内存地址(容量),在内存创建需要插入的内容p,将地址*p放入list的空间中,所以,yListObject的ob_item是指针的指针

扩容的曲线一般就是0,4,8,16,24…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 添加元素
static int
app1(PyListObject *self, PyObject *v)
{
// 获取实际元素个数
Py_ssize_t n = PyList_GET_SIZE(self);

assert (v != NULL);
if (n == PY_SSIZE_T_MAX) {
PyErr_SetString(PyExc_OverflowError,
"cannot add more objects to list");
return -1;
}

// 计算当前容量和内部元素个数
// 直接添加元素/扩容添加
if (list_resize(self, n+1) == -1)
return -1;
// 将元素添加到ob_item,v
Py_INCREF(v);
PyList_SET_ITEM(self, n, v);
return 0;
}

扩容

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
// 扩容机制
// newsize: 已存在元素个数+1
static int
list_resize(PyListObject *self, Py_ssize_t newsize)
{
PyObject **items;
size_t new_allocated;
Py_ssize_t allocated = self->allocated; // 当前的容量

// 1,容量大于个数
// 2,个数大于容量的一半(容量足够且没有内存浪费)
if (allocated >= newsize && newsize >= (allocated >> 1)) {
assert(self->ob_item != NULL || newsize == 0);
Py_SIZE(self) = newsize;
return 0;
}

/*
* The growth pattern is: 0, 4, 8, 16, 25, 35, 46, 58, 72, 88, ...
*/
// 扩容机制的算法
new_allocated = (newsize >> 3) + (newsize < 9 ? 3 : 6);

/* check for integer overflow */
if (new_allocated > PY_SIZE_MAX - newsize) {
PyErr_NoMemory();
return -1;
} else {
new_allocated += newsize;
}

if (newsize == 0)
new_allocated = 0;
// 扩容/缩容(涉及原来元素的迁移)
items = self->ob_item;
if (new_allocated <= (PY_SIZE_MAX / sizeof(PyObject *)))
PyMem_RESIZE(items, PyObject *, new_allocated);
else
items = NULL;
if (items == NULL) {
PyErr_NoMemory();
return -1;
}
// 赋值,更新个数和容量
self->ob_item = items;
Py_SIZE(self) = newsize;
self->allocated = new_allocated;
return 0;
}

3.插入元素

1
2
3
4
5
6
7
name_list = []

name_list.append('武沛齐')
name_list.append('沙雕alex')
name_list.append('py于')

name_list.insert('0, 苑日天')

4.移除元素

list.pop()
删除最后一个元素只需要修改size,不需要清除数据,下次append可以直接覆盖这个位置指定索引位置移除后,向前补位

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
static PyObject *
listpop(PyListObject *self, PyObject *args)
{
Py_ssize_t i = -1;
PyObject *v;
int status;

if (!PyArg_ParseTuple(args, "|n:pop", &i))
return NULL;

if (Py_SIZE(self) == 0) {
/* Special-case most common failure cause */
PyErr_SetString(PyExc_IndexError, "pop from empty list");
return NULL;
}
if (i < 0)
i += Py_SIZE(self);
if (i < 0 || i >= Py_SIZE(self)) {
PyErr_SetString(PyExc_IndexError, "pop index out of range");
return NULL;
}
v = self->ob_item[i];
// 删除最后一个,仅改变size
if (i == Py_SIZE(self) - 1) {
status = list_resize(self, Py_SIZE(self) - 1);
assert(status >= 0);
return v; /* and v now owns the reference the list had */
}
Py_INCREF(v);
// 不是最后一个,需要移动数据位置
status = list_ass_slice(self, i, i+1, (PyObject *)NULL);
assert(status >= 0);
/* Use status, so that in a release build compilers don't
* complain about the unused name.
*/
(void) status;

return v;
}

5.清空元素

list.clear()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
static int
list_clear(PyListObject *a)
{
Py_ssize_t i;
PyObject **item = a->ob_item;
if (item != NULL) {
i = Py_SIZE(a);
// 各个元素设置为空
Py_SIZE(a) = 0;
a->ob_item = NULL;
a->allocated = 0;
// 引用计数器-1
while (--i >= 0) {
Py_XDECREF(item[i]);
}
PyMem_FREE(item);
}

return 0;
}

6.销毁列表

del list

  • 销毁列表对象的操作
  • 将列表的引用计数-1
  • 引用计数>0,还有应用的话不做操作
  • 引用计数=0,没人使用
  1. 处理列表的元素,将所有引用计数-1(GC回收0计数)
  2. ob_item=0,ob_size=0,ob_allocated=0
  3. 将列表从双向链表移除,可以销毁
  4. 为了提高效率,Python结束期在内部为free_list缓存80个list,存放无使用的list,再创建的时候直接从缓存中拿来初始化。如果已经存了80个,del 的时候直接在内存中销毁对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
static void
list_dealloc(PyListObject *op)
{
Py_ssize_t i;
// 判断引用计数是否为0
PyObject_GC_UnTrack(op);
Py_TRASHCAN_SAFE_BEGIN(op)
if (op->ob_item != NULL) {
i = Py_SIZE(op);
while (--i >= 0) {
Py_XDECREF(op->ob_item[i]);
}
PyMem_FREE(op->ob_item);
}
// free_list没有80个的话缓存这个list
if (numfree < PyList_MAXFREELIST && PyList_CheckExact(op))
free_list[numfree++] = op;
else
Py_TYPE(op)->tp_free((PyObject *)op);
Py_TRASHCAN_SAFE_END(op)
}

创建列表时,实际上不会直接开辟内存,而是先看看free_list

1
2
3
4
5
6
7
8
a = [1, 2, 3]
print(id(a))
del a
b = ['sd', 'dsada']
print(id(b))
>>
2925874663944
2925874663944

Object_Oriented_Programming

*args|**kwargs

argsarguments 的缩写,表示位置参数;

kwargs keyword arguments 的缩写,表示关键字参数

Python 中可变参数的两种形式

并且 *args 必须放在 **kwargs 的前面

因为位置参数在关键字参数的前面。

*args就是就是传递一个可变参数列表给函数实参,这个参数列表的数目未知,甚至长度可以为0

**kwargs则是将一个可变的关键字参数的字典传给函数实参,同样参数列表长度可以为0或为其他值。

argskwargs组合起来可以传入任意的参数,这在参数未知的情况下是很有效的,同时加强了函数的可拓展性

*:解包

装饰器【Decorator】

作用

  1. 增加代码可读性
  2. 给function加上一些功能,比如加上时间计时器
  3. 装饰器本质上是一个函数|类【利用函数闭包:函数nested函数】
  4. 减少重复操作
  5. Decorator装饰的函数(define function而不是instance)

Example

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
# 输出所有的不大于num的素数
def is_prime(num):
for item in range(1, num+1):
if item % 2 != 0:
print(item, end=' ')

>>检测is_prime函数的运行时间

import time

def is_prime(num):
t1 = time.time()
for item in range(1, num+1):
if item % 2 != 0:
print(item, end=' ')
t2 = time.time()
print('\n', 'total time:{:.4} s'.format(t2-t1))

>>增加可读性


# 增加装饰器decorator
def display_time(func):
# 如果需要装饰的func中需要添加参数,则在wrapper中添加*args,并且func同样添加
def wrapper(*args):
t1 = time.time()
func(*args)
t2 = time.time()
print('\n', 'total time:{:.4} s'.format(t2 - t1))
return wrapper


@display_time
def is_prime(num):
for item in range(1, num+1):
if item % 2 != 0:
print(item, end=' ')

is_prime(10000)
>>1~9999
total time:0.03494 s

django

DjangoBlog

0.前言

python连接数据库

  1. 在mysql中创建一个数据库mydb
  2. 在python中import pymysql
  3. 连接数据库
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
import pymysql

# 这些信息可以去mysql的GUI查询
db = pymysql.connect(host='localhost',
user='root',
password='*****',
port=3306,
db='mydb')

# python控制mysql的逻辑是模拟cursor
cursor = db.cursor()

# define sql语句
sql = 'SELECT STUDENT_NAME, SCORE\
FORM STUDENT\
WHERE SCORE >= 60\
ORDER BY SCORE DESC'

# 执行sql语句
cursor.execute(sql)

# 将cursor.fetall()return的tuple 赋值给result
result = cursor.fetall()

# 展示data
for item in result:
print(item, end='\n')

# 每次运行后为了保障安全建议关闭db
db.close()

>>
('小红', 95)
('spark', 88)
('小白', 82)
('DRINK', 80)
('NEW', 80)
('OLD', 78)
('SPARK', 72)
('TANT', 70)
('小红', 60)

Python查询Mysql使用fetchone()方法获取单条数据,使用fetchall()方法获取多条数据。

  • fetchone():该方法获取下一个查询结果集。结果集是一个对象[return single tuple]
  • fetchall():接收全部的返回结果行。[return multiple tuples]
  • rowcount:这是一个只读属性,并返回执行execute()方法后影响的行数。

创建虚拟环境

在terminal中输入

1
2
3
4
5
6
7
8
# 在当前目录中创建虚拟环境
python -m venv myvenv[虚拟环境的名字]

# 激活虚拟环境
cd myvenv\Scripts>activate

# 退出虚拟环境
deactivate

虚拟环境的优点

  1. 使不同应用开发环境独立
  2. 环境升级不影响其他应用,也不会影响全局的python环境
  3. 它可以防止系统中出现包管理混乱和版本的冲突

pip list查看已经安装的module

CD命令

1
2
3
4
5
# 切换路径
xxx:cd /d D:+绝对路径

# 返回上一层
xxx:cd..

requirements.txt项目依赖

可以是项目在另一个环境上重新构建项目所需要运行的环境依赖包

【将依赖包目录打包到项目里】

1
2
使用之前先在terminal里使用cd切换好路径
xxx: cd /d +绝对路径

第一种【适合但虚拟环境】

1
2
# terminal windows使用
pip freeze > requirements.txt

这种方式,会将环境中的依赖包全都加入,如果使用的全局环境,则下载的所有包都会在里面,不管是不时当前项目依赖的

第二种【优先选择】

1
2
3
4
5
# 安装
pip install pipreqs

# terminal windows使用
pipreqs . --encoding=utf8 --force

【释放依赖包项目】

1
2
# terminal windows使用
pip install -r requirements.txt

1.配置虚拟环境

虚拟环境(virtualenv、venv)

后端工程师

1.后端项目实战

Python+Django+MySQL

前期准备

1.CD[shell]

  1. cdChange Directory,切换路径,可以通过在后面添加接驱动器符号、完整路径和相对路径来实现路径的转换。
  2. 命令行工具:Windows+R打开运行窗口,输入cmd,进入命令行工具界面,默认目录为当前用户所在的目录

2.使用方法

  1. 回到根目录

    cd/

  2. 回到上一层目录

    cd..

  3. 进入任一目录

    方法1:
    (1) 直接输入[该目录所在盘区]:   ——以此进入该盘区目录;
    (2) 然后输入cd [在盘区下相对路径] ——以此进入该目录;

1
2
3
4
5
6
7
8
9
10
11
12
default C:\Users\Administrator
# 切换硬盘
C:\Users\Administrator:D
D:\>
# 进入盘中的路径
D:\>cd django_project
D:\django_project>
# 返回上一层
D:\django_project>c..
# 直接切换由C-->D
cd /d +绝对路径
cd /d D:\django_project

0.问题

侧重的知识

在我参加的后台开发岗位面试中,主要有以下知识是比较侧重

\1. 数据库CRUD操作/查询优化以及数据库知识量

\2. 框架的对比和了解/项目具体模块的业务逻辑描述

\3. 常见排序算法

\4. Linux系统的常用命令和Linux系统了解程度考核

\5. 项目部署用到的服务器的配置和了解程度

加分项

  1. 数据库基本功必须掌握但同时更重要的是理解为什么和是如何实现的,拓展自己知识面
  2. Linux系统的底层原理和了解程度
  3. 目前Golang在后端中也逐渐流行,有时间还是需要去掌握
  4. 技术没有终点,保持乐于学习和接受新技术的心态,面试官看重技术能力同时也更看重你的心态
  5. 自信且好学

面试提问到的问题

问题就给大家了,答案自己去查找吧,只有经过自己努力的东西才会格外珍惜。

Python相关

  1. 实现一个单例模式
  2. 实现一个装饰器
  3. 用Python实现快排
  4. 如果系统中需要很多个Python版本,如何进行Python版本的管理
  5. 如何实现一个全局变量?(除了global,还可以通过单例模式)

数据库相关

  1. 数据库的数据是实时更新的吗?每点击一次,数据库数据修改一次?
  2. Redis hash的个数
  3. 如何修改Redis数据库的库的个数?
  4. Redis数据库如何实现持久化
  5. Redis数据库支持的数据类型
  6. Redis使用AOF方式持久化,aof文件不断增大,如何处理?
  7. Redis数据库如何设置密码
  8. hash表是如何生成的
  9. MySQL数据库如何使用sql语句插入一条数据
  10. MySQL数据库的慢查询有了解过吗
  11. MySQL数据库如何进行查询优化
  12. 如何很多请求同时对Redis的同一个键进行访问,如何保证数据安全
  13. 说说Redis的淘汰机制
  14. 我的MySQL数据库每天晚上12点进行全备份。第二天有员工在9点钟误删除了一个数据库,但在10点钟才被发现。问如何进行恢复被误删除的数据库并同时保留9点到10点钟新增的数据同时不影响业务的正常运行?
  15. 当数据越来越多,如何避免hash槽中key出现相同的情况?
  16. MongoDB在哪些场合使用过?

项目部署相关

  1. 大家都说Nginx快?快的原因是什么?
  2. 对RPC了解吗?
  3. 如何在服务器上设置业务进程数?
  4. 说说正向代理和反向代理

Linux相关

  1. 如何查看剩余内存
  2. 如何查看端口是否被占用
  3. 如何查看一个程序的PID以及它的所有子进程
  4. 如何为一个目录下的所有文件添加权限
  5. 如果你对一个目录具有写权限,那么你是否具有对这个目录下的所有文件具有删除权限?
  6. 对Linux多路复用的理解
  7. 修改IP地址的方法

前端相关

  1. 对前端HTML CSS 和 JS了解多少?熟悉吗?
  2. 对React和bootstrap了解吗?
  3. 如何进行http优化?(响应头设置Content-Encoding: gzip)

网络编程相关

  1. 说一下实现TCP建立连接的过程以及当时进入了什么状态?为什么建立连接只需要3次,断开连接需要4次?为什么断开连接时第二次和第三次要分开,不能合在一起吗?

项目相关

  1. 说一下一个请求过来到返回response的过程
  2. 如何实现单点登录
  3. JWT token是如何进行生成和校验的
  4. 了解过哪些后端框架?Tornado了解吗?
  5. 了解过webapp2吗
  6. Django如何实现csrf攻击保护
  7. 说说你项目中遇到的困难以及如何解决
  8. 说说你认为自己最有成就感或最深刻的项目
  9. 对KAFKA了解吗?用过哪些消息队列?使用过RabbitMQ吗?
  10. 项目团队几个人?开发多长时间?

版本控制相关

  1. 如何从远程仓库拉取分支到本地
  2. 如何进行版本回退

其他

  1. Celery的原理和应用场景
  2. Elasticsearch 的原理
  3. 平时是如何学习的?有关注哪些技术?
  4. Docker的了解,常用命令,如何暴露端口
  5. 对ERP了解吗?Odoo了解吗?

独到科技

创业过程

2011年初,张文浩、马扬和另一位来自百度的工程师是独到科技第一次创业的核心团队。当时看到中国还没有类似LinkedIn这样的网站,他们就把创业切入点放在了商务社交上。经过分析调查,三个人把产品定义为基于社交网络的招聘服务——通过对新浪微博的用户数据与关系链做分析,填补传统招聘模式中静态简历易过期,对招聘对象缺乏关系链了解的空白。

–正好赶上团购泡沫出现,投资变冷的大环境。

到了2012年1月,由于实在没有钱运转,张文浩忍痛将项目停掉。

冬:百度工程师退出项目组

linkedin–>基于微博数据分析做精准营销

由于“独到传播分析”可以过滤和识别僵尸粉,刚好赶上小米手机青春版发布,在微博上实现200多万次转发。这个热点事件让张文浩找到了推广工具的机会。

“独到传播分析”识别和计算了这200多万次转发中的僵尸粉比例,找到真正参与的用户数,并生成了一份报告。这份报告随即在广告营销圈传播开来,工具的用户人群也开始发生质的变化——需要高级需求的付费用户渐渐增加。

2012年5月,独到科技做成了第一笔生意,与一家广告公司合作,收入几千元。

简介

创建于2012年是国内首家专注于增强智能技术研发和数据服务的公司。独到通过挖掘大数据中蕴含的海量关联信息,利用深度学习,机器学习等技术,发现事物之间的联系,挖掘线索,帮助企业智能运营客户社群,构建私域流量 [2] 。独到科技致力于让世界每个人都可以在计算机的辅助下,超越行业专家,透过数据看世界。核心技术团队来自清华大学。数据源服务、智能算法服务、行业增强智能产品、数据咨询是四大核心业务。

紫豆助手

1
2
初衷:
帮助企业沉淀私域流量,

主要解决行业规范

【教育】

活跃度–>签到

群里不活跃,都是僵尸粉

二维码更新–>使用永久入群二维码【知意活码】

【电商】

入群欢迎语,关键词回复,群发消息

1
大量人工操作,工作繁重,效率低

【门店】

永久入群二维码 用户粘性

1
流量获取困难,用户粘性低,消费全靠随机

【保险】

活跃度 签到

1
群里不活跃,都是僵尸粉

饭卡群

Python开发岗位

1. Python中__new__与__init__方法的区别

new:

触发时机: 在实例化对时触发

参数:至少一个cls 接收当前类

返回值:必须返回一个对象实例

作用:实例化对象

注意:实例化对象是Object类底层实现,其他类继承了Object的__new__才能够实现实例化对象。

init:

触发时机:初始化对象时触发(不是实例化触发,但是和实例化在一个操作中)

参数:至少有一个self,接收对象

返回值:无

作用:初始化对象的成员

注意:使用该方式初始化的成员都是直接写入对象当中,类中无法具有。

2.什么是匿名函数?

Lambda函数,不用担心函数名冲突,不过python对匿名函数支持有限,只有一些简单的情况下可以用

#声明一个简单的lambda表达式

mylamb = lambda x,y:x+y

#调用函数

result = mylamb(8,9)

print(result)

3.简要概述一下python中生成器和迭代器?

(1)迭代器:

迭代器协议:对象需要iter:return self和提供next()方法,它要么返回迭代中的下一项,要么就引起一个StopIteration异常,以终止迭代。

可迭代对象:实现了迭代器协议对象。list、tuple、dict都是Iterable(可迭代对象),但不是Iterator(迭代器对象)。但可以使用内建函数iter(),把这些都变成Iterable(可迭代器对象)。

for item in Iterable 循环的本质就是先通过iter()函数获取可迭代对象Iterable的迭代器,然后对获取到的迭代器不断调用next()方法来获取下一个值并将其赋值给item,遇到StopIteration的异常后循环结束。

(2)生成器:

将列表生成式中[]改变为()数据结构会改变,从列表变为生成器;

列表受内存限制,所以没有必要创建完整的列表(节省大量内存空间),在python中我们可以采用生成器:边循环边计算的机制;

生成器是只能遍历一次的。生成器是一类特殊的迭代器。还能使用 def 定义函数,但是,使用yield而不是return语句返回结果。yield语句一次返回一个结果,在每个结果中间,挂起函数的状态,以便下次从它离开的地方继续执行。

4.Python的垃圾回收机制(garbage collection)

(1)当gc模块的计数器达到阈值,垃圾自动回收

(2)当调用gc.collect(),垃圾收到回收

(3)程序退出的时候,python解释器来回收垃圾

5.函数装饰器的作用?

装饰器本质上是一个Python函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能,装饰器的返回值也是一个函数对象。

它经常用于有切面需求的场景,比如:插入日志、性能测试、事务处理、缓存、权限校验等场景。有了装饰器,我们就可以抽离出大量与函数功能本身无关的雷同代码并继续重用。概括的讲,装饰器的作用就是为已经存在的对象添加额外的功能。

6.进程、线程的区别?

(1)定义的不同

进程是系统进行资源分配和调度的一个独立单位。

线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源。

(2)区别

一个程序至少有一个进程,一个进程至少有一个线程。

线程的划分尺度小于进程(资源比进程少),使得多线程程序的并发性高。

进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程序的运行效率。

线程不能够独立执行,必须依存在进程中。

(3)优缺点

线程和进程在使用上各有优缺点:

线程执行开销小,但不利于资源的管理和保护;而进程正相反。

7.函数的闭包

闭包就是函数式编程的重要语法结构,提高了代码可重复实用性。使用特定或特殊的方式,将局部变量(内部函数)引入到全局环境中使用,这就是闭包操作。

img

8.Python里的拷贝

Copy浅拷贝,只拷贝父元素,deepcopy深拷贝,递归拷贝可变对象的所有元素

9.apache和nginx的区别

(1)nginx 相对 apache 的优点:

轻量级,同样起web 服务,比apache 占用更少的内存及资源

抗并发,nginx 处理请求是异步非阻塞的,支持更多的并发连接,而apache 则是阻塞型的,在高并发下nginx 能保持低资源低消耗高性能

配置简洁

高度模块化的设计,编写模块相对简单

社区活跃

(2)apache 相对nginx 的优点:

rewrite ,比nginx 的rewrite 强大;

模块超多,基本想到的都可以找到;

少bug ,nginx 的bug 相对较多;

超稳定。

10.什么是事务?

事务(Transaction)是并发控制的基本单位。所谓事务,它是一个操作序列,这些操作要么都执行,要么都不执行,它是一个不可分割的工作单位。例如,银行转帐工作:

从一个帐号扣款并使另一个帐号增款,这两个操作要么都执行,要么都不执行。所以,应该把他们看成一个事务。事务是数据库维护数据一致性的单位,在每个事务结束时,都能保持数据一致性。

事务四大特性:

原子性:事务中的全部操作在数据库中是不可分割的,要么全部完成,要么均不执行;

一致性:几个并行执行的事务,其执行结果必须与按某一顺序串行执行的结果相一致;

隔离性:事务的执行不受其他事务的干扰,事务执行的中间结果对其他事务必须是透明的;

持久性:对于任意已提交事务,系统必须保证该事务对数据库的改变不被丢失,即使数据库出现故障。

11. 主键和外键的区别?

主键在本表中是唯一的、不可唯空的,外键可以重复可以唯空;外键和另一张表的主键关联,不能创建对应表中不存在的外键。

12. 在数据库中查询语句速度很慢,如何优化?

(1)建索引 ;

(2)减少表之间的关联 ;

(3)优化sql,尽量让sql很快定位数据,不要让sql做全表查询,应该走索引,把数据 量大的表排在前面 ;

(4)简化查询字段,没用的字段不要,已经对返回结果的控制,尽量返回少量数据 ;

(5)数据库做好读写分离。

13. Oracle和Mysql的区别?

1)库函数不同。

2)Oracle是用表空间来管理的,Mysql不是。

3)显示当前所有的表、用户、改变连接用户、显示当前连接用户、执行外部脚本的语句的不同。

4)分页查询时候时候不同 。

5)sql的语法的不同。

14.tcp和udp的区别?

tcp是一种面向连接的、可靠的、基于字节流的传输层通信协议。是专门为了在不可靠的互联网络上提供一个可靠的端到端字节流而设计的,面向字节流。

udp(用户数据报协议)是iso参考模型中一种无连接的传输层协议,提供面向操作的简单不可靠的非连接传输层服务,面向报文。

它们之间的区别:

1、tcp是基于连接的,安全性高;udp是基于无连接的,安全性较低;

2、由于tcp是连接的通信,需要有三次握手、重新确认等连接过程,会有延时,实时性差;同时过程复杂,也使其易于被攻击;而udp无连接,无建立连接的过程,因而实时性较强,也稍安全;

3、tcp连接是点到点的电话接通通信;udp支持一对一、一对多、多对一、多对多的广播通信。

15.对if name == ‘main’的理解?

“ if name == ‘main‘:”

在Python中分为两类:一种是直接执行,另外一种是作为模块时才被调用。

name 作为模块的内置属性,即”.py”文件的调用方式。如果等于“main“就直接执行本文件,如果是别的就是作为模块被调用。

16. http协议与https协议的区别?

http是超文本传输协议在互联网上应用最为广泛的一种网络协议,所有www文件都必须遵守这个标准,基于TCP/IP通信协议来传递数据(HTML 文件, 图片文件, 查询结果等)连接。

https是超文本传输安全协议,是一种网络安全传输协议。http协议传输的数据都没有加密,一些私密的信息不安全,https经由超文本传输协议(http)进行通信,利用SSL/TLS来加密数据包,https开发的主要目的就是为了保护数据传输的安全性。

HTTPS和HTTP的区别:

1) https协议要申请证书到ca,需要一定经济成本

2) http是明文传输,https是加密的安全传输

3) 连接的端口不一样,http是80,https是443

4)http连接很简单,没有状态;https是ssl加密的传输,身份认证的网络协议,相对http传输比较安全。

17. Python解释器

当我们编写好了的Python代码的时,一.py为扩展名的文件,运行代码的时候,需要python解释器。解释器在执行的程序时,一条一条的解释成机器语言给计算机来执行。因为计算机只能识别机器语言(以二进制的形式)

18. 字典推导式和列表推导式

列表推导式:

格式:[变量 for 变量 in 列表]

普通的字典内涵

变量= {key:value for key,value in 字典.items()}

19.Python2和python3在使用super时区别

img

20.python 列表去重(数组)的几种方法

方法1:用if语句判断,用append函数追加

img

输出结果:

img

方法二:

用set集合:

img

输出的结果:

img

21. 列举您使用过的python网络爬虫所用到的解析数据包

BeautifulSoup、pyquery、Xpath、lxml

22.python常用内置函数:

dir(对象名):返回一个列表,列出该对象所有的属性和方法;

help(函数名、方法名或对象):查看函数、方法或对象的帮助文档;

type(对象名):查看该对象的类型;

isinstance(对象, 类型):判断该对象是否是该类型,返回True或False;

range、input、print就不用多说了。

以上几个使用频率应当是最高的。更多函数,请导入模块”import builtins”,dir(builtins)查看。

23. python中的and、or、not逻辑运算符:

and、or、not两边的值会被放到布尔环境下,作比较

and运算如x and y:

x和y都为True时,那么返回最后一个值y

否则返回两个值中的第一个布尔值为假的值,从左往右运算

or运算如x or y:

只要有一个为真值,就返回第一个布尔值为真的值

如果都为假,返回最后一个布尔值为假的值,从左往右运算

not运算如not x:

当x的布尔值为True,返回False

当x的布尔值为False,返回True

24.参数按值传递和引用传递是怎样实现的?

Python中的一切都是类,所有的变量都是一个对象的引用。引用的值是由函数确定的,因此无法被改变。但是如果一个对象是可以被修改的,你可以改动对象。

25. python内置的数据类型有哪些?

list: 链表, 有序的项目, 通过索引进行查找, 使用方括号”[]”

dict: 字典, 字典是一组键(key)和值(value)的组合, 通过键(key)进行查找, 没有顺序, 使用大括号”{}”

str:字符串,用单或双引号括起来表示字符串

tuple: 元组, 元组将多样的对象集合到一起, 不能修改, 通过索引进行查找, 使用括号”()”

set: 集合,无序, 元素只出现一次, 使用”set([])”,可实现列表快速去重,不过注意返回的是一个集合

int: 整数,如3

float:浮点数,如2.3

complex:复数,如complex(1,2) => 1+2j

可迭代(遍历)对象:list、dict、tuple、set、str

可变类型:list、dict、set,其余为不可变类型

list、tuple、str可通过索引获取当中的元素

set不支持索引查找,因为数据只出现一次, 它只关心数据是否出现, 不关心其位置

26.python中search()和match()的区别

match从起始位置开始往后查找,返回第一个符合规则的

search任何位置开始往后查找,返回第一个符合规则的

27.redis中常用的5种数据类型?

Redis支持5种数据类型:string(字符串),hash(哈希),list(列表),set(集合)及zset(sorted set:有序集合)。

28.请描述一下try……except……else……finally执行的顺序?

try:

#尝试实现某个操作,

#如果没出现异常,任务就可以完成

#如果出现异常,将异常从当前代码块扔出去尝试解决异常

except 异常类型1:

#解决方案1:用于尝试在此处处理异常解决问题

except 异常类型2:

#解决方案2:用于尝试在此处处理异常解决问题

else:

#如果没有出现任何异常,将会执行此处代码

finally:

#管你有没有异常都要执行的代码

29.什么样的字段适合建立索引?

唯一、不为空、经常被查询的字段。

30. Xrange和range的区别是什么?

xrange用法与 range 完全相同。不同的是,xrange生成的不是一个list对象,而是一个生成器。

img

img

如何看待该行业

以后的发展问题

linux的基本骚操作大文件的log查寻机巧

python的单例模式

装饰器

迭代器并发的处理

链表

Linux

安装

  1. 官网上获得版本偶数版为稳定版,奇数版位测试版
  2. 下载VMware14pro,安装码百度即可
  3. 选择编辑添加新虚拟机
  4. 其中虚拟机设置里将CD/DVD 2(IED)设置为centOS-8-×86_64-1905-dcd.iso的路径

如果开始时出现

Pane is dead.窗格已死,解决方法为:

​ 1、把内存从1G升到2G,不管用。

​ 2、按住 Ctrl+alt+F2, 然后输出 reboot, 依然不行。

​ 3、更改ISO默认的映像文件,【step4】

安装时选择工作站版即可

Linux基础

linux指令

切换用户

用户标识

  • $表示普通用户
  • #表示超级用户,也就是root用户

0.创建common user$

1
useradd username

1.切换common user$

1
su username

2.切换到root#

1
sudo su

【用户操作】

  1. 在终端输入exit或logout或使用快捷方式ctrl+d,可以退回到原来用户
  2. 在切换用户时,如果想在切换用户之后使用新用户的工作环境,可以在su和username之间加-,例如:【su - root】

查看进程

【静态查看进程信息】

ps命令

  • ps -aux

  • ps -elf

信息更详细些,包括 PPID (对应的父进程 的PID 号)

【动态查看进程信息】

top命令

M: sorted by memory

N: sorted by time

p: sorted by GPU

q: 退出

查看信息

查看系统公网ip

1
2
3
4
5
6
7
8
9
10
11
# 返回详细信息, 服务器地址-client地址-数据来源
curl cip.cc

# 返回地址
curl ifconfig.io
curl ipconfig.io
curl ifconfig.me

# 从系统文件中查看地址
ifconfig -a
-etho0

查看系统内核

uname -r

查看系统版本

cat /etc/os-release

用户与主机名

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# [root@localhost]
root: 当前登陆的用户
localhost: 当前的主机名

# 修改主机名
1.临时修改
sudo hostname <new-hostname>

2.永久修改 命令修改|配置修改
> sudo hostnamectl set-hostname <newhostname>
> vim手动修改/etc/hostname文件

# 查看当前主机名
hostnamectl

# 修改之后重启生效
reboot

退出|关机|重启linux

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
退出:
exit

关机命令:
1、halt 立刻关机
2、poweroff 立刻关机
3、shutdown -h now 立刻关机(root用户使用)
4、shutdown -h 10 10分钟后自动关机 如果是通过shutdown命令设置关机的话,可以用shutdown -c命令取消重启
5、init0 停机或者关机

重启命令:
1、reboot
2、shutdown -r now 立刻重启(root用户使用)
3、shutdown -r 10 过10分钟自动重启(root用户使用)
4、init6 重启

init一共分为7个级别,这7个级别的所代表的含义如下
0:停机或者关机(千万不能将initdefault设置为0)
1:单用户模式,只root用户进行维护
2:多用户模式,不能使用NFS(Net File System)
3:完全多用户模式(标准的运行级别)
4:安全模式
5:图形化(即图形界面)
6:重启(千万不要把initdefault设置为6)

查找文件

  • whereis

whereis

用于查找文件, 返回文件所在路径

  • 文件应属于原始代码
  • 二进制文件
  • 帮助文件

该指令只能用于查找二进制文件、源代码文件和man手册页,一般文件的定位需使用locate命令。

文件操作

cp

复制文件

1
cp [options] source dest
  • -a:此选项通常在复制目录时使用,它保留链接、文件属性,并复制目录下的所有内容。其作用等于dpR参数组合。
  • -d:复制时保留链接。这里所说的链接相当于 Windows 系统中的快捷方式。
  • -f:覆盖已经存在的目标文件而不给出提示。
  • -i:与 -f 选项相反,在覆盖目标文件之前给出提示,要求用户确认是否覆盖,回答 y 时目标文件将被覆盖。
  • -p:除复制文件的内容外,还把修改时间和访问权限也复制到新文件中。
  • -r:若给出的源文件是一个目录文件,此时将复制该目录下所有的子目录和文件。
  • -l:不复制文件,只是生成链接文件。

yum命令

yum( Yellow dog Updater, Modified)是一个在 Fedora 和 RedHat 以及 SUSE 中的 Shell 前端软件包管理器。

基于 RPM 包管理,能够从指定的服务器自动下载 RPM 包并且安装,可以自动处理依赖性关系,并且一次安装所有依赖的软件包,无须繁琐地一次次下载、安装。

SSH(Secure Shell)

  • 从CVM中获取rsa_pub.pem
  • rsa_pub.pem保存在client的./ssh目录下
  • 从client./ssh中获取rsa_pub
  • rsa_pub中的内容放在CVM的/root/.ssh/authorized_keys
1
2
3
4
5
# client 
ssh root@ip

# ori
ssh -i ~/.ssh/rsa_CVM.pub root@CVM.ip

有关ssh的两个文件

  • 一个是存放client_rsa.pub
  • 一个是修改服务端和客户端ssh设置的
    • etc/ssh/ssh_config
    • etc/ssh/sshd_config

/root/.ssh/authorized_keys

远程主机将用户的公钥,保存在此

  • 存放访问的client的pub_rsa

/etc/ssh/sshd_config

/etc/ssh/ssh_config

  • ssh_config是针对客户端的配置文件

  • sshd_config是针对服务器的配置文件

ssh服务配置文件

文件修改后使用:

service sshd restart 保存

systemctl命令

  • [systemctl]system controller

sudo

super user do: 让普通用户执行只有root用户才可以执行的权限任务

curl

就是模拟数据传输的一个库 可以抓取网页内容 也可传输内容给网页

MySQL

1.MySQL

函数

round

【round(avg(COL_1), NUM)】:计算col_1的平均值并保留num有效数字

四舍五入

1
2
3
4
5
6
SELECT ROUND(AVG(SCORE), 1) AS `AVEAGE SCORE`
FROM STUDNET
>>
90.1
80.2
...

length

length(‘char’)

  • 中文算三个长度
  • alpha和number算一个长度

快捷指令

常用快捷键:

1、执行整篇sql脚本

【Ctrl】+【Shift】+【Enter】

2、执行当前行:

【Ctrl】+【Enter】

3、注释/取消注释:

【Ctrl】+【/】

4、格式化sql语句(美化sql语句):

【Ctrl】+【B】

5、(貌似和输入法有冲突)自动补全:【Ctrl】+【Space】 ,还可以使用edit->Auto-complete手动展示补全。如果有冲突可以去下面的文件找到Auto complete进行修改。Modifier的意思就是Ctrl键。

修改快捷键:

在安装根目录查到\data\main_menu.xml这个文件(Ubuntu放在/usr/share/mysql-workbench)。

6.MySQL Clint清屏

system cls

Error

Error Code: 1054. Unknown column ‘%%’ in ‘where clause’

出现类似的语法错误时,首先考虑是 还是 ` 出错了

一个是syntax error 一个是通配符使用

1
2
3
4
5
6
SELECT COL_NAME
FROM TABLE_NAME
WHERE STUDENT_NAME LIKE '小%';

SELECT `COL_NAME`
FROM `DATABASE_NAME`.`TABLE_NAME`

`DataBase/Table`` ‘wildcard’

问题

Schema和DataBase是否等同?

schema是数据库的组织和结构,是数据库对象的集合,集合包括表,视图,储存过程,索引等。

(1)MySQL的文档中指出,在物理上,模式与数据库是同义的,所以,模式和数据库是一回事。

(2)但是,Oracle的文档却指出,某些对象可以存储在数据库中,但不能存储在schema中。 因此,模式和数据库不是一回事。

(3)而根据这篇SQL Server技术文章SQLServer technical article,schema是数据库SQL Server内部的一个独立的实体。 所以,他们也不是一回事。

因此,取决于您使用的RDBMS,模式和数据库可能不一样。

引擎

img

【InnoDB】事务性数据库

【事务】

子性、一致性、隔离性、持久性。这四个属性通常称为ACID特性

1
2
3
4
5
6
7
原子性(atomicity)。一个事务是一个不可分割的工作单位,事务中包括的操作要么都做,要么都不做。

一致性(consistency)。事务必须是使数据库从一个一致性状态变到另一个一致性状态。一致性与原子性是密切相关的。

隔离性(isolation)。一个事务的执行不能被其他事务干扰。即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。

持久性(durability)。持久性也称永久性(permanence),指一个事务一旦提交,它对数据库中数据的改变就应该是永久性的。接下来的其他操作或故障不应该对其有任何影响。

具有提交、回滚、崩溃恢复能力的事物安全(ACID兼容)能力,并要求实现并发控制同时支持外键

【MyISAM】

数据表主要用来插入查询记录MyISAM引擎能提供较高的处理效率

【Memory】

如果只是临时存放数据,数据量不大,并且不需要较高的数据安全性,可以选择将数据保存在内存中的Memory引擎,MySQL中使用该引擎作为临时表,存放查询的中间结果

【Archive】仓库型数据库

如果只有INSERT和SELECT操作,可以选择Archive,Archive支持高并发的插入操作,但是本身不是事务安全的。Archive非常适合存储归档数据,如记录日志信息可以使用Archive

Algorithm

递归

1.E跳台阶[:star:]

思路

【递归思想,类似于斐波那契数列】

一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法(先后次序不同算不同的结果)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# method 1 recursion 2**n
def drump(n):
if n == 1:
return 1
elif n == 2:
return 2
else:
return drump(n-1) + drump(n-2)

# method 2 loop n
def jump(number):
if number < 1:
return 0

a, b = 0, 1
for _ in range(number):
a, b = b, a + b
return b

排序

1.M排序

给定一个数组,请你编写一个函数,返回该数组排序后的形式。

1
2
3
4
5
6
7
8
9
>>
return sorted(a)
>>
exchange
bubble->select
ordereder
insert->shell
divide and conquer
merge->quick

2.M最小的k个数

给定一个数组,找出其中最小的K个数。例如数组元素是4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4。

  • 0 <= k <= input.length <= 10000
  • 0 <= input[i] <= 10000
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Solution:
def GetLeastNumbers_Solution(self, tinput, k):
# write code here
temp = sorted(tinput)
return temp[:k]
# 使用bubble控制遍历次数得出最小的n个数
a = [4, 5, 1, 6, 2, 7, 3, 8]
temp = []
n = 0
for i in range(len(a)-1, len(a)-5, -1):
for j in range(i):
if a[j+1] > a[j]:
a[j], a[j+1] = a[j+1], a[j]
temp.append(a.pop())
print(temp)

数组

1.E两数之和

思路

【enurmate(iteration)–>return index–value】

给出一个整数数组,请在数组中找出两个加起来等于目标值的数,

你给出的函数twoSum 需要返回这两个数字的下标(index1,index2),需要满足 index1 小于index2.。注意:下标是从1开始的假设给出的数组中只存在唯一解

例如:

给出的数组为 {20, 70, 110, 150},目标值为90
输出 index1=1, index2=2

1
2
3
4
5
6
7
8
9
10
# time complexity O(n^2)
class Solution:
def twoSum(self , numbers , target ):
# write code here
for index1, value1 in enumerate(numbers):
for index2, value2 in enumerate(numbers):
if index1 == index2:
continue
elif value1 + value2 == target:
return [index1+1, index2+1]

2.M最长无重复子数组[DP:star:]

思路

【1.双指针】

方法一和方法二本质上属于同一个解法

【2.temp记录:simulation->queue】

注意使用temp(以list为例)时,所有的测试用例只有再使用slice[i+1:]更新cur_temp时才不会报错,如果直接使用.clear()或者cur_list = []会报错

思路错误:不应该直接更新cur=[],应该将相同的元素排出去,类似于队列(queue)

[1 2 3 4 5 1]–…cur…–>[1 2 3 4 5]

‘1’–>pop// –>[1 |–> 2 3 4 5 1]

result:–>[2 3 4 5 1]

如果将cur变为[],则结果为:four:

【3.DynamicProgramming】

从思路上可以理解为dp【i】与dp【i-1】取max,dp:指当前考虑第i个元素的最大长度与考虑第i-1个元素(不考虑第i个元素)的最长无重复数组

这里设置一个方法def dp_len(temp, ele)用于检测ele是否在temp里,并且更新temp返回更新后的temp和len(temp)用于后续的比较

题目

给定一个数组arr,返回arr的最长无重复元素子数组的长度,无重复指的是所有数字都不相同。

子数组是连续的,比如[1,3,5,7,9]的子数组有[1,3],[3,5,7]等等,但是[1,3,7]不是子数组

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
# method 1 
# 使用队列queue模拟
a = [1, 2, 3, 4, 1, 5]
long = 0
queue = []
for ele in a:
while ele in queue:
queue.pop(0)
queue.append(ele)
long = max(long, len(queue))
print(long)

# methond 2
# list记录,再使用slice更新
arr = [2, 2, 3, 4, 8, 99, 3]
long, cur_seq = 0, []
for i in arr:
if i in cur_seq:
long = max(long, len(cur_seq))
start = cur_seq.index(i)
cur_seq = cur_seq[start + 1:]
cur_seq.append(i)
print(max(long, len(cur_seq)))

# methond3
# 动态规划
class Solution:
def maxLength(self, a):
if len(a) == 1:
return 1
dp = [0]*(len(a)+1)
dp[0] = 0
cur_list = a[:1]
for i in range(1, len(a)):
progress = self.dp_len(cur_list, a[i])
dp[i] = max(dp[i-1], progress[0])
cur_list = progress[1]
return max(dp)
# update the situation
def dp_len(self, temp, ele):
while ele in temp:
temp.pop(0)
temp.append(ele)
return len(temp), temp

3.M合并两个有序的数组

思路

[1.先将数据合成到一个数组中,之后对数组进行排序]

[2.使用merge sort的思想]

例1:

A: [1,2,3,0,0,0],m=3

B: [2,5,6],n=3

合并过后A为:

A: [1,2,2,3,5,6]

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
# methond1 [合并列表再排序,时间复杂度高]
class Solution:
def merge(self , A, m, B, n):
# write code here
for i in range(len(B)):
A[m] = B[i]
m += 1
index = len(A)-1
sign = False
while not sign:
sign = True
for i in range(index):
if A[i] > A[i+1]:
sign = False
A[i], A[i+1] = A[i+1], A[i]
index -= 1
return A

# method2 [利用列表本身已经是排好序的,使用merge思想]
A = [4, 5, 6]
B = [1, 2, 3]
m = 3
n = 3
ls = []
if not A:
print(B)
if not B:
print(A)
i = j = 0
while i < m and j < n:
if A[i] <= B[j]:
ls.append(A[i])
i += 1
else:
ls.append(B[j])
j += 1
ls.extend(A[i:])
ls.extend(B[j:])
print(ls)

# method3 [利用merge排序但是思想上首先排最大值,因为对于AB来说都是已经排序号的链表]
A = [4, 5, 6000]
B = [1, 2, 3]
m = 3
n = 3
if not A:
print(B)
if not B:
print(A)

while m > 0 and n > 0:
if A[m-1] > B[n-1]:
A[m+n-1] = A[m-1]
m -= 1
else:
lA[m+n-1] = B[n-1]
n -= 1
# based on the poroblem, the B alwalys has the remaind ele < == >
#==> m = 0; n>
A[:n] = B[:n]
print(A)

链表

1.E反转链表

思路

【使用指针进行操作:使用前序赋值法替换】

输入一个链表,反转链表后,输出新链表的表头。

示例1

输入:

1
{1,2,3}

返回值:

1
{3,2,1}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
class ListNode:
def __init__(self, x):
self.val = x
self.next = None
class Solution:
# 返回ListNode
def ReverseList(self, pHead):
pre, cur, nex = None, pHead, None
while c:
nex = c.next
cur.next = pre
pre = cur
cur = nex
return pre

2.M链表中的节点每K个一组翻转

将给出的链表中的节点每\ k k 个一组翻转,返回翻转后的链表
如果链表中的节点数不是\ k k 的倍数,将最后剩下的节点保持原样你不能更改节点中的值,只能更改节点本身。

要求空间复杂度 O(1)

例如:

给定的链表是1→2→3→4→5

对于 k=2, 你应该返回 2→1→4→3→5

对于 k=3, 你应该返回 3→2→1→4→5

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
# 链表反转
class Solution:
def reverseKGroup(self , head , k ):
# write code here
fast = head
for _ in range(k):
if not fast:
return head
fast = fast.next
new_head = self.reverse(head, fast)
# 此时head变成了尾巴
# tail = head
head.next = self.reverseKGroup(fast, k)
return new_head

def reverse(self, head, fast):
last = None
p = head
while p is not fast:
temp = p.next
p.next = last
last = p
p = temp
return last

# list反转
temp = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
while True:
result = []
n = int(input('reverse number:'))
if n >= len(temp):
print('the number is too bigger, pls litter')
continue
for i in range(0, len(temp), n):
if i + n <= len(temp):
result.extend((temp[i:i+n])[::-1])
else:
result.extend(temp[i:])
print('->'.join('%s' %i for i in result))

3.E判断链表中是否有环

思路

[快慢指针]:如果链表中存在有环,则slow指针必定会与fast指针相遇

需要注意的是,控{}和{single}制空链表即如果head和head.next都是None则return None

判断给定的链表中是否有环。如果有环则返回true,否则返回false。

你能给出空间复杂度O(1)的解法么?

输入分为2部分,第一部分为链表,第二部分代表是否有环,然后回组成head头结点传入到函数里面。-1代表无环,其他的数字代表有环,这些参数解释仅仅是为了方便读者自测调试

1
2
3
4
5
6
7
8
9
10
11
12
class Solution:
def hasCycle(self , head ):
if head is None or head.next is None:
return False
slow = head
fast = head.next
while fast != slow:
if fast is None or fast.next is None:
return False
fast = fast.next.next
slow = slow.next
return True

字符串

1.E匹配括号

[思路]

要判断括号的有效性,左括号必须和右括号相对应。如果是有效括号,并且他们中间还有括号,那么他们必须也是有效的,所以最简单的一种方式就是使用栈来解决。

我们遍历字符串中的所有字符

1,如果遇到了左括号,就把对应的右括号压栈(比如遇到了字符’(‘,就把字符’)’压栈)。
2,如果遇到了右括号

  • 1)查看栈是否为空,如果为空,说明不能构成有效的括号,直接返回false。
  • 2)如果栈不为空,栈顶元素出栈,然后判断出栈的这个元素是否等于这个右括号,如果不等于,说明不匹配,直接返回false。如果匹配,就继续判断字符串的下一个字符。

3,最后如果栈为空,说明是完全匹配,是有效的括号,否则如果栈不为空,说明不完全匹配,不是有效的括号。

题目

给出一个仅包含字符’(‘,’)’,’{‘,’}’,’[‘和’]’,的字符串,判断给出的字符串是否是合法的括号序列
括号必须以正确的顺序关闭,”()”和”()[]{}”都是合法的括号序列,但”(]”和”([)]”不合法。

1
2
3
4
5
6
7
8
9
10
11
12
13
class Solution:
def isValid(self , s ):
mapping = {")":"(", "]":"[", "}":"{"}
stack = []
for i, char in enumerate(s):
if char not in mapping:#left
stack.append(char)
else:
if not stack or stack[-1] != mapping[char]:
return False
stack.pop()

return len(stack) == 0

1.M遍历二叉树

实现pre-order/in-order/post-order遍历二叉树

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Solution:
def threeOrders(self , root ):
# write code here
self.res = [[],[],[]]
self.dfs(root)
return self.res

def dfs(self,root):
if not root:
return
self.res[0].append(root.val)
self.dfs(root.left)
self.res[1].append(root.val)
self.dfs(root.right)
self.res[2].append(root.val)
return

2.M求二叉树的层序遍历【BFS】

给定一个二叉树,返回该二叉树层序遍历的结果,(从左到右,一层一层地遍历)
例如:
给定的二叉树是{3,9,20,#,#,15,7},

该二叉树层序遍历的结果是
[
[3],
[9,20],
[15,7]
]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Solution:
def levelOrder(self , root ):
# write code here
#队列解决
qune, res = [], []
if not root:
return res
qune.append(root)
while len(qune)!=0:
n = len(qune)
temp = []
for i in range(n):
node = qune.pop(0)
temp.append(node.val)
if node.left:
qune.append(node.left)
if node.right:
qune.append(node.right)
res.append(temp)
return res

3.M树的遍历【前序遍历】

思路【使用递归,本质上是树的深度遍历DSF】

输入几组数据,pid为-1的代表根节点。如果数据为非根节点,那么就要搜索此节点直至找到根节点。

id pid
A -1
A-1 A
A-2 A
A-3 A
A-2-1 A-2
A-2-2 A-2
A-2-3 A-2

result: /A /A/A-1 /A/A-2 /A/A-3 /A/A-2/A-2-1 /A/A-2/A-2-2 /A/A-2/A-2-3

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
d = {
"A":"-1",
"A-1":"A",
"A-2":"A",
"A-3":"A",
"A-2-1":"A-2",
"A-2-2":"A-2",
"A-2-3":"A-2"
}

res = []

def func(node):
# using recursion
array.append(node[0])
if node[1] == "-1":
return
func((node[1], d[node[1]]))

for ele_tuple in d.items():
# 每次设置一个temp=[]
array = []
func(ele_tuple)
string = "/".join(array[::-1])
res.append("/"+string)

for ele in res:
print(ele)

4. 按之字形顺序打印二叉树

[思路]

利用栈后入先出的特性,一个栈保存当前层节点,一个栈保存下一层节点。入栈规则如下:

  • 当前层为奇数层时:下一层为从左到右输出,故从右到左入栈即可。
  • 当前层为偶数层时:下一层为从右到左输出,故从左到右入栈即可。

给定一个二叉树,返回该二叉树的之字形层序遍历,(第一层从左向右,下一层从右向左,一直这样交替)
例如:
给定的二叉树是{1,2,3,#,#,4,5}

该二叉树之字形层序遍历的结果是

[

[1],

[3,2],

[4,5]

]

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
class Solution:
def Print(self, pRoot):
# write code here
if pRoot == None:
return []
# 定义两个栈,一个保存从左到右的打印的层的节点,一个保存从右到左打印的层的节点
stack1 = []
stack2 = []
stack1.append(pRoot)
res = []
while stack1 or stack2:
if stack1:
tempValue = []
while stack1:
tempNode = stack1.pop()
tempValue.append(tempNode.val)
if tempNode.left:
stack2.append(tempNode.left)
if tempNode.right:
stack2.append(tempNode.right)
res.append(tempValue)
if stack2:
tempValue = []
while stack2:
tempNode = stack2.pop()
tempValue.append(tempNode.val)
if tempNode.right:
stack1.append(tempNode.right)
if tempNode.left:
stack1.append(tempNode.left)
res.append(tempValue)
return res

动态规划

1.E子数组的最大累加和[:star::star::star:]

思路

给定一个数组arr,返回子数组的最大累加和

例如,arr = [1, -2, 3, 5, -2, 6, -1],所有子数组中,[3, 5, -2, 6]可以累加出最大的和12,所以返回12.

题目保证没有全为负数的数据

[要求]时间复杂度为O(n),空间复杂度为O(1)

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
# method1:暴力解法
class Solution:
def maxsumofSubarray(self , arr ):
# write code here
sum = arr[0]
presum = 0
for ele in arr:
if presum < 0:
presum = ele
else:
presum += ele
sum = max(presum, sum)
return sum

# method2:动态规划
class Solution:
def maxsumofSubarray(self , arr ):
# write code here
# dp[i]代表到第i位的时侯,以arr[i]结尾的连续子数组最大累加和
dp = [0] * len(arr) # 开辟dp
res = arr[0] # 保存最终的结果
dp[0] = arr[0] # 初始化
for i in range(1,len(arr)):
# 维护dp[i], 如果<0,则舍弃cur
dp[i] = max(dp[i-1],0) + arr[i]
res = max(res,dp[i]) # 每更新一个dp值就更新一下res
return res

# method3:动态规划 不仅可以return max number 还可以返回子串
a = [1, -2, 3, 5, -2, 6, -1]

dp = [0]*len(a)
dp[0] = a[0]


def fun(index):
# 相比如method1、2此处更新状态时,也记录了index变化
if dp[index] <= 0:
pointer.clear()
return 0
pointer.append(index)
return dp[index]


pointer = []
for i in range(1, len(a)):
dp[i] = fun(i-1) + a[i]

print(dp)
print(list((a[i] for i in pointer)))

2.M最短路径和[:star::star::star:]

思路

:red_circle:dp[ij] = a[ij]

:green_book:dp[ij] = a[ij] + min(dp[i-1, j], dp[i-1, j+1])

:ribbon:dp[ij] = a[ij] + min(dp[i-1, j], dp[i-1, j-1])

:yellow_heart:dp[ij] = a[ij] + min(dp[i-1, j-1], dp[j-1, j], dp[i-1, j+1])

【注意边界条件和状态转移】

从上到下找到最短路径(数字之和最小),只能往下层走,同时只能走向相邻的节点,例如图中第一排的2只能走向第二排的3、7,第一排的5可以走向第二排的1、7、3

1 8 5 2
4 1 7 3
3 6 2 9
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
a = [[1, 8, 5, 2],
[4, 1, 7, 3],
[3, 6, 2, 9]]
x = len(a)
y = len(a[0])
dp = [[0 for i in range(y)] for j in range(x)]
# 遍历顺序是每行内的每列。所以遍历a中第一行只执行到if,dp中第一行就确定了,然后再确定dp第二行。
# 要注意两个边界条件
for i in range(x):
for j in range(y):
if i == 0:
dp[i][j] = a[i][j]
elif j == 0:
dp[i][j] = a[i][j] + min(dp[i-1][j], dp[i-1][j+1])
elif j == y-1:
dp[i][j] = a[i][j] + min(dp[i-1][j-1], dp[i-1][j])
else:
dp[i][j] = a[i][j] + min(dp[i-1][j-1], dp[i-1][j], dp[i-1][j+1])

print(min(dp[-1]))

3.M最长公共子串

思路

给定两个字符串str1和str2,输出两个字符串的最长公共子串

题目保证str1和str2的最长公共子串存在且唯一。

len(str1) > len(str2)

1
2
3
4
5
6
7
8
9
10
11
12
13
str1, str2 = "1AB2345CD", "12345EF"
l1, l2 = len(str1), len(str2)
dp = [[''] * (l1+1) for _ in range(l2+1)]
dp[0][0] = ''
ans = ''

for i in range(1, l1):
for j in range(1, l2):
if str1[i] == str2[j]:
dp[i][j] = dp[i-1][j-1] + str1[i]
if len(dp[i][j]) > len(ans):
ans = dp[i][j]
print(ans)

4.H最长公共子序列

思路

LCS可以描述两段文字之间的“相似度”,即它们的雷同程度,从而能够用来辨别抄袭。另一方面,对一段文字进行修改之后,计算改动前后文字的最长公共子序列,将除此子序列外的部分提取出来,这种方法判断修改的部分,往往十分准确。简而言之,百度知道、百度百科都用得上。

若xm=yn(最后一个字符相同),则:
Xm与Yn的最长公共子序列Zk的最后一个字符必定为xm
zk=xm=yn
LCS(Xm,Yn) = LCS(Xm-1,Yn-1)+xm

若xm≠yn,则:
LCS(Xm,Yn)= max{LCS(Xm-1,Yn),LCS(Xm,Yn-1)}

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
# 使用递归
def lcs(str1, str2, m, n):
if m == 0 or n == 0:
return 0
if str1[m-1] == str2[n-1]:
case1 = 1 + lcs(str1, str2, m-1, n-1)
return case1
else:
case2 = max(lcs(str1, str2, m-1, n), lcs(str1, str2, m, n-1))
return case2
str1 = 'bd'
str2 = 'abcd'
m = len(str1)
n = len(str2)
res = lcs(str1, str2, m, n)
print(res)

# 使用暴力遍历求解
str1, str2 = "1AB2345C6D8", "12345E6F8"
l1, l2 = len(str1), len(str2)
temp = []

for i in range(l1):
for j in range(l2):
if str1[i] == str2[j]:
temp.append(str1[i])

print(temp)

5.M最长回文子串

思路

对于一个字符串,请设计一个高效算法,计算其中最长回文子串的长度。

给定字符串A以及它的长度n,请返回最长回文子串的长度。

1
2
输入:"abc1234321ab",12
return:7
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Solution:
def getLongestPalindrome(self, A, n):
# write code here
dp = [[0]*(n) for _ in range(n)]

maxL = 0

for right in range(0, n):
for left in range(right,-1,-1):
if A[left] != A[right]:
continue
if right - left <= 1:
dp[right][left] = 1
else:
dp[right][left] = dp[right-1][left+1]
if dp[right][left]==1 and right-left+1 > maxL:
maxL = right-left+1
return maxL

6.E求路径

思路

一个机器人在m×n(col*row)大小的地图的左上角(起点)。机器人每次向下或向右移动。机器人要到达地图的右下角(终点)。可以有多少种不同的路径从起点走到终点?

起点
终点

对于其它位置来说,到达这个位置有两种情况:

一种是从上面的格子走过来的

另一种是从左边的格子走过来的

所以,我们定义一个𝑚×𝑛大小的二维数组𝑑𝑝

𝑑𝑝[𝑖][𝑗]表示从起点到达第𝑖行第𝑗列的方案数。

先把第一行第一列赋值为1

然后从第二行第二列的元素开始循环

𝑑𝑝[𝑖][𝑗]=𝑑𝑝[𝑖−1][𝑗]+𝑑𝑝[𝑖][𝑗−1]

右下角的dp值就是我们要求的答案

1
2
3
4
5
6
7
8
9
10
11
12
13
# m, n = row * col
class Solution:
def uniquePaths(self , m , n ):
# 创建DP时注意m,n的位置
dp = [[0]*n for _ in range(m)]
# 先traverse col 再 traverse row
for i in range(m):
for j in range(n):
if i == 0 or j == 0:
dp[i][j] = 1
else:
dp[i][j] = dp[i-1][j] + dp[i][j-1]
return dp[-1][-1]

7.E买卖股票的最好时机

假设你有一个数组,其中第i 个元素是股票在第i 天的价格。
你有一次买入和卖出的机会。(只有买入了股票以后才能卖出)。请你设计一个算法来计算可以获得的最大收益。

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
# 使用DynamicProgramming
class Solution:
def maxProfit(self , prices):
# write code here
length = len(prices)
dp = [0] * length
for i in range(1, length):
if dp[i-1] <= 0:
dp[i] = prices[i] - prices[i - 1]
else:
dp[i] = dp[i - 1] + prices[i] - prices[i - 1]
return max(dp)

# 使用暴力双循环
class Solution:
def maxProfit(self , prices):
# write code here
ans = 0
for i in range(len(prices)):
for j in range(len(prices)):
if prices[j] - prices[i] > 0 and i < j:
temp = prices[j] - prices[i]
ans = max(ans, temp)
return ans

# 使用切片
class Solution:
def maxProfit(self , prices):
# write code here
ans = 0
for i in range(len(prices)-1):
temp = max(prices[i+1:])
ans = max(temp - prices[i], ans)
return ans

8.M矩阵的最小路径和

思路

题目

给定一个 n * m 的矩阵 a,从左上角开始每次只能向右或者向下走,最后到达右下角的位置,路径上所有的数字累加起来就是路径和,输出所有的路径中最小的路径和。

1 3 5 9
8 1 3 4
5 0 6 1
8 8 4 0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def minPathSum(self , matrix ):
n, m = len(matrix), len(matrix[0])

dp = [[0]*m for _ in range(n)]

for i in range(n):
for j in range(m):
if i == 0 and j == 0:
dp[i][j] = matrix[0][0]
elif i == 0 and j != 0:
dp[i][j] = dp[i][j-1] + matrix[i][j]
elif i != 0 and j == 0:
dp[i][j] = dp[i-1][j] + matrix[i][j]
else:
dp[i][j] = min(dp[i-1][j], dp[i][j-1]) + matrix[i][j]
return dp[-1][-1]

9.M子数组最大乘积

思路

使用双dp_max和dp_min来控制如果出现-5*-8的更新

题目

给定一个double类型的数组arr,其中的元素可正可负可0,返回子数组累乘的最大乘积。

1
2
输入:[-2.5,4,0,3,0.5,8,-1]
输出:12.00000
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
arr = [0.8, -0.3, 0.4, -2.0, 3.0]
if len(arr) <= 0:
print(0)
n = len(arr)
dpmax = [0]*n
dpmin = [0]*n
dpmax[0] = arr[0]
dpmin[0] = arr[0]
for i in range(1, n):
if arr[i] < 0:
dpmax[i] = max(arr[i], arr[i]*dpmin[i-1])
dpmin[i] = min(arr[i], arr[i]*dpmax[i-1])
else:
dpmax[i] = max(arr[i], arr[i]*dpmax[i-1])
dpmin[i] = min(arr[i], arr[i]*dpmin[i-1])
print(dpmax, dpmin, sep='\n')
maxsum = max(max(dpmax), max(dpmin))
print(maxsum)
>>
[0.8, -0.24, 0.4, 0.24, 3.0]
[0.8, -0.3, -0.12, -2.0, -6.0]
3.0

10.M最长递增子序列的长度

【思路】

[**1.动态规划**](最长上升子序列 - 最长递增子序列 - 力扣(LeetCode) (leetcode-cn.com))

2.贪心算法+二分查找

原始数组为A, 建立一个辅助数组B, 变量end用来记录B数组末尾元素的下标

遍历A中的所有的元素 x = A[i]

  • 如果x > B的末尾元素,则将x追加到B的末尾,end+=1
  • 如果x < B的末尾元素,则利用二分查找,寻找B中第一个大于x的元素,并用x进行替换 e.g. x= 4 B=[1,3,5,6] ==> B=[1,3,4,6]

遍历结束之后,B的长度则为最长递增子序列的长度

Example

题目

给定数组arr,设长度为n,输出arr的最长递增子序列。(如果有多个答案,请输出其中 按数值(注:区别于按单个字符的ASCII码值)进行比较的 字典序最小的那个)

1
2
3
4
输入:[1,2,8,6,4]
输出:[1,2,4]
statement:
其最长递增子序列有3个,(1,2,8)、(1,2,6)、(1,2,4)其中第三个 按数值进行比较的字典序 最小,故答案为(1,2,4)
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
# 使用贪婪算法加二分算法
def get_lis_length(arr):
temp = [arr[0]]
end = 0

for i in range(1, len(arr)):
if arr[i] > temp[end]:
end += 1
temp.append(arr[i])
else:
pos = binary_search(temp, 0, len(temp), arr[i])
temp[pos] = arr[i]
return end + 1

def binary_search(arr, start, end, value):
l = start
r = end-1
while l <= r:
m = (l + r) //2
if arr[m] == value:
return m
elif arr[m] < value:
l = m + 1
else:
r = m - 1
return l

# 使用dynamic programming
class Solution:
def lengthOfLIS(self, nums: List[int]) -> int:
if not nums:
return 0
# 记录长度dp
dp = []
for i in range(len(nums)):
dp.append(1)
for j in range(i):
if nums[i] > nums[j]:
dp[i] = max(dp[i], dp[j] + 1)
return max(dp)

arr = [2, 1, 5, 3, 6, 4, 8, 9, 7]
print(get_lis_length(arr))

10.M求最长递增子序列

贪心算法+二分优化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Solution:
def LIS(self , arr ):
# write code here
import bisect
a=[]
dp=[1]*len(arr)
for i in range(len(arr)):
j=bisect.bisect_left(a, arr[i])
if j==len(a):
a.append(arr[i])
else:
a[j]=arr[i]
dp[i]=j+1
L=len(a)
for i in range(len(dp)-1,-1,-1):
if dp[i]==L:
a[L-1]=arr[i]
L-=1
return a

二分

DFS[深度优先搜索]

1.H字符串排列

思路

recursion

【定义一个set保持在当前位置已经访问过的字符】

输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串abc,则按字典序打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。

输入一个字符串,长度不超过9(可能有字符重复),字符只包括大小写字母。

example

1
2
输入: "ab"
输出:['ab', 'ba']
1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Solution:
def Permutation(self, s):
def bfs(visited, unvisited):
if len(unvisited) == 0:
res.append(visited)
return
d = set()
for i, x in enumerate(unvisited):
if x not in d:
d.add(x)
bfs(visited+x, unvisited[:i]+unvisited[i+1:])
res = []
bfs('', s)
return res

哈希

分治

1.寻找第k大[待解决]

思路

快排+二分,与快排不同的是,利用二分法每次都减少了一半的不必要排序。
当high=low小于k的时候,在后半部分搜索,
当high=low大于k的时候,在前半部分搜索。

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
import sys


def findKth(a, start, end, K):
low, high = start, end
key = a[start]
while start < end:
while start < end and a[end] <= key:
end -= 1
a[start] = a[end]
while start < end and a[start] >= key:
start += 1
a[end] = a[start]
a[start] = key
if start < K - 1:
return findKth(a, start + 1, high, K)
elif start > K - 1:
return findKth(a, low, start - 1, K)
else:
return a[start]


try:
while (1):
line = sys.stdin.readline()
if line == '': break
lines = line.strip().replace('[', '').replace(']', '').split(',')
lines = list(map(int, lines))
n, K = lines[-2], lines[-1]
val = findKth(lines, 0, n - 1, K)
print(val)
except:
pass

模拟

1.M设计LRU缓存结构[待解决]

思路

设计LRU缓存结构,该结构在构造时确定大小,假设大小为K,并有如下两个功能

  • set(key, value):将记录(key, value)插入该结构
  • get(key):返回key对应的value值

[要求]

  1. set和get方法的时间复杂度为O(1)
  2. 某个key的set或get操作一旦发生,认为这个key的记录成了最常使用的。
  3. 当缓存的大小超过K时,移除最不经常使用的记录,即set或get最久远的。

若opt=1,接下来两个整数x, y,表示set(x, y)
若opt=2,接下来一个整数x,表示get(x),若x未出现过或已被移除,则返回-1
对于每个操作2,输出一个答案

1
2
3
4
5
6
7
8
9
10
11
输入:
[[1,1,1],[1,2,2],[1,3,2],[2,1],[1,4,4],[2,2]],3
返回值:
[1,-1]
复制
说明:
第一次操作后:最常使用的记录为("1", 1)
第二次操作后:最常使用的记录为("2", 2),("1", 1)变为最不常用的
第三次操作后:最常使用的记录为("3", 2),("1", 1)还是最不常用的
第四次操作后:最常用的记录为("1", 1),("2", 2)变为最不常用的
第五次操作后:大小超过了3,所以移除此时最不常使用的记录("2", 2),加入记录("4", 4),并且为最常使用的记录,然后("3", 2)变为最不常使用的记录
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Solution:

def LRU(self , operators , k ):
# write code here
keys = []
d = {}
res = []
for op in operators:
if op[0] == 1:
if len(keys) == k:
key = keys.pop(0)
d.pop(key)
keys.append(op[1])
d[op[1]]=op[2]

if op[0] == 2:
ans = d.get(op[1],-1)
res.append(ans)
if ans != -1:
keys.remove(op[1])
keys.append(op[1])
return res