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')
#正确调用方式一 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)
在不修改被装饰函数源代码的情况下为其添加功能(计时)
#-*- 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
在此基础上进一步改进
#-*- 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()
在不修改函数的调用方式的情况下为其添加功能(打印内存地址)
嵌套函数:
def foo(): print("in the foo") def bar():#局部函数,作用域相当于局部变量 print("in the bar") bar()#只能在内部调用,不能再外部调用,因为它是局部函数 foo()
这就是嵌套函数,函数里再定义函数。
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
@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"})
注意:*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()
看完代码有点懵?每一句代码加上断点,调试下,看看代码的走向就明白了。
装饰器就讲这么多了,要想熟练还得实际多操练。
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
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)
如果现在要取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>
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
添加抓异常,
#-*- 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
第二种,循环取值用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
总结:生成器只有在调用时才会生成相应的数据;生成器是怎么节省内存的:它只记录它当前的位置,即不能往前,也不能跳步往后,只有一个__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
>>> '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将所传入的变量的值序列化为一个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把对象从磁盘读到内存时,先把内容读到一个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中可以传入两个参数,一个为需要序列化的变量,另一个为需要写入的文件。
>>> f=open('file_test','rb') >>> l=pickle.load(f) >>> f.close() >>> print(l) [1, 2, 3, 4]
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)
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"}
可是,如果我们每定义一个类,还得再定义一下这个类的实例转换为字典的函数,实在是麻烦!
还有一个简便的办法。
>>> 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 + 购物商城程序
- 额度 15000或自定义
- 实现购物商城,买东西加入 购物车,调用信用卡接口结账
- 可以提现,手续费5%
- 每月22号出账单,每月10号为还款日,过期未还,按欠款总额 万分之5 每日计息
- 支持多账户登录
- 支持账户间转账
- 记录每月日常消费流水
- 提供还款接口
- ATM记录操作日志
- 提供管理接口,包括添加账户、用户额度,冻结账户等。。。
- 用户认证用装饰器
浙公网安备 33010602011771号