标签归档:原创

wordpress启用WP Super Subdomains后子域名登录失效

wordpress启用WP Super Subdomains后子域名登录失效

很开心,终于搞定了WP Super Subdomains。
虽然为了启用它,修改了固定链接,折腾了半小时,又把旧链接跳转到了新链接。
但是除了www.go2live.cn有登录状态,其它几个子域名都是未登录状态。
要编辑文章的时候也很无赖。查看源代码找到了http://www.go2live.cn/?p=id 的短链接,然后把这个id拷贝到编辑页面,就这样勉强过去了。。

这么玩了几天,突然想到,用户不是登录不了,不能发评论了,这可要不得。赶紧百度一下。终于解决了。涉及到两个文件。

1.在wp-config.php加入下面两行代码。

define('COOKIE_DOMAIN', 'go2live.cn');  #这里要顶级域名
define('COOKIEPATH', '/');

2.修改wp-includes/default-constants.php文件

$siteurl = get_site_option( 'siteurl' );#这个东东在后面配置的。按WP Super Subdomains的要求需要是www.go2live.cn
$siteurl = 'go2live.cn'; #重新设置成了go2live.cn

搞定,开心。又可以直接从文章页进去编辑了。

wordpress修改了固定链接后404怎么办?

wordpress修改了固定链接后404怎么办?

今天修改了下固定链接做成了多域名。
原来的固定链接http://go2live.cn/archives/191637.html

现在对应的页面是http://dev.go2live.cn/python/python%E5%AD%A6%E4%B9%A0%E6%89%8B%E5%86%8C.html。

还没有研究过wordpress的api。
查了下404的页面是在 wp-content/themes/你的主题/404.php

我的页面是

<?php get_header(); ?>

<div class="content-wrap">
        <div class="content">
                <?php hui_404() ?>
        </div>
</div>

<?php get_footer(); ?>

代码好简单。就是显示了下找不到内容,然后一个链接回首页。
观察到新的页面和id没有关系,是不可能通过apache或者nginx的rewrite直接重定向过去的。
只有写代码实现了。

今天编辑文章的时候注意到一个短链接形式 http://www.go2live.cn/?p=191637
然后这个链接会跳转到http://dev.go2live.cn/python/python%E5%AD%A6%E4%B9%A0%E6%89%8B%E5%86%8C.html。

好事,这样省略了我根据id去生成链接的过程,毕竟我还没有研究过wordpress api。
办法找到了。

1. http://go2live.cn/archives/191637.html
2. http://www.go2live.cn/?p=191637
3. http://dev.go2live.cn/python/python%E5%AD%A6%E4%B9%A0%E6%89%8B%E5%86%8C.html

很简单的解决方案,直接从链接1中正则解析出id,然后拼成链接2,跳转到链接2,就会重定向到链接3.
达到目的。

从Dash上搜了下redirect,发现真有一个函数wp_redirect做url跳转的事。
最后的代码:

<?php get_header(); ?>

<div class="content-wrap">
        <div class="content">
                <?php 
                if (preg_match('~/archives/(\d*)\.html.*~',$_SERVER['REQUEST_URI'],$matches))
                {   
                $postid = $matches[1];  
                wp_redirect( get_bloginfo('url').'?p='.$postid );
                exit;
                }   
                ?>  
                <?php hui_404() ?>
        </div>
</div>

<?php get_footer(); ?>

改固定链接容易,但是之前的链接可就全404了,这对seo来说可不是好事。。找办法重定向回正确的地址吧。
我干了两次这种事。
第一次通过rewrite规则解决。
第二次改写404.php解决。

文章简单,希望能帮助到你。

python学习手册

python学习手册

前言

还没有系统学习语法,这是我选择的系统学习语法的书。因为之前已经看过4本python的书。在摘抄的时候,过于基本的就不写了。做为一个vim党,idle之类的内容我都略过了。

评价

这是我目前为止看过内容最丰富的python书。以python2.6和python3.0为基础讲解的,并且对比了两个差别。适合大概浏览后放在桌上的参考书。本书定位为基础书籍。讲得比较细,但是对有经验的程序员来说,会感觉很啰嗦,该略过就略过吧,没啥损失。看过这本书之后,之前写代码和看代码时的疑惑都得到了解答。_^

学语法,这本书就够了。
要写出程序,还要看《标准库》。特定领域的功能得熟悉特定领域的模块。
要写网站,可能还得看django或flask等框架。
程序结构要好,易于扩展,要看设计模式。
值得买一本放到桌上:https://s.click.taobao.com/Nq5KsAx

内容

第一部分

第1章 问答部分(可不看)

下面是我个人写的,和书无关:
我选择python的原因比较简单,python的代码量少,很方便地用于学习研究。嗯,我喜欢研究。暂时还是熟悉工具的过程:
我学习python的书单有:

  • 《Flask Web开发 基于Python的Web应用开发实战》
  • 《python绝技:运用python成为顶级黑客》
  • 《python标准库》
  • 《python学习手册》
  • 《python算法》
  • 《python编程实战》
  • 《effective python》
  • 《python cookbook》
  • 《python 高级编程》
  • 《python 核心编程》
  • 《python自动化运维》

看完之后开始阅读源码,先把flask源码看完。

第2章 Python如何运行程序

python filenam.py 执行程序。深入点看下面。
执行过程:

  1. python内部会先将源码编译成字节码。生成.pyc文件。
    ps1:在有写入权限时,.pyc会在磁盘上,否则是在内存中,在磁盘上的pyc可以当成一种缓存,可以直接运行,因为省略了编译过程,执行速度更快。并且在源文件更新过后,.pyc也会更新。
    ps2:字节码并不是机器二进制码,只是特定于python的一种表现形式,这也是python能跨平台,执行速度又没有c/c++快的原因。
  2. python虚拟机(pvm)载入字节码并执行。其实并不存在这样一个专门的pvm, pvm是python本身的一部分,用来执行节字码的一个大循环。

简单描述:
m.py(源文件)—>m.pyc(字节码)—>pvm(运行时)

第3章 如何运行程序

4种方式:
1. 交互模式。直接键入python。 主要是用来测试的。譬如我忘记os模块有哪些功能了。

Python 2.7.12 (default, Nov  4 2016, 00:00:52)
[GCC 4.2.1 Compatible Apple LLVM 8.0.0 (clang-800.0.38)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import os
>>> dir(os)
['EX_CANTCREAT', 'EX_CONFIG', 'EX_DATAERR', 'EX_IOERR', 'EX_NOHOST', 'EX_NOINPUT', 'EX_NOPERM', 'EX_NOUSER', 'EX_OK', 'EX_OSERR', 'EX_OSFILE', 'EX_PROTOCOL', 'EX_SOFTWARE', 'EX_TEMPFAIL', 'EX_UNAVAILABLE', 'EX_USAGE', 'F_OK', 'NGROUPS_MAX', 'O_APPEND', 'O_ASYNC', 'O_CREAT', 'O_DIRECTORY', 'O_DSYNC', 'O_EXCL', 'O_EXLOCK', 'O_NDELAY', 'O_NOCTTY', 'O_NOFOLLOW', 'O_NONBLOCK', 'O_RDONLY', 'O_RDWR', 'O_SHLOCK', 'O_SYNC', 'O_TRUNC', 'O_WRONLY', 'P_NOWAIT', 'P_NOWAITO', 'P_WAIT', 'R_OK', 'SEEK_CUR', 'SEEK_END', 'SEEK_SET', 'TMP_MAX', 'UserDict', 'WCONTINUED', 'WCOREDUMP', 'WEXITSTATUS', 'WIFCONTINUED', 'WIFEXITED', 'WIFSIGNALED', 'WIFSTOPPED', 'WNOHANG', 'WSTOPSIG', 'WTERMSIG', 'WUNTRACED', 'W_OK', 'X_OK', '_Environ', '__all__', '__builtins__', '__doc__', '__file__', '__name__', '__package__', '_copy_reg', '_execvpe', '_exists', '_exit', '_get_exports_list', '_make_stat_result', '_make_statvfs_result', '_pickle_stat_result', '_pickle_statvfs_result', '_spawnvef', 'abort', 'access', 'altsep', 'chdir', 'chflags', 'chmod', 'chown', 'chroot', 'close', 'closerange', 'confstr', 'confstr_names', 'ctermid', 'curdir', 'defpath', 'devnull', 'dup', 'dup2', 'environ', 'errno', 'error', 'execl', 'execle', 'execlp', 'execlpe', 'execv', 'execve', 'execvp', 'execvpe', 'extsep', 'fchdir', 'fchmod', 'fchown', 'fdopen', 'fork', 'forkpty', 'fpathconf', 'fstat', 'fstatvfs', 'fsync', 'ftruncate', 'getcwd', 'getcwdu', 'getegid', 'getenv', 'geteuid', 'getgid', 'getgroups', 'getloadavg', 'getlogin', 'getpgid', 'getpgrp', 'getpid', 'getppid', 'getsid', 'getuid', 'initgroups', 'isatty', 'kill', 'killpg', 'lchflags', 'lchmod', 'lchown', 'linesep', 'link', 'listdir', 'lseek', 'lstat', 'major', 'makedev', 'makedirs', 'minor', 'mkdir', 'mkfifo', 'mknod', 'name', 'nice', 'open', 'openpty', 'pardir', 'path', 'pathconf', 'pathconf_names', 'pathsep', 'pipe', 'popen', 'popen2', 'popen3', 'popen4', 'putenv', 'read', 'readlink', 'remove', 'removedirs', 'rename', 'renames', 'rmdir', 'sep', 'setegid', 'seteuid', 'setgid', 'setgroups', 'setpgid', 'setpgrp', 'setregid', 'setreuid', 'setsid', 'setuid', 'spawnl', 'spawnle', 'spawnlp', 'spawnlpe', 'spawnv', 'spawnve', 'spawnvp', 'spawnvpe', 'stat', 'stat_float_times', 'stat_result', 'statvfs', 'statvfs_result', 'strerror', 'symlink', 'sys', 'sysconf', 'sysconf_names', 'system', 'tcgetpgrp', 'tcsetpgrp', 'tempnam', 'times', 'tmpfile', 'tmpnam', 'ttyname', 'umask', 'uname', 'unlink', 'unsetenv', 'urandom', 'utime', 'wait', 'wait3', 'wait4', 'waitpid', 'walk', 'write']
>>>

我有时也会把它当成一个简单的计算器

Python 2.7.12 (default, Nov  4 2016, 00:00:52)
[GCC 4.2.1 Compatible Apple LLVM 8.0.0 (clang-800.0.38)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> 20000-3500-(20000-3500)*(0.12+0.2)
11220.0
>>>

2.写成一个文件,再执行。

python script1.py

3.导入模块。每一个文件就是一个模块,可以被其它文件导入并执行。文件只有在第一次导入时才会运行代码。如果需要再次运行,需要借助于imp模块的relaod。
在命令行中可以直接使用python -m modulename 来执行模块文件。
ps: from 和 import 差不多,但是from会复制属性到当前模块的命名空间中,好处是使用时去掉了模块名,更好写,缺点是需要注意变量名覆盖问题。尤其是用到了from xxx import *的时候。我就遇到过了变量覆盖的问题。

4.使用exec运行模块文件。例如exec(open(‘module.py’).read())。在2.6中还有一个更简单的函数。execfile(filename),如execfile(‘module.py’)。事实上,还有codeevalcompile模块也能,可以参考下python标准库

第二部分 类型和运算

第4章 介绍Python对象类型

内置对象

对象类型 例子 常量/创建
数字 1234,3.14145,3+4j,Decimal,Fraction
字符串 ‘span’,”go2live.cn”,b’axolc’
列表 [1,[2,’three’],4]
字典 {‘food’:’spam’,’tests’:’yum’}
元组 (1,’spam’,4,’U’)
文件 myfile=open(‘eggs’,’r’)
集合 set(‘abc’),{‘a’,’b’,’c’}
其它类型 类型、None、布尔值
编程单元类型 函数、模块、类
与实现相关的类型 编译的代码堆栈跟踪

ps:虽然我们有时候会用typy,isinstance等判断对象的类型,但是最好不要滥用,因为这样限制了对象的使用,因而也破坏了代码的灵活性,写出来的代码,也就不够’pythonic’

第5章 数字

Python数字类型的完整工具包括:

  • 整数和浮点数 1234,1.23, 0x9ff, 0o177,0B1101
  • 复数 3+4j
  • 固定精度的十进制数
  • 有理分数 Fraction
  • 集合
  • 布尔类型
  • 无穷的整数精度
  • 各种数字内置函数和模块

Python表达式操作符及程序

操作符 描述
yield x 生成器函数发送协议
lambda args: expression 生成匿名函数
x if y else z 三元选择表达式
x or y 逻辑或(只有x为假,才会计算y)
x and y 逻辑与(只有x为真,才会计算y)
not x 逻辑非
x in y, x not in y 成员关系(可迭代对象、集合)
x is y, x is not y 对象实体测试
x < y, x <= y, x >y , x>= y, x==y, x!=y 大小比较,集合子集和超集值相等性操作符
x y
x ^ y 位异或,集合对象差
x & y 位与,集合交集
x << y, x >>y 左移或右移y位

….

第6章 动态类型介绍

变量与对象、引用的关系:

  • 变量是一个系统表的元素,拥有指向对象的连接的空间。在初次赋值时创建。
  • 对象是分配的一块内存,有足够的空间去表示它们所代表的值。
  • 引用是自动形成的从变量到对象的指针。

从技术上来讲,每一个对象都有两个标准的头部信息:一个类型标志符去标识这个对象的类型,
以及一个引用的计数器,用来决定是不是可以回收这个对象。

在Python中,变量名没有类型,类型属于对象,而不是变量

需要注意共享引用和在原处修改

a = [1,2,3]
b = a # a和b指向了同一个对象
a[2] = 3 #修改了列表对象的值后,因为b和a指向同一个对象,b指向的对象值其实也发生了变化。
#在这里看不出来。但如果语句2和语句3跨了几十上百行,可能发生了Bug就不那么好发现了。

关于is和==

  • is 是判断两个变量是否指向同一个对象,即测试对象的一致性。
  • == 是判断两个变量指向的对象的值是否一样,即测试值的相等性。

第7章 字符串

字符串是不可变序列。

常见字符串常量和表达式

操作 解释
s = ” 空字符串
s = “spam’s” 双引号和单引号相同
s = ’snptax00m’ 转义序列
s = “””…””” 三重引号字符串块
s = r’tempspam’ Raw字符串,针对转义序列来的,关闭了转义功能。
s= b’spam’ python3.0中的字节字符串
s = u’spam’ 仅在python2.6中使用的unicode字符串
s1+s2 合并
s*3 重复
s[i], s[i:j], len(s) 索引,分片,求长度
“a %s parrot” % kind 字符串格式化表达式
“a {0} parrot”.format(kind) 字符串格式化方法
s.find(‘pa’) 搜索
s.rstrip() 移动空格
s.replace(‘pa’,’xx’) 替换
s.split(‘,’) 用占位符分隔
s.isdigit() 内容测试
s.lower() 变小写
s.endswith(‘spam’) 尾部测试
‘spam’.join(strlist) 插入分隔符
s.encode(‘latin-1’) 字符串编码
for x in S:print(x) 迭代
‘spam’ in s 成员关系

扩展分片:第三个限制值
完整形式的分片是X[I:J:K], I是起始值,默认为0;J是结束值,默认是字符串长度;K是步进。

Python的设计座右铭之一就是拒绝猜的诱惑

字符串代码转换
单个的字符可以通过将其传给内置的ord函数转换为其对应的ASCII码–这个函数实际上返回的是这个字符在内存中对应的字符的二进制值。而chr函数将会执行相反的操作,获取ASCII码并将其转化为对应的字符。

字符串格式化代码

代码 意义
s 字符串(或任何对象)
r s,但使用repr,而不是str
c 字符
d 十进制(整数)
i 整数
u 无符号整数
o 八进制整数
x 十六进制整数
X x,但打印大写
e 浮点指数
E e,但打印大写
f/F 浮点十进制
g 浮点e或f
G 浮点E或F
% 常量%

还有基于字典的字符串格式化, 左边的转换目标引用右边字典中的键来提取对应的值。

>>> "%(n)d %(x)s" % {"n":1,"x":"spam"}

字符串格式化调用方法
字符串对象的format方法使用主体字符串作为模板,并且接受任意多个表示将要根据模板替换的值的参数。
在主体字符串中,花括号通过位置(例如,{1})或关键字(例如,{food})指出替换目标及要插入的参数。

第8章 列表与字典

列表list是Python中最具灵活性的有序集合对象类型。
常用列表常量和操作

操作 解释
L=[] 一个空的列表
L = [0,1,2,3] 四项:索引为0到3
L = [‘abc’,[‘def’,‘ghi’]] 潜套的子列表
L = list(‘spam’), L = list(range(-4,4)) 可迭代项目的列表,连续整数的列表
L[i], L[i][j] L[i:j], len(L) 索引,索引的索引,分片,求长度
L1+L2 合并
L * 3 重复
for x in L : print(x) 迭代
3 in L 成员关系
L.append(4),L.extend([5,6,7]) 方法:增加
L.sort(),L.index(1),L.insert(I,X),L.reverse() 方法:排序,搜索,插入,反转
del L[k] 方法,语句:缩短
del L[i:j]
L.pop()
L.remove(2)
L[i:j] = []
L[i] = 1 索引赋值,分片赋值
L[i:j] = [4,5,6]
L = [ x**2 for x in range(5)] 列表解析
list(map(ord,’spam’))

除了列表以外,字典(dict)也许是Python之中最灵活的内置数据结构类型。
常见字典常量和操作

操作 解释
D = {} 空字典
D = {‘spam’:2,’eggs’:3} 两项目字典
D = {‘food’:{‘ham’:1,’egg’:2}} 嵌套
D = dict.fromkeys([‘a’,’b’]) 其它构造技术
D = dict(zip(keyslist,valslist)) 关键字、对应的就、键列表
D = dict(name=‘Bob’,age=42)
D[‘eggs’] 以键进行索引运算
D[‘food’][‘ham’]
‘eggs’ in D 成员关系:键存在测试
D.keys() 方法:键
D.values()
D.items() 键+值
D.copy() 副本
D.get(key,default) 不在key时,返回default
D.update(D2) 合并
D.pop(key) 删除等
len(D) 长度
D[key]=42 新增/修改键,删除键
del D[key] 根据键删除条目
list(D.keys()) 字典视图
D1.keys() & D2.keys()
D = { x: x*2 for x in range(10)} 字典解析

字典用法注意事项

  • 序列运算无效。字典是映射机制,不是序列。
  • 对新索引赋值会添加项。
  • 键不一定总是字符串。

字典可用于稀疏数据结构

>>> Matrix = {}
>>> Matrix[(2,3,4)] = 88
>>> Matrix[(7,8,9)] = 99
>>> X=2;Y=3;Z=4
>>> Matrix[(X,Y,Z)]
88
>>> Matrix
{(2, 3, 4): 88, (7, 8, 9): 99}
>>>

三种方式避免missing-key错误:

  • 成员测试 if key in D:
  • try except捕获异常
  • get(key,default)不存在时给默认值。

字典解析
Python3.0中的字典也可以用字典解析来创建。
字典解析隐式地运行一个循环,根据每次迭代收集表达式的键/值结果,并且使用它们来填充一个新的字典。
字典视图
在Python3.0中,字典的keys、vales和items都返回视图对象,而在Python2.6中,它们返回实际的结果列表。视图对象是可迭代的,这就意味着对象每次产生一个结果项,而不是在内存中立即产生结果列表。

字典大小比较不再有效果,has_key方法已死:in 永生。

第9章 元组、文件和其他

元组由简单的对象组构成。元组与列表非常类似,只不过元组不能在原处修改(它们是不可变的),并且通常写成圆括号(而不是方括号)中的一系列项。虽然元组不支持任何方法调用,但元组具有列表大多数属性。
常见元组常量和运算

运算 解释
() 空元组
T = (0,) 单个元素的元组,‘,’号是为了避免歧义。因为(0)可以表求数学运算,结果是0
T = (0,’Ni’,1.2,3) 四个元素的元组
T = 0,’Ni’,1.2,3 另一个四元素的无组(与前列相同)
T = (‘abc’,(‘def’,’ghi’)) 嵌套元组
T = tuple(‘spam’) 一个可迭代对象的项的元组
T[i] 索引
T[i][j] 索引的索引
T[i:j] 分片
len(T) 长度
T1+T2 合并
T * 3 重复
for x in T: print(x) 迭代
‘spam’ in T 成员关系
[x **2 for x in T ]
T.index(‘Ni’) 搜索
T.count(‘Ni’) 计数

作为特殊情况,在不会引起语法冲突的情况下,Python允许忽略元组的圆括号。
元组不能排序,在需要排序时先转成list,排完序再转成tuple。也可以用新的sorted内置方法,它接受任何序列对象。

需要注意元组的不可变性只适用于元组本身顶层而并非其内容。例如,元组内部的列表是可以像往常那样修改的。

>>> T = (1,[2,3],4)
>>> T[1] = 'spam'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment
>>> T[1][0] = 'spam'
>>> T
(1, ['spam', 3], 4)
>>>

文件,调用open方法会返回一个文件对象。
常见文件运算

操作 解释
output = open(r’C:spam’,’w’) 创建输出文件
input = open(‘data’,’r’) 创建输入文件
input = open(‘data’) 同上,’r’是默认值
aString = input.read() 把整个文件读进单一字符串
aString = input.read(N) 读取之后的N个字节到一个字符串
aString = input.readline() 读取下一行(包括行末标识符)到一个字符串
aList = input.readlines() 读取整个文件到字符串列表
output.write(aString) 写入字节字符串到文件
output.writelines(aList) 把列表内的所有字符串写入文件
output.close() 手动关闭
output.flush() 把输出缓冲区刷到硬盘中,但不关闭文件
anyFile.seek(N) 修改文件位置到偏移量N处以便进行下一个操作
for line in open(‘data’):use line 文件迭代器一行一行地读取
open(‘f.txt’,encoding=‘latin-1’) python3.0 Unicode文本文件
open(‘f.bin’,’rb’) python3.0 二进制byte文件

使用文件的一些提示:

  • 文件迭代器是最好的读取行工具
  • 内容是字符串,不是对象
  • close是通常选项
  • 文件是缓冲的并且是可查找的

Python3.0中文本和二进制文件区别:

  • 文本文件把内容表示为常规的str字符串,自动执行Unicode编码和解码,并且默认执行末行转换。
  • 二进制文件把内容表示为一个特殊的bytes字符串类型,并且允许程序不修改地访问文件内容。

文本文件中存储的都是字符串,要还原成Python对象,可以使用转换工具,如int()等,也可以使用eval,但是eval功能太强大,恶意代码同样会执行,可参见python标准库。在数据来源无法依赖的情况下,可以使用pickle来序列化/反序列化Python对象。不过pickle是Python专用的,其它编程语法无法解析。

>>> D={'a':1,'b':2}
>>> F = open('datafile.pkl', 'wb')
>>> import pickle
>>> pickle.dump(D,F)
>>> F.close()
>>> F = open('datafile.pkl','rb')
>>> E = pickle.load(F)
>>> E
{'a': 1, 'b': 2}

pickle做为简单的本地存储还是不错的。当然你也可以用Sqlite。
至于处理结构化二进制数据的模块还有struct,不过你得自己写对模板。
文件上下文管理器
with语句,可以帮助我们自动地关闭文件,而不需要手动调用close

>>> with open(r'datafile.pkl') as myfile:
...     for line in myfile:
...             print(line)

除了open,还有其它一些文件工具:

  • 标准流, sys.stdin,sys.stdout等
  • os模块中的描述文件:处理整数文件,支持诸如文件锁定之类的较低级工具。
  • sockets、pipes和FIFO文件:文件类对象,用于同步进程或者通过网络进行通信。
  • 通过键来存取的文件:通过键直接存储的不变的Python对象。
  • Shell命令流:像os.popen和subprocess.Popen这样的工具,支持产生shell命令,并读取和写入到标准流
  • 还有第三方的PySerail和pexpect等。

对象分类

对象分类 分类 是否可变
数字 数值
字符串 序列
列表 序列
字典 对应
元组 序列
文件 扩展 N/A
Sets 集合
frozenset 集合
bytearray(3.0) 序列

引用 vs 拷贝
赋值操作总是存储对象的引用。这样一来,在共享引用的时候,对a对象的修改。可能会引起其它同样指向a对象变量的修改,这可能不是我们期待的。如果确定这不是我们想用的,用拷贝才合适。
有4种方法获取拷贝:

  • 没有限制条件的分片表达式(L[:])能够复制序列。
  • 字典copy方法(X.copy())能够复制字典。
  • 有些内置函数(例如,list)能够生成拷贝(list(L))。
  • copy标准库模块能够生成完整拷贝。这里需要注意下浅拷贝和深拷贝,copy.copy是浅拷贝,copy.deepcopy是深拷贝。
>>> L = [1,2,3]
>>> import copy
>>> L2 = copy.copy(L)
>>> L2
[1, 2, 3]
>>> L[1]='2'
>>> L
[1, '2', 3]
>>> L2
[1, 2, 3]
>>> L = [1,2,3,['a','b','c']]
>>> L2 = copy.copy(L)
>>> L[3][0] = 'aaaaa'
>>> L
[1, 2, 3, ['aaaaa', 'b', 'c']]
>>> L2
[1, 2, 3, ['aaaaa', 'b', 'c']]
>>>

需要注意:无条件值的分片以及字典copy方法只能做顶层复制,相当于copy.copy,如果需要能够复制嵌套的数据结构,得使用copy.deepcopy。

真假判断

  • 数字如果非零,则为真。
  • 其他对象如果非空,则为真。

对象真值的例子

对象
“spam” True
“” False
[] False
{} False
1 True
0.0 False
None False

Python主要内置对象类型

第三部分 语句和语法

第10章 python语句简介

重访Python程序结构

  • 程序由模块构成。
  • 模块包含语句。
  • 语句包含表达式。
  • 表达式建立并处理对象。

Python语句

语句 角色 例子
赋值 创建引用值 a,b,c = ‘good’,’bad’,’ugly’
调用 执行函数 log.write(‘spam, ham’)
打印调用 打印对象 print(‘The killer’, joke)
if/elif/else 选择动作 if “python” in text: print(text)
for/else 序列迭代 for x in mylist: print(x)
while/else 一般循环 while X > Y: print(‘hello’)
pass 空占位符 while True:pass
break 循环退出 while True: if exittest():break
continue 循环继续 while True: if skiptest(): continue
def 函数和方法 def f(a,b,c=1,*d):print(a+b+c+d[0])
return 函数结果 def f(a,b,c=1,*d):return a+b+c+d[0]
yield 生成器函数 def gen(n): for i in n: yield i*2
global 命名空间
nonlocal 命名空间
import 模块访问 import sys
from 属性访问 from sys import stdin
class 创建对象
try/except finally 捕捉异常
raise 触发异常 raise EndSearch(‘action error’)
assert 调试检查 assert X > Y, ‘X too small’
with/as 环境管理器
del 删除引用

第11章 赋值、表达式和打印

赋值语句形式

运算 解释
spam = ‘Spam’ 基本形式
spam,ham=‘yum’,’YUM’ 元组赋值运算(位置性)
[spam,ham] = [‘yum’,’YUM’] 列表赋值运算(位置性)
a,b,c,d = ‘spam’ 序列赋值运算,通用性
a,*b=‘spam’ 扩展的序列解包(3.0)
spam = ham = ‘lunch’ 多目标赋值运算
spams += 42 增强赋值运算(相当于spams=spams+42)

Python3.0中的扩展序列解包
一个带有单个星号的名称,可以在赋值目标中使用,以指定对于序列的一个更为通用的匹配– 一个列表赋给了带星号的名称,该列表收集了序列中没有赋值给其他名称的所有项。

>>> seq=[1,2,3,4,5,6]
>>> a,b,*c,d = seq
>>> a,b,c,d
(1, 2, [3, 4, 5], 6)
>>> seq=[1,2,3]
>>> a,b,*c,d = seq
>>> a,b,c,d
(1, 2, [], 3)
>>>

调用Python3.0的print函数有如下的形式:
print([object,…][,sep=’ ‘][, end=’n’][, file=sys.stdout])

第12章 if测试和语法规则

避免混合使用制表符和空格:Python3.0中的新的错误检查

if/else的三元表达式是 X if Y else Z。而不是Y?X:Z哦。
在Python2.5之前没有这个表达式,是通过 and or 实现的。如A = ((X and Y) or Z)
现在也还可以使用。使用的前提是,确保Y是布尔真值,否则即便X为真,还是会返回Z,这当然不是你需要的

第13章 while和for循环

while循环的形式
python
while <test>:
<statements1>
else:#只有当循环正常离开时才会执行(也就是没有碰到break语句。)
<statements2>

在Python中,赋值语句只是赋值,而不是表达式,不会像C语言一样返回赋值后的值。
在Python中是没有类似于while((x=next()) !=NULL) {..process x…}这种写法的。

for循环的形式

for <target>in <object>:
    <statements>
else:
    <statements>

python提供了两个内置函数,在for循环内定制迭代:

  • 内置range函数返回一系列连续增加的整数,可作为for中的索引。
  • 内置zip函数返回并行元素的元组的列表,可用于在for中内遍历数个序列。

在python3.0中,range是一个迭代器,会根据需要产生元素。如果需要完整的列表,需要将其包含到一个list调用中。
range(start,end,step)。

对于遍历字符串来说,使用range唯一的真正优点是—-它没有复制字符串,并且不会在Python3.0中创建一个列表,对于很大的字符串来说,这会节省内存。
对于小的字符串来说,使用分片操作比range更方便。

当参数长度不同时,zip会以最短序列的长度为准来截断所得到的元组。如

>>> S1='abc'
>>> S2='xyz123'
>>>
>>> list(zip(S1,S2))
[('a', 'x'), ('b', 'y'), ('c', 'z')]

enumerate函数返回一个一成器对象。调用next()返回偏移量和元素。

>>> S1='abc'
>>> e=enumerate(S1)
>>> e
<enumerate object at 0x10d22e1b0>
>>> next(e)
(0, 'a')
>>> for (index,item) in e:
...     print(index, item)
...
1 b
2 c
>>>

第14章 迭代器和解析,第一部分

Python中所谓的迭代协议:有next方法的对象会前进到下一个结果,而在一系列结果的末尾时,则会引发StopIteration。在Python中,任何这类对象都认为是可迭代的。任何这类对象也能以for循环或其它迭代工具遍历,因为所有迭代工具内部工作起来都是在每次迭代中调用next,并且捕获StopIteration异常来确定何时离开。

按行读取文件内容的最佳实践就是利用这一点,因为文件对象也有next方法,是可迭代的。

>>> for line in open('script1.py'):
...     print(line,end='')
...
import sys                # Load a library module
print(sys.platform)
print(2 ** 100)           # Raise 2 to a power
x = 'Spam!'
print(x * 8)              # String repetition

>>>

迭代器在python中是以C语言的速度运行的,比while循环要快。是迭代的优先选择

Python3.0提供了一个内置函数next,它会自动调用一个对象的next方法。给定一个可迭代对象X,调用next(X)等同于X.next()。

有时候,术语”可迭代的”指的是支持iter的一个对象,而”迭代器”指的是iter所返回的一个支付next(I)的对象

for循环迭代的步骤是:把可迭代对象传给iter获取到迭代器。然后调用迭代器的next方法,直到捕获到StopIteration异常。

列表解析一般是处理列表的最佳实践。

>>> [line.rstrip() for line in open('script1.py')]
['import sys                # Load a library module', 'print(sys.platform)', 'print(2 ** 100)           # Raise 2 to a power', "x = 'Spam!'", 'print(x * 8)              # String repetition', '']

扩展的列表解析语法
表达式中的嵌套for循环可以有一个相关的if语句,来过滤那些测试不为真的结果项。

>>> [line.rstrip() for line in open('script1.py') if line[0]=='p']
['print(sys.platform)', 'print(2 ** 100)           # Raise 2 to a power', 'print(x * 8)              # String repetition']
>>>

列表解析还可以变理更复杂,如包含嵌套的循环,也可能被编写为一系列的for子句。如

>>> [x+y for x in 'abc' for y in 'lmn']
['al', 'am', 'an', 'bl', 'bm', 'bn', 'cl', 'cm', 'cn']

在Python3.0更强制可迭代对象,在2.6中map,range,zip,filter等是返回的结果列表,而在3.0中只是返回一个可迭代对象,为了获取结果,还得调用list。

>>> zip('abc','xyz')
<zip object at 0x10d22dfc8>
>>> list(zip('abc','xyz'))
[('a', 'x'), ('b', 'y'), ('c', 'z')]

多个迭代器 vs 单个迭代器
range对象,它支持len和索引,它不是自己的迭代器(手动迭代时,我们可以使用iter产生一个迭代器),并且,它支持在其结果上的多个迭代器,这些迭代器会记住它们各自的位置,而zip、map和filer因为就是其自身的迭代器,都不支持相同结果上的多个活跃迭代器。

通常针对iter调用返回一个新的对象,来支持多个迭代器;单个的迭代器一般意味着一个对象返回其自身。

第15章 文档

Python文档资源

形式 角色
#注释 文件中的文档
dir函数 对象中可用属性的列表
文档字符串:__doc__ 附加在对象上的文件中的文档
PyDoc:help函数 对象的交互帮助
PyDoc:HTML报表 浏览器中的模块文档
标准手册 正式的语言和库的说明
网站资源 在线教程、例子等
出版的书籍 商业参考书籍

module,class,method,function都有doc属性,如果要取出模块中类的方法函数的文档字符串,可以通过路径访问类:module.class.method.doc

help函数会启用PyDoc从而产生简单的文字报表。

PyDoc启用Gui按下面的方式就可以

>>> import pydoc
>>> pydoc.gui()

会弹出Gui界面,输入搜素的模块,选中一个结果后,选”go to selected”就会打开浏览器,在浏览器中显示文档。

不过在这里我还是推荐mac用户使用dash。参见mac编程装哪些软件

第四部分 函数

第16章 函数基础

函数是Python为了代码最大程度的重用和最小化代码冗余而提供的最基本的程序结构。
函数相关的语句和表达式

语句 例子
Calls myfunc(“spam”,”eggs”,meat=ham)
def, def adder(a,b=1,*c):
return return a+b+c[0]
global def changer(): global x;x=“new”
nonlocal def changer():nonlocal x; x=‘new’
yield def squares(x): for i in rang(x):yield i ** 2
lambda Funcs = [lambda x: x*2, lambda x:x3]

主要概念的简要介绍:

  • def是可执行的代码。函数并不存在,直到Python运行了def后才存在。
  • def创建了一个对象并将其赋值给某一变量名。当Python运行到def语句时,它将会生成一个新的函数对象并将其赋值给这个函数名。
  • lambda创建一个对象但将其作为结果返回。
  • return将一个结果对象发送给调用者。
  • yield向调用者发回一个结果对象,但是记住它离开的地方。
  • global声明了一个模块级的变量并被赋值。如果不改变这个全局变量,也可以不声明global。
  • nonlocal声明了将要赋值的一个封闭的函数变量。感觉像闭包的用法。
  • 函数是通过赋值(对象引用)传递的。
  • 参数、返回值以及变量并不是声明。

Python将对某一对象在某种语法的合理性交由对象自身来判断。这种依赖类型的行为称为多态。
在Python中,代码不应该关心特定的数据类型。这其实要求我们写完善的测试用例来测试代码。关于测试,我推荐Flask Web开发 基于Python的Web应用开发实战

第17章 作用域

当你在一个程序中使用变量名时,Python创建、改变或者查找变量名是在所谓的命名空间(一个保存变量名的地方)中进行的。
在代码中变量名被赋值的位置决定了这个变量名能被访问到的范围。

变量可以在3个不同的地方分配,分别对应3种不同的作用域:

  • 如果一个变量在def内赋值,它被定位在这个函数内。
  • 如果一个变量在一个嵌套的def中赋值,对于嵌套的函数来说,它是非本地的。
  • 如果在def之外赋值,它就是整个文件全局的。

函数定义了本地作用域,而模块定义的是全局作用域。这两个作用域有如下的关系。

  • 内嵌的模块是全局作用域。
  • 全局作用域的作用范围仅限于单个文件。
  • 每次对函数的调用都创建了一个新的本地作用域。
  • 赋值的变量名除非声明为全局变量或非本地变量,否则均为本地变量。如果需要给一个在函数内部却位于模块文件顶层的变量名赋值,需要在函数内部通过global语句声明。如果需要给位于一个嵌套的def中的名称赋值,从Python3.0开始可以通过一条nonlocal语句中声明它来做到。
  • 所有其他的变量都可以归纳为本地、全局或内置的。

交互模式运行的代码实际上真的输入到一个叫做main的内置模块中

变量名解析:LEGB原则
对于一个def语句:

  • 变量名引用分为三个作用域进行查找:首先是本地,之后是函数内(如果有的话),之后全局,最后是内置。
  • 在默认情况下,变量名赋值会创建或者改变本地变量。
  • 全局声明和非本地声明将赋值的变量名映射到模块文件内部的作用域。

Python的变量名解析机制有时称为LEGB法则,这也是由作用域的命令而来的。

  • 当在函数中使用未认证的变量名时,Python搜索4个作用域【本地作用域(L=Local), 之后是上一层结构的def或lambda的本地作用域(E=Enclosing function locals),之后是全局作用域(G=Global),最后是内置作用域(B=Build-in)】,并且在第一处能够找到这个变量的地方停下来。
  • 当在函数中给一个变量名赋值时(而不是在一个表达式中对其进行引用),Python总是创建或改变本地作用域的变量名,除非它已经在那个函数中声明为全局变量。
  • 当在函数之外给一个变量名赋值时(也就是,在一个模块文件的顶层,或者是在交互提示模式下),本地作用域与全局作用域(这个模块的命名空间)是相同的。

这里的LEGB仅对简单变量有用,至于对象属性,那是另一套规则,和继承有关。即LEGB规则只适用于无点号运算的纯变量名。

PyCheker可以检查python代码的问题

关于全局变量:

  • 全局变量是位于模块文件内部的顶层的变量名。
  • 全局变量如果是在函数内被赋值的话,必须经过声明。
  • 全局变量名在函数的内部不经过声明也可以被引用。

一个模块文件的全局变量一旦被导入就成为了这个模块对象的一个属性。

函数嵌套通常用作装饰器。

看个工厂函数的例子

>>> def maker(N):
...     def action(X):
...             return X ** N
...     return action
...

>>> f = maker(2)
>>> f
<function maker.<locals>.action at 0x1077efd08>
>>> f(3)
9

内嵌函数记住了整数2,即maker函数内部的变量N的值,尽管在调用执行f时maker已经返回了值并退出。实际上,在本地作用域内的N被作用执行的状态信息保留了下来。

除了类,全局变量、像这样的嵌套作用域引用以及默认的参数是Python的函数能够保留状态信息的主要方法。

需要注意的是LEGB中的E是在Python2.2才引进的。在这之前需要利用默认参数才能实现。
所以如果看到下面的代码,请不要奇怪。x=x在python2.2之前是不能省略的。。

>>> def func():
...     x = 4
...     action = (lambda n, x=x: x**n)
...     return action
...
>>>

作用域与带有循环变量的默认参数相比较
如果lambda或者def在函数中定义,嵌套在一个循环之中,并且嵌套的函数引用了一个上层作用域的变量,该变量被循环所改变,所有在这个循环中产生的函数将会有相同的值–在最后一次循环中完成时被引用变量的值。

>>> def makeActions():
...     acts = []
...     for i in range(5):
...             acts.append(lambda x: i ** x)
...     return acts
...
>>> acts = makeActions()
>>> acts[0]
<function <lambda> at 0x1096de140>
>>> acts[0](2)
16
>>>

在定义的时候,其实期望的是每个函数记住嵌套作用域中当前变量i的值。
为什么会失效呢?
因为在Python中,嵌套作用域中的变量是在嵌套的函数被调用时才进行查找。在调用的时候,i的值已经变成了4。

怎么解决呢?
可以利用默认参数。因为默认参数是在嵌套函数创建时评估。
代码改成下面的就OK了。

>>> def makeActions():
...     acts = []
...     for i in range(5):
...             acts.append(lambda x,i=i: i**x)
...     return acts
...
>>> acts = makeActions()
>>> acts[0]
<function <lambda> at 0x1096de0c8>
>>> acts[0](2)
0
>>> acts[1](2)
1
>>>

nonlocal是global的近亲。嵌套函数可以引用一个嵌套的函数作用域中的变量,而加上nonlocal声明,则可以修改这个变量了。
和global语句不同的是:当执行一条nonlocal语句时,nonlocal名称必须已经在一个嵌套的def作用域赋值过,否则将会得到一个错误。

函数属性也能保持状态

>>> def tester(start):
...     def nested(label):
...             print(label,nested.state)
...             nested.state += 1
...     nested.state = start
...     return nested
...
>>> f = tester(0)
>>> f('go2live.cn')
('go2live.cn', 0)
>>> f('http://go2live.cn')
('http://go2live.cn', 1)
>>>

第18章 参数

函数传递参数时的简要的关键点:

  • 参数的传递是通过自动将对象赋值给本地变量名来实现的。
  • 在函数内部的参数名的赋值不会影响调用者。
  • 改变函数的可变对象参数的值也许会对调用者有影响。

Python的通过赋值进行传递的机制与C语言传递模型相当相似。

  • 不可变参数”通过值”进行传递。
  • 可变对象是通过”指针”进行传递的。

实际上Python的参数传递模型相当简单:它仅仅是将对象赋值给变量名,并且无论对可变对象或不可变对象都是这样的。

避免可变参数的修改
手动copy一个对象。修改copy的对象就不会影响到原来的对象了。
如果在你没有预期的情况下对象在外部发生了改变,检查一下是不是调用了的函数引起的,并且有必要的话当传入对象时进行拷贝。

参数匹配模型大纲:

  • 位置:从左至右进行匹配
  • 关键字参数:通过参数名进行匹配
  • 默认参数:为没有传入值的参数定义参数值
  • 可变参数:收集任意多基于位置或关键字的参数
  • 可变参数解包:传递任意多的基于位置或关键字的参数
  • Keyword-only参数:参数必须按时名称传递

函数参数匹配表

语法 位置 解释
func(value) 调用者 常规参数:通过位置进行匹配
func(name=value) 调用者 关键字参数:通过变量名匹配
func(*sequence) 调用者 以name传递所有的对象,并作为独立的基于位置的参数
func(**dict) 调用者 以name成对的传递所有的关键字/值,并作用独立的关键字参数
def func(name) 函数 常规参数:通过位置或变量名进行匹配
def func(name=value) 函数 默认参数值,如果没有在调用中传递的话
def func(*name) 函数 匹配并收集(在元组中)所有包含位置的参数
def func(**name) 函数 匹配并收集(在字典中)所有包含key的参数
def func(*args, name) 函数 参数必须在调用中按照关键字传递
def func(*,name=value) 函数 同上,之所以有*,是为了避免歧义(当成默认参数)

如果决定使用并混合特定的参数匹配模型,Python将会遵循下面有关顺序的法则。

  • 在函数调用中,参数必须以此顺序出现:任何位置参数(value),后面跟着任何关键字参数(name=value)和sequence形式的组合,后面跟着*dict形式。
  • 在函数头部,参数必须以此顺序出现:任何一般参数(name),紧跟着任何默认参数(name=value),如果有的话,后面是name(在Python3.0中是)的形式,后面跟着任何name或name=value keyword-only参数(python3.0中),后面跟着**name形式。

Python内部是使用以下的步骤来赋值前进行参数匹配的:

  1. 通过位置分配非关键字参数。
  2. 通过匹配变量名分配关键字参数。
  3. 其他额外的非关键字参数分配到*name元组中。
  4. 其他额外的关键字参数分配到**name字典中。
  5. 用默认值分配给在头部未得到分配的参数。

在python3.0中apply已经废弃,apply(func,pargs,kargs) 替换成 func(pargs,*kargs)

第19章 函数的高级话题

函数设计的指导方针:

  • 耦合性:对于输入使用参数并且对于输出使用return语句。
  • 耦合性:只有在真正必要的情况下使用全局变量。
  • 偶尔性:不要改变可变类型的参数,除非调用者希望这样做。
  • 聚合性:每一个函数都应该有一个单一的、统一的目标。
  • 大小:每一个函数应该相对较小。
  • 耦合:避免直接改变在另一个模块文件中的变量。

函数对象:属性和注解
函数内省:
内省工具允许我们探索实现细节–例如,函数已经附加了代码对象,代码对象提供了函数的本地变量和参数等方面的细节:

>>> def func(a):
...     b = 'spam'
...     return b * a
...
>>> func.__name__
'func'
>>> func.__code__
<code object func at 0x10a2f9eb0, file "<stdin>", line 1>
>>> dir(func.__code__)
['__class__', '__cmp__', '__delattr__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'co_argcount', 'co_cellvars', 'co_code', 'co_consts', 'co_filename', 'co_firstlineno', 'co_flags', 'co_freevars', 'co_lnotab', 'co_name', 'co_names', 'co_nlocals', 'co_stacksize', 'co_varnames']
>>> func.__code__.co_varnames
('a', 'b')
>>> func.__code__.co_argcount
1

工具管理者可以利用这些信息来管理函数。

函数属性
函数对象不仅有系统定义的属性,我们也可以向函数附加任意的用户定义的属性:

>>> func.count = 0
>>> func.count +=1
>>> func.count
1

这样的属性可以用来直接把状态信息附加到函数对象,而不必使用全局、非本地和类等其他技术,从某种意义上讲,这很像其它语言的”静态本地变量“。

Python3.0中的函数注解
注解进一步使函数头部语法通用化。
对于参数,它们出现在紧随参数名之后的冒号之后;对于返回值,它们编写于紧跟在参数列表之后的一个->之后。当提供了注解的时候,Python将它们收集到字典中并且将它们附加给函数对象自身。

>>> def func(a:'spam', b:(1,10), c:float) -> int:
...     return a+b+c
...
>>> func.__annotations__
{'return': <class 'int'>, 'a': 'spam', 'c': <class 'float'>, 'b': (1, 10)}
>>>

匿名函数:lambda
lambda的一般形式是关键字lambda,之后是一个或多个参数(与一个def头部内用括号括起来的参数列表极其相似),紧跟的是一个冒号,之后是一个表达式:
lambda argument1,argument2,… argumentN: expression using arguments。
lambda与def创建的函数区别:

  • lambda是一个表达式,而不是一个语句。
  • lambda的主体是一个单个的表达式,而不是一个代码块。

第20章 迭代和解析,第二部分

列表解析把任意一个表达式而不是一个函数应用于一个迭代对象中的元素。
使用map这种函数式编程工具,可以把一个函数映射遍一个序列,列表解析把一个表达式映射一个序列。

>>> res = list(map(ord,'spam'))
>>> res
[115, 112, 97, 109]
>>> res = [ord(x) for x in 'spam']
>>> res
[115, 112, 97, 109]
>>>
>>>

效率对比:
列表解析>map>for循环

重访迭代器:生成器
有两种语言结构尽可能地延迟结果创建。

  1. 生成器函数:编写为常规的def语句,但是使用yield语句一次返回一个结果,在每个结果之间挂起和继续它们的状态。
  2. 生成器表达式类似于列表解析,但是,它们返回按需要产生结果的一个对象,而不是构建一个结果列表。

生成器函数:yield VS return
和返回一个值并退出的常规函数不同,生成器函数自动在生成值的时刻挂起并继续函数的执行。因此,它们对于提前计算整个一系列值以及在类中手动保存和恢复状态都很有用。
生成器函数和常规函数之间的主要的代码不同之处在于,生成器yields一个值,而不是返回一个值。yield语句挂起该函数并向调用者发送回一个值,但是,保留足够的状态以使得函数能够从它离开的地方继续。

函数包含一条yield语句后,该语句特别编译为生成器。当调用时,它们返回一个迭代器对象,该对象支持用next的自动创建的方法来继续执行的接口。从调用者角度来看,生成器的next方法继续函数并且运行到下一个yield结果返回或引发一个StopIteration异常。

扩展生成器函数的协议:send和next
yield现在是一个表达式的形式,可以返回传入的元素来发送,而不是一个语句[yield X或者A = (yield X)]。表达式必须包含在括号中,除非它是赋值语句右边的唯一一项。例如,X = yield Y没问题,就如同X = (yield Y)+42。

当使用这一额外的协议时,值可以通过调用G.send(value)发送给一个生成器G。之后恢复生成器的代码,并且生成器中的yield表达式返回了为了发送而传入的值。如果提前调用了正常的G.next方法,yield返回None。

>>> def gen():
...     for i in range(10):
...             x = yield i
...             print(x)
...
>>> G = gen()
>>> next(G)
0
>>> G.send(77)
77
1
>>> G.send(88)
88
2
>>> next(G)
None
4

生成器表达式:迭代器遇到列表解析
从语法上讲,生成器表达式就像一般的列表解析一样,但是它们是括在圆括号中而不是方括号中的。

>>> [x ** 2  for x in range(4)]
[0, 1, 4, 9]
>>> ( x ** 2 for x in range(4))
<generator object <genexpr> at 0x1046f75c8>

生成器是单迭代器对象

用遵守迭代协议的类来实现任意的用户定义的生成器对象。
这样的类定义一个特别的iter方法,它由内置的iter函数调用,将返回一个对象,
该对象有一个next方法,该方法由next内置函数调用(一个getitem索引方法作为迭代的退而求其次的选项也是可以的)。

被赋值的变量X在函数内部是当作本地变量名对待的,而不是仅仅在赋值语句以后的语句中才被当作本地变量

>>> X = 99
>>> def selector():
...     print(X)
...     X = 88
...
>>> selector()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in selector
UnboundLocalError: local variable 'X' referenced before assignment
>>>

在函数中,不可能同时使用同一个简单变量名的本地变量和全局变量。如果真的是希望打印全局变量,并在之后设置一个有着相同变量名的本地变量,导入上层的模块,并使用模块的属性标记来获得其全局变量。

>>> X = 99
>>> def selector():
...     import __main__
...     print(__main__.X)
...     X = 88
...     print(X)
...
>>> selector()
99
88

默认参数和可变对象
默认参数是在def语句运行时评估并保存的,而不是在这个函数调用时。从内部来讲,Python会将每一个默认参数保存成一个对象,附加在这个函数本身。这是有用的,他可以利用作用域规则的LEGB中的E,把E中的变量值保存下来。如果没有理解到默认参数是在def语句运行时评估并保存的,而不是在这个函数调用时,很可能发生下面的错误使用。

>>> def saver(x=[]):
...     x.append(1)
...     print(x)
...
>>> saver([2])
[2, 1]
>>> saver()
[1]
>>> saver()#可能你期待的结果是[1], 但因为默认参数是在def语句运行时评估并保存的,事实上会保留一次的值,并且一直追加下去。
[1, 1]
>>> saver()
[1, 1, 1]

第五部分 模块

第21章 模块:宏伟蓝图

每一个文件都是一个模块,并且模块导入其他模块之后就可以使用导入模块定义的变量名。模块可以由两个语句和一个重要的内置函数进行处理。
import 使客户端(导入者)以一个整体获取一个模块。
from 允许客户端从一个模块文件中获取特定的变量名。
imp.reload 在不中止Python程序的情况下,提供了一种重新载入模块文件代码的方法。

模块的三个角色:

  • 代码重用
  • 系统命名空间的划分
  • 实现共享服务和数据

如何组织一个程序

import如何工作
在Python中,导入并非只是把一个文件文本插入另一个文件而已,导入其实是运行时的运算,程序第一次导入指定文件时,会执行三个步骤。

  1. 找到模块文件。
  2. 编译成位码(需要时)。遍历模块搜索路径,找到符合import语句的源代码文件后,有必要的话,Python将其编译成字节码。
  3. 执行模块的代码来创建其所定义的对象。

搜索模块文件的路径sys.path:

  1. 程序的主目录。包含程序的顶层脚本文件的目录。
  2. PYTHONPATH目录(如果已经进行了设置)。
  3. 标准链接库目录。
  4. 任何.pth文件的内容(如果存在的话)。

模块文件选择
import b形式的import叙述可能会加载:

  • 源代码文件b.py。
  • 字节码文件b.pyc。
  • 目录b,包导入。
  • 编译扩展模块(通常用C/C++编写),导入时使用动态连接(例如,Linux的b.so以及Cygwin和Windows的b.dll或b.pyd)。
  • 用C编写的编译好的内置模块,并通过静态连接至Python。
  • ZIP文件组件,导入时会自动解压缩。
  • 内存内映像,对于frozen可执行文件。
  • Java类,在Jython版本的Python中。
  • .Net组件,在IronPython版本中的Python中。

第22章 模块代码编写基础

import vs from
import使一个变量名引用整个模块对象,我们必须通过模块名称来得到该模块的属性(例如,module1.printer)。
from会把变量名复制到另一个作用域,所以它就可以让我们直接在脚本中使用复制后的变量名,而不需要通过模块(例如,printer)。
from *则会取得模块顶层所有赋了值的变量名的拷贝。

从概念上讲,一个像这样的from语句:
from module import name1, name2
与下面这些语句是等效的:
import module
name1 = module.name1
name2 = module.name2
del module

import和from是赋值语句
和def一样,import和from是可执行的语句,而不是编译期间的声明,而且它们可以嵌套在if测试中,出现在函数def之中等,直到执行程序时,python执行到这些语句,才会进行解析。
和def一样,import和from都是隐性的赋值语句。

  • import 将整个模块对象赋值给一个变量名。
  • from将一个或多个变量名赋值给另一个模块中同名的对象。

模块命名空间
模块就是命名空间(变量名建立所在的场所),而存在于模块之内的变量名就是模块对象的属性。

文件生成命名空间

  • 模块语句会在首次导入时执行。系统中,模块在第一次导入时无论在什么地方,Python都会建立空的模块对象,并逐一执行该模块文件内的语句,依照文件从头到尾的顺序。
  • 顶层的赋值语句会创建模块属性。
  • 模块的命名空间能通过属性__dict__或dir(M)获取。
  • 模块是一个独立的作用域(本地变量就是全局变量)。

reload基础
与import和from不同的是:

  • reload是Python中的内置函数,而不是语句。
  • 传给reload的是已经存在的模块对象,而不是变量名。
  • reload是Python3.0中位于模块之中,并且必须导入自己。

reload细节:

  • reload会在模块当前命名空间内执行模块文件的新代码。重新执行模块文件的代码会覆盖其现有的命名空间,并非进行删除而进行重建。
  • 文件中顶层赋值语句会使得变量名换成新值。
  • 重载会影响所有使用import读取了模块的客户端。
  • 重载只会对以后使用from的客户端造成影响。之前使用from来读取属性的客户端并不会受到影响,那些客户端引用的依然是重载前所取出的旧对象。

第23章 模块包

包导入是把计算机上的目录变成另一个Python命名空间,而属性则对应于目录中所包含的子目录和模块文件。
import dir1.dir2.mod
from dir1.dir2.mod import x
上面的语句表明了机器上有个目录dir1,而dir1里有子目录dir2,而dir2内包含有一个名为mod.py(或类似文件)的模块文件。此外,这些导入意味着,dir1位于某个容器目录dir0中,这个目录可以在Python模块搜索路径中(sys.path)找到.

__init__.py包文件
包导入语句的路径中的每个目录内都必须有__init__.py这个文件,否则导入包会失败。

__init__.py可以防止有相同名称的目录不小心隐藏在模块搜索路径中,而之后才出现真正所需要的模块。
__init__.py可以完全是空的,也可以包含Python程序代码。更通常的情况下,__init__.py文件扮演了包初始化的钩子、替目录产生模块命名空间以及使用目录导入时实现from *(也就是from … import *)行为的角色。

  • 包的初始化。Python首次导入某个目录时,会自动执行该目录下__init__.py文件中的所有程序代码。
  • 模块命名空间的初始化。在包导入的模型中,脚本内的目录路劲,在导入会后变成真实的嵌套对象路径。这类文件为目录(没有实际相配的模块文件)所创建的模块对象提供了命名空间。路径中的每个目录名称会变成赋值了模块对象的变量,而模块对象的命名空间是由该目录下的__init__.py文件中所有赋值语句进行初始化的。
  • from*语句的行为。作为一个高级功能,你可以在__init__.py文件内使用__all__列表来定义目录以from*语句形式导入时,需要导出什么。在__init__.py文件中,__all__列表是指当包名称使用from *的时候,应该导入的子模块的名称清单。如果没有设定__all__,from*语句不会自动加载嵌套于该目录内的子模块。取而代之的是,只加载该目录的__init__.py文件中赋值语句定义的变量名,包括该文件中程序代码明确导入的任何子模块。

就像模块文件一样,任何已导入的目录也可以传递给reload,来强制该项目重新执行。

包相对导入
Python2.6首先在导入上隐式地搜索包目录,而Python3.0需要显示地相对导入语法。

相对导入基础知识
from语句现在可以使用前面的点号(“.”)来指定,它们需要位于同一包中的模块(所谓的包相对导入),而不是位于模块导入搜索路径上某处的模块(叫做绝对导入),也就是说:

  • 在Python3.0和Python2.6中,我们可以使用from语句前面的点号来表示,导入应该相对于外围的包–这样的导入将只是在包的内部搜索,并且不会搜索位于导入搜索路径(sys.path)上某处的同名模块。
  • 在Python2.6中,包的代码中的常规导入(没有前面的点号),目前默认一种先相对再绝对的搜索路径顺序,也就是说,它们首先搜索包自己的路径。然而,在Python3.0中,在一个包中导入默认是绝对的–在缺少任何特殊的点语法的时候,导入忽略了包含包自身并在sys.path搜索路径上的某处查找。

导入扔然是相对于CWD的

第24章 高级模块话题

在Python中,模块内的数据隐藏是一种惯例,而不是一种语法约束。

最小化from*的破坏:_X 和__all__
通过在变量名前面加_, 可以防止客户端使from*语句导入模块名时,把其中的那些变量名复制出去。
在模块顶层把变量名的字符串列表赋值给变量__all__,使用些功能时,from*语句只会把列在__all__列表中的这些变量名复制出来。

启用以后的语言特性
开启以后的语言特性,可以使用像以下形式的特定的import语句:
python
from future import featurename

混合用法模式: namemain__
每个模块都有个名为__name
的内置属性,Python会自动设置该属性:

  • 如果文件是以顶层程序文件执行,在启动时,__name__就会设置为字符串”__main__”
  • 如果文件被导入,__name__就会改设为客户端所了解的模块名。

修改模块搜索路径
模块搜索路径是一个目录列表,可以通过环境变量PYTHONPATH以及可能的.pth路径文件进行定制。
sys.path在程序启动后就会进行初始化,但在那之后,可以随意对其元素进行删除、附加和重设。

Import语句和from语句的as扩展
import和from语句都可以扩展,让模块可以在脚本中给予不同的变量名。下面的import语句:

import modulename as name

相当于:
python
import modulename
name = modulename
del modulename

模块是对象:元程序
因为模块通过内置属性显示了它们的大多数有趣的特性,因此,可很容易地编写程序来管理其他程序。
我们通常称这类管理程序为元程序(metaprogram),因为它们是在其他系统之上工作。
这也称为内省(introspection),因为程序能看见和处理对象的内部。

如下所有的表达式都会得到相同的属性和对象。

M.name
M.__dict__['name']
sys.modules['M'].name
getattr(M,'name')

用名称字符串导入模块

  1. 利用exec执行python语句来动态导入。
    python
    >>> exec("import " + modname)
    >>> string
    <module 'string' from '/usr/local/Cellar/python/2.7.12_2/Frameworks/Python.framework/Versions/2.7/lib/python2.7/string.pyc'>
    >>>
  2. 利用__import__函数来导入
>>> modname = "string"
>>> string = __import__(modname)
>>> string
<module 'string' from '/usr/local/Cellar/python/2.7.12_2/Frameworks/Python.framework/Versions/2.7/lib/python2.7/string.pyc'>
>>>

区别:
exec每次都会编译import语句。而import返回模块对象,需要用一个变量名来保存下。

过渡性模块重载
当我们重载一个模块时,Python只重新载入特殊模块的文件,它不会自动重载那些为了导入要重载文件的模块。
例如,如果要重载某个模块A,并且A导入了模块B和C,重载只适用于A,而不适用于B和C。
如果希望模块B和C也能自动重载,可以自己写个小工具,利用模块的dict和types模块。遍历dict获取属性,如果是types.ModuleType,则也reload下,为了避免同一个模块多次reload,可以用个dict存储下reload过的模块。

模块设计理念

  • 总是在Python的模块内编写代码。
  • 模块耦合要降到最低:全局变量。
  • 最大化模块的黏合性:统一目标。
  • 模块应该少去修改其他模块的变量。

顶层代码的语句次序的重要性

  • 在导入时,模块文件顶层的程序代码(不在函数内)一旦Python运行时,就会立刻执行。因为,该语句是无法引用文件后面位置赋值的变量名。
  • 位于函数主体内的代码直到函数被调用后才会运行。因为函数内的变量名在函数实际执行前都不会解析,通常可以引用文件内任意地方的变量。

几个陷进

  1. from复制变量名,而不是连接。
  2. from * 会让变量语义模糊。
  3. reload不会影响from导入。
  4. reload、from以及交互模式测试。reload不会影响之后from导入的,要更新的的话,必须重新调用from。
  5. 递归形式的from导入无法工作。解决办法是:
    1. 小心设计,通常可以避免这种导入循环
    2. 如果无法完全断开循环,就要使用import和点号运算(而不是from),将模块变量名的读取诉到后边,要么就是在函数中,或者在文件末尾附近去执行from(而不是在模块顶层),以延迟其执行。

第六部分

第25章 OOP:宏伟蓝图

属性继承搜索
Python中大多数OOP的故事,都可简化成这个表达式:
object.attribute
当类启用时,上边的Python表达式实际上等于下列自然语言。
找出attribute首次出现的地方,先搜索object,然后是该对象之上的所有类,由下至上,由左至右。

编写类树

  • 每个class语句会生成一个新的类对象。
  • 每次类调用时,就会生成一个新的实例对象。
  • 实例自动连接至创建了这些实例的类。
  • 类连接至其超类的方式是,将超类列在类头部的括号内。其从左至右的顺序会决定树中的次序。

类和属性:

  • 属性通常是在class语句中通过赋值语句添加在类中,而不是嵌入在函数的def语句内。
  • 属性通常是在类内,对传给函数的特殊参数(也就是self),做赋值运算而添加在实例中的。

每次从类产生实例时,Python会自动调用名为__init__的方法。

第26章 类代码编写基础

类产生多个实例对象
类对象和实例对象。类对象提供默认行为,是实例对象的工厂。实例对象是程序处理的实际对象:各自都有独立的命名空间,但是继承(可自动存取)创建该实例的类中的变量名。类对象来自于语句,而实例来自于调用。

类对象提供默认行为
执行class语句,就会得到类对象。以下是Python类主要特性的要点。

  • class语句创建类对象并将其赋值给变量名。
  • class语句内的赋值语句会创建类的属性。
  • 类属性提供对象的状态和行为。

实例对象是具体的元素
当调用类对象时,我们得到了实例对象。以下是类的实例内含的重要概要。

  • 像函数那样调用类对象会创建新的实例对象。
  • 每个实例对象继承类的属性并获得了自己的命名空间。
  • 在方法内对self属性做赋值运算会产生每个实例自己的属性。

Python的继承:继承是在属性点号运算时发生的,而且只与查找连接对象内的变量名有关。

就像Python中的其他事物,实例属性(有时被称作成员)并没有声明。首次赋值后,实例就会存在,就像简单的变量。

类通常是通过对self参数进行赋值运算从而建立实例的所有属性的,但不是必须如此。程序可以取出、修改或创建其所引用的任何对象的属性。

在Python中,实例从类中继承,而类继承于超类。以下是属性继承机制的核心观点。

  • 超类列在了类开头的括号中。
  • 类从其超类中继承属性。当读取属性时,如果它不存在于子类中,Python会自动搜索这个属性。
  • 实例会继承所有可读取类的属性。
  • 每个object.attribute都会开启新的独立搜索。
  • 逻辑的修改是通过创建子类,而不是修改超类。

类是模块内的属性

类可以截获Python运算符
以下是重载运算符主要概念的概要。

  • 以双下划线命名的方法(__X__)是特殊钩子。
  • 当实例出现在内置运算时,这类方法会自动调用。
  • 类可覆盖多数内置类型运算符。
  • 运算符覆盖方法没有默认值,而且也不需要。
  • 运算符可让类与Python的对象模型相集成。

每个实例都连接至其类以便于继承,如果你想看的话,这个连接叫做__class__
类也有一个__bases__属性,它是其超类的元组。

类和实例只是命名空间对象,属性是通过赋值语句动态建立。
Python中的OOP其实就是在已连接命名空间对象内寻找属性而已。

第27章 更多实例

在Python中,模块名使用小写字母开头,而类名使用一个大写字母开头,这是通用的惯例;就好像方法中使用self作为参数名,这可能不是语言所要求的,但是,这种违背惯例的做法很可能让随后阅读你的代码的人搞混淆了。

如下的常规方法调用:
instance.method(args…)
由Python自动地转换为如下的同等形式:
class.method(instance,args…)

在Python3.0中,像__str____getitem__这样的内置操作无法通过通用的属性管理器找到它们的隐式属性:__getattr__(针对未定义的属性运行)及其近亲__getattribute__(针对所有属性运行)都不会调用。

利用内省工具可以去掉硬编码:

  • 内置的instance.__class__属性提供了一个从实例到创建它的类的链接。类反过来有一个__name__,还有一个__bases__序列,提供了超类的访问。我们使用这些来打印创建一个实例的类的名字,而不是通过硬编码来做到。
  • 内置的object.__dict__属性提供了一个字典,带有一个键/值对,以便每个属性都附加到一个命名控制对象(包括模块、类和实例)。由于它是字典,因此我们可以获取键的列表、按时键来索引、迭代其键,等等,从而广泛地处理所有的属性。我们使用这些来打印出任何实例的每个属性,而不是在定制显示中编码。

实例与类属性的关系
继承的类属性只是附加到了类,而没有向下复制到实例。
有两种方式可以拿到向上继承类的属性:

  1. 通过__class__连接,拿到类的__dict__,再通过__bases__拿到超类,再拿超类的__dict__
  2. 直接利用dir函数,dir函数的结果列表中包含了继承的名称。

有两种惯例用法来避免方法被重载

  1. 方法前加_,如_X,这是命名惯例。
  2. 方法前加__,如__X,Python会自动扩展这样的名称,以包含类的名称,从而使它们变成真正的唯一。这一功能通常叫做伪私有类属性

对象持久化通过3个标准的库模块来实现,这3个模块在Python中都可用:

  • pickle:任意Python对象和字符串之间的序列化
  • dbm(在Python2.6中叫做anydbm):实现一个可通过键访问的文件系统,以存储字符串。
  • shelve:使用另两个模块按照键把Python对象存储到一个文件中。

第28章 类代码编写细节

Python的class并不是声明式的。就像def一样,class语句是对象的创建者并且是一个隐含的赋值运算–执行时,它会产生类对象,并把其引用值存储在前面所使用的变量名。就像def一样,class语句也是真正可执行代码。

在class语句内,任何赋值语句都会产生类属性,而且还有特殊名称方法重载运算符。
类几乎就是命名空间。当Python执行class语句(不是调用类),会从头至尾执行其主体内的所有语句。在这个过程中,进行的赋值运算会在这个类作用域中创建变量名,从而成为对应的类对象内的属性。
因此,类就像模块和函数:

  • 就像函数一样,class语句是本地作用域,由内嵌的赋值语句建立的变量名,就存在于这个本地作用域内。
  • 就像模块内的变量名,在class语句内赋值的变量名会变成类对象的属性。

类的主要的不同之处在于其命名空间也是Python继承的基础。在类或实例对象中找不到的所引用的属性,就会从其他类中获取。

对实例的属性进行赋值运算会在该实例内创建或修改变量名,而不是在共享的类中。
通常的情况下,继承搜索只会在属性引用时发生,而不是在赋值运算时发生:对对象属性进行赋值总是会修改该对象,除此之外没有其他的影响。

方法一般是通过实例调用的。不过,如果要保证子类的构造函数也会执行超类构造时的逻辑,一般都必须通过类明确地调用超类的__init__方法。

Python2.6和Python3.0的抽象超类
方法1:在需要由子类重载的方法中用assert或者raise NotImplementedError异常来指明子类必须重载。
方法2:特殊语法
在Python3.0中,我们在一个class头部使用一个关键字参数,以及特殊的@装饰器语法。

>>> from abc import ABCMeta,abstractmethod
>>> class Super(metaclass=ABCMeta):
...     @abstractmethod
...     def method(self):
...             pass
...
>>>

在Python2.6中,我们使用了一个类属性:

>>> from abc import ABCMeta,abstractmethod
>>> class Super:
...     __metaclass__ = ABCMeta
...     @abstractmethod
...     def method(self):
...             pass
...
>>>

当子类没有重载抽象方法时不能实例化的。

命名空间总结:

  • 无点号运算的变量名(例如,X)与作用域相对应。
  • 点号的属性名(例如,object.X)使用的是对象的命名空间。
  • 有些作用域会对对象的命名空间进行初始化(模块和类)。

Python命名空间的禅:赋值瘵变量名分类
在Python中,赋值变量的场所相当重要:这完全决定了变量名所在的作用域或对象。

>>> X = 11 #全局变量
>>> def f():
...     print(X) #引用全局变量
...
>>> def g():
...     X = 22 #本地变量
...     print(X) #引用本地变量
...
>>> class C:
...     X = 33 ##类对象属性
...     def m(self):
...             X = 44 #方法本地变量
...             self.X = 55 #实例对象属性
...
>>>

命名空间字典
模块的命名空间实际上是以字典的形式实现的,并且可以由内置属性__dict__显示这一点。类和实例对象也是如此:属性点号运算其实内部就是字典的索引运算,而属性继承其实就是搜索链接的字典而已。

类与模块的关系

  • 模块:是数据/逻辑包;通过编写Python文件或C扩展来创建;通过导入来使用。
  • 类:实现新的对象;由class语句来创建;通过调用来使用。总是位于一个模块中。

第29章 运算符重载

“运算符重载”只是意味着在类方法中拦截内置的操作–当类的实例出现在内置操作中,Python自动调用你的方法,并且你的方法的返回值变成了相应操作的结果。以下是对重载的关键概念的复习:

  • 运算符重载让类拦截常规的Python运算。
  • 类可重载所有Python表达式运算符。
  • 类也可重载打印、函数调用、属性点号运算等内置运算。
  • 重载使类实例的行为像内置类型。
  • 重载是通过提供特殊名称的类方法来实现的。

构造函数和表达式:init和__sub__
__init__是构造函数。__sub__重载”-“减法。

常见的运算符重载方法

方法 重载 调用
__init__ 构造函数 对象建立:X = Class(args)
__del__ 析构函数 X对象回收
__add__ 运算符+ 如果没有_iadd_,X+Y,X += Y
__or__ 运算符 (位OR)
__repr__,__str__ 打印、转换 print(X)、repr(X)、str(X)
__call__ 函数调用 X(*args,**kargs)
__getattr__ 点号运算 X.undefined
__setattr__ 属性赋值语句 X.any = value
__delattr__ 属性删除 del X.any
__getattribute__ 属性获取 X.any
__getitem__ 索引运算 X[key],X[i:j],没有__iter__时的for循环和其他迭代器
__setitem__ 索引赋值语句 X[key] = value, X[i:j] = sequence
__delitem__ 索引和分片删除 del X[key], del X[i:j]
__len__ 长度 len(X),如果没有__bool__,真值测试
__bool__ 布尔测试 bool(X),真测试(在Python2.6中叫做__nonzero__
__lt__,__gt__,__le__,__ge__,__eq__,__ne__ 特定的比较 X<Y,X>Y,X<= Y,X>=Y,X==Y,X!=Y(在Python2.6中,只有__cmp__)
__radd__ 右侧加法 Other+X
__iadd__ 实地(增加的加法 X += Y (or else __add__
__iter__,__next__ 迭代环境 I=iter(X), next(I);for loops, in if no __contains__, all comprehensions,map(F,X),其他(__next__在Python2.6中称为next)
__contains__ 成员关系测试 item in X (任何可迭代的)
__index__ 整数值 hex(X),bin(X), oct(X), O[X], O[X:](替代Python2中的__oct____hex__)
__enter__,__exit__ 环境管理器 with obj as var:
__get__,__set__,__delete__ 描述符属性 X.attr, X.attr = value, del X.attr
__new__ 创建 __init__之前创建对象

索引与分片:__getitem____setitem__
如果在类中定义了(或继承了)的话,则对于实例的索引运算,会自动调用__getitem__.

拦截分片
除了索引,对于分片表达式也调用getitem。正式地讲,内置类型以同样的方式处理分片。
正式地讲,内置类型以同样的方式处理分片。

>>> class Indexer:
...     data = [5,6,7,8,9]
...     def __getitem__(self,index):
...             print('getitem:',index)
...             return self.data[index]
...
>>> X = Indexer()
>>> X[0]
('getitem:', 0)
5
>>> X[2:4]
('getitem:', slice(2, 4, None))
[7, 8]
>>> X[slice(None,None,2)]
('getitem:', slice(None, None, 2))
[5, 7, 9]
>>>

__getitem__的第二个参数,可能是整数,也可能是分片对象。
在Python2.6中有专门的__getslice____setslice__,并且他们的优先级比__getitem____setitem__高,不过他们已经在Python3.0中移除掉了。

索引迭代:__getitem__
for语句的作用是从0到更大的索引值,重复对序列进行索引运算,直到检测到超出边界的异常。
任何会响应索引运算的内置或用户定义的对象,同样会响应迭代。

迭代对象:__iter____next__
Python中所有的迭代环境都会先尝试__iter__方法,再尝试__getitem__

从技术上讲,迭代环境是通过调用内置函数iter去尝试寻找__item__方法来实现的,而这种方法应该返回一个迭代器对象。如果已经提供了,Python就会重复调用这个迭代器对象的next方法,直到发生StopIteration异常。如果没有找到这类__iter__方法,Python会改用__getitem__机制,就像之前那样通过偏移量重复索引,直到引发IndexError异常。

有多个迭代器的对象
要达到多个迭代器的效果,__iter__只需替迭代器定义新的状态对象,而不是返回self。

成员关系:__contains____iter____getitem__
在迭代领域,类通常把in成员关系运算符实现为一个迭代,使用__iter__方法或__getitem__方法。
要支持更加特定的成员关系,类可能编写一个__contains__方法–当出现的时候,该方法优先于__iter__方法,__iter__方法优先于__getitem__方法。

属性引用:__getattr____setattr__
__getattr__方法是拦截属性点号运算。更确切地说,当通过对未定义(不存在)属性名称和实例进行点号运算时,就会用属性名称作为字符串调用这个方法。如果Python可通过继承树搜索流程找到这个属性,该方法就不会被调用。因为有这种情况,所以__getattr__可以做为钩子来通过通用的方式响应属性请求。如:

>>> class empty:
...     count=0
...     def __getattr__(self,attrname):
...             if attrname == 'age':
...                     return 40
...             else:
...                     raise AttributeError,attrname
...
>>>
>>> X = empty()
>>> X.count
0
>>> X.age
40
>>> X.name
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 7, in __getattr__
AttributeError: name
>>>

__setattr__会拦截所有属性的赋值语句。如果定义了这个方法,self.attr = value会变成self.setattr(‘attr’, value)。
如果要使用这个方法,要确定是通过对属性字典做索引运算来赋值任何实例属性的。也就是说,是使用self.__dict__['name'] = x,而不是self.name = x(会导致无限循环).

模块实例属性的私有性:第一部分
如下示例可以模拟私有性,让每个子类都有自己的私有变量名列表,这些变量名无法通过其实例进行赋值(私有并不绝对,因为可以通过dict直接赋值。)

另有类装饰器可以更加通用地拦截和验证属性。即使私有性可以模拟,但实际应用中几乎不会这么做。

__repr____str__会返回字符串表达式

  • 打印操作会首先尝试__str__和str内置函数(print运行的内部等价形式)。它通常应该返回一个用户友好的显示。
  • __repr__用于所有其他的环境中:用于交互模式下提示回应以及repr函数,如果没有使用__str__,会使用print和str。它通常应该返回一个编码字符串,可以用来重新创建对象,或者给开发者一个详细的显示。

根据一个容器的字符串转换逻辑,__str__的用户友好的显示可能只有当对象出现在一个打印操作顶层的时候才应用,嵌套到较大对象中的对象可能用其__repr__或默认方法打印。

右侧加法和原处加法:__radd____iadd__
只有当+右侧的对象是类实例,而左边对象不是类实例时,Python才会调用__radd__。在其他所有情况下,则由左侧对象调用__add__方法。

为了实现+=原处扩展相加,编写一个__iadd____add____iadd__更加高效,一般返回对象自身。

Call表达式:__call__
如果定义了__call__,Python就会为实例 应用函数调用表达式 运行__call__方法。这样可以让类实例的外观和用法类似于函数。

函数接口和回调代码
利用实例对象保存属性,然后实现__call__把实例对象像函数一样调用,这可能是Python语言中保留状态信息的最好方式,比之前针对函数所讨论的技术更好(全局变量、嵌套函数作用域引用以及默认可变参数等)。

比较:__lt____gt__和其他方法
比较运算符有如下限制:

  • 与前面讨论的__add__/__radd__对不同,比较方法没有右端形式。相反,当只有一个运算数支持比较时,使用其对应方法(例如,__lt____gt__互为对应)。
  • 比较运算符没有隐式关系。例如,==并不意味着!=是假的,因此,__eq____ne__应该定义为确保两个运算符都正确地作用。
  • 在Python2.6中,还有__cmp__方法。它返回一个小于、等于或大于0的数,以表示比较其两个参数的结果。__cmp__在Python3.0中已删除。

布尔测试:__bool____len__
在布尔环境中,Python首先尝试__bool__来获取一个直接的布尔值,然后,如果没有该方法,就尝试__len__类根据对象的长度确定一个真值。

对象析构函数:__del__
每当实例产生时,就会调用__init__构造函数。每当实例空间被收回时(在垃圾收集时),它的对立面__del__,也就是析构函数,就会自动执行。

基于两个原因,在Python中,析构函数不像其他OOP语言那么常用。

  1. 因为Python在实例收回时,会自动收回该实例所拥有的所有空间,对于空间管理来说,是不需要析构函数的。
  2. 无法轻易地预测实例何时收回,通常最好是在有意调用的方法中(或者try/finally语句)编写代码去终止活动。

第30章 类的设计

Python的OOP实现可以概括为三个概念,如下所示。

  • 继承。继承是基于Python中的属性查找的(在X.name表达式中)。
  • 多态。在X.method方法中,method的意义取决于X的类型(类)。
  • 封装。方法和运算符实现行为,数据隐藏默认为是一种惯例。

OOP和委托:“包装”对象
在Python中,委托通常是以__getattr__钩子方法实现的,因为这个方法会拦截对不存在属性的读取,包装类(有时称为代理类)可以使用getattr把任意读取转发给被包装的对象。

变量名压缩概览
变量名压缩的工作方式:class语句内开头有两个下划线,但结尾没有两个下划线的变量名,会自动扩张,从而包含了所在类的名称。例如,像Spam类内__X这样的变量名会自动变成_Spam__X:原始的变量名会在头部加入一个下划线,然后是所在类名称。

Python更关心的是开放而不是约束,这意味着你愿意的话,可以随便的破坏代码

为什么使用伪私有属性

在这个例子中,X是个容易混淆的属性。C1不知道C2和C3在用,C2也不知道C1和C3在用,这里有3个类在使用X,但只存在1个X属性,即I.X。

解决的办法就是用伪私有属性。

方法是对象:绑定或无绑定
方法也是一种对象,并且可以用与其他对象大部分相同的方式来广泛地使用–可以对它们赋值、将其传递给函数、存储在数据结构中,等等。
由于类方法可以从一个实例或一个类访问,它们实际上在Python中有两种形式。

  • 无绑定类方法对象:无self。通过对类进行点号运算从而获取类的函数属性,会传回无绑定(unboud)方法对象(即self参数没有绑定实例对象)。调用该方法时,必须明确提供实例对象作为第一个参数。
  • 绑定实例方法对象:self+函数对。通过对实例进行全运算从而获取类的函数属性,会传回绑定(bound)方法对象(即self参数绑定了实例对象)。Python在绑定方法对象中自动把实例和函数打包,所以,不用传递实例去调用该方法。

在Python3.0中,无绑定方法是函数
在Python3.0中,删除了无绑定方法,把它当成了一个简单函数对待。

绑定方法和其他可调用对象
就像简单函数一样,绑定方法可以任意地在一个程序中传递。

和简单函数一样,绑定方法对象拥有自己的内省信息,包括让它们配对的实例对象和方法函数访问的属性。

类也属于可调用对象的范畴,但是,我们通常调用它们产生实例而不是做实际的工作。

为什么要在意:绑定方法和回调函数
因为绑定方法会自动让实例和类方法函数配对,因此可以在任何希望得到简单函数的地方使用。
另外类通过重载__call__方法可以达到同样的目的。

多重继承:”混合“类
搜索属性时:

  • 在传统类中(默认的类,直到Python3.0),属性搜索处理对所有路径深度优先,直到继承树的顶端,然后从左到右进行。
  • 要新式类(以及Python3.0的所有类中),属性搜索处理沿着树层级、经更加广度优先的方式进行。

第31章 类的高级主题

扩展内置类型

  1. 组合。
  2. 继承。

从Python2.2起,所有内置类型现在都能直接创建子类。像list、str、dir以及tuple这些类型转换函数都变成内置类型的名称:虽然脚本看不见,但是类型转换调用其实主是启用了类型的对象构造函数。

新式类
在Python2.2中,引入一种新的类,称为”新式类“。

  • 对于Python3.0来说,所有的类都是我们所谓的”新式类“,不管它们是否显式地继承自object。所有的类都继承自object,不管显示的还是隐式的,并且,所有的对象都是object的实例。
  • 在Python2.6及其以前的版本中,类必须继承自的类看做是”新式”object(或者其他的内置类型),并且获得所有新式类的特性。

新式类变化

  • 类和类型合并 类现在就是类型,并且类型现在就是类。
  • 继承搜索顺序 先横向搜索再纵向搜索,并且先宽度优先搜索,再深度优先搜索。
  • 针对内置函数的属性获取 __getattr____getattribute__方法不再针对内置运算的隐式属性获取而运行。这意味着,它们不再针对__X__运算符重载方法名而调用,这样的名称搜索从类开始,而不是从实例开始。
  • 新的高级工具 新式类有一组新的类工具,包括slot、特性、描述符和__getattribute__方法。

类型模式变化
在Python3.0中,类就是类型,但是类型(type)也是类(class)。
从技术上讲,每个类都是由一个元类生成–元类是这样的一个类,它要么是type自身,要么是它定制来的扩展或管理生砀类的一个子类。

类型测试的隐含意义
在Python3.0中,类实例的类型直接而有意义地比较,并且以与内置类型对象同样的方式进行。

对于Python2.6或更早版本中的经典类,比较实例类型几乎是无用的。因为所有的实例都具有相同的”实例“类型(type ‘instance’)。要真正地比较类型,必须要比较实例的class属性。

新式类的扩展
类属性__slots__ 限制了实例属性:只有__slots__列表内的这些变量名可赋值为实例属性。
Slot对于Python的动态特性来说是一种违背,而动态特性要求任何名称都可以通过赋值来创建。

有些带有slots的实例也许根本 没有__dict__属性字典。
必须小心使用比__dict__更为存储中立的工具,例如getattr、setattr和dir内置函数,它们根据__dict____slots__存储应用于属性。在某些情况下,两种属性源代码都需要查询以确保完整性。

通过在__slots__中包含__dict__仍然可以容纳额外的属性,额外的属性存储到了__dict__字典中。

由于实例可能没有__slots__,也可能没有__dict__,为了通用地列出所有实例属性,可以考虑下面的代码:

需要注意一下:__slots__是限制实例对象只能用__slots__列表里的属性名设置属性,并不是说实例对象有这些属性,在用之前还是得先有赋值语句。

超类中的多个__slots__列表

  • 如果一个子类继承自一个没有__slots__的超类,那么超类的__dict__属性总是可以访问的,使得子类中的一个__slots__无意义。
  • 如果一个类定义了与超类相同的slot名称,超类slot定义的名称版本只有通过直接从超类获取其描述符才能访问。
  • 由于一个__slots__声明的含义受到它出现其中的类的限制,所以子类将有一个__dict__,除非它们也定义了一个__slots__。
  • 通常从列出实例属性这方面来讲,多类中的slots可能需要手动类树爬升、dir用法,或者把slot名称当做不同的名称领域的政策。
>>> class A:pass
...
>>> class B(A):
...     __slots__ = ['a']
...
>>> b = B()
>>> b.d = 'd' #由于超类A没有slots,所以类B中的__slots__并不限制对象b的属性设置。
>>> b.__dict__
{'d': 'd'}
>>> b.__slots__
['a']
>>>
>>> class A:
...     __slots__ = ['a','b']
...
>>> class B(A):
...     pass
...
>>> b = B()
>>> b.hello = 'go2live.cn'#由于类B并没有设置__slots__,__dict__属性是可用的。并不会因为超类A而对实例对象b产生影响。
>>> b.__dict__
{'hello': 'go2live.cn'}
>>>

要使slots真正起作用,必须子类和超类都有slots属性。

类特性
有一种称为特性(property)的机制,提供另一种方式让新式类定义自动调用的方法,来读取或赋值实例属性。他是__getattr____setattr__重载方法的替代做法。

特性的产生是以三种方法(获得、设置以及删除运算的处理器)以及通过文档字符串调用内置函数property。如果任何参数以None传递或省略,该运算就不能支持。特性一般都是在class语句顶层赋值。

age的获取就会自动调用getage方法。

__getattribute__和描述符
__getattribute__方法只适用于新式类,可以让类拦截所有属性的引用,而不局限于未定义的引用(如同__getattr__)。

除了特性和运算符重载方法,Python支持属性描述符的概念–带有__get____set__方法的类,分配给类属性并且由实例继承,这拦截了对特定属性的读取和写入访问。

元类
元类是子类化了type对象并且拦截类创建调用的类。此外,它们还为管理和扩展类对象提供了一种定义良好的钩子。

静态方法和类方法
静态方法–嵌套在一个类中的没有self参数的简单函数,并且旨在操作类属性而不是实例属性。静态方法不会接受一个自动的self参数,不管是通过一个类还是一个实例调用。

类方法–类的一种方法,传递给它们的第一个参数是一个类对象而不是一个实例,不管是通过一个实例或一个类调用它们。即便是通过一个实例调用,这样的方法也可以通过它们的self类参数来访问类数据。

2.6和3.0中的静态方法:

  • 在Python2.6中,我们必须总是把一个方法声明为静态的,从而不带一个实例而调用它,不管是通过一个类或一个实例调用它。。不声明的话,就是未绑定方法,未绑定方法需要传入一个实例对象。
  • 在Python3.0中,如果方法只通过一个类调用的话,我们不需要将这样的方法声明为静态的,但是,要通过一个实例调用它,我们必须这么做。

2.x中的静态方法和类方法。


使用静态方法内置函数,我们的代码现在允许在Python2.6和Python3.0中通过类或其任何实例来调用无self方法:(如果不使用静态方法内置函数,Pyhton2.6不管是通过类还是实例,都无法调用无self方法,Python3.0则只允许通过类调用无self实例方法)

类方法示例如下:

使用类方法统计每个类的实例
实际上,由于类方法总是接收一个实例树中的最低类:

  • 静态方法和显式类名称可能对于处理一个类本地的数据来说是更好的解决方案。
  • 类方法 可能更适合 处理对层级中的每个类不同的数据。

装饰器和元类:第一部分
从语法上来讲,函数装饰器是它后边的函数的运行时的声明。函数装饰器是写成一行,就在定义函数或方法的def语句之前,而且由@符号、后面跟着所谓的元函数(metafunction)组成:也就是管理另一函数(或其他可调用对象)的函数。

如今的静态方法可以用下面的装饰器语法编写。

>>> class C:
...     @staticmethod
...     def meth():
...             pass
...
>>> C.meth()
>>>

从内部来看,这个语法和下面的写法有相同效果(把函数传递给装饰器,再赋值给最初的变量名)。

>>> class C:
...     def meth():
...             pass
...     meth = staticmethod(meth)
...
>>> C.meth()

类装饰器和元类
类装饰器类似于函数装饰器,但是,它们在一条class语句的末尾运行,并且把一个类名重新绑定到一个可调用对象。
代码结构如下:

def decorator(aClass):...

@decorator
class C:...

被映射为下列相当代码:

def decorator(aClass):...

class C:...
C = decorator(C)

元类是一种类似的基于类的高级工具,其用途往往与类装饰器有所重合。它们提供了一种可选的模式,会把一个类对象的创建导向到顶级type类的一个子类,在一条class语句的最后:

class Meta(type):
    def __new__(meta,classname,supers,classdict):...
class C(metaclass=Meta):...

在Python2.6中,在类头部使用一个类属性而不是一个关键字参数:

class C:
    __metaclass__ = Meta
    ...

元类通常重新定义type类的newinit方法,以实现对一个新的类对象的创建和初始化的控制。

类陷井

  1. 修改类属性的副作用: 会影响子类和实例对象。
  2. 修改可变的类属性也可能产生副作用:譬如通过实例修改类的一个list列表内容,其他所有共享这个列表的内容也都变了。
  3. 多重继承:顺序很重要。 搜索先左后右,广度优先。
  4. ”过度包装“:如果类层次太深,程序就会变得晦涩难懂。

第七部分 异常和工具

第32章 异常基础

几个形式:

  • try/except 捕捉由Python或你引起的异常并恢复
  • try/finally 无论异常是否发生,执行清理行为。
  • raise 手动在代码中触发异常。
  • assert 有条件地在程序代码中触发异常。
  • with/as 在Python2.6和后续版本中实现环境管理器

异常的角色

  1. 错误处理。
  2. 事件通知。
  3. 特殊情况处理。
  4. 终止行为。
  5. 非常规控制淤积。

第33章 异常编码细节

try/except/else语句
在Python2.5以前except和finally不能混在一个try语句中。

Python3.0中的一般形式。

try:
    <statements> # 主逻辑
except <name1>: #捕获异常的处理
    <statements>
except (name2,name3):
    <statements>
except <name4> as <data>:
    <statements>
except:
    <statements>
else: #没有异常时,执行
    <statements>

try/finally语句
一般形式:

try:
    <statements>
finally:
    <statements>

利用这个变体,Python可先执行try首行下的语句代码块。接下来发生的事情,取决于try代码块中是否发生异常。

  • 如果try代码块运行时没有异常发生,Python会跳至执行finally代码块,然后在整个try语句后继续执行下去。
  • 如果try代码块运行时有异常发生,python依然会回来运行finally代码块,但是接着会把异常向上传递到较高的try语句或顶层默认处理器。程序不会在try语句下继续执行。

现在完整的try版本:

try:
    main-action
except Exception1:
    handler1
except Exception2:
    handler2
...
else:
    else-block
finally:
    finally-block

raise语句
要显式地触发异常,可以使用raise语句: raise关键字,后面跟着可选的要引发的类或者类的一个实例。

raise <instance>
raise <class> #自动调用不带构造参数的类,以创建被引发的一个实例。
raise #把最近的异常重新触发下。

Python3.0异常链:raise from
Python3.0也允许raise语句拥有一个可选的from子句:

raise exception from otherexception

当使用from的时候,第二个表达式指定了另一个异常类或实例,它会附加到引发异常的__cause__属性。

assert语句
Python还包括了assert语句,assert可视为条件式的raise语句。

asser <test>,<data>

执行起来就像如下的代码。

if __debug__:
    if not <test>:
        raise AssertionError(<data>)

assert语句是附加的功能,如果使用-O Python命令行标志位,就会从程序编译后的字节码中移除,从而优化程序。

assert几乎就是用来收集用户定义的约束条件,而不是捕捉内在的程序设计错误。

with/as环境管理器
with/as语句的设计是作为常见try/finally用法模式的替代方案。就像try/finally语句,with/as语句也是用于定义必须执行的终止或”清理”行为,无论处理步骤中是否发生异常。
不过,和try/finally不同的是,with语句支持更丰富的基于对象的协议,可以为代码块定义支持进入和离开动作。

with语句的基本格式如下。

with expression [as varibalbe]:
    with-block

在这里的expression要返回一个对象,从而支持环境管理协议。

环境管理协议
以下是with语句实际的工作方式。

  1. 计算表达式,所得到的对象称为环境管理器,它必须有__enter____exit__方法。
  2. 环境管理器的__enter__方法会被调用。如果as子句存在,其返回值会赋值给as子句中的变量,否则,直接丢弃。
  3. 代码块中的嵌套的代码会执行。
  4. 如果with代码块引发异常,__exit__(type,value,traceback)方法就会被调用(带有异常细节)。这引起也是由sys.exc_info返回的相同值。如果此方法返回值为假,则异常会重新引发。否则,异常会终止。正常情况下异常是应该被重新引发,这样的话才能传递到with语句之外。
  5. 如果with代码块没有引发异常,__exit__方法依然会被调用,其type、value以及traceback参数都会以None传递。

示例代码如下:

以下是实际在Python3.0中运行的脚本:

第34章 异常对象

基于类的异常有如下特点。

  • 提供类型分类,对今后的修改有更好的支持。
  • 它们附加了状态信息。
  • 它们支持继承。

在Python2.6和Python3.0之前,可以使用类实例和字符串对象来定义异常。

字符串异常和类异常的主要差别在于,引发的异常在try语句中的excepte子句匹配时的方式不同。

  • 字符串异常是以简单对象识别来匹配的:引发的异常是由Python的is测试来匹配except子句的。
  • 类异常是由超类关系进行匹配的:只要excepte子句列举了异常的类或其任何超类名,引发的异常就会匹配该子句。

第35章 异常的设计

嵌套异常处理器
当发生异常时,Python会回到最近进入、具有相符except分句的try语句。因为每个try语句都会留下标识,Python可检查堆栈的标识,从而跳回到较早的try。
这种处理器的嵌套化,就是我们所谈到的异常向上传递至较高的处理器的意思:这类处理器就是在程序执行流程中较早进入的try语句。

异常的习惯用法

  1. 异常不总是错误。譬如EOFERROR用来标识文件结束。
  2. 函数信号条件和raise。有些时候,我们不能用返回值来代表不寻常的情况,这种时候用异常反而比较好。它代表了某种信号,而不是真的发生异常了。
  3. 关闭文件和服务器连接。
  4. 在try外进行调试。可以利用异常处理器,取代Python的默认顶层异常处理行为(一般用于开发期间调式)。

关于sys.exc_info
sys.exc_info允许一个异常处理器获取对最近引发的异常的访问。如果没有处理器正在处理,就返回包含了三个None值的元组。否则,将会返回(type、value和traceback):

  • type是正在处理的异常的异常类型。
  • value是引发的异常类实例。
  • traceback是一个traceback对象,代表异常最初发生时所调用的堆栈。

与异常有关的技巧
try应该包装什么?
* 经常会失败的运算一般都应该包装在try语句内。如文件开启、套接字调用等。
* 应该在try/finally中实现终止动作,从而保证它们的执行,除非环境管理器作为一个with/as选项可用。
* 偶尔,把对大型函数的调用包装在单个try语句内。

捕捉太多:避免空except语句
Python可选择要捕捉哪些异常,有时候必须小心,不要涵盖太广。
try带空except时,可能会不知不觉阻止重要的结束,如下面文件所示:

经验法则是,尽量让处理器具体化:空except子句很方便,但是可能容易出错。

捕捉过少:使用基于类的分类

第八部分 高级话题

第36章 Unicode和字节字符串

高级的字符串表示法在Python当前版本中已经产生了分歧:

  • Python3.0为二进制数据提供了一种替代字符器类型,并且在其常规的字符串类型中支持Unicode文本(ASCII看作是Unicode的一种简单类型)
  • Python2.6为非ASCII Unicode文本提供了一种替代字符串类型,并且在其常规的字符串类型中支持简单文本和二进制数据。

Python3.0中的字符串修改
Python2.x的str和unicode类型 已经融入了Python3.0的str和bytes类型,并且增加了一种新的可变的类型bytearray。

ASCII– 0~127
Latin-1 — 0~255
8位的字节不够表示更多的字符,于是出现了unicode。

同样的一个字符串可以有多少编码方式,譬如’a’可以按ASCII编码也可以按unicode编码。
字节和字符串之间的来回转换由两个术语定义:

  • 编码是根据一个想要的编码名称,把一个字符串翻译为其原始字节形式。
  • 解码是根据其编码名称,把一个原始字节串翻译为字符串形式的过程。

Python的字符串类型
Python2.X有一种通用的字符串类型来表示二进制数据和像ASCII这样的8位文本,还有一种特定的类型用来表示多字节Unicode文本:

  • str表示8位文本和二进制数据。
  • unicode用来表示宽字符的Unicode文本。

Python3.X带有3种字符串对象类型– 一种用于文本数据,两种用于二进制数据:

  • str表示Unicode文本。
  • bytes表示二进制数据。
  • bytearray,是一种可变的bytes类型。

Python3.0的str类型定义为一个不可改变的字符序列
Python3.0中,bytes类型定义为一个8位整数的不可变序列,表示绝对的字节值。为了方便起见,bytes对象打印为字符串而不是整数。

文本和二进制数据
Python现在在文本文件和二进制文件之间做了一个明显的独立于平台的区分:

  1. 文本文件。当一个文件以文本模式打开的时候,读取其数据会自动将其内容解码,并且将其内容返回为一个str,写入会接受一个str,并且在将其传输到文件之间自动编码。
  2. 二进制文件。通过在内置的open调用的模式字符串参数添加一个b(只能小写),以二进制模式打开一个文件的时候,读取其数据不会以任何方式解码它,而是直接返回其内容raw并且未经修改,写也一样不会修改。

在Python3.0中,所有当前字符串常量形式,’xxx’、”xxx”和三引号字符串块,都产生一个str;在它们任何一种前面添加一个b或B,则会创建一个bytes。

Python3.0基本上要求遵守一种类型或另一种类型,或者手动执行显式转换:

  • str.encode()和bytes(S,encoding)把一个字符串转换为其raw bytes形式,并且在此过程中根据一个str创建一个bytes。
  • bytes.decode()和str(B,encoding)把raw bytes转换为其字符串形式,并且在此过程中根据一个bytes创建一个str。

要编码非ASCII字符,可能在字符串中使用十六进制(“xNN”)或Unicode转义(“uNNNN”和UNNNNNNNN);
十六进制转义限制于单个字节的值;”uNNNN”用来编码1个2字节字符码;”UNNNNNNNN”编码4字节宽度的字符。

有两点需要注意:
首先,Python3.0允许特殊的字符以十六进制和Unicode转义的方式编码到str字符串中,但是,只能以十六进制转义的方式编码到bytes字符串中:Unicode转义会默默地逐字转换为字节常量,而不是转义。

其次,字节常量要求字符要么是ASCII字符,要么如果它们的值大于127就进行转义。
另一方面,str字符串允许常量包含源字符集中的任何字符:

在Python2.6中编码Unicode字符串
为了存储任意的编码的Unicode文本,用u’xxx’常量形式创建一个unicode对象(这个常量在Python3.0中不再可用,因为Python3.0中的所有字符串都支持Unicode)。

源文件字符集编码声明
有两种形式

# -*- coding: latin-1 -*-
# coding=UTF-8

尽管Python3.0中所有的三种字符串类型都可以包含字符值并且支持很多相同的操作,但我们总是应该:

  • 对文本数据使用str;
  • 对二进制数据使用bytes;
  • 对想要原处修改的二进制数据使用bytearray。

用哪种模式打开一个文件,它决定了在脚本中将要使用哪种对象类型表示文件的内容。文本模式意味着str对象,二进制模式意味着bytes对象:

  • 文本模式文件根据Unicode编码来解释文件内容,要么是平台的默认编码,要么是我们传递进的编码名。通过传递一个编码名来打开文件,我们可以强行进行Unicode文件的各种类型的转换。文本模型的文件也执行通用的行末转换:默认地,所有的行末形式(包括’r’,’n’,’rn’)映射为脚本中的一个单个的’n’字符,而不管在什么平台上运行。正如前面所描述的,文本文件也负责阅读和写入在某些Unicode编码方案中存储文件开始处的字节顺序标记(Byte Order Mark, BOM).
  • 二进制模式文件不会返回原始的文件内容,而是作为表示字节值的整数的一个序列,没有编码或解码,也没有行末转换。

在Windows下文本文件自动把n行末字符和rn相互映射,而二进制文件不这么做。

在Python3.0中处理BOM
一些编码方式在文件的开始处存储了一个特殊的字节顺序标记(BOM)序列,来指定数据的大小尾方式或编码类型。如果编码名暗示了BOM的时候,Python在输入和将其输出的时候都会忽略该标记,但是有时候必须使用一个特定的编码名称来迫使显式地处理BOM。

Python3.0中其他字符串工具的变化

  1. re模式匹配模块。这个模块已经泛化为可以用于Python3.0中的任何字符串类型的对象–str、bytes和bytearray,并且返回同样类型的结果子字符串作为目标字符串。
  2. Struct二进制数据模块。打包的数据只是作为bytes和bytearray对象显示,而不是str对象。

    附加两个struct格式说明表格:

  1. Pickle对象序列化模块。pickle模块的Python3.0版本总是创建一个bytes对象。所以写入文件时,需要以二进制模式打开。
  2. XML解析工具。SAX和DOM解析模式。

第37章 管理属性

插入在属性访问时运行的代码

  1. __getattr__和__setattr__方法,把未定义的属性获取和所有的属性赋值指向通用的处理器方法。
  2. __getattribute__方法,把所有属性获取都指向Python2.6的新式类和Python3.0的所有类的一个泛处理器方法。
  3. property内置函数,把特定属性访问定位到get和set处理器函数,也叫做特性(Property)。
  4. 描述符协议,把特定属性访问定位到具有任意get和set处理方法的类的实例。

特性
特性协议允许我们把一个特定属性的get和set操作指向我们所提供的函数或方法,使得我们能够插入在属性访问的时候自动运行的代码,拦截属性删除,并且如果愿意的话,还可为属性提供文档。

attribute = property(fget,fset,fdel,doc)

四个参数如果没有传的话,默认为None,意味着相应的操作不支持,如果调用了就会引发一个异常。

利用装饰器语法可以简化特定的写法。如下示例:

对于Python2.6,property对象也有getter、setter和deleter方法,这些方法指定相应的特性访问器方法赋值并且返回特性自身的一个副本。

描述符
描述符也管理一个单个的、特定的属性。特性实际中只是创建一种特写描述符的方便方法。
描述符作为单独的类编写,并且针对想要拦截的属性访问操作提供特定命名的访问器方法–当以相应的方式访问分配给描述符类实例的属性时,描述符类中的获取、设置和删除等方法自动运行:

class Descriptor:
    "docstring goes here"
    def __get__(self,instance,owner):... #返回属性值
    def __set__(self,instance,value):... #返回None
    def __delete__(self,instance):... #返回None

带有任何这些方法的类都可以看作是描述符,并且当它们的一个实例分配给另一个类的属性的时候,它们的这些方法是特殊的–当访问属性的时候,会自动调用它们。
和特性不同,省略一个__set__意味着允许这个名字在一个实例中重新定义,因此,隐藏了描述符–要使得一个属性是只读的,我们必须定义__set__来捕获赋值并引发一个异常。

当获取X.attr的时候,就好像发生了如下的转换:

X.attr-> Descriptor.__get__(Subject.attr,X,Subject)

当描述符的实例参数为None的时候,该描述符知道将直接访问它。

描述符可以使用实例状态和描述符状态,或者二者的任何组合:

  • 描述符状态用来管理内部用于描述符工作的数据。
  • 实例状态记录了和客户类相关的信息,以及可能由客户类创建的信息。

特性和描述符是如何相关的
可以用下面的描述符来模拟property内置函数。

__getattr__和__getattribute__
不同于特性和描述符只针对单个属性,__getattr____getattribute__(类似的还有__setattr__,__delattr__)更通用:

  • __getattr__针对未定义的属性运行–也就是说,属性没有存储在实例上,或者没有从其类之一继承。
  • __getattribute__针对每个属性,因此,当使用它的时候,必须小心避免通过把属性访问传递给超类而导致递归循环。
def __getattr__(self,name): ..#获取未定义的属性时运行,如obj.name
def __getattribute__(self,name):...#获取所有属性时运行,如obj.name
def __setattr__(self, name,value):...#设置所有属性时运行,如obj.name=value
def __delattr__(self,name):...#删除属性时运行。如del obj.name

避免属性拦截方法中的循环

  1. 利用命名空间字典。
def __setattr__(self,name,value):
    self.__dict__['other'] = value #ok
    self.other = value #bad,会再次调用__setattr__从而导致死循环。

2.利用超类方法调用。

def __getattribute__(self, name):
    x = self.other #bad,self.other会再次调用__getattribute__,从而导致死循环
    x = object.__getattribute__(self,'other') #ok,利用超类方法调用 没问题

拦截内置操作属性
对于隐式地使用内置操作获取的方法名属性,这些方法(__getattr____getattribute__等)可能根本不会运行。这意味着操作符重载方法调用不能委托给被包装的对象,除非包装类自己重新定义这些方法。

如针对__str____add____getitem__方法的属性获取分别通过打印、+表达式和索引隐式运行,而不会指向Python3.0中的类属性拦截方法。特别是:

  • 在Python3.0中,__getattr____getattribute__都不会针对这样的属性而运行。
  • 在Python2.6中,如果属性在类中未定义的话,__getattr__会针对这样的属性运行。
  • 在Python2.6中,__getattribute__只对于新式类可用,并且在Python3.0中也可以使用。

在Python2.X中,这样的操作调用的方法在运行时从实例中查找,就像所有其他属性一样;在Python3.0中,这样的方法在中查找。

第38章 装饰器

装饰是为函数和类指定管理代码的一种方式。装饰器本身的形式是处理其他的可调用对象的可调用对象(如函数)。
装饰器提供了一种方法,在函数和类定义语句的末尾插入自动运行代码

通过针对随后的调用安装包装器对象可以实现:

  • 函数装饰器安装包装器对象,以在需要的时候拦截随后的函数调用并处理它们。
  • 类装饰器安装包装器对象,以在需要的时候拦截随后的实例创建调用并处理它们。

Flask Web开发 基于Python的Web应用开发实战 这本书里有好多装饰器的使用,可以参考下。

为什么使用装饰器?

  1. 装饰器有一种非常明确的语法,这使得它们比那些可能任意地远离主体函数或类的辅助函数调用更容易为人们发现。
  2. 当主体函数或类定义的时候,装饰器应用一次;在对类或函数的每次调用的时候,不必添加额外的代码。
  3. 由于前面两点,装饰器使得一个API的用户不太可能忘记根据API需要扩展一个函数或类。

函数装饰器是一种关于函数的运行时声明,函数的定义需要遵守此声明。
装饰器在紧挨着定义一个函数或方法的def语句之前的一行编写,并且它由@符号以及紧随其后的对于元函数的一个引用组成–这是管理另一个函数的一个函数。

在编码方面,函数装饰器自动将如下的语法:

@decorator #Decorate function
def F(arg):
    ...
F(99) #调用函数

映射为这一对等的形式,其中装饰器是一个单参数的可调用对象,它返回与F具有相同数目的参数的一个可调用对象:

def F(arg):
    ...

F = decorator(F) #rebind function name to decorator result
F(99) # Essentially calls decorator(F)(99)

这一自动名称重绑定在def语句上有效,不管它针对一个简单的函数或是类中的一个方法。当随后调用F函数的时候,它自动调用装饰器所返回的对象,该对象可能是实现了所需的包装逻辑的另一个对象,或者是最初的函数本身。

装饰器自身是一个返回可调用对象的可调用对象
有一种常用的编码模式–装饰器返回了一个包装器,包装器把最初的函数保持到一个封闭的作用域中:

当随后调用名称func的时候,它硬实调用装饰器所返回的包装器函数;随后包装器函数可能会运行最初的func,因为它在一个封闭的作用域中仍然可以使用。当以这种方式编码的时候,每个装饰器的函数都会产生一个新的作用域来保持状态。

我们也可以通过对类来重载call方法,从而把类转成一个可调用对象,并且使用实例属性而不是封闭的作用域:

有一点需要注意,通过类实现的装饰器对象并不能工作在类方法上。
因为:当一个方法名绑定只是绑定到一个简单的函数时,Python向self传递了隐含的主体实例;当它是一个可调用类的实例的时候,就传递这个类的实例。
从技术上讲,当方法是一个简单函数的时候,Python只是创建了一个绑定的方法对象,其中包含了主体实例。
反而是利用封闭作用域的嵌套函数工作的更好,既能支持简单函数,也能支持实例方法。

类装饰器和函数装饰器很类似,只不过管理的是类。
通过函数实现,返回了一个包装器类。

每个被装饰的类都创建一个新的作用域,它记住了最初的类。

工厂函数通常在封闭的作用域引用中保持状态,类通常在属性中保持状态。

需要注意通过类实现的类装饰器,看如下的错误示例:

每个被装饰的类都返回了一个Decorator的实例。
但是对给定的类创建多个实例时出问题了—会对一个Decorator实例反复调用call方法,从而后面的的实例创建调用都覆盖了前面保存的实例。。(也许我们可以利用这个特性来实现单例模式??)

装饰器嵌套
为了支持多步骤的扩展,装饰器语法允许我们向一个装饰的函数或方法添加包装器逻辑的多个层。
这种形式的装饰器语法:

@A
@B
@C 
def f(...):
    ...

如下这样运行:

def f(...):
    ...

f = A(B(C(f)))

类装饰器类似。。

装饰器参数
函数装饰器和类装饰器似乎都能接受参数,尽管实际上这些参数传递给了真正返回装饰器的一个可调用对象,而装饰器反过来又返回了一个可调用对象。例如,如下代码:

@decorator(A,B)
def F(arg):
    ...
F(99)

自动地映射到其对等的形式,其中装饰器是一个可调用对象,它返回实际的装饰器。返回的装饰器反过来返回可调用的对象,这个对象随后运行以调用最初的函数名:

def F(arg):
    ...
F = decorator(A,B)(F) #Rebind F to result of decorator's return value
F(99) #Essentially calls decorator(A,B)(F)(99)

装饰器参数在装饰发生之前就解析了,并且它们通常用来保持状态信息供随后的调用使用。
例如,这个例子中的装饰器函数,可能采用如下的形式:

def decorator(A,B):
    #save or use A,B
    def actualDecorator(F):
        #Save or use function F
        #Return a callable:nested def, class with __call__, etc.
        return callable
    return actualDecorator

换句话说,装饰器参数往往意味着可调用对象的3个层级:接受装饰器参数的一个可调用对象,它返回一个可调用对象以作装饰器,该装饰器返回一个可调用对象来处理对最初的函数或类的调用。这3个层级的每一个都可能是一个函数或类,并且可能以作用域或类属性的形式保存了状态。

装饰器管理函数和类
装饰器不光可以管理随后对函数和类的调用,还能管理函数和类本身。如下所示,返回函数和类本身:

def decorator(o):
    #Save or augment function or class o
    return o
    
@decorator
def F():... #F=decorator(F)

@decorator
class C:... #C = decorator(C)

函数装饰器有几种办法来保持装饰的时候所提供的状态信息,以便在实际函数调用过程中使用:

  1. 实例属性。
  2. 全局变量
  3. 非局部变量
  4. 函数属性。

在运用描述符的情况下,我们也能把通过类实现的装饰器运用到 类方法上,只是有点复杂,如下所示:

当person实际调用giveRaise的时候,先是获取giveRaise属性会触发描述符tracer的get调用。get返回了wrapper对象。而wrapper对象又保持了tracer实例和person实例。
当调用giveRaise时,其实是调用的wrapper实例的call方法。wrapper实例的call方法又回调
tracer的call方法,利用wrapper保持的person实例把person实例当成参数也传了回去。
调用顺序如下:

person.giveRaise()->wrapper.__call__()->tracer.__call__()

这个例子中把wrapper类改成嵌套的函数也可以,而且代码量更少,如下:

类装饰器(函数装饰器)的两个潜在缺陷:

  • 类型修改。当插入包装器的时候,一个装饰器函数或类不会保持其最初的类型–其名称重新绑定到一个包装器对象,在使用对象名称或测试对象类型的程序中,这可能会很重要。
  • 额外调用。通过装饰添加一个包装层,在每次调用装饰对象的时候,会引发一次额外调用所需要的额外性能成本–调用是相对耗费时间的操作。

与管理器(即辅助)函数解决方案相比,装饰器提供:

  • 明确的语法
  • 代码可维护性
  • 一致性

函数内省

参数假设

  • 在调用中,所有的位置参数出现在所有关键字参数之前。
  • 在def中,所有的非默认参数出现在所有的默认参数之前。

装饰器参数 VS 函数注解
利用函数注解,可以简化功能的实现:因为状态信息现在在函数自身,装饰器不再需要一层封闭的作用域来保持状态。
对比下装饰器参数代码和函数注解的代码:

第39章 元类

从某种意义上讲,元类只是扩展了装饰器的代码插入模式。
元类主要是针对那些构建API和工具供他人使用的程序员。

Python构建工具:

  1. 内省属性。如__class__、__dict__
  2. 运算符重载方法。如__str__、__add__
  3. 属性拦截方法。如__getattr__、__setattr__、__getattribute__。
  4. 类特性。内置函数property。拦截特定的属性。
  5. 类属性描述符。拦截特定的属性。特定只是定义根据访问自动运行函数的属性描述符的一种简洁方式。
  6. 函数和类装饰器。
  7. 元类。

元类允许我们在在一条class语句的末尾,插入当创建一个类对象的时候自动运行的逻辑。
这个逻辑不会把类名重新绑定到一个装饰器可调用对象,而是把类自身的创建指向特定的逻辑。

和类装饰器不同,它通常是添加实例创建时运行的逻辑,元类在类创建时运行。
同样的,它们都是通常用来管理或扩展类的钩子,而不是管理其实例。

通过声明一个元类,我们告诉Python把类对象的创建路由到我们所提供的另一个类:

由于创建类的时候,Python在class语句的末尾自动调用元类,因此它可以根据需要扩展、注册或管理类。

类也是某物的实例:

  • 在Python3.0中,用户定义的类对象是名为type的对象的实例,type本身是一个类。
  • 在Python2.6中,新式类继承自object,它是type的一个子类;传统类是type的一个实例,并且并不创建自一个类。
    实例创建自类,而类创建自type。
    类是类型,类型也是类
  • 类型由派生自type的类定义。
  • 用户定义的类是类型类的实例。
  • 用户定义的类是产生它们自己的实例的类型。

类根本不是一个独立的概念:它们就是用户定义的类型,并且type自身也是由一个类定义的。

由于类实际上是type类的实例,从type的定制的子类创建类允许我们实现各种定制的类。
在Python3.0中以及在Python2.6的新式类中:

  • type是产生用户定义的类的一个类。
  • 元类是type类的一个子类。
  • 类对象是type类的一个实例,或一个子类。
  • 实例对象产生自一个类。

换句话说,为了控制创建类以及扩展其行为的方式,我们所需要做的只是指定个用户定义的类创建自一个用户定义的元类,而不是常规的type类。

从技术上讲,Python遵从一个标准的协议来使这发生:在一条class语句的末尾,并且在运行了一个命名控件词典中的所有嵌套代码之后,它调用type对象来创建class对象:

class = type(classname, superclasses,attrbuteddict)

type对象反过来定义了一个__call__运算符重载方法,当调用type对象的时候,该方法运行两个其他方法:

type.__new__(typeclass, classname,superclasses,attributeddict)
type.__init__(class,classname,superclasses,attributedict)

__new__方法创建并返回了新的class对象,并且随后__init__方法初始化了新创建的对象。
这是type的元类子类通常用来定制类的钩子。

尽管重新定义type超类的__new____init__方法是元类向类对象创建过程插入逻辑的最常见方法,其他方案也是可能的。
实际上任何可调用对象都可以用作一个元类,只要它接收传递的参数并且返回与目标类兼容的一个对象。

另外,我们也可以重定义元类的__call__, 以拦截创建调用。

实例与继承的关系

  • 元类继承自type类。
  • 元类声明由子类继承。在用户定义的类中,metaclass=M声明由该类的子类继承,因此,对于在超类链中继承了这一声明的每个类的构建,该元类都将运行。
  • 元类属性没有由类实例继承。元类声明指定了一个实例关系,它和继承不同。由于类是元类的实例,所以元类中定义的行为应用于类,而不是类随后的实例。实例从它们的类和超类获取行为,但是,不是从任何元类获取行为。从技术上讲,实例属性查找通过只是搜索实例及期所有类的__dict__字典;元类不包含在实例查找中。

元类与类装饰器在功能上有重合。

  • 在class语句末尾,类装饰器把类名重新绑定到一个函数的结果。
  • 元类通过在一条class语句的末尾把类对象创建过程路由到一个对象来工作。

python标准库

python标准库

前言

在这之前已经学过《廖雪峰的python教程》,也看过了《flaskweb实战》,之前还看过《head first in python》,又因为对黑客的好奇,看过了《python绝技:运用python成为顶级黑客》这本书。
熟悉语法之后,接下来其实是熟悉标准库。前面看的书,只是勾起我的兴趣。兴趣才是最大的老师呀。
下面是我划分的学习阶段:
语法大概只占了一门语言的1%,然后还有5%是官方库,10%是第三方库, 10%是开源的框架和SDK, 剩下的是背后的思想,算法,操作系统,协议什么的。

廖雪峰的python教程和《head first in python》其实对语法讲的都不细致。只能说看完之后能写代码了,遇到问题再去搜索。暂时选择标准库来看,回头还要再补习全面的语法。

评价

书有点老,10年的,不过不长,只有329页。到处找了下,貌似都是10/06/07的那本。
书上的例子只有2.0下运行通过。我用的2.7,除了极个别的,都运行通过了。
讲的并不深入,就是对python的标准模块有个了解,有个大概的印象。基本上每个模块都给了个简单的例子。
还是推荐阅读下,全面了解下python标准模块对于阅读理解各种第三方模块的源码很有帮助。我个人感觉到的就是python还真是强大,各种功能都有,确实是unix管理者和黑客爱的玩意儿。我估计也会在这条路上走的越来越远。(虽然以前也写了好几年的php,objective-c,java,但从来没有这么认真过。_)
可以搭配着《unix环境高级编程》一起阅读,要不然有些概念是看了完全没有印象的。
代码已经入github

内容

sys 模块可以让你访问解释器相关参数,比如模块搜索路径,解释器版本号等.
operator 模块提供了和内建操作符作用相同的函数.
copy 模块允许你复制对象

核心模块

内置模块__builtin__

apply, 感觉和js中的apply一样。python中的参数其实大体上只有两种形式,一个是位置参数,另一个是关键字参数。函数参数可以参考廖雪峰的函数的参数

__import__ import 语句,其实是调用的内建的 import 函数。可以根据字符串动态地把module import进来,有啥用呢? 很多框架里面其实都用到了这个。假设你的框架里用了MVC,有个controlers目录,你就可以遍历这个目录,把定义好的名字如UserController.py,等 动态地全部import进来。。而不用一个一个手动去import。

reload有个注意事项:

注意,当你重加载模块时, 它会被重新编译, 新的模块会代替模块字典里的老模 块. 但是, 已经用原模块里的类建立的实例仍然使用的是老模块(不会被更新).
同样地, 使用 from-import 直接创建的到模块内容的引用也是不会被更新的.

dir,没啥好说,看帮助文档就用dir和help。

vars, 这个感觉有也用。运用的好,可以极大地减少代码量。

type 函数,我感觉这个更多地是做类型检查。可以加强代码的健壮性。同时也增加了动态的能力。

__call__ 使得类实现变成callable, 具体实现可以参见廖雪峰的Day 5 – 编写Web框架, 这个框架可以看看,其它框架其实也是这个思路。

你不能使用 type 函数来测试一个实例是否属于一个给定的类; 所有 的实例都是同样的类型! 为解决这个问题,有isinstance。
issubclass 检查的是类型树。

eval,js中也有eval,作用相同。把字符串当成脚本执行。同样都要注意安全问题,字符串可能包含恶意代码。
在python中可以是删除文件。 在js中可以是直接把cookie信息发送到另一个服务器。
在python中,可以通过第二个参数,限定eval能使用的模块。

eval处理简单的表达式,复杂的得用 compileexec函数。

execfile函数,提供了一个从文件加载代码,编译代码,执行代码 快捷方式。

因为 Python 在检查局部名称空间和模块名称空间前不会检查内建函数, 所以 有时候你可能要显式地引用 __builtin__ 模块.(注:就是说你定义的名字,覆盖掉了__builtin__的东西,想调用原来的东西就得显示引用__builtin__模块)

exceptions模块

这个我还是建议了解下,当你自定义的异常选择了恰当的父类,会显得你非常专业。
该模块定义了以下标准异常:

  • Exception 是所有异常的基类. 强烈建议(但不是必须)自定义的异常异 常也继承这个类.
  • SystemExit(Exception) 由 sys.exit 函数引发. 如果它在最顶层没有 被 try-except 语句捕获, 那么解释器将直接关闭而不会显示任何跟踪 返回信息.
  • StandardError(Exception) 是所有内建异常的基类(除 SystemExit 外).
  • KeyboardInterrupt(StandardError) 在用户按下 Control-C(或其他打 断按键)后 被引发. 如果它可能会在你使用 “捕获所有” 的 try-except 语句时导致奇怪的问题.
  • ImportError(StandardError) 在 Python 导入模块失败时被引发.
  • EnvironmentError 作为所有解释器环境引发异常的基类. (也就是说,
    这些异常一般不是由于程序 bug 引起).
  • IOError(EnvironmentError) 用于标记 I/O 相关错误.
  • OSError(EnvironmentError) 用于标记 os 模块引起的错误.
  • WindowsError(OSError) 用于标记 os 模块中 Windows 相关错误.
  • NameError(StandardError) 在 Python 查找全局或局部名称失败时被引
    发.
  • UnboundLocalError(NameError) , 当一个局部变量还没有赋值就被使用
    时, 会引发这个异常. 这个异常只有在 2.0 及之后的版本有; 早期版本
    只会引发一个普通的 NameError .
  • AttributeError(StandardError) , 当 Python 寻找(或赋值)给一个实
    例属性, 方法, 模块功能或其它有效的命名失败时, 会引发这个异常.
  • SyntaxError(StandardError) , 当解释器在编译时遇到语法错误, 这个
    异常就被引发.
  • (2.0 及以后版本) IndentationError(SyntaxError) 在遇到非法的缩进
    时被引发. 该异常只用于 2.0 及以后版本, 之前版本会引发一个 SyntaxError 异常.
  • (2.0 及以后版本) TabError(IndentationError) , 当使用 -tt 选项检 查不一致缩进时有可能被引发. 该异常只用于 2.0 及以后版本, 之前版 本会引发一个 SyntaxError 异常.
  • TypeError(StandardError) , 当给定类型的对象不支持一个操作时被引 发.
  • AssertionError(StandardError) 在 assert 语句失败时被引发(即表达 式为 false 时).
  • LookupError(StandardError) 作为序列或字典没有包含给定索引或键时 所引发异常的基类.
  • IndexError(LookupError) , 当序列对象使用给定索引数索引失败时(不 存在索引对应对象)引发该异常.
  • KeyError(LookupError) 当字典对象使用给定索引索引失败时(不存在索 引对应对象)引发该异常.
  • ArithmeticError(StandardError) 作为数学计算相关异常的基类.
  • OverflowError(ArithmeticError) 在操作溢出时被引发(例如当一个整
    数太大, 导致不能符合给定类型).
  • ZeroDivisionError(ArithmeticError) , 当你尝试用 0 除某个数时被
    引发.
  • FloatingPointError(ArithmeticError) , 当浮点数操作失败时被引发.
  • ValueError(StandardError) , 当一个参数类型正确但值不合法时被引
    发.
  • (2.0 及以后版本) UnicodeError(ValueError) , Unicode 字符串类型相
    关异常. 只使用在 2.0 及以后版本.
  • RuntimeError(StandardError) , 当出现运行时问题时引发, 包括在限
    制模式下尝试访问外部内容, 未知的硬件问题等等.
  • NotImplementedError(RuntimeError) , 用于标记未实现的函数, 或无
    效的方法.
  • SystemError(StandardError) , 解释器内部错误. 该异常值会包含更多
    的细节 (经常会是一些深层次的东西, 比如 “eval_code2: NULL globals” ) . 这本书的作者编了 5 年程序都没见过这个错误. (想必是 没有用 raise SystemError ).
  • MemoryError(StandardError) , 当解释器耗尽内存时会引发该异常. 注 意只有在底层内存分配抱怨时这个异常才会发生; 如果是在你的旧机器 上, 这个异常发生之前系统会陷入混乱的内存交换中

os模块

封装了操作系统细节,跨平台用的。
open/file 处理文件
listdir 列出目录列表,需要注意的是并没有包含”.”(当前目录)和”..”(上级目录)两个目录。
getcwdchdir 函数分别用于获得和改变当前工作目录.
makedirsremovedirs 函数用于创建或删除目录层。removedirs只能移除空目录。如果目录里有文件,会抛异常。(如果需要删除非空目录, 你可以使用shutil 模块中的 rmtree 函数)
mkdirrmdir 函数只能处理单个目录级
statfstat 函数可以用来获取一个存在文件的信息.(在管道中文件是没有名字的,只能用fstat)
chmodutime 函数修改文件的权限模式和时间属性
system 函数在当前进程下执行一个新命令, 并等待它完成。其实system 就是打开一个新进程(fork),新进程执行命令(exec),父进程等待子进程完成(wait),拿到输出结果。
exec 函数会使用新进程替换当前进程。基本上exec簇总是和fork在一起的。Flask Web开发 基于Python的Web应用开发实战这本书里,巧妙地在设置环境变量后用exec函数重启应用。

所有的守护进程都没有控制终端,其终端名设置为问号
创建守护进程的方法很简单,先fork复制一个进程,然后把自己退出。这样子进程就成了孤儿进程。
完整的经验规则是:

   1. 首先要做的是调用umask将文件模式创建屏蔽字设置为一个已知值(通常是0)。由继承得来的文件模式创建屏蔽字
      可能会被设置为拒绝某些权限。
   2. 调用fork,然后使父进程exit。这样做实现了下面几点。第一,如果该守护进程是作为一条简单的shell命令启动的,那么
      父进程终止会让shell认为这条命令已经执行完毕。第二,虽然子进程继承了父进程的进程组ID,但获得了一个新的进程ID,
      这就保证了子进程不是一个进程组的组长长进程。这是下面将要进得的setsid调用的先决条件。
   3. 调用setsid创建一个新会话。使进程:a 成为新会话的首进程 b、成为一个新进程组的组长进程。c、没有控制终端
   4. 将当前工作目录更改为根目录。从父进程处继承过来的当前工作目录可能是一个挂载的文件系统中。
      因为守护进程通常是在系统再引导之前是一直存在的,所以如果守护进程的当前工作目录在一个挂载文件系统中,
      那么该文件系统就不能被卸载。
   5. 关闭不再需要的文件描述符。
   6. 某些守护进程打开/dev/null使其具有文件描述符0、1、2.这样,任何一个试图读标准输入、写标准输出或标准错误的库例程
      都不会产生任何效果。

exit在被捕获异常的情况下,并不会真的终止进程,而_exit则会。

os.path处理文件名

os.path 模块包含了许多与平台无关的处理长文件名的函数. 也就是说, 你不需要处理前后斜杠, 冒号等
expanduser 把目录中的”~”,解析成实际的用户目录。
expandvars 函数将文件名中的环境变量替换为对应值。
walk 函数会帮你找出一个目录树下的所有文件。感觉类似于find命令。至少可以很方便地利用walk实现find命令。
os.listdir 只列表一级目录。再往下得自己去递归。
文中的DirectoryWalker 并不是generator,而是因为实现了getitem方法,成为了可迭代对象。

其它模块

string, 字符串估计是要打交道最多的模块了。用的时候,用dir(string)看下有哪些功能。平时用的更多是字符串方法。
re, 正则表达式语法没啥。关键是知道正达式是啥,语法反而不重要。推荐看下《精通正则表达式》,讲的很透彻。这里要吐槽下Java的正则表达式。。有次项目里居然在正则表达式这里挂了,因为匹配失败,居然迭代了几十亿次,直接导致后面的代码没机会执行(参考:http://blog.csdn.net/shixing_11/article/details/5997567),阿里还有人专门自己写了个java的正则表达式。
math 模块实现了许多对浮点数的数学运算函数. 这些函数一般是对平台 C 库 中同名函数的简单封装
cmath 模块实现复数的计算。
operator 模块为 Python 提供了一个 “功能性” 的标准操作符接口
需要注意的是单个字符也是序列。在使用时得注意下。
copy 模块提供拷贝功能。分浅拷贝和深拷贝。copy.copy是浅拷贝,copy.deepcopy是深拷贝。
sys 模块提供了许多函数和变量来处理 Python 运行时环境的不同部分。在命令行脚本中用提比较多。

    * sys.argv, sys.path, sys.builtin_module_names,sys.modules。标准库可以多用下help和        dir来获取信息。
    * python通过引用计数来管理的内存,引用计数可以通过sys.getrefcount获取到。
    * 可以通过sys.platform判断当前是啥系统,然后写针对该系统的特定代码。
    * sys.setprofile函数允许你配置一个分析函数(profiling function). 这个函数 会在每次调用      某个函数或方法时被调用(明确或隐含的), 或是遇到异常的时候 被调用。适宜做分析工具,基于该函     数, profile 模块提供了一个完整的分析器框架.
    * sys.settrace, pdb基于这个实现的。
    * sys.stdin,sys.stdout,sys.stderr 要重定向输出只要创建一个对象, 并实现它的 write 方法.
    * sys.exit退出程序,可捕获。
    * sys.exitfunc,可以在exit之前做一些清理操作。。感觉是那些不会自动释放的资源有必要用这个东西来做。

atexit
对sys.exitfunc封装了一下,可以方便地注册多个退出钩子函数。后注册的先调用。
time模块 提供了获取当前时间,字符串到时间对象的相互转换。
types 模块包含了标准解释器定义的所有类型的类型对象
gc 模块,提供了到内建循环垃圾收集器的接口。gc.collect用来回收自引用的数据结构的对象。

更多标准模块

  • fileinput 模块允许你循环一个或多个文本文件的内容。利用glob.glob可以模糊搜索文件,然后利用fileinput模块可以把文件统一处理。
  • shutil 模块 shell util?shutil 实用模块包含了一些用于复制文件和文件夹的函数.可以删除非空目录哦,os.removedirs可不行。
  • tempfile模块。tempfile.mktemp其实并不安全,在获取临时文件名和创建文件之间有个时间窗口,在这期间可能有其它程序创建了同名文件。不如用tempfile.TemporaryFile()
  • StringIO 模块。提供了内建文件一样的函数,区别是在内存中执行,速度会很快。当然由于受到内存的限制,不能处理过大的文件。
  • cStringIO 模块是StringIO更快速的实现。
  • mmap 模块提供了操作系统内存映射函数的接口。把文件的数据 映射到一块内存操作。效率很高。具体概念请看内存映射文件原理
  • UserDict 模块包含了一个可继承的字典类 (事实上是对内建字典类型的 Python 封装).
  • UserList 模块包含了一个可继承的列表类 (事实上是对内建列表类型的 Python 封装).
  • UserString 模块包含两个类, UserString 和 MutableString .MutableString性能不高,不如用array模块
  • traceback 模块允许你在程序里打印异常的跟踪返回 (Traceback)信息, 类似未捕获异常时解释器所做的。可以使用 traceback.extract_tb 函数格式化跟踪返回信息, 得到包含错误信息的列表
  • errno 模块定义了许多的符号错误码, 比如 ENOENT (“没有该目录入口”) 以及 EPERM (“权限被拒绝”).
  • getopt 模块包含用于抽出命令行选项和参数的函数, 它可以处理多种格式的选 项。在python绝技:运用python成为顶级黑客中用的挺多,不过书里用的是optparse,现在应该用getopt了。
  • getpass 模块提供了平台无关的在命令行下输入密码的方法. 在做需要命令行用户认证时有用。
  • glob 根据给定模式生成满足该模式的文件名列表, 和 Unix shell 相同.星号(* ) 匹配零个或更多个字符, 问号(? ) 匹配单个字符. 你也可以使用方括号来指定字符范围, 例如 [0-9] 代表一个数字. 其他所有字符都代表它们本身。
  • fnmatch 模块使用模式来匹配文件名. glob是根据模式返回文件名列表。fnmatch是用模式来判断文件名是否匹配模式,返回True/False。fnmatch.translate可以把模式转成正则表达式。glob 和 find 模块在内部使用 fnmatch 模块来实现.
  • random 模块包含许多随机数生成器。choice随机挑选,shuffle打乱。gauss (高斯)函数来生成满足高斯分的布随机数字(是伪随机生成器,并不适合密码学用途)
  • md5 (Message-Digest Algorithm 5)模块用于计算信息密文(信息摘要). 新版用hashlib。工作中基本是用的16进制的,即hexdigest。
  • sha 模块提供了计算信息摘要(密文)的另种方法。已过期,新版用hashlib。
  • crypt 模块实现了单向的 DES 加密, Unix 系统使用这个 加密算法来储存密码, 这个模块真正也就只在检查这样的密码时有用.密码验证一定得用不可逆的加密算法,这样即使暴库,也不至于泄露密码。即使这样也应该提供salt,不同的密码用不同的salt,这样防止别人搞一个md5库和crypt库暴力反解(彩虹库暴力破解)。crypt-example-2.py在mac上测试失败,密码都是*号,同时获取密码最不好不用raw_input,用getpass模块比较好,不会在屏幕上回显密码
  • zlib 模块为 “zlib” 压缩提供支持。compress 和 decompress 提供压缩和解压。在python绝技:运用python成为顶级黑客中用到了,用来暴力破解。在内容少的时候,经过压缩文件反而变大了,呃。。
  • code 模块提供了一些用于模拟标准交互解释器行为的函数。类似功能的还有evalcompileexec函数、execfile模块

线程和进程

为了保证两个线程可以同时访问相同的内部数据, Python 使用了 global interpreter lock (全局解释器锁) . 在同一时间只可能有一个线程执行 Python 代码;(这意味着python不能运用到多核CPU的威力,真要用只能使用多进程了。)
threading 模块为线程提供了一个高级接口。
Queue 模块提供了一个线程安全的队列 (queue) 实现.
thread 模块提为线程提供了一个低级 (low_level) 的接口.注意当主程序退出的时候, 所有的线程也随着退出. 而 threading 模块不存在 这个问题 . (该行为可改变)
commands 模块(仅适应unix)包含一些用于执行外部命令的函数
pipes 模块(只用于 Unix)提供了 “转换管道 (conversion pipelines)” 的支持. 你可以创建包含许多外部工具调用的管道来处理多个文件.
popen2 模块允许你执行外部命令, 并通过流来分别访问它的 stdin 和 stdout ( 可能还有 stderr ). popen2已经被subprocess替代。
signal 模块配置你自己的信号处理器 (signal handler)

数据表示

marshal 和 pickle 模块用于在不同的 Python 程序间共享/传递数据.
marshal 模块使用了简单的自描述格式( Self-Describing Formats ), 它支持 大多的内建数据类型, 包括 code 对象. Python 自身也使用了这个格式来储存 编译后代码( .pyc 文件).
pickle 模块提供了更复杂的格式, 它支持用户定义的类, 自引用数据结构等等. pickle 是用 Python 写的, 相对来说速度较慢, 不过还有一个 cPickle 模块, 使用 C 实现了相同的功能, 速度和 marshal 不相上下.

  • array 模块实现了一个有效的阵列储存类型. 阵列和列表类似, 但其中所有的 项目必须为相同的类型. 该类型在阵列创建时指定.
  • struct 模块用于转换二进制字符串和 Python 元组. pack 函数接受格式字符串以及额外参数, 根据指定格式将额外参数转换为二进制字符串. upack 函数 接受一个字符串作为参数, 返回一个元组.
  • xdrlib 模块用于在 Python 数据类型和 Sun 的 external data representation (XDR) 间相互转化
  • marshal 模块可以把不连续的数据组合起来 – 与字符串相互转化, 这样它们就 可以写入文件或是在网络中传输.
  • pickle 模块同 marshal 模块相同, 将数据连续化, 便于保存传输. 它比 marshal 要慢一些, 但它可以处理类实例, 共享的元素, 以及递归数据结构等,pickle 不能处理 code 对象
  • cPickle 模块是针对 pickle 模块的一个更快的实现.
  • copy_reg 模块注册你自己的扩展类型.这样 pickle 和 copy 模 块就会知道如何处理非标准类型.
  • pprint 模块( pretty printer )用于打印 Python 数据结构.
  • repr 模块提供了内建 repr 函数的另个版本. 它限制了很多(字符串长度, 递 归等).
  • base64 base64 编码体系用于将任意二进制数据转换为纯文本. 它将一个 3 字节的二 进制字节组转换为 4 个文本字符组储存, 而且规定只允许以下集合中的字符出 现:
    ABCDEFGHIJKLMNOPQRSTUVWXYZ
    abcdefghijklmnopqrstuvwxyz
    0123456789+/
    另外, = 用于填充数据流的末尾. 有时我们会把base64作为url的参数传输,这时要注意末尾的=号要处理下。
  • binhex 模块用于到 Macintosh BinHex 格式的相互转化.
  • quopri 模块基于 MIME 标准实现了引用的可打印编码
  • uuuu 编码体系用于将任意二进制数据转换为普通文本格式. 该格式在新闻组中很 流行, 但逐渐被 base64 编码取代.
  • binascii binascii 提供了多个编码的支持函数, 包括 base64 , binhex , 以及 uu

文件格式

  • xmllib 模块. 已经被xml.sax替代。
  • xml.parsers.expat 模块是 James Clark’s Expat XML parser 的接口.
  • sgmllib 模块, 提供了一个基本的 SGML 语法分析器. 它与 xmllib 分析器基 本相同, 但限制更少(而且不是很完善).
  • htmlib 模块包含了一个标签驱动的( tag-driven ) HTML 语法分析器, 它会将数据发送至一个格式化对象.
  • htmlentitydefs 模块包含一个由 HTML 中 ISO Latin-1 字符实体构成的字典.
  • formatter 模块提供了一些可用于 htmllib 的格式类( formatter classes ).这些类有两种, formatter 和 writer . formatter 将 HTML 解析器的标签和数 据流转换为适合输出设备的事件流( event stream ), 而 writer 将事件流输出 到设备上.
  • ConfigParser 模块用于读取配置文件.格式类似于ini文件。
  • netrc 模块可以用来解析 .netrc 配置文件。该文件 用于在用户的 home 目录储存 FTP 用户名和密码. (别忘记设置这个文件的属 性为: “chmod 0600 ~/.netrc,” 这样只有当前用户能访问).
  • shlex 模块为基于 Unix shell 语法的语言提供了一个简单的 lexer (也就是 tokenizer).
  • zipfile 模块可以用来读写 ZIP 格式.
  • gzip 模块用来读写 gzip 格式的压缩文件.

邮件和新闻消息处理

  • rfc822 模块包括了一个邮件和新闻组的解析器 (也可用于其它符合 RFC 822 标准的消息, 比如 HTTP 头).
    通常, RFC 822 格式的消息包含一些标头字段, 后面至少有一个空行, 然后是信 息主体.
  • mimetools 模块包含一些读写 MIME 信息的工具. 它还提供了一个类似 rfc822 模块中 Message 的类, 用于处理 MIME 编码的信息.
  • MimeWriter 模块用于生成符合 MIME 邮件标准的 “multipart” 的信息。已被email模块替代。
  • mailbox 模块用来处理各种不同类型的邮箱格式。
  • mailcap 模块用于处理 mailcap 文件, 该文件指定了不同的文档格式的处理方 法( Unix 系统下).
  • mimetypes 模块可以判断给定 url ( uniform resource locator , 统一资源定 位符) 的 MIME 类型. 它基于一个内建的表, 还可能搜索 Apache 和 Netscape 的配置文件
  • packmail 模块可以用来创建 Unix shell 档案. 已经废弃
  • mimify 模块用于在 MIME 编码的文本信息和普通文本信息(例如 ISO Latin 1 文本)间相互转换.已被email模块替代。
  • multifile 模块允许你将一个多部分的 MIME 信息的每部分作为单独的文件处理2.5之后废弃

网络协议(这个是web开发需要关注的重点)

  • socket 模块实现了到 socket 通讯层的接口. 你可以使用该模块创建客户端或 是服务器的 socket
  • select 模块允许你检查一个或多个 socket , 管道, 以及其他流兼容对象所接 受的数据。
  • asyncore 模块提供了一个 “反馈性的( reactive )” socket 实现. 该模块允许 你定义特定过程完成后所执行的代码, 而不是创建 socket 对象, 调用它们的方法. 你只需要继承 dispatcher 类, 然后重载如下方法 (可以选择重载某一 个或多个)就可以实现异步的 socket 处理器.
    • handle_connect : 一个连接成功建立后被调用.
    • handle_expt : 连接失败后被调用.
    • handle_accept : 连接请求建立到一个监听 socket 上时被调用. 回调时( callback )应该使用 accept 方法来获得客户端 socket .
    • handle_read : 有来自 socket 的数据等待读取时被调用. 回调时应该使用 recv 方法来获得数据.
    • handle_write : socket 可以写入数据的时候被调用. 使用 send 方法写入数据.
    • handle_close : 当 socket 被关闭或复位时被调用.
    • handle_error(type, value, traceback) 在任何一个回调函数发生Python 错误时被调用. 默认的实现会打印跟踪返回消息到 sys.stdout .
  • asynchat 模块是对 asyncore 的一个扩展. 它提供对面向行( line-oriented ) 的协议的额外支持. 它还提供了增强的缓冲区支持(通过 push 方法和 “producer” 机制.
  • urlib 模块为 HTTP , FTP , 以及 gopher 提供了一个统一的客户端接口. 它会 自动地根据 URL 选择合适的协议处理器.
  • urlparse 模块包含用于处理 URL 的函数, 可以在 URL 和平台特定的文件名间 相互转换
  • cookie 模块为 HTTP 客户端和服务器提供了基本的 cookie 支持.
  • robotparser 模块用来读取 robots.txt 文件. 做为一个有良心的爬虫,最好还是遵循下robot协议。
  • ftplib 模块包含了一个 File Transfer Protocol (FTP , 文件传输协议)客户 端的实现.
  • gopherlib 模块包含了一个 gopher 客户端实现.Gopher是Internet上一个非常有名的信息查找系统,它将Internet上的文件组织成某种索引,很方便地将用户从Internet的一处带到另一处。在WWW出现之前,Gopher是Internet上最主要的信息检索工具,Gopher站点也是最主要的站点,现在已经过时。
  • httplib 模块提供了一个 HTTP 客户端接口
  • poplib 模块(如 Example 7-28 所示) 提供了一个 Post Office Protocol( POP3 协议) 客户端实现. 这个协议用来从邮件服务器 “pop” (拷贝) 信息到 你的个人电脑.
  • imaplib 模块提供了一个 Internet Message Access Protocol ( IMAP, Internet 消息访问协议) 的客户端实现. 这个协议允许你访问邮件服务器的邮件目录, 就好像是在本机访问一样.
  • smtplib 模块提供了一个 Simple Mail Transfer Protocol (SMTP , 简单邮件 传输协议) 客户端实现. 该协议用于通过 Unix 邮件服务器发送邮件。 读取邮件请使用 poplib 或 imaplib 模块.
  • telnetlib 模块提供了一个 telnet 客户端实现.
  • nntplib 模块提供了一个网络新闻传输协议( Network News Transfer Protocol, NNTP,网络新闻传输协议)客户端的实现.
  • SocketServer 为各种基于 socket 的服务器提供了一个框架. 该模块提供了大量的类, 你可以用它们来创建不同的服务器.
  • BaseHTTPServer 模块,这是一个建立在 SocketServer 框架上的基本框架, 用于 HTTP 服务器.
  • SimpleHTTPServer 模块是一个简单的 HTTP 服务器, 它提供了标准的 GET 和 HEAD 请求处理器.
  • CGIHTTPServer 模块是一个可以通过公共网关接口( common gateway interface , CGI )调用外部脚本的 HTTP 服务器.
  • cgi 模块为 CGI 脚本提供了函数和类支持. 它还可以处理 CGI 表单数据.
  • webbrowser 模块提供了一个到系统标准 web 浏览器的接口. 哈。居然调起了chrome在打开页面。。

国际化

  • locale 模块提供了 C 本地化( localization )函数的接口
  • unicodedata 模块包含了 Unicode 字符的属性, 例如字符类别, 分解数据, 以及数值
  • ucnhash 模块为一些 Unicode 字符代码提供了特定的命名. 你可以直接使用 \N{} 转义符将 Unicode 字符名称映射到字符代码上.
    ##多媒体相关模块
  • imghdr 模块可识别不同格式的图片文件. 当前版本可以识别 bmp , gif , jpeg , pbm , pgm , png , ppm , rast (Sun raster), rgb (SGI), tiff , 以及 xbm 图 像.
  • sndhdr 模块, 可来识别不同的音频文件格式, 并提取文件内容相关信息
  • aifc 模块用于读写 AIFF 和 AIFC 音频文件(在 SGI 和 Macintosh 的计算机 上使用).
  • sunau 模块用于读写 Sun AU 音频文件
  • sunaudio 模块用于识别 Sun AU 音频文件, 并提取其基本信息. sunau 模块为 Sun AU 文件提供了更完成的支持.
  • wave 模块用于读写 Microsoft WAV 音频文件
  • audiodev 为 Sun 和 SGI 计算机提供了音频播放支持.(only unix)
  • winsound 模块允许你在 Winodws 平台上播放 Wave 文件.(only windows)

数据储存

Python 提供了多种相似数据库管理( database manager )的驱动, 它们的模型 都基于 Unix 的 dbm 库.

  • anydbm 模块为简单数据库驱动提供了统一标准的接口.anydbm 模块会自动寻找一个合适的数据库驱动, 按照 dbhash , gdbm , dbm , 或 dumbdbm 的顺序尝试. 如果没有找到任何模块, 它 将引发一个 ImportError 异常.
  • whichdb 模块可以判断给定数据库文件的格式。
  • shelve 模块使用数据库驱动实现了字典对象的持久保存. shelve 对象使用字 符串作为键, 但值可以是任意类型, 所有可以被 pickle 模块处理的对象都可 以作为它的值.
  • dbhash 模块为 bsddb 数据库驱动提供了一个 dbm 兼容的接口
  • dbm 模块提供了一个到 dbm 数据库驱动的接口(在许多 Unix 平台上都 可用)
  • dumbdbm 模块是一个简单的数据库实现, 与 dbm 一类相似, 但使用纯 Python 实现. 它使用两个文件: 一个二进制文件 (.dat ) 用于储存数据, 一个文本文 件 (.dir ) 用于数据描述.
  • gdbm 模块提供了到 GNU dbm 数据驱动的接口
    ##工具和实用程序
    标准库中有一些模块既可用作模块又可以作为命令行实用程序.
  • dis 模块是 Python 的反汇编器. 它可以把字节码转换为更容易让人看懂的格式。个人觉得要成为真正的大师,汇编一定得学学。我在mac上,python2.7 并不能直接dis.py hello.py, 改成python -m dis hello.py可以
  • pdb 模块是标准 Python 调试器( debugger ). 它基于 bdb 调试器框架.
  • bdb 模块为提供了一个调试器框架. 你可以使用它来创建自定义的调试器
  • profile 模块是标准 Python 分析器.
  • pstats 模块用于分析 Python 分析器收集的数据
  • tabnanny 模块用于检查 Python 源文件中的含糊的缩进. 当文件 混合了 tab 和空格两种缩进时候, nanny (保姆)会立即给出提示.
    ##其它模块
  • fcntl 模块(只用于 Unix)为 Unix 上的 ioctl 和 fcntl 函数提供了一个接口. 它们用于文件句柄和 I/O 设备句柄的 “out of band” 操作, 包括读取扩展属性, 控制阻塞. 更改终端行为等等.
  • pwd (只用于 Unix) 提供了一个到 Unix 密码/password “数据库”( /etc/passwd 以及相关文件 )的接口
  • grp 模块(只用于 Unix) 提供了一个到 Unix 用户组/group ( /etc/group )数 据库的接口.
  • nis 模块 This module contains functions for accessing NIS maps.
  • curses 模块 The curses module provides an interface to the curses library, the de-facto standard for portable advanced terminal handling.
  • termios 模块 (只用于 Unix , 可选) termios 为 Unix 的终端控制设备提供了一个接口. 它可用于控制终端通讯端口的大多方面.
  • tty 模块(只用于 Unix) 包含一些用于处理 tty 设备的工具函数。
  • resource 模块(只用于 Unix , 可选) 用于查询或修改当前系统资源限制设置.
  • syslog 模块(只用于 Unix 可选) 用于向系统日志设备发送信息( syslogd ). 这些信息如何处理依不同的系统而定, 通常会被记录在一个 log 文件中, 例如 /var/log/messages , /var/adm/syslog , 或者其他类似处理.
  • msvcrt 模块(只用于 Windows/DOS ) 用于访问 Microsoft Visual C/C++ Runtime Library (MSVCRT) 中函数的方法.
  • nt 模块(非直接使用模块, 只用于 Windows ) nt 模块是 os 模块在 Windows 平台下调 用的执行模块. 几乎没有任何原因直接使用这个模块, 请使用 os 模块替代.
  • _winreg 模块(只用于 Windows , 2.0 中新增) _winreg 模块提供了访问 Windows 注册表数 据库的一个基本接口.
  • posix 模块(非直接使用模块, 只用于 Unix/POSIX ) posix 模块是 os 模块在 Unix 及其 他 POSIX 系统下使用的实现模块. 一般只需要通过 os 模块访问它即可.

执行支持模块

  • dospath 模块提供了 DOS 平台下的 os.path 功能
  • macpath 模块( 参见 Example 13-2 )提供了 Macintosh 平台下的 os.path 功 能. 你也可以使用它在其他平台处理 Macintosh 路径.
  • ntpath 模块( 参见 Example 13-3 )提供了 Windows 平台下的 os.path 功能. 你也可以使用它在其他平台处理 Windows 路径.
  • posixpath 模块( 参见 Example 13-4 )提供了 Unix 和其他 POSIX 兼容平台下 的 os.path 功能
  • imp 模块包含的函数可以用于实现自定义的 import 行为
  • new 模块是一个底层的模块, 你可以使用它来创建不同的内建对象, 例如类对 象, 函数对象, 以及其他由 Python 运行时系统创建的类型.
  • py_compile 模块用于将 Python 模块编译为字节代码. 它和 Python 的 import 语句行为类似, 不过它接受文件名而不是模块名作为参数
  • compileall 模块用于将给定目录下(以及 Python path )的所有 Python 脚本编 译为字节代码. 它也可以作为可执行脚本使用(在 Unix 系统下, Python 安装 时会自动调用执行它).
  • ihooks 模块为替换导入提供了一个框架. 这允许多个导入机制共存.
  • linecache 模块用于从模块源文件中读取代码. 它会缓存最近访问的模块 (整 个源文件).traceback 模块使用这个模块实现了对导入操作的跟踪.
  • macurl2path 模块用于 URL 和 Macintosh 文件名的相互映射. 一般没有必要直接使用它, 请使用 urllib 中的机制.
  • nturl2path 模块用于 URL 和 Windows 文件名的相互映射.
  • tokenize 模块将一段 Python 源文件分割成不同的 token . 你可以在代码高亮工具中使用它.
  • keyword 模块有一个包含当前 Python 版本所使用的关键字的列表. 它还提供了一个字典, 以关键字作为key, 以一个描述性函数作为value, 它可用于检查给定单词是否是 Python 关键字.
  • parser 模块提供了一个到 Python 内建语法分析器和编译器的接口
  • symbol 模块包含 Python 语法中的非终止符号. 可能只有你涉及 parser 模块 的时候用到它
  • token 模块包含标准 Python tokenizer 所使用的 token 标记.

其它模块(少用)

  • pyclbr 模块包含一个基本的 Python 类解析器
  • filecmp 模块用于比较文件和目录
  • cmd 模块为命令行接口( command-line interfaces , CLI )提供了一个简单的 框架. 它被用在 pdb 模块中, 当然你也可以在自己的程序中使用它。你只需要继承 Cmd 类, 定义 do 和 help 方法. 基类会自动地将这些方法转换 为对应命令.
  • readline 模块使用 GNU readline 库(或兼容库)实现了 Unix 下增强的 输入编辑支持
  • rlcompleter 模块为 readline 模块提供了单词自动完 成功能.
  • statvfs 模块包含一些与 os.statvfs (可选)函数配合使用的常量和函数, 该 函数会返回文件系统的相关信息
  • calendar 模块是 Unix cal 命令的 Python 实现. 它可以将给定年份/月份的 日历输出到标准输出设备上.
  • sched 模块为非线程环境提供了一个简单的计划任务模式
  • statcache 模块提供了访问文件相关信息的相关函数. 它是 os.stat 的扩展模 块, 而且它会缓存收集到的信息.2.7没看到
  • grep 模块提供了在文本文件中搜索字符串的另种方法已经废弃
  • bisect 模块用于向排序后的序列插入对象.insort(sequence, item) 将条目插入到序列中, 并且保证序列的排序. 序列可 以是任意实现了 __getitem__ 和 insert 方法的序列对象.
    其它废弃的模块没写了。

python绝技:运用python成为顶级黑客

python绝技:运用python成为顶级黑客

前言

有多少人是因为看了电视,看了那些牛逼的黑客选择成为程序员的。
我貌似也是其中一个,只是自从成为程序员以来,天天都是加班coding,到家就是睡倒床上。兴趣变成了压力。
直到我选择离职,在家修养,才有精力重新把编程变成兴趣。因为Python的无所不能,我选择Python作为主要编程语言。
在这之前已经学过《廖雪峰的python教程》,也看过了《flaskweb实战》,之前还看过《head first in python》,选择《python绝技:运用python成为顶级黑客》这本书,是因为我想知道黑客到底干了啥。

书籍评价

有简单的基础语法介绍,最好还是先有系统学过python再看这本书,会事半功倍。
书籍偏老,2012年的,有些链接失效。另外建议学习期间使用virtualenv, 避免影响到其它项目。
建议看原版,翻译的感觉很一般,有点像是google翻译的。虽然连蒙带猜的看懂,看的有点痛苦,还是建议翻原版,篇幅主要是代码, 英文应该也好看。

有些示例代码可能得相应地改下。譬如pexpect连接ssh那个,代码里是期望提示值是password:
但是我测试的Centos的格式是user@host’s password: 需要自己根据情况去修改下。
ftp破解后,上传文件的代码在python3上执行失败,抛异常了。python2.7没事。
建议用python2.7来运行他的代码。

里面的攻击手段其实已经过期了,仅能参考下。

章节介绍

第一章:语法介绍

从第二章开始我把代码都写了下来,放到了github上。讲了扫描端口,暴力破解ssh,ftp,smb等。参考我的git

第三章:注册表,回收站都是基于windows的。阅读pdf元数据的pypdf在python2.7上可以执行,python3上报错。Skype和Firefox是用sqLite存储的数据。IPhone的GPS信息存储在了consolidated.db(也是SqLite)。

第四章:一个ip到经纬度的数据库,以及相应的解析模块。dpkt不支持3.x,需要注意。
pcap包可以考虑用wireshark抓包生成一个。LOIC这个,得用pcap包才可以用来验证是否是用户下载的。(go2live.cn注:pcap包是服务器开启tcpdump抓包的,问题是谁没事去开启tcpdump呢,这东西一般是我们研发在调试阶段才用的,第三章中有根据firefox本地存储的下载记录取证的,也可以证明是否是用户下载。)
后面都是根据pcap包,做关键词分析,分析是否下载,是否有hive指令,tcp流量是否过高。
需要注意下tcpdump命令, -i 参数是指监听的端口。我的服务器上外网的端口其实是eth1,而不是文中的eth0。
打印TTL时,使用scapy模块报错了(我是mac,通过pip install pcapy和编译安装https://github.com/dugsong/libdnet.git 之后可以运行了,但是在下面的章节中,send调用一直出错,没有解决掉。)
FAST流量和DOMAIN流量攻击都是利用DNS。也是通过分析流量包来确认攻击。
从这章可以看出,玩黑客攻防需要对各层协议非常熟悉,然后才能想出正确的方法。
tcp三次握手和syn攻击可以参考图解TCP协议中的三次握手和四次挥手
snot安装:(mac下可以直接brew install snort,前提是你有homebrew, centos7 可以参考centos7(x64)安装snort
snot最终还是没有成功(_).(配置文件不会整)

第五章:
开启无线的混杂模式后,就会捕获所有的WiFi流量,包括其它人上网数据。
所谓混杂模式,用最简单的语言就是让网卡抓取任何经过它的数据包,不管这个数据包是不是发给它或者是它发出的,点击【http://en.wikipedia.org/wiki/Promiscuous_mode】获取更多有关混杂模式的资料。
我的mac已经是混杂模式。还是用的scapy抓包,分析包的数据…..
现在goole全是https,文中的Google的例子抓不到数据了。
接下来的ftp的例子好可怕。以前以为只有提供WiFi的人可以抓包,却原来任何人在开启混杂模式之后都可以
而ftp传输还是明文的。。真是获取用户名/密码,然后偷取资料的好方法。

利用电脑或手机在重启WiFi后,会从首选网络列表里一个个去尝试链接的特性,可以抓包获取到,分析出电脑或手机的首选网络列表,从而知道连接过哪些网络。

这章同样是建立在流量包分析上。
无人机这个也是通过混杂模式捕获流量之后,分析出通信协议,然后再用Scapy伪造指令发送。
通过scapy命令,可以得到各种公开协议需要的字段,然后就可以伪造了。

蓝牙这块是用的新的蓝牙模块。包括扫描周边的设备。隐藏的蓝牙设备是利用scapy流量抓包到,通过名字查询来确认。(文里仅限制iPhone,利用OUI特征码来识别)
mac下通过pip和easy_install 安装PyBluez都失败,通过https://github.com/karulis/pybluez 下载安装成功。
有些制造商非提供RFCOMM的加密功能,利用些问题,可以利用RFCOMM来发送控制命令或者下载设备内容。
运用的就是蓝牙模块。蓝牙模块不光能发现周围的蓝牙设备,也能发现某蓝牙设备提供了哪些服务。其中有个服务是OBEX,类似于FTP,可以上传/下载文件。利用RFCOMM的17信道可以发送AT命令,从而偷取联系人列表。

第六章:WEB侦查
用Mechanize模块提供的功能来浏览网页。
代理这块,我没有成功。可能是这个Mechanize模块在mac上有问题,除了第一个demo,后面的我基本没有跑通,而且我感觉利用Mechanize+Beautiful Soup 不过就是抓取网页然后分析,没啥好神秘的,没有细看了。
匿名邮件利用十分钟邮箱https://10minutemail.com/10MinuteMail/index.html?dswid=-1507
来注册垃圾账户。
接下其实是结合前面的抓取你的社交网络朋友,然后模拟朋友的语气,通过smtp模块发邮件给你。。邮件内容里可以带上一个有问题的网站链接。

第七章:躲避杀毒系统
大多数杀毒软件主要的检测方法是基于签名检测。Metasploit库才是真的强大。
这里提供了一个攻击思路。写个python脚本,分发给潜在受害者,执行后把cmd.exe和TCP的某个端口关联起来。攻击者就可以利用这个tcp端口远程执行cmd命令了。
这么牛B,我忍不住安装了Parallels Desktop, 安装了windows虚拟机验证下。失败了,并没有看到1337端口listening。在线检查恶意软件的http://vscan.novirusthanks.org/ 我是打不开。。
可以换成http://www.virscan.org/。 这一章我是啥也没有学到。。

介绍的包。

1.python-nmap 扫描端口。需要依赖安装nmap包。
2. pexpect 用来和程序交互,通过正则来寻找预期的输出,然后基于预期做出响应。可以写出自动登录ssh等工具。通过附带的pxssh模块更简单。
3. PyPDF 一个优秀的第三方管理 PDF 文件很实用的库,可以从网站 http://pybrary.net/pyPdf/获得。它提供了文档的信息提取,分割,合并,加密 和解密的能力
4. BeautifulSoup 解析html和xml。
5. PIL 图像库。现在用的是Pillow
6. pygeoip, 一个针对特定功能的库,用来查询 GeoLiteCity数据库的(免费的) 。通过ip地址查询得到经纬度。
7. dpkt: 解析网络报文的,如ftp,sctp,bpg,ipv6等
8. scapy:Scapy是一个可以让用户发送、侦听和解析并伪装网络报文的Python程序。这些功能可以用于制作侦测、扫描和攻击网络的工具.文中用到最重要的模块
9. IPy: ip处理模块
10. PyBluez: 蓝牙模块
11. Mechanize:模拟自然浏览器行为来完成与网页之间的交互, 貌似有点老,现在应该是被selenium踢掉了。插一句,之所以要这玩意,应该是为了执行js代码。有些网页的局部是用js动态生成的。
12. Beautiful Soup: 解析html和xml的工具。
13. ctypes库:ctypes是Python的一个外部库,提供和C语言兼容的数据类型,可以很方便地调用C DLL中的函数。
14. pyinstaller, 打包成exe,给没有安装python的用户使用的。

体会

最终的感受是黑客没有想像中的那么神奇。黑客并不是技术达人,相反的,黑客更多的是利用现存的工具。
需要对协议非常熟悉,知道各种漏洞。最主要的是想像力,在没有揭开答案前感觉各种神奇,一旦揭开了答案,你又会觉得不过如此。
有个安全公司的朋友说过,黑客的成功靠的就是想像力+运气。

资源

http://digitaloffense.net/tools/debian-openssl/debian_ssh_dsa_1024_x86.tar.bz2
https://www.rapid7.com/products/metasploit/download/
代理:http://www.xicidaili.com/
ua:http://www.useragentstring.com/pages/useragentstring.php

Flask Web开发 基于Python的Web应用开发实战

Flask Web开发 基于Python的Web应用开发实战

概述

Flask Web开发。一步一步教你用flask框架写web网站。
亮点:浅显易懂,介绍了将近20个开发工具包,最大的特色是单元测试这块。以前看过的书基本就是一两个demo,不像这里有完整的代码,包括了model测试,模拟客户端请求测试,api测试,还有模拟浏览器点击的自动化测试。这大大补齐了我对单元测试的用法的理解。
要求:已经学过python,对python语法熟悉。

整理用到了哪些库

  1. forgerypy 生成虚拟信息
  2. flask_Bootstrap ui样式
  3. Flask-Login 登录和权限管理
  4. Flask-Mail 发送邮件
  5. Flask-Migrate 数据库的版本管理。自动生成升级和降级代码。
  6. Flask-Moment 时间本地化
  7. Flask-SQLAlchemy 数据库ORM
  8. Flask-Script 启动管理。可以运行服务器。可以直接运行Shell, 可以增加管理migrate,可以运行单元测试
  9. Flask-WTF 表单管理,自动生成和验证功能。
  10. Jinja2 模板
  11. Werkzeug
  12. PageDown:使用 JavaScript 实现的客户端 Markdown 到 HTML 的转换程序。
  13. Flask-PageDown:为 Flask 包装的 PageDown,把 PageDown 集成到 Flask-WTF 表单中。
  14. Markdown:使用 Python 实现的服务器端 Markdown 到 HTML 的转换程序。
  15. Bleach:使用 Python 实现的 HTML 清理器。
  16. flask-httpauth auth认证
  17. selenium 模拟浏览器的自动化测试。

其它工具
1. coverage 代码测试覆盖率

问题

  1. css等静态文件 缺少版本号管理。不过在查找Flask扩展 中 推荐了个Flask-Assets可以处理这个问题。

其它

Git源码:https://github.com/miguelgrinberg/flasky
书籍:https://s.click.taobao.com/nNaOsAx

学会提问十大关键问题之论题和结论是什么

”是什么“问题和”应不应该“问题

”是什么“问题又叫描述性问题,他是有关世界过去、现在或未来是什么样的问题。

譬如:

音乐学习是不是有助于提高一个人的数学能力?

”应不应该“问题又叫规定性问题,他是在关世界应该是什么样的问题。

譬如:

公立学校里应不应该教授智能设计?

什么是结论

有说明力的交流或论证的基本结构是:甲之所以成立是因为乙。”甲“是指结论,”乙“是指结论的支撑材料。
这个结构代表了推理的过程。

结论是一个观点,需要其它观点来进行支撑。我们把没有证据支撑的断言称为纯观点。

找到结论有线索可寻

线索一:问问论题是什么。

线索二:寻找指示词。

结论前面常有指示词引导,告诉我们接下来出现的就是结论。
以下我们为你列举一些指示词:

  • 因此(consequently)
  • 表明(sugguests that)
  • 由此可知(therefore)
  • 由此得出(thus)
  • 因此可以断定(it follows that)
  • 我要说的重点是(the point I’m trying to make is)
  • 显示出 (shows that)
  • 证明(proves that)
  • 告诉我们(indicates that)
  • 问题的实质是(the truth of the matter is)

线索三:在可能的位置查看一下。

结论一般都在特定的位置出现。首先要注意的两个地方是文章的开头和结尾。

线索四:记住不可能作为结论的东西。

以下这些都不可能作为结论出现:
* 例句
* 数据
* 定义
* 背景材料
* 证据

线索五:检查一下交流的语境和作者的背景。

线索六:问一问”所以呢?“

推荐阅读:
学会提问

centos6.5安装coreseek替换wordpress的搜索功能

引言

实在受不了wordpress乌龟般慢的搜索速度了,感觉网站挂了似得。
之前已经苦逼过怎么优化wordpress了,参见另一篇文章数据过万后wordpress优化过程记录2, 也换成了全世界最好的编程语言php7,过程见于安装php7为wordpress提速。这次是要把wordpress自带的mysql like的搜索替换成全文搜索引擎。

安装coreseek

安装依赖包

yum install make gcc g++ gcc-c++ libtool autoconf automake imake mysql-devel libxml2-devel  expat-devel* mysql-server mysql

coreseek下载

官网打不开了,网上找了个包,放到了csdn上。

tar xzvf coreseek-3.2.14.tar.gz
cd coreseek-3.2.14

安装mmseg

$ cd mmseg-3.2.14
$ ./bootstrap    #输出的warning信息可以忽略,如果出现error则需要解决
$ ./configure --prefix=/usr/local/mmseg3
$ make && make install
$ cd ..
##如果提示libtool: unrecognized option `--tag=CC' ,请查看libtool问题解决方案
##安装完成后,mmseg使用的词典和配置文件,将自动安装到/usr/local/mmseg3/etc中
##中文分词测试,如果显示不正常,请检查当前环境下的locale和UTF-8中文字符显示设置
$  /usr/local/mmseg3/bin/mmseg -d /usr/local/mmseg3/etc mmseg-3.2.14/src/t1.txt
    中文/x 分/x 词/x 测试/x
    中国人/x 上海市/x

Word Splite took: 1 ms.

安装coreseek

$ cd csft-4.1
#执行configure,进行编译配置:
$ sh buildconf.sh
$ ./configure --prefix=/usr/local/coreseek  --without-unixodbc --with-mmseg --with-mmseg-includes=/usr/local/mmseg3/include/mmseg/ --with-mmseg-libs=/usr/local/mmseg3/lib/ --with-mysql
$ make && make install

测试coreseek

cd ../testpack
$  /usr/local/coreseek/bin/indexer -c etc/csft.conf

以下为正常情况下的提示信息:

Coreseek Fulltext 4.1 [ Sphinx 2.0.2-dev (r2922)]
Copyright (c) 2007-2011,
Beijing Choice Software Technologies Inc (http://www.coreseek.com)

ERROR: nothing to do.

$  /usr/local/coreseek/bin/indexer -c etc/csft.conf --all

以下为正常索引全部数据时的提示信息:
Coreseek Fulltext 4.1 [ Sphinx 2.0.2-dev (r2922)]
Copyright (c) 2007-2011,
Beijing Choice Software Technologies Inc (http://www.coreseek.com)

using config file ‘etc/csft.conf’…
indexing index ‘xml’…
collected 3 docs, 0.0 MB
sorted 0.0 Mhits, 100.0% done
total 3 docs, 7585 bytes
total 0.007 sec, 979088 bytes/sec, 387.24 docs/sec
total 3 reads, 0.000 sec, 2.8 kb/call avg, 0.0 msec/call avg
total 9 writes, 0.000 sec, 2.2 kb/call avg, 0.0 msec/call avg

$  /usr/local/coreseek/bin/indexer -c etc/csft.conf xml

以下为正常索引指定数据时的提示信息:
Coreseek Fulltext 4.1 [ Sphinx 2.0.2-dev (r2922)]
Copyright (c) 2007-2011,
Beijing Choice Software Technologies Inc (http://www.coreseek.com)

using config file ‘etc/csft.conf’…
indexing index ‘xml’…
collected 3 docs, 0.0 MB
sorted 0.0 Mhits, 100.0% done
total 3 docs, 7585 bytes
total 0.006 sec, 1260594 bytes/sec, 498.58 docs/sec
total 3 reads, 0.000 sec, 2.8 kb/call avg, 0.0 msec/call avg
total 9 writes, 0.000 sec, 2.2 kb/call avg, 0.0 msec/call avg

$  /usr/local/coreseek/bin/search -c etc/csft.conf

以下为正常测试搜索时的提示信息:
Coreseek Fulltext 4.1 [ Sphinx 2.0.2-dev (r2922)]
Copyright (c) 2007-2011,
Beijing Choice Software Technologies Inc (http://www.coreseek.com)

using config file ‘etc/csft.conf’…
index ‘xml’: query ”: returned 3 matches of 3 total in 0.000 sec

displaying matches:
1. document=1, weight=1, published=Thu Apr 1 22:20:07 2010, author_id=1
2. document=2, weight=1, published=Thu Apr 1 23:25:48 2010, author_id=1
3. document=3, weight=1, published=Thu Apr 1 12:01:00 2010, author_id=2

words:

$  /usr/local/coreseek/bin/search -c etc/csft.conf -a Twittter和Opera都提供了搜索服务

以下为正常测试搜索关键词时的提示信息:
Coreseek Fulltext 4.1 [ Sphinx 2.0.2-dev (r2922)]
Copyright (c) 2007-2011,
Beijing Choice Software Technologies Inc (http://www.coreseek.com)

using config file ‘etc/csft.conf’…
index ‘xml’: query ‘Twittter和Opera都提供了搜索服务 ‘: returned 0 matches of 0 total in 0.015 sec

words:
1. ‘twittter’: 1 documents, 3 hits
2. ‘和’: 3 documents, 15 hits
3. ‘opera’: 1 documents, 25 hits
4. ‘都’: 2 documents, 4 hits
5. ‘提供’: 0 documents, 0 hits
6. ‘了’: 3 documents, 18 hits
7. ‘搜索’: 2 documents, 5 hits
8. ‘服务’: 1 documents, 1 hits

$  /usr/local/coreseek/bin/searchd -c etc/csft.conf

以下为正常开启搜索服务时的提示信息:(csft-4.0版类似)
Coreseek Fulltext 4.1 [ Sphinx 2.0.2-dev (r2922)]
Copyright (c) 2007-2011,
Beijing Choice Software Technologies Inc (http://www.coreseek.com)

using config file ‘etc/csft.conf’…
WARNING: compat_sphinxql_magics=1 is deprecated; please update your application and config
listening on all interfaces, port=9312
precaching index ‘xml’
precached 1 indexes in 0.001 se

配置coreseek支持mysql数据源

配置csft_mysql.conf文件

复制mysql配置文件到coreseek安装目录etc/下(比如/usr/local/coreseek/etc/)

cp /usr/local/src/coreseek-3.2.14/testpack/etc/csft_mysql.conf /usr/local/coreseek/etc/
cd /usr/local/coreseek/etc/
vi csft_mysql.conf

源定义

source phperz
{
type = mysql

sql_host                = localhost
sql_user                = root
sql_pass                = xxxx
sql_db                    = phperz
sql_port                = 3306
sql_query_pre            = SET NAMES utf8

sql_query                = SELECT ID, post_author, UNIX_TIMESTAMP(post_date) AS post_date, post_title, post_content FROM wp_posts where post_status='publish' and post_type in ('page','post')
                                                          #sql_query第一列id需为整数
                                                          #title、content作为字符串/文本字段,被全文索引
sql_attr_uint            =  post_author#从SQL读取到的值必须为整数
sql_attr_timestamp        = post_date#从SQL读取到的值必须为整数,作为时间属性

sql_query_info_pre      = SET NAMES utf8                                        #命令行查询时,设置正确的字符集
sql_query_info            = SELECT * FROM wp_posts WHERE ID=$id #命令行查询时,从数据库读取原始数据信息

}

index定义

index phperz
{
source = phperz #对应的source名称
path = /usr/local/coreseek/var/data/phperz #请修改为实际使用的绝对路径,例如:/usr/local/coreseek/var/…
docinfo = extern
mlock = 0
morphology = none
min_word_len = 1
html_strip = 0

#中文分词配置,详情请查看:http://www.coreseek.cn/products-install/coreseek_mmseg/
charset_dictpath = /usr/local/mmseg3/etc/ #BSD、Linux环境下设置,/符号结尾
#charset_dictpath = etc/                             #Windows环境下设置,/符号结尾,最好给出绝对路径,例如:C:/usr/local/coreseek/etc/...
charset_type        = zh_cn.utf-8

}

全局index定义

indexer
{
mem_limit = 128M
}

searchd服务定义

searchd
{
listen = 9312
read_timeout = 5
max_children = 30
max_matches = 1000
seamless_rotate = 0
preopen_indexes = 0
unlink_old = 1
pid_file = /usr/local/coreseek/var/log/searchd_mysql.pid #请修改为实际使用的绝对路径,例如:/usr/local/coreseek/var/…
log = /usr/local/coreseek/var/log/searchd_mysql.log #请修改为实际使用的绝对路径,例如:/usr/local/coreseek/var/…
query_log = /usr/local/coreseek/var/log/query_mysql.log #请修改为实际使用的绝对路径,例如:/usr/local/coreseek/var/…
}

建立索引

路经部分需要改成你自己的地址
/usr/local/coreseek/bin/indexer -c /usr/local/coreseek/etc/csft_mysql.conf –all

可能出现的错误
ERROR: index ‘phperz’: sql_connect: Can’t connect to local MySQL server through socket ‘/var/lib/mysql/mysql.sock’ (2) (DSN=mysql://root:***@localhost:3306/phperz).
这是因为mysql的sock文件路经不正确导致的.
确认一下你的mysql.sock路经,建立一个软连接,比如
ln -s /tmp/mysql.sock /var/lib/mysql/mysql.sock

coreseek+php使用方式

复制安装目录下的/usr/local/src/coreseek-4.1-beta/testpack/api/sphinxapi.php文件到你的项目里
你的程序里include sphinxapi.php
php使用方法见/usr/local/src/coreseek-4.1-beta/testpack/api/test.php

wordpress修改, 把api目录拷贝到主题目录中后,修改search.php,我的修改如下(和主题相关,不能完全照抄,仅供参考)

<?php get_header(); ?>
<?php
//coreseek搜索结果
include_once( dirname(__FILE__) . "/api/sphinxapi.php" );
$cl = new SphinxClient ();
$cl->SetServer ( '127.0.0.1', 9312);
$cl->SetConnectTimeout(3);
$cl->SetMatchMode(1);
//以下设置用于返回数组形式的结果
$cl->SetArrayResult ( true );

$paged = (get_query_var('paged')) ? get_query_var('paged') : 1;
$start = ($paged - 1) * 10;
$cl->SetLimits($start,10);

$keyword =  $s = isset($_GET['s']) ? htmlspecialchars(trim($_GET['s'])) : ''; //获取搜索词
$res = $cl->Query ( "$keyword", "*" );


#print_r($res); //查看全文索引结果
$total = $res['total'];  //所有返回文章数,用于分页

if ( $res===false )
{
    print "Query failed: " . $cl->GetLastError() . ".\n";

} else
{
    if ( $cl->GetLastWarning() )
        print "WARNING: " . $cl->GetLastWarning() . "\n\n";
}
$have_posts = true;
if(isset($res['matches']) && !empty($res['matches'])) {
    foreach($res['matches'] as $value) {
        $id_arr[] = $value['id'];
    }
}
else
{
$have_posts = false;
}

$id_str = @implode(",", $id_arr);
$args  = array();
$args = array( 'include' => $id_arr);
wp_reset_query();
$sql = "select * from wp_posts where ID in($id_str) and post_type in ('post','page') AND post_status = 'publish'";  //根据ID读取文章数据
$data = $wpdb->get_results($sql);  //输出数据,之后你可以使用foreach数据$post object
?>
<div class="content-wrap">
        <div class="content">
                <?php echo _hui('ads_search_01_s') ? '<div class="ads ads-content">'.hui_get_adcode('ads_search_01').'</div>' : '' ?>
                <h1 class="title"><strong><?php echo htmlspecialchars($s); ?> <?php echo __('的搜索结果', 'haoui') ?></strong></h1>
                <?php if ( !$have_posts ) : ?>
                        <h3 class="text-muted text-center"><?php echo __('暂无搜索结果', 'haoui') ?></h3>
                <?php else: ?>
                        <?php get_template_part( 'excerpt' );  ?>
                <?php endif; ?>
        </div>
</div>

<?php
get_sidebar();

get_footer();
?>



coreseek日常维护

启动
/usr/local/coreseek/bin/searchd -c /usr/local/coreseek/etc/csft_mysql.conf
停止
/usr/local/coreseek/bin/searchd -c /usr/local/coreseek/etc/csft_mysql.conf –stop
建立索引
/usr/local/coreseek/bin/indexer -c /usr/local/coreseek/etc/csft_mysql.conf –all
重建索引
/usr/local/coreseek/bin/indexer -c /usr/local/coreseek/etc/csft_mysql.conf –all –rotate

你需要把启动命令加到开机自启动里
把重建索引命令加到计划任务里每天执行

后记

折腾了3小时才搞定,希望对大家有用。可以访问我的网站试试搜索效果。写这篇博客的时候,我的博客已经快3万了哦。

经查证,直接修改search.php没有用。因为sql查询在之前就已经查了,search.php相当于一个模板页面了。

阅读完原代码,修改了wp-include/query.php, 终于优化完成了。

优化前:

Page generated in 2.18491s, 38.61% PHP, 61.39% MySQL

优化后:

Page generated in 0.54773s, 95.51% PHP, 4.49% MySQL

 

安装php7为wordpress提速

前言

PHP是应用非常广泛的动态语言,其简单直接的语法,优异的性能及与LNMP/LAMP的黄金组合,得到了很多开发者的认可。PHP 7比PHP 6整体性能提高了一倍,自其一发布就引来了全球PHP开发者的强烈关注。本文就PHP7的安装作具体说明。

内容

安装前的准备

系统:CentOS 6.5

Web服务器:Nginx v1.9.12

下载PHP 7:http://php.net/downloads.php
打开比较慢,php7.0.10 可以直接下载

正式安装

  1. 安装PHP相关依赖
# yum install libxml2 libxml2-devel openssl-devel bzip2-devel libcurl-devel enchant enchant-devel libpng-devel gmp-devel libc-client libc-client-devel pam-devel firebird-devel libicu-devel openldap openldap-devel libmcrypt-devel unixODBC-devel freetds freetds-devel
  1. 编译安装PHP 7

PHP 5.6及以上已经内置了php-fpm,不需要再使用外挂的方式安装php-fpm。只需要编译时加上“–enable-fpm”参数即可。

# cd /path/to/downloads
# tar php-7.0.10.tar.gz
# cd php-7.0.10
# ./configure --prefix=/usr/local/php-7.0.10 --enable-fpm --with-xml --with-curl --enable-mbstring  --with-mcrypt --with-mysqli=mysqlnd --with-pdo-mysql=mysqlnd --with-zlib  --with-gd --with-freetype-dir --enable-gd-native-ttf --enable-gd-jis-conv --with-gettext --with-openssl 
# make && make install && make clean

以上为只标注了部份php扩展,如需打开全部扩展,可以使用”–enable-all”,如若需要查看具体参数说明,可通过如下命令查看:

# ./configure --help

配置PHP

  1. 配置php.ini文件

display_errors=Off
default_time_zone=Asia/Chongqing

  1. 启动php-fpm
  2. # /usr/local/php-7.0.4/sbin/php-fpm -D
    

    3.配置测试虚拟机

    # mkdir /webapps /webapps/test.mydomain.com
    # chmod -R 755 /webapps/test.mydomain.com
    

    增加nginx站点设置:

server {
       listen       80;
       server_name test.mydomain.com;
       root /webapps/test.mydomain.com;
       index index.html index.php;
       # 如果需要php, php-fpm运行环境
       location ~ \.php$ {
           fastcgi_pass 127.0.0.1:9000;
           fastcgi_index index.php;
           fastcgi_param SCRIPT_FILENAME  $document_root$fastcgi_script_name;
           include fastcgi_params;
       }
}

增加test.mydomain.com本机hosts,如下:

# vim /etc/hosts
127.0.0.1 test.mydomain.com

编写测试文件

# vim phpinfo.php
<?php phpinfo();
重启nginx与php-fpm服务:

# service nginx reload
# killall -TERM php-fpm
# /usr/local/php-7.0.4/sbin/php-fpm -D

打开浏览器,输入地址http://php.mydomain.com/phpinfo.php,即可看到php相关环境信息输出页面。

写在最后

编译安装php7感觉比较麻烦,一开始是mysqli没有安装好,结果wordpress运行不起来。后来上传插件安装,又发现缺少zlib库。现在又发现缺少gd库。
不过不管怎么说,wordpress性能又提升了2倍,还是不错的。其它优化方式可以参考另一篇文章

翻译:Andriod注解支持(Annotations)

Android 支持注解

这篇博客,我们将要说说Android Support Annotations 库,以及我们需要关注它。

support library 19.1版引入一个新的注解包,这个注解包里面有一系列有用的注解,使用它能很方便地捕获bugs。这些注解将会帮助 Android studio 来检测你代码里可能的错误并报告给你。在版本22.2中,这些注解进一步丰富了,新增了13个注解。

添加库到你的工程里

默认情况下,你的项目里没有注解库,因为它是一个独立的库。如果你使用appcompat库,则你可以使用这些注解,因为appcompat库依赖于它。

不管哪种情况, 为了在项目中使用注解, 添加以下代码到你的build.gradle文件中。

compile 'com.android.support:support-annotations:<latest-library-version>'

这些注解基于它们的使用和功能的相似性组合在一起,并根据这些分组,我们有以下注解类:

  • NULL的含量注解
  • 资源注释
  • 线程注释
  • 值约束注解
  • 其他是:权限注解,CheckResults注解和CallSuper注解。

Nullness 注解

@Nullable and @NonNull annotations 用于检查变量、参数以及方法的返回值是否为null。 @NonNull annotation 意味着变量、参数以及方法的返回值不能是null. 如果违法了注解规则,Android studio产生一条警告信息。例如:

//pass a null argument to a method annotated as @NonNull
doubleNumber(null);

public int doubleNumber(@NonNull int num) {
    return num * 2;
}

@Nullable 意味着变量的值可以是null, 如果注解用于方法,则指明方法的返回值可以是null。 不论何时使用@Nullable注解, 都应该在变量和方法返回值上做Null检查。

资源注解

因为在Android中,资源是做为一个整数在传递的, 一段期望字符串id的代码,可以传递一个指向drawable的整形值,这种情况下编译器居然是可以接受的。资源类型注解允许在这种情况下检查资源的类型。譬如添加一个@StringRes的注解, 能确保传递的是字符串资源,如果是其它资源,IDE就会标志。

public void setButtonText(@StringRes int id) {
     //set text on some button
}

//this will be flagged by the IDE
setButtonText(R.drawable.icon);

每个android资源类型都有相应的资源类型注解。譬如有 @DrawableRes, @ColorRes, @InterpolatorRes等等. 总得原则是,如果资源类型是“Foo”,那么相应的注解就是“FooRes”.

最后, 有一个特别的资源注解,它是@AnyRes表明是个资源,但不明确到底是什么资源类型.

线程注解

线程注解检查一个方法是否在特定的线程类型中被调用。支持的线程注解有:

  • @UiThread
  • @MainThread
  • @WorkerThread
  • @BinderThread
    @MainThread@UiThread 注解是可以互换的。

当类中的方法全都在同样类型的线程中调用时,可以把注解直接加到类上。一个演示线程注解使用的好例子是在AsyncTask中

@WorkerThread
protected abstract Result doInBackground(Params... params);

@MainThread
protected void onProgressUpdate(Progress... values) {
}

如果onProgressUpdate方法不是在主线程中调用,IDE就会标志一个错误出来。

值限制注解

@IntRange, @FloatRange and @Size annotations 用于验证参数的有效性。 @IntRange annotation 验证参数的值是在指定的整数范围内. 下面这个例子中,setAlpha方法会检查alpha的值是从0到255。

public void setAlpha(@IntRange(from=0, to=255) int alpha) {
    //set alpha
}

@FloatRange 类似,验证参数是在指定的浮点数范围内. @Size annotation 有点不一样, 它用于检查集合或数组的大小, 也用于检查字符串长度. @Size(min=1) annotation 用于检查数组非空, @Size(2) annotation 检查数组刚好有两个元素。

CheckResult Annotations

这个用于确保方法的返回值确实有使用。主要目的是帮助在使用一些API的时候,去使用它的返回值,而不是理所当然地认为函数有边际效应。在官方文档中有解释,一个好的例子是字符串。
trim 方法, 会让Java开发新手误用,认为方法调用完,字符串的值已经去掉了空白符。trim 方法使用@CheckResult注解。当调用者使用了trim方法,又没有使用返回值时,IDE就会标志。

@CheckResult
public String trim(@NonNull String string) {
    //remove whitespace from string
}

String s = "hello world ";

//this will make the IDE flag an error since the result from the @CheckResult
//annotated method is not used
s.trim();

其他值得关注的注解还有 @CallSuper, @Keep and @RequiresPermission. 完整的列表请参考官方的文档。

写在最后

使用注解支持库可以帮助我们更好地理解代码意图。它使用的代码结果是可预测的,同时方便其它开发人员整合你的代码,也方便你将来再次修改它。

更多关于Android的文章可以参考演道网

参考

Improve code Inspection with Annotations – Android developer doc

Support Annotation documentation

http://michaelevans.org/blog/2015/07/14/improving-your-code-with-android-support-annotations/

原文:http://mayojava.github.io/android/android-support-annotations/?utm_source=Android+Weekly&utm_campaign=c0a2159802-Android_Weekly_222&utm_medium=email&utm_term=0_4eb677ad19-c0a2159802-337909737