1 | <table style="width:1000px"> |
水平方向对齐:
text-align: right;
text-align: center;
text-align: left;
垂直方向对齐:
vertical-align: top;
vertical-align: middle;
vertical-align: bottom;
1 | <table style="width:1000px"> |
水平方向对齐:
text-align: right;
text-align: center;
text-align: left;
垂直方向对齐:
vertical-align: top;
vertical-align: middle;
vertical-align: bottom;
根据项目需要,搞一个配置文件Pipfile,传到github里,完事儿。
首先,我们假设….算了,不扯那些没用的。做Python的开发,无论你用的什么工具,最麻烦的事儿应该莫过于包的管理。pipenv简单来说就是一个python官方推荐的包管理工具。
相比于pip等其他工具,pipenv最大的特点是可以根据项目,生成不同的环境,在ProjectA中设置的Pipfile不会影响到ProjectB,更不会影响到系统默认的pip列表。这个为项目定制的虚拟环境,我们称之为shell
。
安装pipenv之前,首先确定系统里有以下两样东西:
pipenv是通过pip安装的工具,所以执行1
pip install pipenv
由于pipenv可以在系统里不同版本的Python间任意切换,所以执行pip的时候不同太在乎Python版本问题。
pipenv 管理Python环境的配置文件是Pipfile
,大致长这个样子:
1 | [[source]] |
我们可以直接写,也可以用pipenv install
来生成默认的Pipfile, 不过还有个更方便的方法是直接由pip生成的requirements.txt配置文件来生成Pipfile。
1 | # 首先在当前路径里使用pip生成requirements.txt |
pipenv可以指定虚拟环境的python版本,随时通过修改Pipfile来切换(修改是通过命令行修改的意思,当然你要直接修改也没什么区别,但是不用命令来修改配置文件就会觉得很low啊)1
2
3
4
5
6
7
8
9## 使用下面的命令,从当前系统已经安装的不同版本的Python中选择一个
pipenv --python 版本号
## 比如
pipenv --python 3.5
pipenv --python 3.7
pipenv --python 2.6
如果Pipfile中指定的Python版本在当前系统下不存在,之后启动虚拟环境时会跳出警告。
更新Pipfile之后,启动虚拟环境之前,我们需要把Pipfile中列出的包安装好,或者删除已经不用的包,这个操作就是pipenv lock
由于需要从服务器下载相应的包(这是我猜的,鬼晓得为什么这玩意儿怎么那么慢,官方似乎也没有什么行之有效的办法)
都搞定之后,pipenv shell
跑起来,我们就进入定制的虚拟环境了。
说了那么老多,其实安装好pipenv之后,直接一个pipenv -h
查看help文档就可以了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
61Options:
--where Output project home information.
--venv Output virtualenv information.
--py Output Python interpreter information.
--envs Output Environment Variable options.
--rm Remove the virtualenv.
--bare Minimal output.
--completion Output completion (to be eval'd).
--man Display manpage.
--support Output diagnostic information for use in GitHub issues.
--site-packages Enable site-packages for the virtualenv. [env var:
PIPENV_SITE_PACKAGES]
--python TEXT Specify which version of Python virtualenv should use.
--three / --two Use Python 3/2 when creating virtualenv.
--clear Clears caches (pipenv, pip, and pip-tools). [env var:
PIPENV_CLEAR]
-v, --verbose Verbose mode.
--pypi-mirror TEXT Specify a PyPI mirror.
--version Show the version and exit.
-h, --help Show this message and exit.
Usage Examples:
Create a new project using Python 3.7, specifically:
$ pipenv --python 3.7
Remove project virtualenv (inferred from current directory):
$ pipenv --rm
Install all dependencies for a project (including dev):
$ pipenv install --dev
Create a lockfile containing pre-releases:
$ pipenv lock --pre
Show a graph of your installed dependencies:
$ pipenv graph
Check your installed dependencies for security vulnerabilities:
$ pipenv check
Install a local setup.py into your virtual environment/Pipfile:
$ pipenv install -e .
Use a lower-level pip command:
$ pipenv run pip freeze
Commands:
check Checks for security vulnerabilities and against PEP 508 markers
provided in Pipfile.
clean Uninstalls all packages not specified in Pipfile.lock.
graph Displays currently-installed dependency graph information.
install Installs provided packages and adds them to Pipfile, or (if no
packages are given), installs all packages from Pipfile.
lock Generates Pipfile.lock.
open View a given module in your editor.
run Spawns a command installed into the virtualenv.
shell Spawns a shell within the virtualenv.
sync Installs all packages specified in Pipfile.lock.
uninstall Un-installs a provided package and removes it from Pipfile.
update Runs lock, then sync.
是对误差的一个估计,其定义为所有估值和样本的误差平方的平均值。
在python中这样写:1
2
3from sklearn.metrics import mean_squared_error
mean_sqlared_error(df['data'],predict)
维基百科的定义是:用于度量因变量的变异中可由自变量解释部分所占的比例,以此来判断统计模型的解释力。
在python中这样写:1
2
3
4
5from sklearn.linear_model import LinearRegression
lm=LinearRegression()
lm.score(X,Y)
线性回归是统计学中的一种回归分析方法,我们可以简单的记住线性回归中有两个重要变量:
如果线性回归方程中只有一个x,则称为简单线性回归,大于一个x时,成为多元线性回归。
举个例子1
y = b0 + b1*x
这个简单线性回归方程中,x是自变量,y是因变量,b0是intercept, b1叫做slope
1 | from sklearn.linear_model import LinearRegression |
首先来个例子:1
2
3Stream.of("aaa", "aa", "bbbb", "bb", "ccccc", "cc", "dddddd", "eeeeeee")
.filter(len -> len.length() > 3)
.forEach(System.out::println);
stream在使用的时候,分为中间操作(intermediate operation)和终端操作(terminal operation),例子中的filter()
就是中间操作,中间操作可以有多个,跟火车厢一样接起来就可以。
而终端操作(或者叫终结操作)只能有一个。终端操作很容易理解成是对stream的结束,事实上终端操作最主要的作用是执行。如果没有终端操作,中间操作的内容将不会生效。所以,下面这个stream执行后是什么效果?1
2
3
4
5Stream.of("aaa", "aa", "bbbb", "bb", "ccccc", "cc", "dddddd", "eeeeeee")
.mapToInt(String::length)
.filter(len -> len > 3)
.peek(System.out::println)
.limit(2);
答案是:什么也不输出。因为limit()
是个中间方法,对这个stream的操作将不会生效。
弄懂了什么中间操作和终端操作的区别以后,我们就要面对下一个疑问了:中间操作和终端操作的执行顺序是什么?
很简单,来个例子瞧瞧:1
2
3
4
5
6Stream.of("aaa", "aa", "bbbb", "bb", "ccccc", "cc", "dddddd", "eeeeeee")
.filter(len -> len.length() > 3)
.peek(System.out::println)
.mapToInt(String::length)
.limit(2)
.forEach(System.out::println);
输出结果有几行?内容是?
这里重点在peek()
,limit()
和forEach()
的生效顺序,从结果上来看,应该是stream中的每一个元素,对应一组打包的操作,所以输出字符串b之后紧接着输出的是b的长度,而不是字符串c。而limit()
限制了处理的元素个数,大于limit的元素到达不了终端操作,自然不会生效。
我们换个方法再验证一遍,这次终端操作用sum()
来替代forEach()
,终端操作不是对元素逐一的操作了,对中间操作的执行顺序会有什么影响吗?1
2
3
4
5
6
7Stream.of("aaa", "aa", "bbbb", "bb", "ccccc", "cc", "dddddd", "eeeeeee")
.filter(len -> len.length() > 3)
.peek(System.out::println)
.mapToInt(String::length)
.limit(2)
.peek(System.out::println)
.sum();
结果跟forEach()
是一样的。
比如debug,我们可以使用下面三种方法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/**
* Logs a message object with the {@link Level#DEBUG DEBUG} level.
*
* @param message the message string to log.
*/
void debug(String message);
/**
* Logs a message with parameters at the {@link Level#DEBUG DEBUG} level.
*
* @param message the message to log; the format depends on the message factory.
* @param params parameters to the message.
* @see #getMessageFactory()
*/
void debug(String message, Object... params);
/**
* Logs a message with parameters which are only to be constructed if the logging level is the {@link Level#DEBUG
* DEBUG} level.
*
* @param message the message to log; the format depends on the message factory.
* @param paramSuppliers An array of functions, which when called, produce the desired log message parameters.
* @since 2.4
*/
void debug(String message, Supplier<?>... paramSuppliers);
最不环保的写法,甚至新手的话可能会出现logger.debug("A is " + a + ", B is" + b + ".");
这种写法,首先使用+
来生成log文本是非常耗时的,其次,无论log输出的级别是否低于debug,这段字符串的处理都会被执行。
OpenJDK 8之前这么写是没问题的,使用MessageFactory来生成log既增加了可读性,又比字符串连接要节能。不过问题是log的生成还是总需要被执行。
doc中写了:only to be constructed if the logging level is the DEBUG level.
,还需要解释吗?
1 | logger.debug(objectA.toString()); // 一般写法 |
不妨通过字面意思来理解,HAS-A 就是有什么的意思,而IS-A就是是什么的意思。在面向对象编程(OOP)中,类的继承就是IS-A的关系,简单来说就是“张三是个人,旺财是条狗”的意思。而类的对象使用,多数时候可以看作是HAS-A的关系,比如把身高,体重都看作是类的话,我们可以说“张三有身高,有体重”。
谷歌搜到的第一篇说明就很浅显易懂
1 | Class Car{ ... } // 车是一个类 |
首先是线程的最基本写法。Java中有两种最基本的线程编写方法:
继承Thread类,在run()
中书写程序内容,使用start()
来启动线程。
1 | public class Sample{ |
实现Runnable接口。Thread
的构造体可以接受一个Runnable的参数,所以我们也可以通过写一个实现了Runnable接口的类来生成线程。虽然也是在run方法中写所要执行的任务,但是实现Runnable的类是作为生成线程的参数来使用,所以我们可以理解为创建了一类工作,然后以该工作为参数创建(灵魂召唤)多个工人来执行。
1 | public class Sample { |
Runnable也可以使用Lambda表达式来定义,这样我们就可以直接写run方法的内容而不用去定义一整个类了。
1 | Thread c = new Thread(() -> { |
java.lang
的列举型Thread.State中定义了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/**
* A Thread which has not yet started.
*/
NEW,
/**
* A Thread which is running or suspended.
*/
RUNNABLE,
/**
* A Thread which is blocked on a monitor.
*/
BLOCKED,
/**
* A Thread which is waiting with no timeout.
*/
WAITING,
/**
* A Thread which is waiting with a timeout.
*/
TIMED_WAITING,
/**
* A thread which is no longer alive.
*/
TERMINATED
一个线程只能由start()
启动一次,多次启动会产生IllegalThreadStateException
在Thread类中定义了三个优先级常数:1
2
3
4
5
6
7
8
9
10
11
12/**
* The maximum priority value for a Thread.
*/
public final static int MAX_PRIORITY = 10; // Maximum allowed priority for a thread
/**
* The minimum priority value for a Thread.
*/
public final static int MIN_PRIORITY = 1; // Minimum allowed priority for a thread
/**
* The default priority value for a Thread.
*/
public final static int NORM_PRIORITY = 5; // Normal priority for a thread
线程的优先级可以通过priority
的getter和setter来设置和确认,当两个进行中的线程发生冲突时,一般优先级高的线程会先运行,但这并不等于说优先级高的线程一定会被优先启动。系统在启动线程的时候并不看优先级的高低。
Thread类提供了各种用来控制线程启动,暂停,再开,终止的方法,这里记录以下两个点的理解:
并发编程一个很常见的问题就是不同线程对同一资源的读写控制,这里就要提到两个很基本的概念,同步和排他(锁)。
排他锁比较好理解,就是通过关键字或者方法来指定某一部分同一时间只能被一个线程使用,被调用时这部分资源会被锁定,其他想要使用该资源的线程则会被挂起,知道资源解锁,轮班使用。synchronized
是用来指定锁定资源的关键字,可以用来修饰方法,也可以用来指定代码块。1
2
3
4
5
6
7
8public synchronized void add(int a){...}
// 或者
public void add(int a){
synchronized(想要锁定的对象){
//指定的对象在这段代码运行过程中被上锁
}
}
同步则是指使用wait(),notify(),notifyAll()等方法,具体的控制线程的运行。
wait()可以指定也可以不指定超时的值,wait可以让线程进入等待,直到等待超时或者其他的线程使用了notify(),notifyAll()或者interrupt()方法,使等待的线程进入可执行的状态。
notify()会随机让一个等待的线程再开,notifyAll()则是让所有该对象的线程再开。
wait(),notify(),notifyAll()都是用来控制线程访问被锁资源的,所以如果没有synchronized锁定资源,这些方法的会抛出IllegalMonitorStateException
排他锁无法避免会出现不同的线程锁定不同的资源大家都干不了活儿的局面,这就是死锁;或者因为要使用的资源不断被其他线程占用而导致某一线程进展缓慢,这叫活锁。
死锁和活锁都不会在编译或者运行时报错,所以要留意设置超时,一定时间后手动解锁资源。等待资源访问时线程会处于饥饿
的状态,饥饿的线程越多,则说明程序效率越低。为了减少饥饿,资源的调用顺序,锁定的粒度都需要好好思考。
Dockerfile 是一种用来定制镜像的文本文件。我们可以在Dockerfile中指定基础镜像,执行命令,开放端口,设置环境变量等等。
每一条指令会构建一层。参考docker 文档,docker的镜像具有下面的层结构(image layers),增加层的厚度自然会增加镜像的大小,所以写Dockerfile时,尽量使用\
和 &&
巧妙地将命令的内容合并到一层。
比如Docker入门到实践中的例子,写一个构建nginx
镜像的Dockerfile:
1 | FROM nginx |
这个Dockerfile包含了两个指令,FROM
制定了基础镜像,基础镜像可以是各种服务类的官方镜像,也可以是干净的操作系统镜像,还可以是空白镜像scratch
。RUN
是最常用的指令,用来执行命令行命令。
接下来我们执行1
docker build -t nginx:v3 .
来构建镜像,it nginx:v3
的的意思是构建的目标名为nginx,标签为v3。
docker构建镜像的log将每一条指令分的很清楚,每一条指令作为一个step来执行,完成后我们就可以看到新的镜像nginx:v3
了。
执行下面命令,通过新构建的镜像生成一个容器:1
docker run --name hello -d -p 8080:80 nginx:v3
然后我们访问 localhost:8080
就可以看到欢迎页了:
docker的指令一般有两种书写方式,一种是指令后跟命令行,一种是使用[]
的函数调用写法,这里主要记录一下命令行写法。
COPYCOPY <context path> <target path>
这个指令将上下文目录中的文件或者目录复制到新一层的目标路径中,要复制的文件或者目录可以有多个,可以使用通配符(符合golang的filepath.Match规则),目标路径可以是绝对路径,也可以是工作目录的相对路径。
还可以加上--chown=<user>:<group>
来改变文件的所属用户和所属组
上下文路径?context path?
ADD
使用方法跟COPY基本一样。功能会丰富一点,比如源路径可以设置为url,使用url路径时docker会讲文件打包下载到目标路径。因为需要多余的RUN来处理权限,解压缩,筛选内容,这个命令并不是很实用。
CMD
用于指定默认的容器主进程的启动命令。指令的写法也有两种:
CMD 命令
CMD ["可执行文件", "参数", ..."参数" ]
使用shell格式写的内容会被解析为CMD ["sh", "-c", "命令"]
,所以命令中可以使用环境变量,但是命令必须前台执行,如果命令结束后直接结束了sh
程序,则容器也会推出。
Dockerfile中设置的启动命令可以在运行时重新指定,比如我们写docker run -it imagename
的话,就会执行默认的命令,在镜像名后面添加命令,比如cat etc/os-release
,就可以了。
ENTRYPOINT
跟CMD一样用来设置启动时的命令行和参数,不过在重新定义的时候,要在docker run
后面使用命令--entrypoint
来设置。
ENTRYPOINT和CMD共存的意义在于,定义了ENTRYPOINT之后,CMD的内容将不再被直接执行,而是作为参数传递给ENTRYPOINT。比如Docker入门-Dockerfile指令详解-NETRYPOINT入口点里面介绍的例子:
CMD [ "curl", "-s", "https://ip.cn" ]
添加参数,直接在镜像名后面加肯定是不行的,那样会替换掉所有命令(全部重写那就另当别论了),如果使用CMD来获取参数,定义ENTRYPOINT [ "curl", "-s", "https://ip.cn" ]
,就可以实现参数的自由设置了。用ENTRYPOINT来处理CMD做不到的工作。
1 | FROM alpine:3.4 |
docker-entrypoint.sh的内容为:
1 |
|
这里将cmd作为参数获取的方法,在ENTRYPOINT中设置了一个脚本,通过参数的内容来区别启动用户的身份。
ENV
定义环境变量,就这么简单。可以用=
,可以用空格,可以用等号加空格一行定义一群,也可以用\
加入换行。
ARG
定义参数。参数和环境变量不同的是,参数只在构建的时候有用,在容器运行时不会被引入容器内部。参数的定义可以在docker build
时使用--build-arg <name>=<value>
的格式来覆盖。
VOLUME
匿名卷
EXPOSE
暴露端口
WORKDIR
指定工作目录
USER
指定当前用户
HEALTHCHECK
健康检查
ONBUILD
构建时执行
DB2的免费版本在 2019年的社区版DB2 中可以找到,提供了三种免费版的使用方法:
这里记一下使用第一种方法Docker镜像的构建过程。
1 | docker run --name db2 --privileged -p 50000:50000 -e LICENSE=accept -e DB2INST1_PASSWORD=password -e DBNAME=MYDATABASE ibmcom/db2 |
简单解释一下
--name
容器的名字,以后启动关闭的时候用 --privileged
不知道(弄懂了再写)-p
端口映射,这里的意思是本地的50000端口映射到容器的50000,这样我们就可以通过localhost:50000
来访问容器里的数据库了-e
给容器里设置一些环境变量LICENSE
嗯。。。DB2INST1_PASSWORD
为默认的用户名db2inst1设置的密码。DBNAME
直接使用变量作为名字建立一个databaseibmcom/db2
是镜像名1 | url: jdbc:db2://localhost:50000/MYDATABASE |
驱动文件可以在IBM的服务网页找到 db2jcc4.jar
有时候会提示port无法使用,容器不能启动。先确定port50000没有被占用,如果可以使用,就重启docker。