文档内容学习于
http://www.cnblogs.com/xiaozhiqi/
装饰器 :
定义: 装饰器 本质是函数,功能是装饰其他函数,就是为其他函数添加附加功能。
原则:
1、不能修改被装饰的函数的源代码
2、不能修改被装饰的函数的调用方式
看以下
[root@localhost day4]# cat decorator.py
#!/usr/bin/env python3 import time
def timmer(func): #装饰器。 其实编写当时和写函数 是一样的
def warpper(*args,**kwargs):
start_time = time.time()
func()
stop_time =time.time()
print ("the func run time is %s" %(stop_time-start_time))
return warpper @timmer #使用装饰器(这个装饰器的作用其实是统计test1 这个函数的运行时间),然后这个装饰器 对被修饰的 test1这个函数 这个其实是不影响的。 并没有违背以上2条原则。 def test1():
time.sleep(3)
print ("this is test")
return 0 test1() [root@localhost day4]# ./decorator.py
this is test
the func run time is 3.0033674240112305 #装饰器的效果 ,统计test1 函数的运行时时间。
我们 一步 一步 把装饰器 拆开来 慢慢 理解 。
装饰器的知识储备
1. 函数即“变量”
这个概念 可以看视屏day4__5. 简单说就是 python通过内存地址(函数名,变量名)来调用变量(函数)。
2.高阶函数
3.嵌套函数
高阶函数+ 嵌套函数==> 装饰器
然后 我们 引用
1.函数即“变量”
2.高阶函数
之前我们提到了 高阶函数的 概念
1. 把一个函数名当做实参传给另一个函数
2. 返回值中包含函数名
的概念 其实功能 就类似以下。 但是以下不是装饰器。只是功能类似。因为他违背了2、不能修改被装饰的函数的调用方式 的规则(这个说明只是为了帮助理解)
import time def bar(): #定义了bar 这个函数
time.sleep(3)
print ("in the bar") def test(func): # bar作为实参传进来, 这个 其实这个时候func就等于bar 这个函数了
start_time = time.time()
func() # 运行 func这个变量(函数),, 其实就运行了bar这个函数
stop_time =time.time()
print ("the func run time is %s" %(stop_time-start_time)) test(bar) # 将bar作为时实参传进去, 此处还需要注意,这边是传函数,不是传函数的执行结果,所有不能bar()这么写
PS :
在以上 例子中bar其实是源代码, 虽然我们没有变化bar的源代码 。 但到最后其实调用bar的方式改变了(将bar作为实参使用了)。违背了第2条原则。 所以以上并不是装饰器。
然后我们上面看到了 高阶函数的 第二条定义
2. 返回值中包含函数名
我们做以下尝试
import time def bar():
print ("in the bar") def test(func):
start_time = time.time()
func()
stop_time =time.time()
print ("the func run time is %s" %(stop_time-start_time))
return func # 进行这个变量(函数)作为返回值 ,这样这个函数的执行结果就是这个函数的内存地址(这个函数的变量名),就可以定义在外面定义变量了。 bar = test(bar) #这样再定义bar,就可以不修改 bar这个源代码的调用方式了。
bar()
通过以上例子 我们可以发现
返回值中包含函数名 的 作用相当于 不修改函数的调用方式
这样,我们对于高阶函数的定义 ,在装饰器中可以理解为
1. 把一个函数名当做实参传给另一个函数(不修改被装饰函数的情况下不修改源代码 )
2. 返回值中包含函数名(不修改函数的调用方式 )
嵌套函数
就是在函数里面 在再定义一个函数
def foo():
print("in the foo")
def bar (): #这里定义的bar函数,一定要在foo里面才能调用, 作用类似于局部变量,bar这个函数在全局是不生效的。
print ("in the bar")
bar() foo()
看以下
[root@master day4]# cat decarate.py
#!/usr/bin/env python3 x = 0
def grandba():
x = 1
def dad():
x = 2
def son():
x = 3
print (x)
son()
dad() grandba() [root@master day4]# ./decarate.py
3
然后将高阶函数和 嵌套函数结合起来使用
[root@localhost day4]# cat decorator2.py
#!/usr/bin/env python3 import time def timer(func): # 高阶函数: 把函数作为实参进来
def deco(): # 嵌套函数:函数里面定义函数
start_time=time.time()
func()
stop_time=time.time()
print ("the func run time is %s" %(stop_time-start_time))
return deco #高阶函数:用函数作为返回值。 这边定义的返回值其实是deco这个函数, 在timer这个函数里面无需执行 def test1():
time.sleep(3)
print ("in the test1") test1 = timer(test1) # 外部调用函数
test1()
[root@localhost day4]# ./decorator2.py
in the test1
the func run time is 3.0034003257751465
@符
在python3中可以直接使用 @ 符号来调用装饰器。
1.它一定要写在被修饰函数的前面
[root@localhost day4]# cat decorator2.py
#!/usr/bin/env python3 import time def timer(func):
def deco():
start_time=time.time()
func()
stop_time=time.time()
print ("the func run time is %s" %(stop_time-start_time))
return deco @timer #直接@符加修饰器名字。 但是一定要写在被修饰函数的前面 @time ==== test1=timer(test1)
def test1():
time.sleep(3)
print ("in the test1") test1()
[root@localhost day4]# ./decorator2.py
in the test1
the func run time is 3.003570795059204
完整的装饰器
装饰器的源代码使用传参
理解 一下。 是在理解不了,就死记吧。 要传参就是,需要deco和 func这边也将形参写好。
[root@localhost day4]# cat decorator2.py
#!/usr/bin/env python3 import time def timer(func):
def deco(*args,**kwargs): ### 进行传参
start_time=time.time()
func(*args,**kwargs) ### 因为这个func函数在这边是直接调用的,不是定义的。所以参数需要deco传进来
stop_time=time.time()
print ("the func run time is %s" %(stop_time-start_time))
return deco @timer # test2 = timer(test2) ==> 其实 test2()== fun() 执行是 deco(),所以test2()传参就是 deco() 传参 再传给func() def test2(name,age):
time.sleep(1)
print ("this is %s,%s" %(name,age) ) test2("haha",55) [root@localhost day4]# ./decorator2.py
this is haha,55
the func run time is 1.0164666175842285
查看 以下代码:
有3个页面 index home bbs 。 本来是直接就能访问。 现在需要有的页面认证才能访问 。
所以 就有了一个 认证的装饰器 。
# Author ricky username,passwd = "ricky","123" def auth(func): #认证页面的装饰器
def wrapper(*args,**kwargs):
Uname = input ("please input your name ").strip()
Passwd = input ("please input your passwd ").strip()
if Uname == username and Passwd == passwd: # 判断用户和密码
print("\033[32;1mAuth is ok\033[0m")
func(*args,**kwargs)
else:
print ("\033[31;1mInvaild Auth\033[0m") #认证不通过直接就退出了
exit()
return wrapper def index(): #index不需要认证
print ("welcome to index") @auth #home页面需要认证
def home():
print ("welcome to home") @auth #bbs页面需要认证
def bbs():
print ("welcome to bbs") index()
home()
bbs()
执行一下. 结果如下 。
如果我们源码里面就有 return的, 这个时候为了不改变他的调用方式。需要进行以下修改
[root@master day4]# cat decarate2.py
#!/usr/bin/env python3 username,passwd = "ricky","123" def auth(func):
def wrapper(*args,**kwargs):
Uname = input ("please input your name ").strip()
Passwd = input ("please input your passwd ").strip()
if Uname == username and Passwd == passwd:
print("\033[32;1mAuth is ok\033[0m")
res = func(*args,**kwargs) #用一个了变量来指定这个func的执行结果。 同时这个函数已经执行了 。
print ("after func") # 标记位
return res # 返回func这个函数的执行结果,不能直接return func。因为直接return func的话。就等于这个func又执行了一下。所以需要用变量来代替。
else:
print ("\033[31;1mInvaild Auth\033[0m")
return wrapper def index():
print ("welcome to index") def home():
print ("welcome to home") @auth
def bbs():
print ("welcome to bbs")
return ("from bbs") #函数本生是有return。
index()
home()
print (bbs()) # 直接打印执行结果 [root@master day4]# ./decarate2.py
welcome to index
welcome to home
please input your name ricky
please input your passwd 123
Auth is ok # 认证ok
welcome to bbs # 执行wrapper(func===bbs)函数、 其实是在定义res变量的时候执行的
after func # 标记位
from bbs # 指定了 bbs函数的执行结果 、
上面例子中。 如果我们在 wrapper 里面直接 return了 func(),func()会有执行一遍。 查看以下
username,passwd = "ricky","123" def auth(func):
def wrapper(*args,**kwargs):
Uname = input ("please input your name ").strip()
Passwd = input ("please input your passwd ").strip()
if Uname == username and Passwd == passwd:
print("\033[32;1mAuth is ok\033[0m")
func(*args,**kwargs)
print ("after func")
return func(*args,**kwargs) # 接 return了 func(), 这个如果直接return了 func的话 只是打印func这个函数的内存地址。上面有提到过。 所以func(),才是他的执行结果
else:
print ("\033[31;1mInvaild Auth\033[0m")
return wrapper def index():
print ("welcome to index") def home():
print ("welcome to home") @auth
def bbs():
print ("welcome to bbs")
return ("from bbs") index()
home()
print (bbs())
看,func执行了2遍。 所以正确的做法需要用个变量和存他的执行结果 。
完整的装饰器2
这边知识点是,装饰器使用参数 。
以上的脚本的认证使用时了用户名和密码。 如果这边有个 ldap服务的 也可以进行认证 。 那么这边就需要判断 是使用用户名进行认证 还是ldap进行认证
其实不难,就是加了一层函数。来进行判断
[root@master day4]# cat decarate3.py
#!/usr/bin/env python3 # Author ricky username,passwd = "ricky","123" def auth(auth_type): #看这边的形参,已经不是func了。 而是装饰器调用时传参使用的变量
def out_wrapper(func): #多加了一层函数,这边使用的形参才是 func
def wrapper(*args, **kwargs):
if auth_type == "local": #这边就是用来判断认证方式的,就是外面装饰器传参的变量
print ("Need you username and passwd")
Uname = input("please input your name ").strip()
Passwd = input("please input your passwd ").strip()
if Uname == username and Passwd == passwd:
print("\033[32;1mAuth is ok\033[0m")
res = func(*args, **kwargs)
return res
else:
print("\033[31;1mInvaild Auth\033[0m")
elif auth_type == "ldap": #这边就是用来判断认证方式的,就是外面装饰器传参的变量
print ("it is ldap")
return wrapper # return第一层
return out_wrapper # return第二层 def index():
print ("welcome to index") @auth(auth_type="local") #这边先定义了认证的方式,其实就是装饰器传参
def home():
print ("welcome to home") @auth(auth_type="ldap") #这边先定义了认证的方式,其实就是装饰器传参
def bbs():
print ("welcome to bbs")
return ("from bbs") index()
home()
bbs() [root@master day4]# ./decarate3.py
welcome to index
Need you username and passwd
please input your name ricky
please input your passwd 123
Auth is ok
welcome to home
it is ldap
[root@master day4]#
迭代器&生成器
列表生成式
[root@localhost day4]# cat new.py
#!/usr/bin/env python3 a = [ i*2 for i in range(10)] # 生成列表, 0-9的数字, 都会乘以2.
print ( a )
[root@localhost day4]# ./new.py
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
什么是生成器?
通过列表生成式,我们可以直接创建一个列表,但是,受到内存限制,列表容量肯定是有限的,而且创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。
所以,如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的list,从而节省大量的空间,在Python中,这种一边循环一边计算的机制,称为生成器:generator
1.当列表中有100W字符的时候 列表是会将100W字符都先生产。不管用不用。
生成器的 只会先告诉你生成了。 其实并没有全部生成。 只有当你调用的时候才会生成相应的数据。
[root@localhost day4]# cat generator.py
#!/usr/bin/env python3 a = [ i*2 for i in range(10)]
print ( type(a) ) b = ( i*2 for i in range(10)) # 其实就是讲【】 改成()
print( b ) [root@localhost day4]# ./generator.py
<class 'list'>
<generator object <genexpr> at 0x7f5296f1ed58>
调用生成器,出了用for循环你。 还可以用__next__()方法
b = ( i*2 for i in range(10))
print( b ) print ( b.__next__())
print ( b.__next__())
print ( b.__next__())
next 就是一个 一个往后调用。而且 因为他只是记录了当前的数据。所以只能往后生成。不能往前。
例子:
斐波那契 数列
这个数列从第3项开始,每一项都等于前两项之和
[root@master day4]# cat feibo.py
#!/usr/bin/env python3 def feibo(max):
n,a,b = 0,0,1
while n < max:
print (b)
a,b = b, a + b # 注意 这边一定要这个样子赋值,因为只有这个样子赋值的时候。 虽然a=b, 但是给b赋值的时候,其实a还没有生效。 还是前一次的数值。
n = n+1
return 'done' feibo(10)
[root@master day4]# ./feibo.py
1
1
2
3
5
8
13
21
34
55
查看赋值语句
a,b = b, a + b
其实等于 t = (a, a+b) #t 是一个tuple 相当于一个临时变量
a = t[0]
b = t[1]
如果不写在,在给b赋值的时候,a就已经生效了,会出现以下效果
[root@master day4]# cat feibo.py
#!/usr/bin/env python3 def feibo(max):
n,a,b = 0,0,1
while n < max:
print (b)
a = b
b = a + b #当给b赋值的时候, a其实已经生效了。 所以就会出现翻倍的效果
n = n+1
return 'done' feibo(10)
[root@master day4]# ./feibo.py
1
2
4
8
16
32
64
128
256
512
然后这个数列,离生成器, 只差一步了。 就是将 print改成 yield
[root@master day4]# cat feibo.py
#!/usr/bin/env python3 # Author ricky def feibo(max):
n,a,b = 0,0,1
while n < max:
# print (b)
yield b
a,b = b, a + b
n = n+1
return 'done' print ( feibo(10) ) # 函数里面没有 print了。 直接当他变成生成器了。 所以直接执行函数是没有效果的 。这边需要打印他。
[root@master day4]# ./feibo.py
<generator object feibo at 0x7f2e7079be08>
然后可以使用__next__() 取值
[root@master day4]# cat feibo.py
#!/usr/bin/env python3 # Author ricky def feibo(max):
n,a,b = 0,0,1
while n < max:
# print (b)
yield b
a,b = b, a + b
n = n+1
return 'done' f = feibo(10)
print ( f.__next__() )
print ( f.__next__() )
print ( f.__next__() )
print ( f.__next__() )
print ( f.__next__() ) [root@master day4]# ./feibo.py
1
1
2
3
5
也可以通过,for循环来取。因为他生成器,所以取过的值,它不会再有了
[root@master day4]# cat feibo.py
#!/usr/bin/env python3 # Author ricky def feibo(max):
n,a,b = 0,0,1
while n < max:
# print (b)
yield b
a,b = b, a + b
n = n+1
return 'done' f = feibo(10) print ( f.__next__() )
print ( f.__next__() )
print ( f.__next__() )
print ( f.__next__() )
print ( f.__next__() ) print ("======start loop")
for i in f :
print (i)
[root@master day4]# ./feibo.py
1
1
2
3
5
======start loop
8
13
21
34
55
抓异常
我们再函数中 有return。
当我们使用 for循环进行读取生成器的时候,没有问题
但是当我们一直使用 next的,会报错。因为如果只有10个值,取多了。他就没值了。 所以return会报错 。
看以下,我明明只有10个值,但是 我next好多。然后再执行的时候 ,就要报错。
然后会报 StopIteration 的错
为了处理这个异常,我们需要使用try。来抓这个异常 。
课里面 暂时没有细讲。 先死记吧。
[root@master day4]# cat feibo.py
#!/usr/bin/env python3 # Author ricky def feibo(max):
n,a,b = 0,0,1
while n < max:
# print (b)
yield b
a,b = b, a + b
n = n+1
return 'done' f = feibo(10) while True: # 主要就是这段 while代码。来抓取异常 。
try:
x = next(f)
print ('f:',x)
except StopIteration as e: # 这个异常字段,就是前面报的字段。 如果是报其他字段,这段代码就不生效了(要么进行字段调整) 。
print("Generator return value" , e.value)
break [root@master day4]# ./feibo.py
f: 1
f: 1
f: 2
f: 3
f: 5
f: 8
f: 13
f: 21
f: 34
f: 55
Generator return value done
yield 传参
next是能调用yield,如果要传参的话。可以使用send。
[root@localhost day4]# cat new.py
#!/usr/bin/env python3 def consumer(name):
print("%s 准备吃包子" %name)
while True:
baozi = yield
print ("包子[%s]来了,被[%s]吃了" %(baozi,name)) c = consumer("AAA") c.__next__()
c.__next__()
c.send("vegetables") # 可以传递参数
[root@localhost day4]# ./new.py
AAA 准备吃包子
包子[None]来了,被[AAA]吃了
包子[vegetables]来了,被[AAA]吃了
试一下以下代码,单线程下的并行, 可以叫‘协程’,是一种性能很高的 运行方式。
# Author ricky import time def consumer(name):
print("%s 准备吃包子" %name)
while True:
baozi = yield
print ("包子[%s]来了,被[%s]吃了" %(baozi,name)) def producer():
c = consumer( 'A')
c2 = consumer('B')
c.__next__() #类似于初始化
c2.__next__() #类似于初始化
print ("开始准备包子")
for i in range(10):
time.sleep(1)
print ("一个分2半")
c.send(i)
c2.send(i) producer()
迭代器
我们已经知道,可以直接作用于for
循环的数据类型有以下几种:
一类是集合数据类型,如list
、tuple
、dict
、set
、str
等;
一类是generator
,包括生成器和带yield
的generator function。
这些可以直接作用于for
循环的对象统称为可迭代对象:Iterable
。
可以使用isinstance()
判断一个对象是否是Iterable
对象:
可以使用,查看如下类型
[root@localhost day4]# cat Iterable.py
#!/usr/bin/env python3 from collections import Iterable print ( isinstance ([],Iterable)) # 列表
print ( isinstance ((),Iterable) ) # 元组
print ( isinstance ('abc',Iterable)) # 字符串
print ( isinstance (100,Iterable)) # 数字 [root@localhost day4]# ./Iterable.py
True
True
True
False
而生成器不但可以作用于for
循环,还可以被next()
函数不断调用并返回下一个值,直到最后抛出StopIteration
错误表示无法继续返回下一个值了。
可以被next()
函数调用并不断返回下一个值的对象称为迭代器:Iterator
。
是不是 迭代器, 可以用dir查看数据类型中,有没有 next。如果没有 就不是。
下面是列表,数据类型。 没有next。不是迭代器 。
[root@localhost day4]# cat Iterable.py
#!/usr/bin/env python3 a = [1,2,3]
print (dir(a))
[root@localhost day4]# ./Iterable.py
['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']
[root@localhost day4]#
可以使用模块进行判定
[root@localhost day4]# cat Iterable.py
#!/usr/bin/env python3 from collections import Iterator
print ( isinstance (( x for x in range(10)),Iterator) )
[root@localhost day4]# ./Iterable.py
True
生成器都是Iterator
对象,但list
、dict
、str
虽然是Iterable
,却不是Iterator
。
把list
、dict
、str
等Iterable
变成Iterator
可以使用iter()
函数:
[root@localhost day4]# cat Iterable.py
#!/usr/bin/env python3 from collections import Iterator
print ( isinstance (( x for x in range(10)),Iterator) )
print ( isinstance ([],Iterator) )
print ( isinstance (iter([]),Iterator) )
[root@localhost day4]# ./Iterable.py
True
False
True
你可能会问,为什么list
、dict
、str
等数据类型不是Iterator
?
这是因为Python的Iterator
对象表示的是一个数据流,Iterator对象可以被next()
函数调用并不断返回下一个数据,直到没有数据时抛出StopIteration
错误。可以把这个数据流看做是一个有序序列,但我们却不能提前知道序列的长度,只能不断通过next()
函数实现按需计算下一个数据,所以Iterator
的计算是惰性的,只有在需要返回下一个数据时它才会计算。
Iterator
甚至可以表示一个无限大的数据流,例如全体自然数。而使用list是永远不可能存储全体自然数的。
小结
凡是可作用于for
循环的对象都是Iterable
类型;
凡是可作用于next()
函数的对象都是Iterator
类型,它们表示一个惰性计算的序列;
集合数据类型如list
、dict
、str
等是Iterable
但不是Iterator
,不过可以通过iter()
函数获得一个Iterator
对象。
Python的for
循环本质上就是通过不断调用next()
函数实现的,例如:
for x in [1, 2, 3, 4, 5]:
pass
实际上完全等价于:
# 首先获得Iterator对象:
it = iter([1, 2, 3, 4, 5])
# 循环:
while True:
try:
# 获得下一个值:
x = next(it)
except StopIteration:
# 遇到StopIteration就退出循环
break
json序列化
我们把变量从内存中变成可存储或传输的过程称之为序列化,就是将内存中的数据以字符串的形式存到存储空间中。
就像以下
进行序列化
[root@localhost day4]# cat serial.py
#!/usr/bin/env python3 info = {
"name ": "AAA",
"age":22
} f = open("test.test","w") # 以字符串的形式,写到文件里面。
f.write(str(info)) f.close() [root@localhost day4]# ls
decorator1.py decorator2.py decorator.py func.py generator.py Iterable.py new.py serial.py
[root@localhost day4]# ./serial.py
[root@localhost day4]# ls
decorator1.py decorator2.py decorator.py func.py generator.py Iterable.py new.py serial.py test.test # 写在文件里面了
[root@localhost day4]# cat test.test
{'name ': 'AAA', 'age': 22}[root@localhost day4]#
然后其实可以使用json的形式下 。
使用dumps
import json
info = {
"name ": "AAA",
"age":22
} f = open("test.test","w")
f.write( json.dumps(info) ) == json.dump(info,f) # 注意 后面是dump 写法和前面面不一样。但是执行效果是完全一样的 f.close()
使用json实施反序列化,
使用loads
import json f = open("test.test","r")
data = json.loads(f.read()) == data = json.load(f) # 注意 后面是load 写法和前面面不一样。但是执行效果是完全一样的 print (data["age"]) f.close()
执行结果
pickle
在python里面 json只能处理简单的数据。 如果需要处理复杂的问题,像函数这种的 需要使用pickle
import pickle #调用pickle模块 def sayhi(name): # 定义一个sayhi的函数
print ("you name:",name) info = {
"name ": "AAA",
"age":22,
'func': sayhi # 调用sayhi
} f = open("test.test","wb") # 这里需要使用wb。 以为pickle默认是使用字节的形式。
f.write(pickle.dumps(info)) # 使用pockle调用
f.close()
然后处理出来的文件是这种格式的。 这个其实不是乱码。是pickle的数据排列格式。
反序列化的话 ,可以这么写
import pickle def sayhi(name): # 在反序列话的时候 需要把这个函数再写一遍。因为这个函数是上一个程序的。 程序执行完的时候。函数的内存地址就已经被释放了。 所以这边需要再写一遍
print ("you name:",name) f = open("test.test","rb")
data = pickle.loads(f.read()) print (data) f.close()
执行结果,可以看到他将“sayhi”这个函数的 内存地址直接打印出来 。 名字就叫“func”
可以直接执行这个func
# Author ricky import pickle def sayhi(name):
print ("you name:",name) f = open("test.test","rb")
data = pickle.loads(f.read()) print (data["func"]("hahaha")) #注意格式,要传参的 f.close()
执行结果:
同时在反序列化中,只需要函数的名字一样就可以了。 函数的内容不需要完全一致。
看以下:
# Author ricky import pickle def sayhi(name):
print ("this is name2:",name) # 函数名不变,但是内容不同。 f = open("test.test","rb")
data = pickle.loads(f.read()) print (data["func"]("hahaha")) f.close()
看执行结果,依然可以执行。所以他序列化是他的整个数据对象。