我的博客

django外键批量数据查询减少数据库访问

目录

在Django使用外键时,可以直接通过含有外键的的model对象访问对应的外键对象的属性。例如:学生选课中:

StudentCourseRelation对象有两个外键:一个是学生,一个是课程班级。
student_course是学生选课关系的对象。
那么可以通过 student_course.student.name ,直接获得student的名称。但是这并不意味着,这部操作不需要额外的数据库查询。

事实上当你获得一个 StudentCourseRelation对象时
student_course ,仅有 学生的id和课程的id是已经载入内存的,如果需要通过
student_course.student或 student_course.course访问student和course的属性的时候都是需要额外的数据库操作的。而且即使是只访问他们的id,如
student_course.student.id,也同样无法避免额外一次数据库查询。而且每使用一次这个变量都会产生一次数据库查询(没有缓存)。所以这个方法是非常低效的,不适用于大批数据的操作,比如一些统计分析型的任务。

如果现在需要读取全部的数据,而且这些数据包含外键。我想到的一个更快速的方法是:

分别读出各个model的全部数据,并以id 为key建立dict。然后依上例,使用
student_course.student_id 而不是 student_course.student.id 获取外键的id再,通过字典获取相应的数据

这里有一个实际的测试:

大概2万8千学生。27万人次选课记录。学生中有1000人左右没有选任何一门课。我们希望找出没选任何课的学生。

def get_abnormal_student():
    scs = StudentCourse.objects.all()
    students = Student.objects.all()
    stu = {}
    for s in students:
        stu[s.id] = s
    for sc in scs:
        if sc.student.id in stu:
            stu.pop(sc.student.id)
    for s in stu:
        print(s, stu[s].name)

这段代码由于使用了两次sc.student.id,所以运行速度很慢。经过测试,耗时159,769 ms,即159秒 (第二次测试163秒)。
把第一个sc.student.id改为sc.student_id后,再次测试耗时19,844 ms,即19秒 (第二次测试耗时20秒)。
把第二个 sc.student.id也改为sc.student_id后 ,仅耗时3371 ms,也就是3秒(第二次测试3秒)。

def get_abnormal_student2():
    students = Student.objects.all()
    for s in students:
        if StudentCourse.objects.filter(student=s).count() == 0:
            print(s.name)

另一种方法,耗时19秒。
StudentCourse.objects.filter(student=s).count() 换成s.studentcourse_set.count()耗时17秒

kms

目录

1、搭建KMS服务器

2、激活

(1)Windows

​ a. 执行命令 slmgr /skms <kms服务器的ip或域名>

​ b.执行命令 slmgr /ato

(2)Office

1
2
3
4
5
6
cd 'C:\Program Files\Microsoft Office\Office15'
cscript OSPP.VBS /remhst
cscript ospp.vbs /setprt:1688
cscript ospp.vbs /sethst:192.168.1.1
cscript ospp.vbs /act
cscript ospp.vbs /dstatus

kill后台执行的python进程输出重定向为空

目录
  1. 实验

当使用 & 在后台执行linux命令时,使用输出重定型保存输出到文件中。再使用kill命令杀掉进程后,发现结果文件为空。

解决方法


可以使用 kill -2 <进程号> 杀进程,这样相当于给进程发送ctrl + c。

实验

编辑文件 test.py

import time

while true:
  print('hello')
  time.sleep(1)

这段代码每秒输出一个hello

执行命令 python test.sh &> log,一段时间后按下ctrl + c 退出,发现log文件中有内容,在刚刚命令后加 &,即 python test.sh &> log & , 一段时间后kill进程,发现log为空。再次执行 python test.sh &> log & ,使用kill -2 杀进程,发现log中有正确的内容(包括一个KeyboardInterrupt的异常信息),过程如下

sxw@DESKTOP:/mnt/c/Users$ python t.py &> log &
[1] 241
sxw@DESKTOP:/mnt/c/Users$ kill 241
sxw@DESKTOP:/mnt/c/Users$
[1]+  Terminated              python t.py &> log
sxw@DESKTOP:/mnt/c/Users$ cat log
sxw@DESKTOP:/mnt/c/Users$
sxw@DESKTOP:/mnt/c/Users$ python t.py &> log &
[1] 243
sxw@DESKTOP:/mnt/c/Users$ kill -2 243
sxw@DESKTOP:/mnt/c/Users$
[1]+  Exit 1                  python t.py &> log
sxw@DESKTOP:/mnt/c/Users$ cat log
hello
hello
hello
hello
hello
hello
hello
hello
Traceback (most recent call last):
  File "t.py", line 5, in <module>
    time.sleep(1)
KeyboardInterrupt
sxw@DESKTOP:/mnt/c/Users$

修改文件,而不重写不变部分

目录

使用 ‘r+’ 参数打开文件,定位文件指针到指定位置然后修改即可。这是写入内容将覆盖原位置的内容,原来其他的内容将保持不变。但是如果修改内容与原内容长度不同时无法采取这种方法。 假设有这样的任务,2亿行的文件每行只有一个a,需要把第三行的a改为b。 使用python的做法(另附两种其他的方法,一种python读取在全部重新写入,一种awk) 1、生成测试文件:

一个2亿行的文件 test_file,每行都只有一个a。文件大小382M。

1
2
3
4
f = open('test_file', 'w')
for i in range(200000000):
f.write('a\n')
f.close()

运行时间 45.916s

2、测试

(1)用r+参数读原文件,直接在原文件上修改

1
2
3
4
5
import os
f = open('test_file', 'r+')
f.seek(4)
f.write('b')
f.close()

运行时间 0m0.014s

(2)读原文件,然后把改动后的结果写入另一个文件:

1
2
3
4
5
6
7
8
9
10
11
fr = open('test_file', 'r')
fw = open('test_file_mod', 'w')
i = 0
for l in fr:
i += 1
if i == 3:
fw.write('b\n')
else:
fw.write(l)
fr.close()
fw.close()

运行时间 1m35.701s

(3)用awk把输出结果到新文件

1
awk '{if(NR==3){print "b"}else{print}}' test_file > test_file_mod

运行时间0m36.397s

使用time命令测试执行时间(取real时间)。三种方法对内存消耗都很小。时间消耗差别很大主要是使用w+时仅写入改动部分,磁盘IO量比另两种方法小很多。 另外测试了使用python按行读完整个test_file的时间是 0m42.689s。 测试使用的代码如下:

1
2
3
4
5
f = open('test_file')
while True:
l = f.readline()
if not l: break
f.close()

在阿里云ECS上进行测试CPU为Intel(R) Xeon(R) CPU E5-2682 v4 @ 2.50GHz

Python从后往前读文件

目录
  1. 使用os.path.getsize 获取文件大小
  2. 使用 file_obj.seek 移动文件指针到文件末尾减一处
  3. 每次读取一个字符,并将文件指针向前移动一

代码实现如下:

#!/usr/bin/env python
# -- coding: UTF-8 --

import os

def tail_read(f, n):
cur_pos = f.tell()
buf = b’’
while n:
b = f.read(1)
buf = b + buf
cur_pos -= 1
if cur_pos < 0: break
f.seek(cur_pos)
n -= 1
return buf

def tail_open(file):
fsize = os.path.getsize(file)
f = open(file, ‘rb’)
f.seek(fsize - 1)
return f

if __name__ == ‘__main__‘:
import sys
f = tail_open(sys.argv[1])
buf = tail_read(f, int(sys.argv[2]))
print (buf)

python2和python3都可运行 测试方法(读取文件名为file的文件的最后100个字节):

  1. 把代码保存成tail_read.py
  2. 执行 python tail_read.py file 100

搭建d3.js开发环境

目录

1、安装node.js 2、初始化项目

创建目录,进入目录,执行命令 npm init -y

然后建立src目录用于存放源码,dist用于存放编译好的代码

3、安装和配置webpack

执行npm install webpack webpack-cli --save-dev

创建文件 webpack.config.js ,写入以下内容

const path = require(‘path’);

module.exports = {
entry: ‘./src/index.js’,
output: {
filename: ‘main.js’,
path: path.resolve(__dirname, ‘dist’)
}
};

修改package.json,在scripts中加入"build": "webpack" 4、安装d3.js

执行 npm install d3 --save

5、编写代码

创建dist/index.html,并写入以下内容

<!doctype html>


Getting Started




创建src/index.js,并写入以下内容

import * as d3 from “d3”;

function SimpleWidget(spec) {
var instance = {}; // <– A
var headline, description; // <– B
instance.render = function () {
var div = d3.select(‘body’).append(“div”);
div.append(“h3”).text(headline); // <– C
div.attr(“class”, “box”)
.attr(“style”, “color:” + spec.color) // <– D
.append(“p”)
.text(description); // <– E
return instance; // <– F
};
instance.headline = function (h) {
if (!arguments.length) return headline; // <– G
headline = h;
return instance; // <– H
};
instance.description = function (d) {
if (!arguments.length) return description;
description = d;
return instance;
};
return instance; // <– I
}
var widget = SimpleWidget({color: “#6495ed”})
.headline(“Simple Widget”)
.description(“This is a simple widget demonstrating functional javascript.”);
widget.render();

6、编译与运行

执行 npm run-script build该命令生成了dist/main.js

在浏览器中打开dist/index.html即可看到代码运行后的效果

参考资料 1、https://webpack.js.org/guides/getting-started/ 2、D3.js数据可视化实战手册 3、https://github.com/NickQiZhu/d3-cookbook/

clang生成抽象语法树

目录

Clang(发音为/ˈklæŋ/类似英文单字clang[1]) 是一个C、C++、Objective-C和Objective-C++编程语言的编译器前端。它采用了LLVM作为其后端,而且由LLVM2.6开始,一起发布新版本。它的目标是提供一个GNU编译器套装(GCC)的替代品,支持了GNU编译器大多数的编译设置以及非官方语言的扩展。官网地址 https://clang.llvm.org 可以用来生成抽象语法树(AST)。 使用命令 clang -Xclang -ast-dump file.cpp 可以生成file.cpp的抽象语法树,输出到标准输出。 但是实际上clang执行的参数是非常复杂的,可以使用 -### 让clang输出他要执行的命令(但不实际执行)

clang -### -Xclang -ast-dump x.cpp

可以看到clang实际会执行两条命令,其中第一条的参数 “-fcolor-diagnostics”可以去掉,这样输出的抽象语法树就不带颜色了。

参考资料:

[1]https://stackoverflow.com/questions/18560019/how-to-view-clang-ast

[2]https://zh.wikipedia.org/wiki/Clang

Thrift简单使用的例子

目录

Apache Thrift软件框架,可以允许不同语言通过网络相互调用,支持的语言有C++,Java,Python,PHP,Ruby,Erlang,Perl,Haskell,C#,Cocoa,JavaScript,Node.js,Smalltalk,OCaml和Delphi等,官方网站是 http://thrift.apache.org/ 使用Apache Thrift的步骤是(此处以python调用java函数为例)

  1. 安装thrift 官方网站有exe和源码下载 http://thrift.apache.org/download
  2. 编写thrift文件,定义接口
  3. 使用thrift编译器由thrift文件产生python的代码和java的代码。
  4. 在自己写python和java的程序中分别import上一步自动生成的代码以及thrift的库

此处以两数相加的函数为例,详细的步骤如下 1、编写thrift文件 文件calculator.thrift

service Calculator {
i32 add(1:i32 num1, 2:i32 num2)
}

serveice Calculator声明了一个服务, i32 add(1: i32 num1, 2:i32 num2) 声明了一个函数add,i32是变量类型——32为有符号整型,add函数需要两个参数num1和num2,类型都是i32,返回值的类型也是i32 2、自动生成代码 一条命令 thrift-0.11.0.exe --gen py calculator.thrift 这是在windows平台下,直接下载一个exe文件就可以,Linux平台好像需要从源码编译 命令的结果是在当前目录下生成了gen-py文件夹,里面有空的__init__.py和calculator文件夹,calculator文件夹里有我们需要的Calculator.py 3、python服务端编写 需要import上一步生成的Calculator.py,在gen-py/calculator/里 需要安装依赖 pip install thrift

import Calculator

from thrift.transport import TSocket
from thrift.transport import TTransport
from thrift.protocol import TBinaryProtocol
from thrift.server import TServer

class CalculatorHandler:
def __init__(self):
self.log = {}

def add(self, n1, n2):
    print('add(%d,%d)' % (n1, n2))
    return n1 + n2

if __name__ == ‘__main__‘:
handler = CalculatorHandler()
processor = Calculator.Processor(handler)
transport = TSocket.TServerSocket(host=’127.0.0.1’, port=9090)
tfactory = TTransport.TBufferedTransportFactory()
pfactory = TBinaryProtocol.TBinaryProtocolFactory()

server = TServer.TSimpleServer(processor, transport, tfactory, pfactory)

# You could do one of these for a multithreaded server
# server = TServer.TThreadedServer(
#     processor, transport, tfactory, pfactory)
# server = TServer.TThreadPoolServer(
#     processor, transport, tfactory, pfactory)

print('Starting the server...')
server.serve()
print('done.')

4、python客户端端编写 同样需要第2步生成的Calculator.py和安装依赖thrift

from gen_py.calc import Calculator

from thrift import Thrift
from thrift.transport import TSocket
from thrift.transport import TTransport
from thrift.protocol import TBinaryProtocol

def main():

# Make socket
transport = TSocket.TSocket('localhost', 9090)

# Buffering is critical. Raw sockets are very slow
transport = TTransport.TBufferedTransport(transport)

# Wrap in a protocol
protocol = TBinaryProtocol.TBinaryProtocol(transport)

# Create a client to use the protocol encoder
client = Calculator.Client(protocol)

# Connect!
transport.open()

sum_ = client.add(1, 1)
print('1+1=%d' % sum_)

transport.close()

if __name__ == ‘__main__‘:
try:
main()
except Thrift.TException as tx:
print(‘%s’ % tx.message)

5、测试 先运行服务端,再执行客户端可以看到服务端打印了客户端发送的请求,客户端也返回了服务端传回的结果

编译安装python 3.7

目录
  1. 下载源码
  2. 安装依赖
  3. configure
  4. 安装
  5. 创建软链(可选)
  6. 配置pip源(可选)

下载源码

下载

wget https://www.python.org/ftp/python/3.7.3/Python-3.7.3.tgz

解压

tar -xzvf Python-3.7.3.tgz

安装依赖

yum install openssl-devel bzip2-devel expat-devel gdbm-devel sqlite-devel libffi-devel

configure

./configure --enable-optimizations

安装

1
2
make
make install

创建软链(可选)

我一般喜欢把python3.7链接为py这与windows下的名称一致

ln /usr/local/bin/python3.7 /usr/local/bin/py -s

这样就可以用 py 来启动python3.7了

配置pip源(可选)

pip3 config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple

我一般使用清华源,速度快且支持IPv6