jupyter notebook remote 远程访问

jupyter notebook --generate-config

>>> from notebook.auth import passwd

>>> passwd()

Enter password:

Verify password:

c.NotebookApp.ip='*'

c.NotebookApp.password = u'sha1:db239b9c0c83:3a39xxxxxxxxxx

c.NotebookApp.open_browser = False

c.NotebookApp.port =8888 #随便指定一个端口

Python 正则表达式

正则表达式语法在这里就不提及了,主要是熟悉一下python的api

import re;
 
abcRegex = re.compile(“abc”);
mo = abcRegex.search(“abc123”);
print(mo.group());

这个例子太简单了, group方法会返回abc, 语法上也和javascript的正则差不多

分组的写法是

import re;
 
abcRegex = re.compile(“(abc)(123)”);
mo = abcRegex.search(“abc123abc”);
print(mo.group(1));
print(mo.group(2));

输出是

abc
123

python的正则表达式默认是贪心模式

如果你要查找3个或者5个相同字符串,它会尽量匹配5个

hahaRegex = re.compile(“(ha){3,5}”);
mo = hahaRegex.search(“hahahahaha”);
print(mo.group());

上面的代码会给出

hahahahaha

要改成非贪心模式的话需要在表达式末尾加一个?

hahaRegex = re.compile(“(ha){3,5}?”);
mo = hahaRegex.search(“hahahahaha”);
print(mo.group());

结果为

hahaha

python还提供一种返回数组的findall方法

abcRegex = re.compile(“\d”);
mo = abcRegex.findall(“a1b2c3d4”);
print(mo);
[‘1’, ‘2’, ‘3’, ‘4’]

如果不想区分大小写的话可以使用 re.IGNORECASE 或者 re.I

abcRegex = re.compile(“B”,re.IGNORECASE);
mo = abcRegex.findall(“a1b2c3d4”);
print(mo);
[‘b’]

sub() 方法可以用来替换匹配的文本

abcRegex = re.compile(“B”,re.IGNORECASE);
mo = abcRegex.sub(“B”,’abcd’);
print(mo);
aBcd

The Tower of HANOI

汉诺塔问题经常被用于递归的教学,当然还有一个印度传说

在世界中心贝拿勒斯(在印度北部)的圣庙里,一块黄铜板上插着三根宝石针。印度教的主神梵天在创造世界的时候,在其中一根针上从下到上地穿好了由大到小的64片金片,这就是所谓的汉诺塔。不论白天黑夜,总有一个僧侣在按照下面的法则移动这些金片:一次只移动一片,不管在哪根针上,小片必须在大片上面。僧侣们预言,当所有的金片都从梵天穿好的那根针上移到另外一根针上时,世界就将在一声霹雳中消灭,而梵塔、庙宇和众生也都将同归于尽。

这里纯粹是无聊重新研究一下

先考虑一下”小”问题

先假设如果有1块disk,当然只需要1步就可以结束整个过程,我们用T1来表示总共需要的步数 T1 = 1

如果有2块disk, 很容易想到是 T2 = 3 (自己脑补一下过程)

3块的时候一共需要7步, T3 = 7 (还是自己脑补)

然后考虑”大”一点

按照前面的经验,

首先需要把除了最大的一个disk之外的所有disk都移动到中间  Tn-1

然后移动最大的一块, T1 ( 也就是1 )

然后把刚才中间的Tn-1这些disk移动到最大那个上面, 又是一个Tn-1

然后重复这个过程

也就是说移动 Tn = 2* (Tn-1) + 1

当然还有一个T0 = 0的情况

写成公式就是

Tn = 0                               ( n=0)

Tn = 2* (Tn-1) + 1        ( n>0)

递归只是帮助让问题变得简单,不过估计没人愿意用递归计算T100, 实际公式是

Tn=2-1

好吧这又是一个数学归纳法问题, 计算机才不会care计算有多麻烦,所以递归是最好的 ( 愚蠢的程序员和他的无聊程序啊 )

出于好奇这里稍微看一下归纳法是如何做的

先带入刚才T0那个公式, T0=20-1 显然是对的 结果是 0

然后试试n>0的情况, 将递归公式和归纳法得出的公式混合一下

将 Tn = 2* (Tn-1) + 1 [递归公式] 里边的Tn-1用Tn=2-1 [归纳法公式] 替换 

因为 Tn-1 = 2(n-1) -1  就是把n写成n-1

所以Tn = 2*(Tn-1 )+1 = 2*(2(n-1) -1) + 1 = 2n-1

所以n=100时, 你需要 2100-1 = 1267650600228229401496703205375 

用Python写个递归试一下看看结果对不对


def countStep(n):
    if n==0:
        return 0;
    else:
        return 2*countStep(n-1)+1
    
print(countStep(100));
1267650600228229401496703205375
 

Python 多进程 多线程

实际上Python比java提供了更激进的并发模式 (当然java也可以做到,不过没人愿意启动那么多虚拟机吧)

它使用多进程的方式来处理并发需求

下面是一个简单例子,看起来跟多线程都差不多

from multiprocessing import Process
import os

# 子进程要执行的代码
def run_proc(name):
    print ('Run child process %s (%s)...' % (name, os.getpid()));

if __name__=='__main__':
    print ('Parent process %s.' % os.getpid());
    p = Process(target=run_proc, args=('test',));
    print ('Process will start.');
    p.start();
    p.join();
    print ('Process end.');

然后我们来尝试一下上一个计算pi的例子,看看多进程模式下python的并发性能

from multiprocessing import Process
import os
from decimal import *;
import sys;
import math;
import time;

def bellard(n):
   pi=Decimal(0)
   k=0
   while k < n:
       pi+=(Decimal(-1)**k/(1024**k))*( Decimal(256)/(10*k+1)+Decimal(1)/(10*k+9)-Decimal(64)/(10*k+3)-Decimal(32)/(4*k+1)-Decimal(4)/(10*k+5)-Decimal(4)/(10*k+7)-Decimal(1)/(4*k+3))
       k+=1
   pi=pi*1/(2**6)
   return pi

def say(name):
    start = time.clock();
    for i in range(100):
        p = bellard(1000);
#        print("running..."+str(p));
    end = time.clock();
    print ("read: %f s" % (end - start));

def run_proc(name):
    print ('Run child process %s (%s)...' % (name, os.getpid()));

if __name__=='__main__':
    print ('Parent process %s.' % os.getpid());
    p = Process(target=say, args=('test',));
    print ('Process will start.');
    p.start();
    p.join();
    print ('Process end.');

单进程下结果

Parent process 8184.
Process will start.
read: 25.787227 s
Process end.

双进程稍微修改一下程序

from multiprocessing import Process
import os
from decimal import *;
import sys;
import math;
import time;

def bellard(n):
   pi=Decimal(0)
   k=0
   while k < n:
       pi+=(Decimal(-1)**k/(1024**k))*( Decimal(256)/(10*k+1)+Decimal(1)/(10*k+9)-Decimal(64)/(10*k+3)-Decimal(32)/(4*k+1)-Decimal(4)/(10*k+5)-Decimal(4)/(10*k+7)-Decimal(1)/(4*k+3))
       k+=1
   pi=pi*1/(2**6)
   return pi

def say(name):
    start = time.clock();
    for i in range(100):
        p = bellard(1000);
#        print("running..."+str(p));
    end = time.clock();
    print ("read: %f s" % (end - start));

def run_proc(name):
    print ('Run child process %s (%s)...' % (name, os.getpid()));

if __name__=='__main__':
    print ('Parent process %s.' % os.getpid());
    p = Process(target=say, args=('test',));
    p2 = Process(target=say, args=('test',));
    print ('Process will start.');
    p.start();
    p2.start();
#    p.join();
    print ('Process end.');
Parent process 7020.
Process will start.
Process end.
read: 26.193302 s
read: 26.187929 s
 

可以看到并没有消耗两倍的时间,是真实并发

4 进程消耗如下

Parent process 7796.
Process will start.
Process end.
read: 29.337095 s
read: 29.761048 s
read: 29.792244 s
read: 30.208371 s
 

最后8进程,在4核cpu上消耗了差不多两倍时间

Parent process 5844.
Process will start.
Process end.
read: 51.267404 s
read: 55.194120 s
read: 58.598105 s
read: 58.615099 s
read: 59.172344 s
read: 59.067506 s
read: 59.797957 s
read: 60.902044 s
 

Java Python 多线程区别 GIL 以及线程安全 内核级线程

threading包比thread提供的功能更全面,所以这里使用threading为例

不过本文不想过多讨论基础操作, 我比较好奇的是Python的GIL和线程安全问题(Java写多了)

import threading

def say(name):
    for i in range(5):
        print("from thread "+str(name));

t1 = threading.Thread(target=say,args=("1"));
t2 = threading.Thread(target=say,args=("2"));

t1.start();
t2.start();

上面这个例子会交替打印 message1 和message2 

我们稍微做一下改变,让这两个线程一直循环下去好观察cpu的占用率

import threading

def say(name):
    while(True):
        print("from thread "+str(name));

t1 = threading.Thread(target=say,args=("1"));
t2 = threading.Thread(target=say,args=("2"));

t1.start();
t2.start();

因为window7本身是内核级进程(也是大多数Linux现在的默认设置),所以可以很清楚的观察到两个线程被平均分配到了4个内核中(我的环境是i7 4内核)

程序启动以后,4个核心的占用率直接飙升

即使使用一个线程进行while(true)循环你也会看到4个内核占用率同时提高,因为操作系统使用的线程实际上是一种轻量级进程

我们来测试一下使用python多线程执行密集型运算

import threading;
import sys;
import math;
import time;
from decimal import *;

def bellard(n):
   pi=Decimal(0)
   k=0
   while k < n:
       pi+=(Decimal(-1)**k/(1024**k))*( Decimal(256)/(10*k+1)+Decimal(1)/(10*k+9)-Decimal(64)/(10*k+3)-Decimal(32)/(4*k+1)-Decimal(4)/(10*k+5)-Decimal(4)/(10*k+7)-Decimal(1)/(4*k+3))
       k+=1
   pi=pi*1/(2**6)
   return pi

def say(name):
    start = time.clock();
    for i in range(100):
        p = bellard(1000);
#        print("running..."+str(p));
    end = time.clock();
    print ("read: %f s" % (end - start));

t1 = threading.Thread(target=say,args=("1"));
t2 = threading.Thread(target=say,args=("2"));

t1.start();
t2.start();

双线程运行pi计算上面的代码会给出

read: 51.493309 s
read: 51.516034 s

改成单线程之后会得到如下结果

read: 24.826255 s

你会惊喜的发现时间缩短一倍, 那我们猜测如果3个线程的话会得到3倍左右的运算时间

read: 75.466136 s
read: 75.925743 s
read: 76.911878 s

Python使用一种叫做GIL ( 全局解释器锁 ) 的机制来调度线程

在主轮询中(这里鄙视一下某些书的翻译,把轮询翻译成循环) 同时只能有一个线程执行

有点像单核cpu执行多线程的模式,然后而又有点区别, java在单核cpu多线程时也需要考虑线程安全,因为有些变量是多线程之间共享的

Python这种一刀切的方式,干脆相当于在所有方法前都加上了 synchronized 同步

这里还有一个问题要强调一下, java 中 volatile 并不能保证变量是线程安全的

volatile 只是告诉虚拟机在每次使用这个变量时,去堆内存中重新读一下, 如果主内存变量发生修改之后,线程工作内存中的值由于已经加载,不会产生对应的变化

这里我们用java的多线程模型做对比

java没有GIL, 是真实的多线程,也就是说一个线程在4内核上需要10秒钟的运算,两个线程在4内核上也需要10秒,因为java可以真正同时使用两个内核进行运算(两个内核各运行10秒)

这里需要注意以下几点

1. 不要在循环中使用System.out.print输出任何字符,因为会造成IO消耗,等待时间大部分都是IO造成的,测试不准确

2. 不要使用惯用的sleep方法来测试,sleep方法只是计时器,不能造成cpu密集型运算

public class Test {
    static Runnable r = new Runnable(){
        @Override
        public void run() {
            long startTime=System.currentTimeMillis(); 
            for(int i=0;i<10000000;i++){
                cut(20L);
            }
            System.out.println("running...");
            long endTime=System.currentTimeMillis(); 
            System.out.println("程序运行时间: "+(endTime-startTime)+"ms");
        }
        
    };
    
    public static void main(String args[]) throws InterruptedException{
        
        Thread t1 = new Thread(r);
        Thread t2 = new Thread(r);
        t1.start();
        t2.start();
        
    }
    
    static void cut(Long n){  
        double y=1.0;  
        for(Long i=0L;i<=n;i++){  
            double π=3*Math.pow(2, i)*y;  
//            System.out.println("第"+i+"次切割,为正"+(6+6*i)+"边形,圆周率π≈"+π);  
            y=Math.sqrt(2-Math.sqrt(4-y*y));  
        }  
    }
}
 

上面的程序在调用两个线程时会给出下面的结果

running…
程序运行时间: 14933ms
running…
程序运行时间: 14937ms

 

在调用三个线程时会给出

running…
程序运行时间: 15639ms
running…
程序运行时间: 15654ms
running…
程序运行时间: 15689ms
 

调用四个线程

running…
程序运行时间: 16069ms
running…
程序运行时间: 16099ms
running…
程序运行时间: 16154ms
running…
程序运行时间: 16197ms

这个时候我的4内核cpu应该已经处于饱和状态了,最后我们尝试调用8个线程

running…
程序运行时间: 31569ms
running…
程序运行时间: 32132ms
running…
程序运行时间: 32156ms
running…
程序运行时间: 32166ms
running…
程序运行时间: 32132ms
running…
程序运行时间: 32208ms
running…
程序运行时间: 32370ms
running…
程序运行时间: 32321ms
 

正如所料,消耗了差不多4线程的两倍时间,因为我的cpu已经饱和了

java的jvm实际上只有一个进程,但是由于使用了内核级线程(轻量级进程),操作系统会把运算工作分发给4个内核同时运行,是真正的多线程

Python 文件操作

大多数情况下file()和open()可以互换

要一次性读取全部内容可以使用,如果不使用rsstrip()的话会发现每行输出之间会有一个空行,这是因为line本身带有空行+print()函数产生的空行

file = open(“data.txt”);
alllines = file.readlines();
for line in alllines:
    print(line.rstrip());
file.close();

虽然理论上只要内存够大,python可以直出文件的所有内容,不过还是推荐使用迭代器

file = open(“data.txt”);
for line in file:
    print(line.rstrip());
file.close();

加上try finally的话长成这样

try:
    file = open(“data.txt”);
    for line in file:
        print(line.rstrip());
finally:
    file.close();

使用with as的话可以避免忘记close

with所求值的对象必须有一个__enter__()方法,一个__exit__()方法

类似java的一种接口,使用过后对象会被自动释放

with open(“data.txt”) as file:
    for line in file:
        print(line.rstrip());
 

拷贝文件性能受影响最小(直接call操作系统命令)但是兼容性最差的方法是system包

import os;
os.system (“copy %s %s” % (“data.txt”, “data.txt2”));
os.system (“dir”);

Python 模块和类

python中的模块是扩展名为.py的文件

mymodule.py

def myfunction():
    print(“this is a function”);

exe.py

import com.module.mymodulecom.module.mymodule.myfunction();

上面的exe.py也可以写成

from com.module import mymodulemymodule.myfunction();

还可以更进一步,from指定module, import直接导入一个特定的function

from com.module.mymodule import myfunctionmyfunction();

使用import时可以给函数或者模块名起一个别名

from com.module.mymodule import myfunction as mf
import com.module.mymodule as mmmf();
mm.myfunction();

如果要掉入一个模块中的所有方法

from com.module.mymodule import *myfunction();

下面看看class

2.7 之前的版本你需要从object继承一个子类

class Dog(object):    def __init__(self, params):

3.0以后跟java差不多啦

class Dog():    def __init__(self, params):
       

我们在com.animal路径下创建一个Dog.py文件

class Dog():
    
    name = “”;
    age = 0;    def __init__(self, name, age):
        self.name = name;
        self.age = age;
        
    def say(self):
        print(“i am ” + self.name + ” and i am ” + str(self.age) + ” years old”);
       
 

在com.clazz目录下创建run.py

这里from身后是文件路径, import身后是类名, 写多了java这样看还是觉得别扭啊

Python的文件名和类名不一定非用一个

from com.animal.Dog import Dog;dog = Dog(“tom”,2);
dog.say();
 

我们再给Dog类创建一个子类

class Dog():
    
    name = “”;
    age = 0;    def __init__(self, name, age):
        self.name = name;
        self.age = age;
        
    def say(self):
        print(“i am ” + self.name + ” and i am ” + str(self.age) + ” years old”);
       
class BigDog(Dog):
    type=”big”;
    def __init__(self, name, age):
        super().__init__(name,age);
    
    

总觉得python的继承用起来怪怪的,尤其是它居然需要显性的调用父类的构造函数,我们知道java虽然也会在创建子类对象的时候先调用父类的构造函数不过它并不需要你明确的写出来,而是自己隐性的调用

from com.animal.DogFile import Dog;
from com.animal.DogFile import BigDog;dog = Dog(“tom”,2);
dog.say();bigDog = BigDog(“bigtom”,2);
bigDog.say();

如果是2.7版本的话需要使用这样的super

super(BigDog,self).__init__(name,age);