对python的语言特性,多线程机制,以及性能局限的梳理
运行环境
由于Python不同版本,尤其是Python2与Pyhton3之间差异明显,所以运行不同项目时往往需要不同版本的运行环境,这种情况下,就需要能快速切换版本的运行环境
现在存在着virtualenv pyenv conda anaconda等虚拟环境
Anaconda 一个科学计算环境,Python的发行版本 :包括了Conda
Conda --包和虚拟环境管理工具
virtualenv 轻量级第三方虚拟环境管理工具,没有Anaconda好用
pyenv python版本管理工具
PythonicPythonic就是以Python的方式写出简洁优美的代码
上下文管理器
上下文管理器就算在想要执行的目标代码前做一些预处理工作,然后再目标代码执行后,做一些后续扫尾工作 在上下文管理协议中,有两个方法enter和exit,分别实现上述两个功能。 使用with语句,以及一个支持上下文协议的对象,就可以使用上下文管理器装饰器contextmanager 将一个函数中yield语句之前的代码当做enter方法执行,yield语句之后的代码当做exit方法执行。同时yield返回值赋值给as后的变量。
@contextlib.contextmanager
def open_func(file_name):
# __enter__方法
print('open file:', file_name, 'in __enter__')
file_handler = open(file_name, 'r')
yield file_handler
# __exit__方法
print('close file:', file_name, 'in __exit__')
file_handler.close()
return
with open_func('python_base.py') as file_in:
for line in file_in:
print(line)
装饰器
装饰器实际上就是给其他函数和类附加额外功能 值得注意的是使用装饰器后代码的执行顺序装饰器的逻辑是,将被装饰的函数传入装饰器,返回一个装饰器的函数对象。在这个对象里面猜实际执行被装饰的方法
下面的例子,把foo函数传入的装饰器函数中去,装饰器函数返回一个可执行的wrapper函数,wrapper函数里面再执行foo函数
import logging
def use_logging(func):
def wrapper(*args, **kwargs):
logging.warn("%sis running" % func.__name__)
return func(*args, **kwargs) # 把 foo 当做参数传递进来时,执行func()就相当于执行foo()
return wrapper
@use_logging # 装饰器的逻辑是,把foo函数传入的装饰器函数中去,装饰器函数返回一个可执行的wrapper函数,wrapper函数里面再执行foo函数
def foo():
print("i am foo")
foo()
生成器
生成器与列表推导有点类似,区别在于,列表推导一开始就执行完计算,返回的就是一个列表,而生成器返回的是一个生成器对象,里面保存的是生产数据的算法,可以步进的执行里面的逻辑
>>> L = [x * x for x in range(10)] # 列表
>>> L
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
>>> g = (x * x for x in range(10)) # 生成器对象
>>> g
at 0x7fbea411e9a8>
可以用next(g)来获得生成器的下一次结果,但一般使用迭代,generator也是可迭代对象
>>> g = (x * x for x in range(10))
>>> for n in g:
... print(n)
...
0
1
4
...
可以使用yield将函数变成生成器
def fib(max):
n, a, b = 0, 0, 1
while n < max:
yield b
a, b = b, a + b
n = n + 1
return 'done'
>>> f = fib(6)
>>> f
描述符使用描述符的类中,多次实例化描述,不同变量保存的描述符是独立的,但是类被多次实例化的话,不同实例的同名描述符变量之间是共享实例的 下面的例子说明,对描述符变量的读写都会被转接到对象内部的get与set方法
from weakref import WeakKeyDictionary
class NonNegative(object):
def __init__(self, default):
self.default = default
self.data = WeakKeyDictionary()
self.value = default
def __get__(self, instance, owner):
return self.data.get(instance, self.default)
def __set__(self, instance, value):
if value < 0:
raise ValueError("Negative value not allowed:%s" % value)
self.data[instance] = value
class Movie(object):
View = NonNegative(0)
def __init__(self, View):
self.View = View
def profit(self):
return self.View +1
m = Movie(9)
#
print(m.View) # calls Movie.View.__get__(m, Movie)
m.View = 100 # calls Movie.View.__set__(m, 100)
元类
python中类不但可以创建对象,而且本身就是一个对象 (python这种类的实现方式,感觉有点想javascirpt)可以像操作普通对象一样操作类对象
>>> class ObjectCreator(object):
... pass
...
>>> ObjectCreator
>>> ObjectCreator.new_attribute = 'foo' # 添加属性
>>> print(ObjectCreator.new_attribute)
foo
>>> ObjectCreatorMirror = ObjectCreator # 赋值给其他变量
>>> print(ObjectCreatorMirror)
动态地创建类 type有一种完全不同的能力,它也能动态的创建类。type可以接受一个类的描述作为参数,然后返回一个类
# 使用方式
type(类名, 父类的元组(针对继承的情况,可以为空),包含属性的字典(名称和值))
例如这样的类定义
>>> class MyShinyClass(object):
… bar = True
可以用type来实现,type 接受一个字典来为类定义属性
>>> MyShinyClass = type('MyShinyClass', (), {'bar':True}) # 返回一个类对象
>>> print MyShinyClass
>>> print MyShinyClass() # 创建一个该类的实例
<__main__.MyShinyClass object at 0x8997cec>元类可以理解为创建类的类,主要目的是为了当创建类时能够自动地改变类。
Python中所有的东西都是对象。包括整数、字符串、函数以及类。而且它们都是从元类type衍生而来
>>> language = 'Python'
>>> language.__class__
>>> language.__class__.__class__
元类的实际应用 metaclass属性 metaclass属性 可以指定使用什么元类来创建当前类对象,它可以设置在类中,也可以在设置在模块这一层级上
class Foo(object):
__metaclass__ = something…自定义元类
# 元类会自动将你通常传给‘type’的参数作为自己的参数传入
def upper_attr(future_class_name, future_class_parents, future_class_attr):
'''返回一个类对象,将属性都转为大写形式'''
# 选择所有不以'__'开头的属性
attrs = ((name, value) for name, value in future_class_attr.items() if not name.startswith('__'))
# 将它们转为大写形式
uppercase_attr = dict((name.upper(), value) for name, value in attrs)
# 通过'type'来做类对象的创建
return type(future_class_name, future_class_parents, uppercase_attr)
__metaclass__ = upper_attr # 这会作用到这个模块中的所有类
class Foo(object):
# 我们也可以只在这里定义__metaclass__,这样就只会作用于这个类中
bar = 'bip'
print hasattr(Foo, 'bar')
# 输出: False
print hasattr(Foo, 'BAR')
# 输出:True
f = Foo()
print f.BAR
# 输出:'bip'
其他python的特殊语法:变量交换
a, b = b, a # 快速交换变量值列表推导 这个其实类似于生成器了,只是会直接进行计算,返回计算结果
[ i*i for i in range(30, 41) if i% 2 == 0 ]
代码组织与包管理机制
Python工程目录结构python 引入模块要注意避免循环引用
包 :只要一个文件夹下面有个 init.py 文件,那么这个文件夹就可以看做是一个包
python使用pip进行包管理,包下载后,当前版本的执行环境下都可以进行引用,这区别与php comporsor 将包引入项目内部TODO 与go,php composor的包管理机制的对比
数据结构
python中的元组 tuple,可以认为是一种特殊的列表,只是一经创建,内容不可修改
tup1 = ('physics', 'chemistry', 1997, 2000)
不过对于tuple中的引用类型数据(借用静态语言的概念),在不修改引用本身的情况下,是可以对这个引用内部的数据进行修改的 tuple中相当于保存的是指针,无需改变指针,就可以对指向的数据进行修改
>>> t = ('a', 'b', ['A', 'B'])
>>> t[2][0] = 'X'
>>> t[2][1] = 'Y'
>>> t
('a', 'b', ['X', 'Y'])
多线程编程
python的多线程编程会用到一些内置特性来进行线程间通信,与并发安全控制(虽然默认的CPython不会真正的并行,但是在多个线程间切换执行,仍绕会面临并发安全问题) 线程间通信 event与queue,锁 线程安全 -锁机制子线程启动后,
有join,主线程阻塞在join的位置
没有join,主线程与子线程并发执行,主线程执行完所有逻辑后,子线程仍然能继续执行(主线程执行完退出) 下面的例子,在多个线程操作同一变量时使用锁来保证线程安全
import time, threading
from threading import Thread
num = 0
lock = threading.Lock() #使用锁来协调线程的并发执行
class tWork(Thread):
def __init__(self,step=0):
Thread.__init__(self)
self.step = step
def run(self):
global num
for i in range(10):
lock.acquire() # 先要获取锁:
print(str(num) + '+'+str(self.step))
num = num+self.step
print(str(num) + '-' + str(self.step))
num = num-self.step
lock.release()
threadSer = tWork(5)
threadSer2 = tWork(8)
threadSer.start()
threadSer2.start()
解释器,多线程GIL 与性能优化
python的性能一直挺受诟病,一个是默认的cpyton解释器的执行效率确实不高,另外一个就是鼎鼎大名的GIL全局锁导致python的多线程直接是互斥执行的,同一时间只会有一个线程在执行解释器慢的问题,有 Pypy, Jython等第三方解释器 其中pypy的性能优化做的挺不错,使用了JIT,我的实测对比,pypy的速度大致是cpython的5倍,另外由于使用了JIT,对一个变量不停的追加内容,会导致明显的性能下降 Jython则是在jvm中执行python,看到一些测评,性能并不如意
CPython 使用带condition的互斥锁来实现GIL,并且在线程执行碰到阻塞时,会释放锁,交给其他线程继续支持
针对CPython解释器GIL的问题 Jython与IronPython中实现了真正的多线程并发,PyPy也有独立的分支版本PyPy-stm来支持多线程 从单核性能来看,首选pypy来执行,考虑多线程的时候,可以使用PyPy-stm更多实例代码可以我的github项目 wosiwo/pythonic
参考