Python简介
- Python是一种动态解释型编程语言,在模块载入时将源码编译成字节码, 这些字节码被虚拟机PVM解释执行,其中解释执行是Python性能较低的主要原因;
- Python使用C语言编写,可以和C,C++,Java等语言结合使用:Java在Python上的实现Jython,具体参考:https://docs.python.org/2/tutorial/index.html,和.NET互通的IronPython,https://ironpython.codeplex.com/
编译执行
编译通常发生在模块载入的时候,编译器先将源码编译成字节码,保存在pyc文件中,如果使用-O参数,可生成pyo文件,是一种简单优化后的pyc文件;
对于pyc文件,编译过程如下:
- 核对文件Magic标记,Magic为一个特殊数字,由Python版本号计算得出,作为pyc文件检查的标记;
- 检查时间戳和源码文件修改时间是否相同,以确定是否需要重新编译;
- 载入模块;
对于py文件,编译过程如下:
- 对源码进行AST(抽象语法树)分析;
- 将分析结果编译成PyCodeObject;
- 将 Magic、源码⽂件修改时间、PyCodeObject 保存到 pyc 文件中;
- 载入模块;
执行可使用eval和exec函数,eval()用来执行一个表达式,exec用来执行一个代码片段,execfile()可动态执行一个py文件;
Pyhton基础与实践
主要介绍Python语言中一些基础但又很重要的知识,python对象、函数、类、正则表达式、编码转换等。
对象
一切皆对象
Python中一切皆为对象,每个对象都包含一个标准头,头信息由 "引用计数" 和 "类型指针" 组成。
"引用计数"为PVM中主要的垃圾回收机制,每当对象被引用时增加,超出作用域或调用del手工释放后递减,计数为0时被回收;
通过"类型指针"可明确知道对象的类型,指向Type对象,包含了其继承关系以及静态成员信息;
可使用sys.getrefcount(x)来查看对象的引用计数;
type(x)和x.__class__可查看对象类型;
hex(id(x))返回对象内存地址;
对于变长对象,其头部会多出一个记录元素数量的字段;
x=10
sys.getrefcount(x) #查看x引用计数,形参会增加一次计数; import types
types(x) #查看x的类型信息;
x.__class__ #__class__通过类型指针来获取对象类型;
对象类型
不可变类型:None,bool,int,float,long,complex,string,unicode,tuple,frozenset;
可变类型:list,dict,set
#bool
int(True) # 1
int(False) # 0 #float
'''float默认采用双精度,可能不能精确表示十进制小数'''
float('0.1')*5 == float('0.5') # False
round('2.675',2) # 2.67
#Decimal可精确控制位数,一般用Decimal代替float
from decimal import Decimal
Decimal('0.1')*5 == Decimal('0.5') # True #str,unicode
'''需要注意编码问题,使用decode(),encode()方法,一个项目中使用不同类型的编码,很容易造成错误'''
import logging
logger = logging.getLogger('test')
logger.info('%s is not %s' % (u'测试汉字', '测试汉字')) #Error,混用两种不同类型编码
#python2.x默认编码为ascii,一般情况下需要进行转码操作,大部分情况下,我们在Python脚本头部都会将默认编码设为utf-8
# -*- coding: utf-8 -*-
import sys
sys.getdefaultencoding() # ascii
#字符串拼接,一般情况下:join > '%s%s' > +,字符串前面加'r',表示不转义; #dict
#对于大字典,建议用迭代器代替keys(),values(),items()方法,降低内存开销
d={'a':'xxx'.....}
d.iterkeys(),d.itervalues(),d.iteritems()
#求两个dict差集
d1 = {'a':1, 'b':2}
d2 = {'b':2, 'c':3}
v1 = d1.viewitems()
v2 = d2.viewitems()
v1 & v2, v1 | v2, v1 - v2, vi ^ v2 # 相当于set
#dict是哈希表,默认是无序的,如需有序字典,可使用OrderedDict
from collections import OrderedDict
od = OrderedDict()
od['a'] = 'qqq'
od['b'] = 'bbb'
for k,v in od.items(): print k,v # 按添加顺序输出 #set
#判重公式: (a is b) or (hash(a) == hash(b) and eq(a,b))
#要将自定义类型放入set,需要重写__hash__和__eq__方法
class User(Object):
def __init__(self, name):
self.name = name def __hash__(self):
return hash(self.name) def __eq__(self, other):
if not other or not isinstance(other, User):
return false
return self.name == other.name
表达式
正则表达式
正则表达式基本在任何语言中都出现,对于开发来说,是必备的技能之一,Python正则表达式详见https://docs.python.org/2/library/re.html;
#IP地址匹配
import re
def check_ip(ip):
pattren = r'^(25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|[0-9])\.XXX$' #不全,剩下类似
if re.match(pattern, ip):
return True
return False #由zimbra邮箱同步数据到AD的脚本中正则表达式应用实例:
def get_mail_attrs(self, mail_info):
pattern = r'^cn:|^displayName|^mail:|^zimbraACE:.*ownDistList$|^(?!zimbra).*@'
attrs = {}
attrs['managedBy'] = []
split_info = mail_info.split('\n')
for line in split_info:
if re.match(pattern, line):
item = line.strip().split(': ')
if item[0] == 'zimbraACE':
_id = item[1].split()[0]
if _id in self.id_mail:
_mail = self.id_mail[_id]
attrs['managedBy'].append(_mail)
else:
print 'Error, %s has no mail' % _id
elif len(item) > 1:
attrs[item[0]] = item[1]
else:
if 'members' not in attrs:
attrs['members'] = []
attrs['members'].append(item[0])
#在字符串中使用正则表达式,如果不使用'r'标记,需要使用双重转义;
语句
注意循环和异常中的else,如:while...else...,for...else...,try...except...else...finally...,else在循环正常退出时执行,在except没有捕捉到异常时执行;
列表推导式
#举例说明列表推导式的应用
def test(x):
return x**2
a = [test(x) for x in xrange(10) if x%2]
b = [text(x) for x in open('number.txt', 'r')]
#生成字典
c = {c:ord(c) for c in 'abc'}
d = {d:chr(d) for d in '123'} #enumerate使用
for idx,num in enumerate([x for x in range(10)]):
print "index[{0}] = {1}".format(idx, num)
函数
函数在同一名字空间中是不能重载的(由 __dict__[name] 唯一性决定),且总是有返回值,默认返回None;
引用传递
Python中都是按引用传递的,不管是赋值还是函数传参,不管是可变类型还是不可变类型;
按引用传递与按值传递的区别:引用传递传递的是内存地址,而值传递传递的是当前值的一个副本;
a = 10
b = 10
hex(id(a)) == hex(id(b)) #True
a is b #True
#函数参数传递,需要注意顺序,默认参数必须在位置参数之后,*args和**kwargs必须在默认参数之后;
def test(a, b, c=10, d='ww', *args, **kwargs):
print args,kwargs
#如默认参数是使用def创建函数时生成的对象,以后调用函数会复用此对象,如第一次调用test后,a为[1],第二次调用后,a为[1,1];
def test(a=[]):
a.append(1)
print hex(id(a))
print a #对于可变对象,可使用deepcopy来进行深度复制,copy.deepcopy(x);
名字空间
python名字空间可理解为系统和自定义对象的name/object字典;
Python变量是一个字符串对象,它和目标对象一起在名字空间中构成一个 name/object 关联项,名字空间决定了对象的作用域和生存周期;
变量没有类型,对象有,变量的作用仅仅是在某个时刻与名字空间中的目标对象进行关联;
globals()获取模块级别名字空间,locals()获取当前上下文名字空间;
可以通过<module>.__dict__访问其他模块的名字空间;
对象查询顺序遵循LEGB原则,即locals -> enclosing -> globals -> __builtins__
常用函数
Python有许多常用函数可直接调用,如lambda,map,zip等,具体使用不再详细介绍。
类
Python 2.X版本中类模型有两种,Classic Class和New-Style Class,Classic Class支持多继承,但已被Python 3.0抛弃,大家在使用类时建议多使用New-Style Class,即继承object的类。
类中访问成员时,成员查找顺序: instance.__dict__ -> class.__dict__ -> baseclass.__dict__;
Python的内置类方法:getattr(obj, name[, default]),setattr(obj, name, value),hasattr(obj, name),delattr(obj, name);
Python的内置类属性:__dict__,__name__,__doc__,__module__,__bases__等;
属性
Python内置函数property()可以实现属性;
class Test(object): def get_name(self):
return self.__name
def set_name(self, value):
self.__name = value
def del_name(self):
del self.__name name = property(get_name, set_name, del_name) class Test2(object): user_id = property(lambda self: self._user_id)
user_name = property(lambda self: self._user_id, lambda self,value: setattr(self, '_user_name', value))
垃圾回收
JSON与time模块
#JSON与Python对象之间的转换
json.dumps(x) #encoding的过程,将Python对象x编码转换为JSON字符串
json.loads(x) #decoding的过程,将JSON字符串x解码转换为Python对象 #time模块
#Python有两种表示时间的方式,时间戳以及数组形式。
#常用函数
time.clock() #第一次执行获取当前程序的执行时间,之后获取从第一次到当前程序运行所花费时间;
time.sleep() #单位为秒
time.ctime() #将时间戳转换为时间字符串
time.localtime([seconds]) #将时间戳转换为当前时区的数组形式
time.mktime(tuple) #将数组形式time对象转换为时间戳
time.strftime(format[, tuple]) #将数组形式time对象转换为指定格式化形式
time.strptime(string, format) #将时间字符串根据指定格式转换为数组形式time对象
time.time() #获取当前时间的时间戳 #获取当前文件所在绝对路径
os.path.dirname(os.path.abspath(__file__))