1.quote
Lisp 提供 quote 作为一种保护表达式被求值的方式。Common Lisp 定义 ' 作为 quote 的缩写。
[62]> (quote (+ 3 5))
(+ 3 5)
[64]> '(+ 3 5)
(+ 3 5)
这意味着 Lisp 程序可以写出 Lisp 代码。 Lisp 程序员能(并且经常)写出能为自己写程序的程序。
2. if
[20]> (if t 'a 'b)
A
[21]> (if nil 'b 'a)
A
第一个参数是判断,第二个是为true时求值,第三个是false求值。
3. and和or
逻辑操作符 and 和 or 与条件式 (conditionals) 类似。两者都接受任意数目的参数,但只对能够决定返回值的那几个参数来作求值。如果所有的参数都为 真 (即不为 nil ),那么 and 会返回最后一个参数的值,如果其中一个参数为 假 ,那么之后的所有参数都不会被求值。 or 也是如此,只要碰到一个是 真 的参数,就停止对之后的所有的参数求值。
[23]> (and t t (+ 1 3) t)
T
[24]> (and t t (+ 1 3) nil)
NIL
[25]> (and t t (+ 1 3) nil t)
NIL
[26]> (or nil nil (+ 4 5))
9
[28]> (or nil nil (+ 4 5) nil)
9
[29]> (or nil nil (+ 4 5) (- 8 7) nil)
9
其实也可以这么理解,对每一个参数求值,当and遇到nil,或者or遇到非nil,终止求值,返回nil或者非nil。没遇到就返回最后一个参数的值。
Read more...
Archive for 算法-编程
common lisp的符号、数字、列表和字符串
符号,字母开头,包括字母数字和连接符。
5
17
-34
+6
2/4
1/3
3.1415
1.722e-15
#c(1.722e-15 0.75)
有趣撒~ lisp支持分数形式,下面几个测试:
[1]> (+ 3 2/3)
11/3
[2]> (+ 3.0 2/3)
3.6666667
[3]> (* 2.0 1/3)
0.6666667
[4]> (* 3.0 1/3)
1.0
[5]> (* 3.00 1/3)
1.0
[6]> (/ 3.0 1/3)
9.0
[7]> (/ 2.0 1/3)
6.0
lisp支持复数形式#c(r,i),r为实部,i为虚部。
lisp字符串包含在双引号中,#\a #\3表示字符。反斜杠为转义字符。
命名约定:在全局变量开头和结尾带有“*”,常量都已“+”开头结尾。
构造一个列表:(list 4 5 6)
[4]> (cons 'a '(b c))
(A B C)
用函数 cons 来构造列表。如果传入的第二个参数是一个列表,则返回一个由第二个参数所组成的新列表,其中新列表的第一个元素是传入的第一个参数。
[6]> (cons 'a (cons 'b nil))
(A B)
来取出列表元素的基本函数是 car 和 cdr 。列表的 car 是第一个元素,而列表的 cdr 是第一个元素之后的所有元素
[13]> (setq l '(a b c))
(A B C)
[14]> l
(A B C)
[15]> (car l)
A
[16]> (cdr l)
(B C)
这个像erlang里列表操作,或者应该说erlang学的lisp。
nil唯一一个既是原子又是列表的对象。
Read more...
common lisp入门安装
又准备开始学lisp,前些时候就想学,但是就怕贪多嚼不烂。现在又想:喜欢就学,先学习语法,不用深。说不定以后就用到。
lisp方言很多,没什么太多了解,先学一个撒~
下载地址:www.clisp.org,一路next安装windows版完成。
一个lisp的IDE:http://www.daansystems.com/lispide/
[1]> 10
10
[2]> (* 2 3)
6
感觉想极了编译原理里面学的,三元式还是啥的来。
[3]> (format t "hello world")
hello world
NIL
t 是标准输出,NIL是对format求值的结果。列表的第一个元素是函数名。nil代表false,t代表true。
写成函数形式:
[4]> (defun hello-world () (format t "hello world"))
HELLO-WORLD
[5]> (hello-world)
hello world
NIL
求值后,得到一个名为HELLO-WORLD的函数,函数没有参数。
Read more...
Network programming with Go
很不错的教程,讲的很详细。《Network programming with Go》http://jan.newmarch.name/golang/,照着教程写了一个小程序。
Read more...
//server.go
package main
import(
"fmt"
"net"
"os"
"time"
)
func handleClient(conn *net.UDPConn){
var buf [512]byte
n, addr, err := conn.ReadFromUDP(buf[0:])
if err != nil{
return
}
fmt.Println(string(buf[0:]))
daytime := time.Now().String()
fmt.Println(n,addr)
conn.WriteToUDP([]byte(daytime),addr)
}
func checkError(err error){
if err != nil{
fmt.Fprintf(os.Stderr,"Fatal error:%s",err.Error())
os.Exit(1)
}
}
func main(){
service := ":1200"
udpAddr, err := net.ResolveUDPAddr("up4",service)
checkError(err)
conn,err := net.ListenUDP("udp",udpAddr)
checkError(err)
for{
handleClient(conn)
}
}
//client.go
package main
import(
"fmt"
"net"
"os"
)
func checkError(err error){
if err != nil{
fmt.Fprintf(os.Stderr,"Fatal error:%s",err.Error())
os.Exit(1)
}
}
func main(){
if len(os.Args) != 2{
fmt.Fprintf(os.Stderr, "Usage:%s host:port", os.Args[0])
os.Exit(1)
}
service := os.Args[1]
udpAddr, err := net.ResolveUDPAddr("up4",service)
checkError(err)
conn,err := net.DialUDP("udp",nil,udpAddr)
checkError(err)
_,err = conn.Write([]byte("hello"))
checkError(err)
var buf [512]byte
n,err := conn.Read(buf[0:])
checkError(err)
fmt.Println(string(buf[0:n]))
os.Exit(0)
}
,以后再扩展功能,先睡觉。
golang中strings包学习第二篇
func Join(a []string, sep string) string
,连接字符串,以sep作为分隔符
例子:
s := []string{"foo", "bar", "baz"}
fmt.Println(strings.Join(s, ", "))
输出:
foo, bar, baz
,源代码分析
1.判断a的长度,0时,返回空字符串,长度为1时,返回a[0].
2.否则,在变量n中保存将要生产字符串的长度。计算方法len(a)-1个sep的长度 + 数组a中字符串长度和。
3.创建一个长度为n的byte数组。将数据复制到数组中,copy()将返回复制元素的个数,用bp来记录复制的位置。最后转成string返回。
func Split(s, sep string) []string
,分割字符串,sep为空字串时,与上一篇讲到的一样,相当于每个字符之间的间隔。s中没有sep,返回值里只有一个元素s。
例子:
fmt.Printf("%q\n", strings.Split("a,b,c", ","))
fmt.Printf("%q\n", strings.Split("a man a plan a canal panama", "a "))
fmt.Printf("%q\n", strings.Split(" xyz ", ""))
fmt.Printf("%q\n", strings.Split("", "Bernardo O'Higgins"))
输出:
["a" "b" "c"]
["" "man " "plan " "canal panama"]
[" " "x" "y" "z" " "]
[""]
,源代码return genSplit(s, sep, 0, -1)
看看func genSplit(s, sep string, sepSave, n int) []string。
func genSplit(s, sep string, sepSave, n int) []string
,有几个函数都用到这个函数,通用分割字符串的函数。
n 是返回分割的个数,-1时,返回所有。
sepSave是被分割的位置在sep所在位置的偏移,被用于SplitAfter和SplitAfterN这些函数,使用len(sep)作为sepsave可以返回sep最后一个字符前面的字串作为一个分割。
1.n为0时,返回nil。
2.sep为空字串,return explode(s, n),
3.当n<0时,应该返回所有,n为Count(s, sep) + 1,表示长度。
4.定义几个变量,c是用于比较是否和sep相等,都是先比较第一个字符。相等再看字符串长度为一,则找到;不为一,比较整个sep。a是被返回的数组,na是在数组中存储的位置。
东西零碎 utf8包的内容,写下来的目的是使自己是真正的明白,而不是一看知道是这么回事,但是不想为什么要这样写。现在感觉自己用来学习的时间很少,珍惜一下。
golang中strings包学习
打算把golang的常用包都看一遍,分析一下包的代码,觉得从中会学到golang的编程方法^_^。
Read more...
func Index(s, sep string) int
返回s中第一次出现sep的位置,如果没有返回-1.
例子:
代码:
fmt.Println(strings.Index("chicken", "ken"))
fmt.Println(strings.Index("chicken", "dmr"))
输出:
4
-1
看源码中index的实现方法,
1.取得sep的长度。如果长度为零,返回0。否则取sep第一个字符。
2.如果长度为1,for循环比较s[i]和sep[0],相等,返回i。都不相等,返回-1。
3.如果长度大于1,for循环比较(先比较s[i]和sep[0],不相等就i++,再比较s[i:i+n] == sep。)成立返回i,否则返回-1。比较的上限是i+n。
相关函数:
func Contains(s, substr string) bool
如果substr在s中,返回true。
代码:
fmt.Println(strings.Contains("seafood", "foo"))
fmt.Println(strings.Contains("seafood", "bar"))
fmt.Println(strings.Contains("seafood", ""))
fmt.Println(strings.Contains("", ""))
输出:
true
false
true
true
源代码中用index函数实现,return Index(s, substr) >= 0。
func LastIndex(s, sep string) int
,返回在s中最后一次出现sep的位置,不存在返回-1。
例子就过了,源代码分析,与index()相似,但是判断的时候是从s的后边向前判断。
func Count(s, sep string) int
,统计sep在s中出现的次数,非重叠的,比如s=“eeee”,sep="ee",结果返回2.
例子:
fmt.Println(strings.Count("cheese", "e"))
fmt.Println(strings.Count("five", "")) // before & after each rune
结果:
3
5
,我对第二个例子不理解,在群里问了一番,开始回答的我没明白。后来第二个给我回答我才明白:这里“”字符串指的就是字符串中每个字符之间的空字符串T_T,函数返回了s中有几个这样的空字符串。后面的英文注释直接看不懂T_T,悲催。现在看应该是每个字符的前面加后面。。。
源代码中“”时,返回utf8.RuneCountInString(s) + 1,这里不用len,我测试了一下对于一个中文len返回的长度是3,这里不符,所以用utf8.RuneCountInString。utf8.RuneCountInString是使用range来遍历的字符串,与len计数不同,一个中文长度为1。
但是在接下来遍历s和sep都是用的len(),也就是说比如
fmt.Println(strings.Count("我的我的", "\x88"))你会得到结果2,这是因为
”我的“会被\xe6\x88\x91\xe7\x9a\x84遍历,你将从中找到\x88。
fmt.Println(strings.Count("five", ""))
然后还是分两种情况,len(sep)=1和大于1两种情况。
golang的Panic和Recover
在看《build web application with golang》地址:https://github.com/astaxie/build-web-application-with-golang
看到Panic和Recover这一节,对Panic和Recover有了了解。
Go没有例如像Java那样的异常机制:不能抛出一个异常。作为代替,它使用了panic和recover机制。一定要记得,这应当作为最后的手段被使用,你的代码中应当没有,或者很少的令人恐慌的东西。这是个强大的工具,明智的使用它。那么,应该如何使用它。
Panic
Read more...
是一个内建函数,可以中断原有的控制流程,进入一个令人恐慌的流程中。当函数F调用panic,函数F的执行被中断,但是F中的延迟函数会正常执 行,然后F返回到调用它的地方。在调用的地方,F的行为就像调用了panic。这一过程继续向上,直到发生panic的goroutine中所有调用的函 数返回,此时程序退出。恐慌可以直接调用panic产生。也可以由运行时错误产生,例如访问越界的数组。Recover
是一个内建的函数,可以让进入令人恐慌的流程中的goroutine恢复过来。Recover仅在延迟函数中有效。在正常的执行过程中,调用 recover会返回nil,并且没有其他任何效果。如果当前的goroutine 陷入恐慌,调用recover可以捕获到panic的输入值,并且恢复正常的执行。最容易理解就是给个例子,文章里有例子:
package main
import(
"fmt"
//"os"
)
var user = ""
func inita() {
defer func(){
fmt.Print("defer##\n")
}()
if user == "" {
fmt.Print("@@@before panic\n")
panic("no value for user\n")
fmt.Print("!!after panic\n")
}
}
func throwsPanic (f func()) (b bool){
defer func(){
if x:= recover(); x != nil{
fmt.Print(x)
b = true
}
}()
f()
fmt.Print("after the func run")
return
}
func main(){
throwsPanic(inita)
}
执行结果:
D:\go>go run b.go
@@@before panic
defer##
no value for user
如上面所说的:
panic在user=""时,打断了函数的执行,fmt.Print("!!after panic\n")没有执行。
但函数中的延迟函数会正常执行,打印了 ”defer##“。然后返回到调用该函数的地方,继续上面的过程。
直到执行完所有函数的defer,退出程序。
Recover可以捕获到panic的值,上面的打印“no value for user”。并且恢复正常的执行。
pdb调试python程序
以前在windows下一直用的idel带的功能调试python程序,在linux下没调试过。(很多时候只是print)就从网上查找一下~
方法:python -m pdb a.py
a.py是python文件。
(Pdb)模式下的常用命令:
q
退出debug
h 即help,打印所有可以命令
h w
打印命令 w 的含意
n
执行当前行直到到达下一行或直到它返回
s
执行当前行,一有可能就停止(比如当前行有一个函数调用)。它和n(next)的区别是当前行中有函数调用时s(step)会停止当前行的执行而去调用那个函数,而n不会停止,直到计算完成这一行(到达下一行)。
b(reak) [[filename:]lineno | function[, condition]]
设置断点,可以是行号或函数。如 b 10, b foo, b foo,n == 5,最后在函数foo()入口处设置一个断点,但仅当n的值是5时才有效。当b命令无参数时,打印所有断点。
tb(reak) [[filename:]lineno | function[, condition]]
临时断点,只hit一次。
disable [bpnumber [bpnumber ...]]
使指点行的断点(们)失效
enable [bpnumber [bpnumber ...]]
使指定行上的断点(们)有效
c
继续执行程序,直到遇到下一个断点
w
即where,打印当前执行点的位置
l [first, [,last]]
输出当前行附近的源码
p expression
执行一个表达式当打印其值。
a(rgs)
打印当前函数的参数及其值
<直接回车>
重复执行上次的命令
并遇到pdb不认识的命令时,会将它当成python表达式执行。如果你想执行的表达时,最前面加上 ! 字符,比如 !n == 5
多个debug命令可以写在一行上,中间用两个分号分隔,如 s;;s。
Read more...
python 持久性管理-pickle模块介绍
什么是持久性?
持 久性的基本思想很简单。假定有一个 Python 程序,它可能是一个管理日常待办事项的程序,您希望在多次执行这个程序之间可以保存应用程序对象(待办事项)。换句话说,您希望将对象存储在磁盘上,便于 以后检索。这就是持久性。要达到这个目的,有几种方法,每一种方法都有其优缺点。
例如,可以将对象数据存储在某种格式的文本文件中,譬如 CSV 文件。或者可以用关系数据库,譬如 Gadfly、MySQL、PostgreSQL 或者 DB2。这些文件格式和数据库都非常优秀,对于所有这些存储机制,Python 都有健壮的接口。
这 些存储机制都有一个共同点:存储的数据是独立于对这些数据进行操作的对象和程序。这样做的好处是,数据可以作为共享的资源,供其它应用程序使用。缺点 是,用这种方式,可以允许其它程序访问对象的数据,这违背了面向对象的封装性原则 — 即对象的数据只能通过这个对象自身的公共(public)接口来访问。
另外,对于某些应用程序,关系数据库 方法可能不是很理想。尤其是,关系数据库不理解对象。相反,关系数据库会强行 使用自己的类型系统和关系数据模型(表),每张表包含一组元组(行),每行包含具有固定数目的静态类型字段(列)。如果应用程序的对象模型不能够方便地转 换到关系模型,那么在将对象映射到元组以及将元组映射回对象方面,会碰到一定难度。这种困难常被称为阻碍性不匹配(impedence- mismatch)问题。
一些经过 pickle 的 Python
pickle 模块及其同类模块 cPickle 向 Python 提供了 pickle 支持。后者是用 C 编码的,它具有更好的性能,对于大多数应用程序,推荐使用该模块。我们将继续讨论 pickle ,但本文的示例实际是利用了 cPickle 。由于其中大多数示例要用 Python shell 来显示,所以先展示一下如何导入 cPickle ,并可以作为 pickle 来引用它:
>>> import cPickle as pickle
现在已经导入了该模块,接下来让我们看一下 pickle 接口。 pickle 模块提供了以下函数对: dumps(object) 返回一个字符串,它包含一个 pickle 格式的对象; loads(string) 返回包含在 pickle 字符串中的对象; dump(object, file) 将对象写到文件,这个文件可以是实际的物理文件,但也可以是任何类似于文件的对象,这个对象具有 write() 方法,可以接受单个的字符串参数; load(file) 返回包含在 pickle 文件中的对象。
缺省情况下, dumps() 和 dump() 使用可打印的 ASCII 表示来创建 pickle。两者都有一个 final 参数(可选),如果为 True ,则该参数指定用更快以及更小的二进制表示来创建 pickle。 loads() 和 load() 函数自动检测 pickle 是二进制格式还是文本格式。
清单 1 显示了一个交互式会话,这里使用了刚才所描述的 dumps() 和 loads() 函数:
清单 1. dumps() 和 loads() 的演示
Read more...
Welcome To PyCrust 0.7.2 - The Flakiest Python Shell
Sponsored by Orbtech - Your source for Python programming expertise.
Python 2.2.1 (#1, Aug 27 2002, 10:22:32)
[GCC 3.2 (Mandrake Linux 9.0 3.2-1mdk)] on linux-i386
Type "copyright", "credits" or "license" for more information.
>>> import cPickle as pickle
>>> t1 = ('this is a string', 42, [1, 2, 3], None)
>>> t1
('this is a string', 42, [1, 2, 3], None)
>>> p1 = pickle.dumps(t1)
>>> p1
"(S'this is a string'\nI42\n(lp1\nI1\naI2\naI3\naNtp2\n."
>>> print p1
(S'this is a string'
I42
(lp1
I1
aI2
aI3
aNtp2
.
>>> t2 = pickle.loads(p1)
>>> t2
('this is a string', 42, [1, 2, 3], None)
>>> p2 = pickle.dumps(t1, True)
>>> p2
'(U\x10this is a stringK*]q\x01(K\x01K\x02K\x03eNtq\x02.'
>>> t3 = pickle.loads(p2)
>>> t3
('this is a string', 42, [1, 2, 3], None)
注:该文本 pickle 格式很简单,这里就不解释了。事实上,在 pickle 模块中记录了所有使用的约定。我们还应该指出,在我们的示例中使用的都是简单对象,因此使用二进制 pickle 格式不会在节省空间上显示出太大的效率。然而,在实际使用复杂对象的系统中,您会看到,使用二进制格式可以在大小和速度方面带来显著的改进。
接下来,我们看一些示例,这些示例用到了 dump() 和 load() ,它们使用文件和类似文件的对象。这些函数的操作非常类似于我们刚才所看到的 dumps() 和 loads() ,区别在于它们还有另一种能力 — dump() 函数能一个接着一个地将几个对象转储到同一个文件。随后调用 load() 来以同样的顺序检索这些对象。清单 2 显示了这种能力的实际应用:
清单 2. dump() 和 load() 示例
>>> a1 = 'apple'
>>> b1 = {1: 'One', 2: 'Two', 3: 'Three'}
>>> c1 = ['fee', 'fie', 'foe', 'fum']
>>> f1 = file('temp.pkl', 'wb')
>>> pickle.dump(a1, f1, True)
>>> pickle.dump(b1, f1, True)
>>> pickle.dump(c1, f1, True)
>>> f1.close()
>>> f2 = file('temp.pkl', 'rb')
>>> a2 = pickle.load(f2)
>>> a2
'apple'
>>> b2 = pickle.load(f2)
>>> b2
{1: 'One', 2: 'Two', 3: 'Three'}
>>> c2 = pickle.load(f2)
>>> c2
['fee', 'fie', 'foe', 'fum']
>>> f2.close()
Pickle 的威力
到目前为止,我们讲述了关于 pickle 方面的基本知识。在这一节,将讨论一些高级问题,当您开始 pickle 复杂对象时,会遇到这些问题,其中包括定制类的实例。幸运的是,Python 可以很容易地处理这种情形。
可移植性
从 空间和时间上说,Pickle 是可移植的。换句话说,pickle 文件格式独立于机器的体系结构,这意味着,例如,可以在 Linux 下创建一个 pickle,然后将它发送到在 Windows 或 Mac OS 下运行的 Python 程序。并且,当升级到更新版本的 Python 时,不必担心可能要废弃已有的 pickle。Python 开发人员已经保证 pickle 格式将可以向后兼容 Python 各个版本。事实上,在 pickle 模块中提供了有关目前以及所支持的格式方面的详细信息:
清单 3. 检索所支持的格式
>>> pickle.format_version
'1.3'
>>> pickle.compatible_formats
['1.0', '1.1', '1.2']
多个引用,同一对象
在 Python 中,变量是对象的引用。同时,也可以用多个变量引用同一个对象。经证明,Python 在用经过 pickle 的对象维护这种行为方面丝毫没有困难,如清单 4 所示:
清单 4. 对象引用的维护
>>> a = [1, 2, 3]
>>> b = a
>>> a
[1, 2, 3]
>>> b
[1, 2, 3]
>>> a.append(4)
>>> a
[1, 2, 3, 4]
>>> b
[1, 2, 3, 4]
>>> c = pickle.dumps((a, b))
>>> d, e = pickle.loads(c)
>>> d
[1, 2, 3, 4]
>>> e
[1, 2, 3, 4]
>>> d.append(5)
>>> d
[1, 2, 3, 4, 5]
>>> e
[1, 2, 3, 4, 5]
循环引用和递归引用
可以将刚才演示过的对象引用支持扩展到 循环引用(两个对象各自包含对对方的引用)和 递归引用(一个对象包含对其自身的引用)。下面两个清单着重显示这种能力。我们先看一下递归引用:
>清单 5. 递归引用
>>> l = [1, 2, 3]
>>> l.append(l)
>>> l
[1, 2, 3, [...]]
>>> l[3]
[1, 2, 3, [...]]
>>> l[3][3]
[1, 2, 3, [...]]
>>> p = pickle.dumps(l)
>>> l2 = pickle.loads(p)
>>> l2
[1, 2, 3, [...]]
>>> l2[3]
[1, 2, 3, [...]]
>>> l2[3][3]
[1, 2, 3, [...]]
现在,看一个循环引用的示例:
清单 6. 循环引用
>>> a = [1, 2]
>>> b = [3, 4]
>>> a.append(b)
>>> a
[1, 2, [3, 4]]
>>> b.append(a)
>>> a
[1, 2, [3, 4, [...]]]
>>> b
[3, 4, [1, 2, [...]]]
>>> a[2]
[3, 4, [1, 2, [...]]]
>>> b[2]
[1, 2, [3, 4, [...]]]
>>> a[2] is b
1
>>> b[2] is a
1
>>> f = file('temp.pkl', 'w')
>>> pickle.dump((a, b), f)
>>> f.close()
>>> f = file('temp.pkl', 'r')
>>> c, d = pickle.load(f)
>>> f.close()
>>> c
[1, 2, [3, 4, [...]]]
>>> d
[3, 4, [1, 2, [...]]]
>>> c[2]
[3, 4, [1, 2, [...]]]
>>> d[2]
[1, 2, [3, 4, [...]]]
>>> c[2] is d
1
>>> d[2] is c
1
注意,如果分别 pickle 每个对象,而不是在一个元组中一起 pickle 所有对象,会得到略微不同(但很重要)的结果,如清单 7 所示:
清单 7. 分别 pickle vs. 在一个元组中一起 pickle
>>> f = file('temp.pkl', 'w')
>>> pickle.dump(a, f)
>>> pickle.dump(b, f)
>>> f.close()
>>> f = file('temp.pkl', 'r')
>>> c = pickle.load(f)
>>> d = pickle.load(f)
>>> f.close()
>>> c
[1, 2, [3, 4, [...]]]
>>> d
[3, 4, [1, 2, [...]]]
>>> c[2]
[3, 4, [1, 2, [...]]]
>>> d[2]
[1, 2, [3, 4, [...]]]
>>> c[2] is d
0
>>> d[2] is c
0
相等,但并不总是相同
正如在上一个示例所暗示的,只有在这些对象引用内存中同一个对象时,它们才是相同的。在 pickle 情形中,每个对象被恢复到一个与原来对象相等的对象,但不是同一个对象。换句话说,每个 pickle 都是原来对象的一个副本:
清单 8. 作为原来对象副本的被恢复的对象
>>> j = [1, 2, 3]
>>> k = j
>>> k is j
1
>>> x = pickle.dumps(k)
>>> y = pickle.loads(x)
>>> y
[1, 2, 3]
>>> y == k
1
>>> y is k
0
>>> y is j
0
>>> k is j
1
同时,我们看到 Python 能够维护对象之间的引用,这些对象是作为一个单元进行 pickle 的。然而,我们还看到分别调用 dump() 会使 Python 无法维护对在该单元外部进行 pickle 的对象的引用。相反,Python 复制了被引用对象,并将副本和被 pickle 的对象存储在一起。对于 pickle 和恢复单个对象层次结构的应用程序,这是没有问题的。但要意识到还有其它情形。
值得指出的是,有一个选项确实允许分别 pickle 对象,并维护相互之间的引用,只要这些对象都是 pickle 到同一文件即可。 pickle 和 cPickle 模块提供了一个 Pickler (与此相对应是 Unpickler ),它能够跟踪已经被 pickle 的对象。通过使用这个 Pickler ,将会通过引用而不是通过值来 pickle 共享和循环引用:
清单 9. 维护分别 pickle 的对象间的引用
>>> f = file('temp.pkl', 'w')
>>> pickler = pickle.Pickler(f)
>>> pickler.dump(a)
>>> pickler.dump(b)
>>> f.close()
>>> f = file('temp.pkl', 'r')
>>> unpickler = pickle.Unpickler(f)
>>> c = unpickler.load()
>>> d = unpickler.load()
>>> c[2]
[3, 4, [1, 2, [...]]]
>>> d[2]
[1, 2, [3, 4, [...]]]
>>> c[2] is d
1
>>> d[2] is c
1
不可 pickle 的对象
一 些对象类型是不可 pickle 的。例如,Python 不能 pickle 文件对象(或者任何带有对文件对象引用的对象),因为 Python 在 unpickle 时不能保证它可以重建该文件的状态(另一个示例比较难懂,在这类文章中不值得提出来)。试图 pickle 文件对象会导致以下错误:
清单 10. 试图 pickle 文件对象的结果
>>> f = file('temp.pkl', 'w')
>>> p = pickle.dumps(f)
Traceback (most recent call last):
File "", line 1, in ?
File "/usr/lib/python2.2/copy_reg.py", line 57, in _reduce
raise TypeError, "can't pickle %s objects" % base.__name__
TypeError: can't pickle file objects
类实例
与 pickle 简单对象类型相比,pickle 类实例要多加留意。这主要由于 Python 会 pickle 实例数据(通常是 _dict_ 属性)和类的名称,而不会 pickle 类的代码。当 Python unpickle 类的实例时,它会试图使用在 pickle 该实例时的确切的类名称和模块名称(包括任何包的路径前缀)导入包含该类定义的模块。另外要注意,类定义必须出现在模块的最顶层,这意味着它们不能是嵌套 的类(在其它类或函数中定义的类)。
当 unpickle 类的实例时,通常不会再调用它们的 _init_() 方法。相反,Python 创建一个通用类实例,并应用已进行过 pickle 的实例属性,同时设置该实例的 _class_ 属性,使其指向原来的类。
对 Python 2.2 中引入的新型类进行 unpickle 的机制与原来的略有不同。虽然处理的结果实际上与对旧型类处理的结果相同,但 Python 使用 copy_reg 模块的 _reconstructor() 函数来恢复新型类的实例。
如果希望对新型或旧型类的实例修改缺省的 pickle 行为,则可以定义特殊的类的方法 _getstate_() 和 _setstate_() ,在保存和恢复类实例的状态信息期间,Python 会调用这些方法。在以下几节中,我们会看到一些示例利用了这些特殊的方法。
现在,我们看一个简单的类实例。首先,创建一个 persist.py 的 Python 模块,它包含以下新型类的定义:
清单 11. 新型类的定义
class Foo(object):
def __init__(self, value):
self.value = value
现在可以 pickle Foo 实例,并看一下它的表示:
清单 12. pickle Foo 实例
>>> import cPickle as pickle
>>> from Orbtech.examples.persist import Foo
>>> foo = Foo('What is a Foo?')
>>> p = pickle.dumps(foo)
>>> print p
ccopy_reg
_reconstructor
p1
(cOrbtech.examples.persist
Foo
p2
c__builtin__
object
p3
NtRp4
(dp5
S'value'
p6
S'What is a Foo?'
sb.
>>>
可以看到这个类的名称 Foo 和全限定的模块名称 Orbtech.examples.persist 都存储在 pickle 中。如果将这个实例 pickle 成一个文件,稍后再 unpickle 它或在另一台机器上 unpickle,则 Python 会试图导入 Orbtech.examples.persist 模块,如果不能导入,则会抛出异常。如果重命名该类和该模块或者将该模块移到另一个目录,则也会发生类似的错误。
这里有一个 Python 发出错误消息的示例,当我们重命名 Foo 类,然后试图装入先前进行过 pickle 的 Foo 实例时会发生该错误:
清单 13. 试图装入一个被重命名的 Foo 类的经过 pickle 的实例
>>> import cPickle as pickle
>>> f = file('temp.pkl', 'r')
>>> foo = pickle.load(f)
Traceback (most recent call last):
File "", line 1, in ?
AttributeError: 'module' object has no attribute 'Foo'
在重命名 persist.py 模块之后,也会发生类似的错误:
清单 14. 试图装入一个被重命名的 persist.py 模块的经过 pickle 的实例
>>> import cPickle as pickle
>>> f = file('temp.pkl', 'r')
>>> foo = pickle.load(f)
Traceback (most recent call last):
File "", line 1, in ?
ImportError: No module named persist
我们会在下面 模式改进这一节提供一些技术来管理这类更改,而不会破坏现有的 pickle。
特殊的状态方法
前面提到对一些对象类型(譬如,文件对象)不能进行 pickle。处理这种不能 pickle 的对象的实例属性时可以使用特殊的方法( _getstate_() 和 _setstate_() )来修改类实例的状态。这里有一个 Foo 类的示例,我们已经对它进行了修改以处理文件对象属性:
清单 15. 处理不能 pickle 的实例属性
class Foo(object):
def __init__(self, value, filename):
self.value = value
self.logfile = file(filename, 'w')
def __getstate__(self):
"""Return state values to be pickled."""
f = self.logfile
return (self.value, f.name, f.tell())
def __setstate__(self, state):
"""Restore state from the unpickled state values."""
self.value, name, position = state
f = file(name, 'w')
f.seek(position)
self.logfile = f
模式改进
随 着时间的推移,您会发现自己必须要更改类的定义。如果已经对某个类实例进行了 pickle,而现在又需要更改这个类,则您可能要检索和更新那些实例,以便它们能在新的类定义下继续正常工作。而我们已经看到在对类或模块进行某些更改 时,会出现一些错误。幸运的是,pickle 和 unpickle 过程提供了一些 hook,我们可以用它们来支持这种模式改进的需要。
在 这一节,我们将探讨一些方法来预测常见问题以及如何解决这些问题。由于不能 pickle 类实例代码,因此可以添加、更改和除去方法,而不会影响现有的经过 pickle 的实例。出于同样的原因,可以不必担心类的属性。您必须确保包含类定义的代码模块在 unpickle 环境中可用。同时还必须为这些可能导致 unpickle 问题的更改做好规划,这些更改包括:更改类名、添加或除去实例的属性以及改变类定义模块的名称或位置。
类名的更改
要 更改类名,而不破坏先前经过 pickle 的实例,请遵循以下步骤。首先,确保原来的类的定义没有被更改,以便在 unpickle 现有实例时可以找到它。不要更改原来的名称,而是在与原来类定义所在的同一个模块中,创建该类定义的一个副本,同时给它一个新的类名。然后使用实际的新类 名来替代 NewClassName ,将以下方法添加到原来类的定义中:
清单 16. 更改类名:添加到原来类定义的方法
def __setstate__(self, state):
self.__dict__.update(state)
self.__class__ = NewClassName
当 unpickle 现有实例时,Python 将查找原来类的定义,并调用实例的 _setstate_() 方法,同时将给新的类定义重新分配该实例的 _class_ 属性。一旦确定所有现有的实例都已经 unpickle、更新和重新 pickle 后,可以从源代码模块中除去旧的类定义。
属性的添加和删除
这些特殊的状态方法 _getstate_() 和 _setstate_() 再一次使我们能控制每个实例的状态,并使我们有机会处理实例属性中的更改。让我们看一个简单的类的定义,我们将向其添加和除去一些属性。这是是最初的定义:
清单 17. 最初的类定义
class Person(object):
def __init__(self, firstname, lastname):
self.firstname = firstname
self.lastname = lastname
假定已经创建并 pickle 了 Person 的实例,现在我们决定真的只想存储一个名称属性,而不是分别存储姓和名。这里有一种方式可以更改类的定义,它将先前经过 pickle 的实例迁移到新的定义:
清单 18. 新的类定义
class Person(object):
def __init__(self, fullname):
self.fullname = fullname
def __setstate__(self, state):
if 'fullname' not in state:
first = ''
last = ''
if 'firstname' in state:
first = state['firstname']
del state['firstname']
if 'lastname' in state:
last = state['lastname']
del state['lastname']
self.fullname = " ".join([first, last]).strip()
self.__dict__.update(state)
在这个示例,我们添加了一个新的属性 fullname ,并除去了两个现有的属性 firstname 和 lastname 。当对先前进行过 pickle 的实例执行 unpickle 时,其先前进行过 pickle 的状态会作为字典传递给 _setstate_() ,它将包括 firstname 和 lastname 属性的值。接下来,将这两个值组合起来,并将它们分配给新属性 fullname 。在这个过程中,我们删除了状态字典中旧的属性。更新和重新 pickle 先前进行过 pickle 的所有实例之后,现在可以从类定义中除去 _setstate_() 方法。
模块的修改
在概念上,模块的名称或位置的改变类似于类名称的改变,但处理方式却完全不同。那是因为模块的信息存储在 pickle 中,而不是通过标准的 pickle 接口就可以修改的属性。事实上,改变模块信息的唯一办法是对实际的 pickle 文件本身执行查找和替换操作。至于如何确切地去做,这取决于具体的操作系统和可使用的工具。很显然,在这种情况下,您会想备份您的文件,以免发生错误。但 这种改动应该非常简单,并且对二进制 pickle 格式进行更改与对文本 pickle 格式进行更改应该一样有效。
结束语
对象持久性依赖于底层编程语言的对象序列化能力。对于 Python 对象即意味着 pickle。Python 的 pickle 为 Python 对象有效的持久性管理提供了健壮的和可靠的基础。在下面的 参考资料中,您将会找到有关建立在 Python pickle 能力之上的系统的信息。原文链接:http://www.cnblogs.com/evening/archive/2012/04/01/2427876.html
python多线程学习-post提交加密码生成
真正使用多线程进行程序的编写,以前学习时的笔记《python threading模块学习join()》,现在用在实践上。
写代码在发现对他不熟悉的时候,总是缩手缩脚的,不知道为什么也不去尝试一下。过分依赖搜索引擎,自从上次写golang读取id3信息的那个程序开始,学会了用英文关键字检索信息,会检索到很多有用的信息,像stackoverflow上的回答。
先介绍一下功能:多线程生成密码,多线程提交数据。密码是排列组合生产的,五个元素全排列5!,这个密码长度是5。5个元素,密码长度为2,5×4 。适合那种通用型密码的破解。
程序代码:
Read more...
# encoding:utf-8
#
#author:0x55aa
#team:Pax.Mac Team
#time:2012.08.17
#
import urllib2,urllib,time
import threading
from Queue import Queue
from BeautifulSoup import BeautifulSoup
#提交的地址
URL = ""
#密码列表example:['0x55aa','1','b']
passwdlist = ['0x55aa','123','a']
#生产密码长度
passwdlen = 3
#密码生成完毕
passwd_done = False
#队列为空啦,这个可以用Queue.empty()
#post_done = False
#锁,用于输出
lock = threading.Lock()
#tag标识是否找到密码退出
tag = False
#密码
def perm(items, n=None):
if n is None:
n = len(items)
for i in range(len(items)):
v = items[i:i+1]
if n == 1:
yield v
else:
rest = items[:i] + items[i+1:]
for p in perm(rest, n-1):
yield v + p
def sqlPost(password):
"""提交奥"""
#提交字段的设置在这里。。
post_params = [
('UserName', 'root'),
('Password', password),
]
data = urllib.urlencode(post_params)
req = urllib2.Request(URL)
r = urllib2.urlopen(req,data)
#这里加入返回数据的判断
"""
soup = BeautifulSoup(r.read())
list1 = soup.find('value')
"""
return list1
class PasswordProduce(threading.Thread):
"""生成密码"""
def __init__(self,queue,first,items,n):
threading.Thread.__init__(self)
self.passwd = queue
self.first = first
self.items = items
self.n = n
def run(self):
#生成密码
r = perm(self.items,self.n)
for i in r:
p = self.first + ''.join(i)
#密码插入队列
self.passwd.put(p)
"""
if lock.acquire():
print p
lock.release()
"""
class Sqlpost(threading.Thread):
"""提交"""
def __init__(self,queue):
threading.Thread.__init__(self)
self.passwd = queue
def run(self):
#post提交
global passwd_done
while not (passwd_done and self.passwd.empty()):
"""
if lock.acquire():
print self.getName(),passwd_done , self.passwd.empty()
lock.release()
"""
#取得密码
if not self.passwd.empty():
p = self.passwd.get()
else:
time.sleep(2)
continue
"""
if lock.acquire():
print p
lock.release()
"""
#提交
r = sqlPost(p)
#打印返回信息
global tag
if r:
tag = True
if lock.acquire():
print "password:",p,">>the result:",r
#print r
lock.release()
if tag:
break
def main():
queue = Queue()
#密码线程
passwdthread = []
#post
postthread = []
#线程数
s = len(passwdlist)
for i in range(s):
first = passwdlist[i]
lastlist = passwdlist[:i]+passwdlist[i+1:]
thelen = passwdlen - 1
pp = PasswordProduce(queue,first,lastlist,thelen)
sp = Sqlpost(queue)
passwdthread.append(pp)
postthread.append(sp)
pp.start()
sp.start()
for t in passwdthread:
t.join()
#设置,密码生产完毕
global passwd_done
passwd_done = True
print "\npassword is produced done\n"
for t in postthread:
t.join()
print "\nAll done!\n"
if __name__ == '__main__':
main()
排列组合代码Google而来,等再写一篇文章进行分析,这篇写多线程编程。
1.join方法的使用。可以参考上面这篇文章,在密码生产线程都结束后,进行passwd_done变量的设置。
2.queue的使用。queue可以方便的进行线程间的通信数据交换。生产密码添加到queue,post线程提交从队列中获取密码。这里有两个判断条件:passwd_done判断密码生产是否结束,self.passwd.empty()检查队列是否为空。当这两个条件都符合时,密码就全部跑完了。还有一个全局变量tag用于当密码成功后,结束线程。
3.密码为了多线程生成,我取其中一个元素作为密码的首位,也就是有len(passwdlist)个线程。不知道这里用什么方法实现好。
4.数据的打印用了threading.Lock()