Why Python
本文默认有一定java基础或者深入理解java更好,首次在工作中接触python的第一个疑问就是,为什么用python,和java对比如何?
两种语言核心对比点是:
对比项目 | JAVA | PYTHON | 总结 |
---|---|---|---|
语言类型(对变量的处理方式) | 静态类型:一旦声明类型,不可动态更改 | 动态类型:不必声明类型,python根据运行时的值进行类型指定,同样可以string替换一个integer。 | 关于静态类型和动态类型相关的争论已经持续很久了,有一个比较有意思的谈话是关于 Guido van Rossum关于Python运行时、类型方面的思考 Strong versus Weak Typing。主要讨论还是集中在开发效率、运行效率和企业级应用的构建上。Python胜在原型构建速度上,java胜在运行效率和企业级应用上。 |
原型构建速度和系统运行效率 | Java运行效率更高 | Python的产品构架你速度更高 | 在许多研究结论中都可以得出一个结论:Python比Java的生产效率高出两倍。(1)Python为什么会比Java构建效率高可以深入参考Python速度虽然慢,但它工作效率高啊。(2)Python运行效率为什么低,有篇好文分析的很深入why python is slow |
JVM(HotSpot)和Python解释(CPython) | Java现阶段比较流行的虚拟机是HotSpot(包括了一个解释器和两个编译器) | Cpython解释器是实现python的其中的一个版本,也是现在官方的、最流行的版本 | Java和Python的实现都依托于各自的解释器和编译器。JVM虚拟机有自己很完善的架构并且在运行前进行编译成Bytecode并在运行时解释成机器语言。Cpython是在运行期间将Python解释成机器语言在效率上要慢一些 |
在Python支持者的观点中有一点非常好:要准确的定位到自身产品和系统的瓶颈。如今的计算机和网络,在系统的前期甚至是中后期,运行效率和伸缩复用性一般都不会成为系统的瓶颈,往往业务上的速度至关重要。
Python 安装
- http://www.python.org/download/下载2.7版本的安装包
- 安装python
- 把python的安装目录添加到path系统变量中
Python & Java Side by Side基础
输出和运行
Java
public class HelloWorld { public static void main(String[] args) { System.out.println("Hello World"); } } 运行:javac HelloWorld.java java HelloWorld
Python
print 'Hello World' 运行:python HelloWorld.python
变量(数值,字符,列表,字典)和赋值
Java
Java是静态类型语言,必须制定变量类型
Integer a = 10; String b = "abc"; List<String> c = Arrays.asList(1,2,3,4); Map<String, String> d = new HashMap<>(); d.put("key","value");
Python
Python是动态类型语言,动态类型语言,运行期间制定类型
a = 10 b = "abc" c = [1, 2, 3, 4] d = {'key': 'value', 'key1': 'value1'} print type(a) print type(b) print type(c) print type(d) 输出:<type 'int'> <type 'str'> <type 'list'> <type 'dict'>
代码块(if条件实例)
Java
Java中if、class、while、for、方法等都是通过“{}”大括号进行开始和结尾的,进行代码块区分
if (a == b) { System.out.println("a equals b"); } else if (a > b){ System.out.println("a is bigger"); } else { System.out.println("b is bigger"); }
Python
Python中的if、class、def(方法)、for等这样的复合语句以关键字开始,冒号结束。同时通过4个空格进行缩紧区分代码块
if a == b: print "a equals b" elif a > b: print "a is bigger" else: print "b is bigger"
循环
Java
for (Integer one : a) { System.out.println(a); } for (Map.Entry<String, String> entry : b.entrySet()) { System.out.println(entry.getKey()); }
Python
for one in c: print one for one in d: print one
文件操作
Java
File file = new File(filePath); InputStream in = null; try { System.out.println("以字节为单位读取文件内容,一次读一个字节:"); // 一次读一个字节 in = new FileInputStream(file); int tempbyte; while ((tempbyte = in.read()) != -1) { System.out.write(tempbyte); } in.close(); } catch (IOException e) { e.printStackTrace(); return; }
Python
myFile = open(filePath)
print myFile.read()
函数
Java
public static Integer functionName(Integer a, Integer b) { return a + b; } public static void main(String[] args) { functionName(1, 2) }
Python
python方法的命名以下划线分割
def function_name(code=None, type=None): return code + type if __name__ == '__main__': r = function_name(code = 1, type = 2) print r
类
Java
public class MyClass { public Integer functionName(Integer a, Integer b) { return a + b; } public static void main(String[] args) { MyClass myClass = new MyClass(); myClass.functionName(1, 2); } }
Python
python并不像Java一样强制面向对象创建类,可以不进行类的创建
class MyClass(object): def __init__(self, code=None): self.code = code print 'created a instance' def show_code(self): print self.code if __name__ == '__main__': my_class = MyClass(1) my_class.show_code()
模块
python
python中的模块是将代码分成有组织的代码段,一个文件可以被看成一个独立的模块。模块的文件名就是模块的名字加上扩展名.py
>>> import mymoudle 输出: Traceback (most recent call last): File "<stdin>", line 1, in <module> ImportError: No module named mymoudle
python查找模块的路径
>>> import sys >>> sys.path 输出: ['', '/Library/Python/2.7/site-packages/pip-9.0.1-py2.7.egg', '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python27.zip', '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7', '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/plat-darwin', '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/plat-mac', '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/plat-mac/lib-scriptpackages', '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/lib-tk', '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/lib-old', '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/lib-dynload', '/Library/Python/2.7/site-packages', '/System/Library/Frameworks/Python.framework/Versions/2.7/Extras/lib/python', '/System/Library/Frameworks/Python.framework/Versions/2.7/Extras/lib/python/PyObjC']
以上输出结果就是python默认查找模块的地址,可以把自己的mymoudle放到当前目录就可以进行import
mymoudle.py def sum(a, b): print a + b >>> import mymoudle >>> mymoudle.my_sum(1, 2) 3
标准类型分类
Python标准类型可以分为:数值、字符串、列表、元组合字典
- 按照存储模型分类(能存储多少对象)
分类 | Python 类型 |
---|---|
原子类型 | 数值、字符串 |
容器类型 | 列表、元组、字典 |
* 按照更新类型分类(对象创建后它的值是否可以改变)
分类 | Python 类型 |
---|---|
不可变类型 | 数值、字符串 |
可变类型 | 列表、元组、字典 |
“`
a = 1
id(a)
140718964233288
a = 2
id(a)
140718964233264
str = ‘xxxx’
id(str)
4459015312
str = ‘aaaa’
id(str)
4459015504
既然a可以被重新赋值,那为什么说是不可变类型。原因就像输出一样,实际是新创建了一个对象,而不是修改原有对象的值
“`
“`
alist = [1, 2, 3]
alist[2]
3
id(alist)
4458858400
alist[2] = alist[2] + 1
alist
[1, 2, 4]
id(alist)
4458858400
对象的值可以进行修改
“`
“`
a=256
b=256
id(a)
140718964239024
id(b)
140718964239024
a=257
b=257
id(a)
140718964266976
id(b)
140718964267072
Python和Java一样会对常用的int数值进行缓存。Python缓存到256
“`
- 综上:在方法中修改不可变类型的参数时,方法外并不会体现**
Python高级主题
CPython(Python 解释器)
一说到编译器和解释器,Java虚拟机总是有很多说不完的话题如内存模型、垃圾回收、类加载和优化等,但是Python的解释器CPython甚至在许多Python的书中没有一个小节去介绍。
多线程和锁
创建多线程
import threading
def thread_my():
thread = threading.current_thread()
print 'thread name --'
print thread.getName()
t = threading.Thread(target=thread_my)
t.start()
thread_my()
t.join()
锁的使用
经典的 n += 1问题看看Python用锁怎么处理
import threading
n = 0
lock = threading.Lock()
def foo():
global n
with lock:
n += 1
threads = []
for i in range(100):
t = threading.Thread(target=foo)
threads.append(t)
for t in threads:
t.start()
for t in threads:
t.join()
print(n)
尴尬的锁
线程和锁在原生Python中概念非常弱化甚至有些不友好。基本上源于CPython的全局解释器锁(GIL),它的设计目的是保证同一时刻只能有一个线程在运行。在IO比较密集的任务的单核机器略显吃力。
static PyThread_type_lock interpreter_lock = 0; /* This is the GIL */
上面是Cpython的 ceval.c的一段源码,解释器中所有代码执行的时候都需要保持这个锁。
那么这个锁什么时候释放那?
- 一个线程无论何时开始睡眠或等待网络 I/O其他线程总有机会获取 GIL 执行 Python 代码,这是协同式多任务处理
- 如果一个线程不间断地在 Python 2 中运行 1000 字节码指令,或者不间断地在 Python 3 运行15 毫秒,那么它便会放弃 GIL,这是抢占式多任务处理
可以把Python想象成2000年单核cpu的年代的机器,多个任务抢占一个cpu,而Python是多个线程抢占一个GIL
为什么说原生Python在多核任务中略显吃力
def dead_loop():
while True:
pass
dead_loop()
上面这段代码如果跑在双核cpu的机器上,会发现只是占用50%的cpu。那按照Java的思路如何跑满两个核,ok再创建一个线程继续跑
import threading
def dead_loop():
while True:
pass
t = threading.Thread(target=dead_loop)
t.start()
dead_loop()
t.join()
但是问题出现了,效果还是只有50%的占用率,并没有跑满两个核,这里面的原因就是因为GIL。为什么Python作者这么设计,Python中没有去掉GIL。去掉全局锁必然会带来细粒度的锁或者Lock-free模式,在1999年做过一个分支去掉了GIL,但是在测试过程中发现单线程的效率降低了两倍,并且随着cpu的增加效率的提升并不是线性的,而是非常缓慢,可以看下开发者的Greg’s写的信 Gregs Free threading。
Python的并行
因为存在着GIL,导致Python的线程不能实现真正意义上的并行,但是也并不是没有办法处理
- 采用多进程处理,这样失去了线程间的共享变量和通信的便利了
- 通过ctypes引入c代码来实现真正意义上的并行
Web编程
Web系统架构
- 一般网络架构从上层到下层包括:DNS服务器->LVS服务器->应用容器->应用系统
- 在开发过程中只需要关注应用容器和应用系统就好,在Java中应用容器有Resin、Tomcat、Jetty等。应用系统框架有EJB、Spring、Struts等
- Python中应用系统框架有Django、Flask、Tornado。Python中没有容器的概念,但是大同小异Python把容器拆成公共规范(WSGI)和中间件,而公共规范和中间件加一起所做的事情又非常类似Java中的容器(servlet容器和连接器)。servlet容器约定了调用service()方法,应用框架或者程序需要实现servie(),连接器负责包装request和response给servlet容器。同样的WSGI里面预定了相应的应用框架和程序需要时间的方法,而中间件负责将request和response进行封装
- WSGI 的全称是Web Server Gateway Interface,他是一个规范。它所做的事情和servlet类似,让应用程序可以很方便的在各种应用容器上进行迁移,不必因为用了某个特定容器而使用指定的应用框架,也不必因为用了某个特定框架而实用指定容器。想要深入了解可以看下PEP333。还有中文介绍理解WSGI
Python应用框架
Instagram是Python的重度依赖用户,Instagram 的总注册用户达到 30 亿,月活用户超过 7 亿 (作为对比,微信最新披露的月活跃用户为 9.38 亿)。而令人吃惊的是,这么高的访问量背后,竟完全是由以速度慢著称的 Python + Django 支撑
Flask & Django
业界比较成熟的两个框架,有一篇关于两者对比的文章Flask VS Django,主要阐述了Flask小而灵活,只包含了必要的模块。而Django大而全,比较臃肿,会默认引入许多模块
Flask实现简单服务
安装pip(官网标准http://flask.pocoo.org/docs/0.12/installation/#installation)
windows步骤:
(1)下载https://bootstrap.pypa.io/get-pip.py
(2)执行python get-pip.py install
(3)将Python27\Scripts加入到环境变量
(4)pip install Flask
(5)创建hello.py
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello_world():
return 'Hello, World!'
$ set FLASK_APP=hello.py
$ flask run
访问http://127.0.0.1:5000/