python编程 基础入门学习笔记四

本节内容:

1.装饰器

2.生成器

3.迭代器

4.内置函数

5.json和pickle序列化

6.软件目录开发规范

7.练习

 

1.装饰器                                                                                                                                                                    

装饰器:

定义:本质是函数,(目的是装饰其他函数)就是为其他函数添加附件功能。

原则:1).不能修改被装饰的函数的源代码

   2).不能修改被装饰函数的调用方式

实现装饰器所需的知识储备:

  1).函数即“变量”

  2).高阶函数

  3).嵌套函数

  高阶函数+嵌套函数=> 装饰器

解释函数即“变量”前,先看下以下例子:

#问题代码一
def foo():
    print('in the foo')
    bar()

foo()

#问题代码二
def foo():
    print('in the foo')
    bar()

foo()

def bar():
    print('in the bar')
看两段问题代码
报错:NameError: name 'bar' is not defined
#正确调用方式一
def bar():
    print('in the bar')

def foo():
    print('in the foo')
    bar()

foo()

#正确调用方式二
def foo():
    print('in the foo')
    bar()

def bar():
    print('in the bar')

foo()
正确代码

方式一和方式二是完全一样的。

为什么会这样的情况出现呢?

这里就涉及到内存分配与回收机制,举个简单的

def定义函数的时候,就是在内存中开辟分配一块空间,这一点和变量是一样的(函数名就相当于变量名。对应的函数体相当于变量的值),当变量名被销毁/删除时,内存即被回收。

但还有一个特例,那就是匿名函数lambda,如lambda x:x*3,定义了3*x的函数,这个匿名函数在没有引用的时候就立马回收。

理解了内存的分配与回收机制,再回头看上面的代码,就明白了。

高阶函数:a:把一个函数名当作实参传给另外一个函数

     b:返回值中包含函数名

具体可见python编程 基础入门学习笔记三 第4节4)中的介绍。

#-*- coding:utf-8 -*-
#Author:'Yang'
import time

#原函数
def bar():
    time.sleep(1)
    print("in the bar")

#定义一个高阶函数,用来装饰bar
def test1(func):
    start_time=time.time()
    func()#run bar
    stop_time=time.time()
    print("The func run time is %s" %(stop_time-start_time))

test1(bar)
View Code

在不修改被装饰函数源代码的情况下为其添加功能(计时)

#-*- coding:utf-8 -*-
#Author:'Yang'
import time

#原函数
def bar():
    time.sleep(1)
    print("in the bar")

#定义一个高阶函数,用来装饰bar
def test2(func):
    print(func)#打印内存地址,相当于添加的功能
    return func

t=test2(bar) #将返回值赋给t,t是一个内存地址
#test2(bar())#写成bar()就是把返回值传给了test,不符合高阶函数的定义,是错误的
t()#run bar
View Code

在此基础上进一步改进

#-*- coding:utf-8 -*-
#Author:'Yang'
import time

#原函数
def bar():
    time.sleep(1)
    print("in the bar")

#定义一个高阶函数,用来装饰bar
def test2(func):
    print(func)#打印内存地址。相当于添加的功能
    return func

bar=test2(bar)
bar()
View Code

在不修改函数的调用方式的情况下为其添加功能(打印内存地址)

嵌套函数:

def foo():
    print("in the foo")
    def bar():#局部函数,作用域相当于局部变量
        print("in the bar")
    bar()#只能在内部调用,不能再外部调用,因为它是局部函数

foo()
View Code

这就是嵌套函数,函数里再定义函数。

def bar():
print("in the bar")
def foo():
print("in the foo")
bar()

注意:这种形式,bar()只是调用,并不是嵌套函数。

介绍了这么多内容,下面让我们来认识下真正的装饰器

#高阶函数+嵌套函数
def timer(func):    #timer(test1),把test1的内存地址传给func  func=test1
    def deco():
        start_time=time.time()
        func()#run test1
        stop_time=time.time()
        print("The func run time is %s" %(stop_time-start_time))
    return deco     #返回deco函数的内存地址

@timer    #test1=timer(test1)
def test1():
    time.sleep(1)
    print("in the test1")

@timer    # test2=timer(test2)
def test2():
    time.sleep(1)
    print("in the test2")


test1()    #实际就是在执行deco

test2()    #实际就是在执行deco
View Code

@timer 在python语法中叫语法糖,需要在哪个函数上添加功能就在哪个函数前面添加语法糖,为了消化吸收,可以将上面代码全部加断点调试看一下代码运行的走向,就会明白了。

如果已有函数有参数,该怎么使用装饰器呢?下面再进一步深入装饰器:

#高阶函数+嵌套函数
def timer(func):#timer(test1),把test1的内存地址传给func  func=test1
    def deco(*args,**kwargs):
        start_time=time.time()
        func(*args,**kwargs)#run test1
        stop_time=time.time()
        print("The func run time is %s" %(stop_time-start_time))
    return deco #返回deco函数的内存地址

@timer#test1=timer(test1)
def test1():
    time.sleep(1)
    print("in the test1")

@timer# test2=timer(test2)
def test2(name,age,dict):
    time.sleep(1)
    print("in the test2:%s-%d" %(name,age))
    for k,v in enumerate(dict):
        print(v,dict[v])


test1()#实际就在执行deco

test2("Rose",18,{"shcool":"Music.edu","grade":"one"})
View Code

注意:*args,**kwargs参数组的使用,以上格式的代码基本能满足日常开发的90%需求

举个实际应用的例子:需要给已有页面加上登录验证

#三个函数源代码
def index():
    print("Welcome to index")
def home():
    print("Welcome to home")
def bbs():
    print("Welcome to bbs")

#调用函数的方式
index()
home()
bbs()
原模块代码
#-*- coding:utf-8 -*-
#Author:'Yang'

user,passwd="Rose","abc123"

'''装饰器函数:高阶函数+嵌套函数'''
def auth(func):
    def wrapper(*args,**kwargs):#内嵌函数
        username=input("请输入用户名>>:")
        password=input("请输入密码>>:")
        if username==user and password==passwd:
            func(*args,**kwargs)
            print("\033[32;1mUser has passed authentication.\033[0m")
        else:
            print("\033[31;1mInvalid username or password.\033[0m")
    return wrapper


def index():
    print("Welcome to index")
@auth
def home():
    print("Welcome to home")
@auth
def bbs():
    print("Welcome to bbs")

index()
home()
bbs()
添加登录验证

在原模块的基础上为home和bbs页面添加登录验证,这里使用了装饰器:在不改变函数源代码和函数调用方式的前提下,添加新功能实现了登录验证。

大家注意了,如果在home函数里有个返回值,在使用装饰器函数后怎么获取这个home的返回值呢?直接print(home()),返回的是None,这就需要进一步修改装饰器函数

#-*- coding:utf-8 -*-
#Author:'Yang'

user,passwd="Rose","abc123"

'''装饰器函数:高阶函数+嵌套函数'''
def auth(func):
    def wrapper(*args,**kwargs):#内嵌函数
        username=input("请输入用户名>>:")
        password=input("请输入密码>>:")
        if username==user and password==passwd:
            res=func(*args,**kwargs)#将函数(如home)返回值赋给变量res
            print("\033[32;1mUser has passed authentication.\033[0m")
            return res #return一个返回值
        else:
            print("\033[31;1mInvalid username or password.\033[0m")
    return wrapper


def index():
    print("Welcome to index")
@auth
def home():
    print("Welcome to home")
    return "from home"
@auth
def bbs():
    print("Welcome to bbs")

index()
print(home())
bbs()
添加返回值

现在问题变得更复杂了:希望home页面进行本地(local)认证,bbs页面进行统一(ldap)认证,该如何优化代码呢?

#-*- coding:utf-8 -*-
#Author:'Yang'

user,passwd="Rose","abc123"

'''装饰器函数:高阶函数+嵌套函数'''
def auth(auth_type):
    print("auth_type:",auth_type)
    def outwrapper(func):
        print("outwrapper func:",func())#func是一个函数返回值的内存地址,加了()就是返回值
        def wrapper(*args,**kwargs):#内嵌函数
            print("wrpper func args:",*args,**kwargs)
            if auth_type=="local":
                username=input("请输入用户名>>:")
                password=input("请输入密码>>:")
                if username==user and password==passwd:
                    res=func(*args,**kwargs)#将函数(如home)返回值赋给变量res
                    print("\033[32;1mUser has passed authentication.\033[0m")
                    return res #return一个返回值
                else:
                    print("\033[31;1mInvalid username or password.\033[0m")
            elif auth_type=="ldap":
                print("ldap,不会")
        return wrapper
    return outwrapper

def index():
    print("Welcome to index")
@auth(auth_type="local")
def home():
    print("Welcome to home page.")
    return "from home"
@auth(auth_type="ldap")
def bbs():
    print("Welcome to bbs page.")

index()
print(home())#wrapper
bbs()
View Code

看完代码有点懵?每一句代码加上断点,调试下,看看代码的走向就明白了。

装饰器就讲这么多了,要想熟练还得实际多操练。

2.生成器                                                                                                                                                                    

 用以下代码可以生成一个列表,

>>> a=[]
>>> for i in range(10):
    a.append(i*2)

    
>>> a
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]

现在用列表生成式来实现,

>>> a=[i*2 for i in range(10)]  #列表生成式
>>> a
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]

可以看到代码变得简洁了许多

>>> (i*2 for i in range(10))  #用语句做生成器
<generator object <genexpr> at 0x02CD6648>

 在 i*2 for i in range(10) 语句上加上 () 就变成了生成器(其实它只准备好了一个算法,不用提前准备好所有数据,用哪个数据调用哪个数据,节省了内存空间)

将这个生成器赋值给b,再看下结果

>>> b=(i*2 for i in range(10))
>>> b
<generator object <genexpr> at 0x02CD8C88>
>>> for j in b:
    print(j)

    
0
2
4
6
8
10
12
14
16
18
>>> b[1]
Traceback (most recent call last):
  File "<pyshell#18>", line 1, in <module>
    b[1]
TypeError: 'generator' object is not subscriptable
View Code

b[1]取值会出错,因为生成器不支持列表式的切片取值,需要用for循环一个个来取值

 如果要单个取值,只能用__next__()方法

>>> b=(i*2 for i in range(10))
>>> b.__next__()
0
>>> b.__next__()
2
>>> b.__next__()
4

前面介绍了用语句做生成器,下面再来介绍下用函数做生成器

先来看个斐波那契函数

#-*- coding:utf-8 -*-
#Author:'Yang'

def fib(max):
    n,a,b=0,0,1
    while (n<max):
        print(b)
        a,b=b,a+b   #相当于:如a=1,b=2,t=(b,a+b),a=t[0],b=t[1]
        n+=1
    return  "done"

fib(10)
View Code

如果现在要取1,000,000,000个斐波那契数,那么就得先存储这1,000,000,000个数,占用了大量内存,有没有什么方法,可以在我想取哪个数时再输出,不取时不占用内存呢?答案是:有,用yield关键字做函数生成器。

#-*- coding:utf-8 -*-
#Author:'Yang'

def fib(max):
    n,a,b=0,0,1
    while (n<max):
        yield b
        a,b=b,a+b   #相当于:如a=1,b=2,t=(b,a+b),a=t[0],b=t[1],t是一个tuple
        n+=1
    return  "done"

f=fib(10)
print(f)

代码执行结果:
<generator object fib at 0x021E9300>
View Code

print(b)改成yield b,print(f)发现输出 <generator object fib at 0x021E9300>,可见fib函数已经变成一个生成器,如何取值呢?

第一种,当前取值用 f.__next__(),

#-*- coding:utf-8 -*-
#Author:'Yang'

def fib(max):
    n,a,b=0,0,1
    while (n<max):
        # print(b)
        yield b
        a,b=b,a+b   #相当于:如a=1,b=2,t=(b,a+b),a=t[0],b=t[1],t是一个tuple
        n+=1
    return  "done"

f=fib(10)
print(f)
print("分割线".center(50,"*"))
print(f.__next__())
print(f.__next__())
print(f.__next__())
print(f.__next__())
print(f.__next__())
print(f.__next__())
print(f.__next__())
print(f.__next__())
print(f.__next__())
print(f.__next__())
print(f.__next__()) #用__next__()方法可以取到return返回值,但是会报错,需要抓异常

代码执行结果:
<generator object fib at 0x005F93C8>
***********************分割线************************
1
1
2
3
5
8
13
21
34
55
Traceback (most recent call last):
  File "C:/Users/Yang/PycharmProjects/s14/day4/fib.py", line 26, in <module>
    print(f.__next__())#用__next__()方法可以取到return返回值,但是会报错,需要抓异常
StopIteration: done
View Code

添加抓异常,

#-*- coding:utf-8 -*-
#Author:'Yang'

def fib(max):
    n,a,b=0,0,1
    while (n<max):
        yield b
        a,b=b,a+b   #相当于:如a=1,b=2,t=(b,a+b),a=t[0],b=t[1],t是一个tuple
        n+=1
    return  "done"

f=fib(10)
print(f)
print("分割线".center(50,"*"))

while True:
    try:
        print(f.__next__())    #唤醒yield
    except StopIteration as e:    #抓异常,获取返回值
        print("Generator return value:",e.value)
        break

代码执行结果:
<generator object fib at 0x021D9490>
***********************分割线************************
1
1
2
3
5
8
13
21
34
55
Generator return value: done
View Code

第二种,循环取值用for i in f:print(i)

#-*- coding:utf-8 -*-
#Author:'Yang'

def fib(max):
    n,a,b=0,0,1
    while (n<max):
        yield b
        a,b=b,a+b   #相当于:如a=1,b=2,t=(b,a+b),a=t[0],b=t[1],t是一个tuple
        n+=1
    return  "done"

f=fib(10)
print(f)
print("分割线".center(50,"*"))
print(f.__next__())
print(f.__next__())
print(f.__next__())
print(f.__next__())

print("分割线".center(50,"*"))
for i in f:    #用for 循环取值时,没法取到return返回值
    print(i)

代码执行结果:
<generator object fib at 0x01EE93F0>
***********************分割线************************
1
1
2
3
***********************分割线************************
5
8
13
21
34
55
View Code


总结:生成器只有在调用时才会生成相应的数据;生成器是怎么节省内存的:它只记录它当前的位置,即不能往前,也不能跳步往后,只有一个__next__()方法(用的比较少,一般都是用for循环取值)

学习了生成器,我们来看一个生成器并行应用:通过yield,在单线程的情况下实现并发运算的效果

先看一段代码,

#-*- coding:utf-8 -*-
def consumer(name):
    print("%s 准备吃包子啦!"% name)
    while True:
        baozi=yield 
        print("包子[%s]来了,被[%s]吃了!"%(baozi,name))

c=consumer('Jack')    #c变成了生成器
c.__next__()    #生成器要执行,需要调用next,执行一次next到yield就停止

代码执行结果:

Jack 准备吃包子啦!

如果想要往下执行,就需要再调用__next__(),

#-*- coding:utf-8 -*-
def consumer(name):
    print("%s 准备吃包子啦!"% name)
    while True:
        baozi=yield   #yield保存当前状态,没有返回值时,就是空
        print("包子[%s]来了,被[%s]吃了!"%(baozi,name))

c=consumer('Jack')  #变成了生成器
c.__next__()
c.__next__()  #yield只调用,不传值
c.__next__()  #yield只调用,不传值
c.__next__()  #yield只调用,不传值

代码执行结果:

Jack 准备吃包子啦!
包子[None]来了,被[Jack]吃了!
包子[None]来了,被[Jack]吃了!
包子[None]来了,被[Jack]吃了!

那么要想传值进去,怎么处理呢?

#-*- coding:utf-8 -*-
def consumer(name):
    print("%s 准备吃包子啦!"% name)
    while True:
        baozi=yield     #yield没有返回值时,就是空
        print("包子[%s]来了,被[%s]吃了!"%(baozi,name))

c=consumer('Jack')    #变成了生成器
c.__next__()
b1="粉丝馅"
c.send(b1)    #调用yield,并传值

现在来思考下,如何让吃包子和做包子这个过程并行呢?

#-*- coding:utf-8 -*-

#单线程的并行效果=>协程
import time
#消费
def consumer(name):
    print("%s 准备吃包子啦!"% name)
    while True:
        baozi=yield     #yield保存当前状态返回,没有返回值时,就是空
        print("包子[%s]来了,被[%s]吃了!"%(baozi,name))

#生产
def producer(name):
    c1=consumer('Jack')
    c2=consumer('Rose')
    c1.__next__()
    c2.__next__()
    print("厨师[%s]开始准备做包子啦!" % name)
    for i in range(3):
        time.sleep(1)
        print("做了1个包子,分两半")
        c1.send(i)
        c2.send(i)

producer('David')
单线程的并行效果

这种程序我们称作协程,协程是比线程更小的一个单位。

3.迭代器                                                                                                                                                                    

 可直接作用于for循环的数据有以下几种:

一类是数据集合类型,如list、tuple、dict、set、str等;

一类是generator,包括生成器和带yield的generator function。

这些可以直接作用于for循环的对象统称为:可迭代对象(Iterable)

>>> from collections import Iterable     #导入模块
>>> isinstance([],Iterable)     #list可迭代
True
>>> isinstance('123',Iterable)    #str可迭代
True
>>> isinstance(123,Iterable)    #数字不可迭代
False

isinstance()可以判断一个对象是否是可迭代对象

认识了可迭代对象后,我们来认识什么是迭代器,

可以被next()函数调用并不断返回下一个值的对象称为迭代器(Iterator)

>>> from collections import Iterator
>>> isinstance((i*2 for i in range(10)),Iterator)
True

isinstance()可以判断一个对象是否是可迭代对象

生成器都是迭代器对象,但是list、dict、str虽然是可迭代对象(Iterable),却不是迭代器(Iterator)

把list、dict、str等可迭代对象(Iterable)变成迭代器(Iterator),可以使用iter()函数

>>> iter(b)
<generator object <genexpr> at 0x02CD8C88>
>>> iter(b).__next__()
0
>>> iter(b).__next__()
2

python的迭代器(Iterator)对象表示的是一个数据流,迭代器(Iterator)对象可以被__next__()调用并不断返回下一个数据,直到没有数据时才抛出StopIteration错误。另外,我们通常无法预先知道迭代器(Iterator)的长度,只能不断的调用__next__()实现按需计算下一个数据,因此迭代器(Iterator)的计算是惰性的,只有在需要返回下一个数据时它才会计算

迭代器(Iterator)甚至可以表示一个无限大的数据流,例如全体自然数。而使用列表(List)不可能存储全体自然数。

总结:凡是可作用于for循环的对象都是Iterable类型;凡是可作用于next()函数(或__next__()方法)的对象都是Iterator类型,它表示一个惰性计算的序列;集合数据类型如list、dict、str等是Iterable但不是Iterator,不过,可以通过iter()函数获得一个Iterator对象。

在python3.x里,range(10)默认就是一个迭代器,只是已经被封装在内部。

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)  # 等同于 x=it.__next__()
    except StopIteration:
        break   #遇到StopIteration就退出循环

4.内置函数                                                                                                                                                                 

 主要罗列序列处理函数、数学相关 、逻辑判断、IO操作、字符串处理等常用内置函数。

>>> len([1,3,5,7,8,"Hello","Rose"])    #获取序列的长度
7

>>> b=filter(lambda x:x%2==0,[1,3,5,7,8])     #在py3.X里是个迭代器了,按照括号里的规则过滤序列
>>> b
<filter object at 0x02CF2D10>
>>> for i in b:
    print(i)
    
8

>>> a=[1,2]
>>> b=[3,4]
>>> m=map(lambda x,y:x*y,a,b)    #在py3.X里是个迭代器了,并行遍历,可接受一个function类型的参数
>>> for i in m:
    print(i)

    
3
8

import functools    #reduce封装在该模块
>>> l=range(1,10)
>>> functools.reduce(lambda x,y:x+y,l)    #归并,等同于1+2+...+10
45

>>> s=['a','b','c']
>>> n=[1,2,3]
>>> t=['100','200','300']
>>> z=zip(s,n,t)    #在py3.X里是个迭代器了,并行遍历
>>> for i in z:
    print(i)

    
('a', 1, '100')
('b', 2, '200')
('c', 3, '300')

>>> s=['a','b','c']
>>> n=[1,2]    #如果序列长度不同时,会是下面这样的结果
>>> t=['100','200','300']
>>> z=zip(s,n,t)
>>> for i in z:
    print(i)

    
('a', 1, '100')
('b', 2, '200')
序列处理函数
>>> abs(-2)#绝对值
2
>>> max(1,4,3,6)#最大值
6
>>> min([1,4,3,0])#最小值
0
>>> complex(2,3)#创建一个复数
(2+3j)
>>> divmod(7,2)#分别取商和余数
(3, 1)
>>> float(10)#将一个数转换为浮点数
10.0
>>> int(8.8)#取小数点前的整数
8

>>> type('11')
<class 'str'>
>>> type(int('11'))#int('11')将字符串转为int类型
<class 'int'>

>>> pow(3,2)#返回3的2次幂
9
>>> round(3.6)#四舍五入取整数
4
>>> sum([1,2,3])#对集合求和
6
>>> range(6)#产生一个序列,默认从0开始
range(0, 6)
>>> bool('ttt')#转换为bool类型,非零即为真
True
>>> chr(97)#把数字对应到ascii码
'a'
>>> ord('b')#把ascii码对应到数字
98
>>> bin(255)#将整数转换为二进制字符串
'0b11111111'
>>> oct(16)#将一个整数转化为八进制
'0o20'
>>> hex(16)#将一个整数转化为十六进制
'0x10'
数学相关
>>> all([1,3,-1])
True
>>> all([1,3,0])#集合中的元素都为真的时候为真
False
>>> all([1,3,])#特别的,若为空串返回为True
True

>>> any([0])
False
>>> any([0,1,3])#集合中的元素有一个为真的时候为真
True
>>> any([0,1,])#特别的,若为空串返回为True
True

>>> import operator
>>> operator.eq("B","B")#比较字符是否相等,相等返回真
True
>>> operator.eq("Hello","hello")
False
>>> callable(abs)#函数是否可调用,也可以是自定义函数
True
>>> isinstance("hello",str)#类型判断
True
逻辑判断
>>> a=input("请输入:") #获取用户输入,默认输入均为字符串
请输入:1988
>>> a
'1988'

>>> f=open('test.txt') #打开文件,前提是该文件在当前目录下存在
>>> f=open('test.txt''w') #打开文件,该文件在当前目录下不存在则创建

>>> print("Hello")#打印函数
Hello
IO操作
>>> 'Hello'.split('e')    #字符串切割
['H', 'llo']
>>> 'Hello'.replace('H','2')    #字符串替换
'2ello'
>>> 'hello'.capitalize()    #首字母大写
'Hello'
字符串处理

这里不作一一列举了,大家需要的话可以查看相关手册。

5.json和pickle序列化                                                                                                                                               

序列化是将对象状态(如变量)转换为可存储或传输的格式的过程。与序列化相对的是反序列化,它将流转换为对象。这两个过程结合起来,可以轻松地存储和传输数据。

在python中提供了两个模块可进行序列化。分别是pickle和json。

pickle:是python中独有的序列化模块,所谓独有,就是指不能和其他编程语言的序列化进行交互,因为pickle将数据对象转化为bytes

>>> import pickle
>>> a="Hello"
>>> type(a)
<class 'str'>
>>> type(pickle.dumps(a))    #pickle.dumps(a)是进行序列化
<class 'bytes'>
>>> b=[1,2,3,4]
>>> type(b)
<class 'list'>
>>> type(pickle.dumps(b))    #pickle.dumps(b)是进行序列化
<class 'bytes'>

pickle模块提供了四个功能:dumps、dump、loads、load。dumps和dump都是进行序列化,而loads和load则是反序列化。

>>> import pickle
>>> b=[1,2,3,4]
>>> pickle.dumps(b)
b'\x80\x03]q\x00(K\x01K\x02K\x03K\x04e.'
dumps

dumps将所传入的变量的值序列化为一个bytes,然后,就可以将这个bytes写入磁盘或者进行传输。

>>> import pickle
>>> b=[1,2,3,4]
>>> p=pickle.dumps(b)
>>> print(p)
b'\x80\x03]q\x00(K\x01K\x02K\x03K\x04e.'
>>> pickle.loads(p)
[1, 2, 3, 4]
loads

loads把对象从磁盘读到内存时,先把内容读到一个bytes,然后用loads方法反序列化出对象。

>>> import pickle
>>> f=open('file_test','wb')
>>> b=[1,2,3,4]
>>> pickle.dump(b,f)
>>> f.close()
>>> f=open('file_test','rb')
>>> f.read()
b'\x80\x03]q\x00(K\x01K\x02K\x03K\x04e.'
dump

dump一步到位,在dump中可以传入两个参数,一个为需要序列化的变量,另一个为需要写入的文件。

>>> f=open('file_test','rb')
>>> l=pickle.load(f)
>>> f.close()
>>> print(l)
[1, 2, 3, 4]
load

load方法直接反序列化一个文件。

 json:如果我们要在不同的编程语言之间传递对象,就必须把对象序列化为标准格式,比如XML,但更好的方法是序列化为JSON,因为JSON表示出来就是一个字符串,可以被所有语言读取,也可以方便地存储到磁盘或者通过网络传输。JSON不仅是标准格式,并且比XML更快,而且可以直接在Web页面中读取,非常方便。

json中的方法和pickle中差不多,也是dumps,dump,loads,load。使用上也没有什么区别,区别在于,json中的序列化后格式为str

>>> import json
>>> b=[1,2,3,4]
>>> type(b)
<class 'list'>
>>> type(json.dumps(b))   # json.dumps(b)进行序列化
<class 'str'>

python中一切事物皆对象,而所有对象都是基于类创建的,所以,‘类’在python中占据了相当大的比重。能否将类的实例进行序列化呢?

>>> class stu(object):
    def __init__(self,name,age,course):
        self.name=name
        self.age=age
        self.course=course

        
>>> a=stu('Rose',18,'Music')
>>> import json
>>> json.dumps(a)
View Code

json.dumps(a)时报错(TypeError: <__main__.stu object at 0x02CF0630> is not JSON serializable)了,Why?

之所以无法把stu类实例序列化为JSON,是因为默认情况下,dumps方法不知道如何将student实例变为一个JSON的'{}'对象。

我们需要’告诉‘json模块如何转换。

>>> def st_to_dict(a):
    return{'name':a.name,'age':a.age,'course':a.course}

>>> print(json.dumps(a,default=st_to_dict))
{"age": 18, "course": "Music", "name": "Rose"}
View Code

可是,如果我们每定义一个类,还得再定义一下这个类的实例转换为字典的函数,实在是麻烦!

还有一个简便的办法。

>>> print(json.dumps(a,default=lambda obj:obj.__dict__))
{"course": "Music", "name": "Rose", "age": 18}

这里的__dict__不需我们在类中定义,因为通常class的实例都有一个__dict__属性,它就是一个字典,用来存储实例变量。

>>> print(a.__dict__)
{'course': 'Music', 'name': 'Rose', 'age': 18}

 注:在实际应用写程序时你就只load/loads一次,dump/dumps一次,因为你dump/dumps一次就会冲掉老数据,每次dump/dumps得到的都是最新的数据,如果想多次dump/dumps,建议存成多个文件。

6.软件目录开发规范                                                                                                                                                  

用个简单的目录树来举例,

ATM/  这是一个项目名称

├─atm/

│  ├─bin/

│  │  └─atm  可执行文件

│  ├─__init__.py 创建atm (python package)时自动生成的

│  │  

│  ├─main.py 主程序入口

│  │  

│  ├─tests/ 用来做项目测试使用

│  │  ├─__init__.py

│  │  └─test_main.py

│  ├─docs/ 存储一些项目相关的文档

│  │  ├─__init__.py

│  │  └─test_main.py    

│  ├─conf/ 配置文件存放位置

│  │  ├─settings.py

│  │  └─test_main.py

│  ├─setup.py 安装、部署、打包的脚本

│  │

│  ├─requirements.txt  依赖的外部python包列表   

│  └─README 项目说明文件
目录结构树

README.txt每个项目都应该有,目的是简要描述该项目的详细,让用户快递了解这个项目,README.txt的内容需要交代以下情况:

1)软件的定位,软件的基本功能

2)代码运行的方法:安装环境、启动命令等

3)简要的使用说明

4)代码目录结构说明,更详细点可以说明软件的基本原理

5)常见问题说明

7.练习                                                                                                                                                                       

模拟实现一个ATM + 购物商城程序

  1. 额度 15000或自定义
  2. 实现购物商城,买东西加入 购物车,调用信用卡接口结账
  3. 可以提现,手续费5%
  4. 每月22号出账单,每月10号为还款日,过期未还,按欠款总额 万分之5 每日计息
  5. 支持多账户登录
  6. 支持账户间转账
  7. 记录每月日常消费流水
  8. 提供还款接口
  9. ATM记录操作日志 
  10. 提供管理接口,包括添加账户、用户额度,冻结账户等。。。
  11. 用户认证用装饰器

posted on 2017-06-20 14:35  奔跑的蜗牛~~  阅读(155)  评论(0)    收藏  举报

导航