python学习笔记(11):文件的访问与函数式编程
一、文本文件读写的三种方法
1.直接读入
file1 = open('E:/hello/hello.txt') file2 = open('output.txt','w') #w是可写的文件 while True: line = file1.readline() #readline()是读取一行 # 这里可以进行逻辑处理 file2.write('"'+line[:]+'"'+",") if not line : #如果行读取完成,就直接跳出循环 break #记住文件处理完成关闭文件是一个号习惯 file1.close() file2.close()
读文件有3种方法:
- read()将文本文件所有行读到一个字符串中。
- readline()是一行一行的读,在读取中间可以做一些判断
- readlines()是将文本文件中所有行读到一个list中,文本文件每一行是list的一个元素。 优点:readline()可以在读行过程中跳过特定行。
# # 第二种读取的方法 # #使用文件迭代器的方法,用for循环的方法 # file2 = open('output.txt','w') # for line in open('E:/hello/hello.txt'): #以一个迭代器的方式打开,每一个line就是迭代器中的一行,可以在file2中继续写入 # # 这里可以进行逻辑的处理 # file2.write('"'+line[:]+'"'+',')
二、二进制文件读写
Python默认读取的都是文本文件。要是想要读取二进制文件,需要把刚刚的'r'改成'rb'.
f = open('EDC.jpg', 'rb') print(f.read()) # 输出 '\xff\xd8\xff\xe1\x00\x18Exif\x00\x00...' # 十六进制表示的字节
简单说就是,任何非标准的文本文件(对于Py2来说,标准是ASCII,对于Py3来说,标准是unicode),你就需要用二进制读入这个文件,然后再用 .decode('...')的方法来解码这个二进制文件:
f = open('DegangGuo.txt', 'rb') # 读入郭德纲老师的作文, 但是郭老师用的是参合着错别字的繁体编码,假设叫个"DeyunCode" # 那么你读入以后,就需要解码它 u = f.read().decode('DeyunCode')
三、文件和目录操作
在图形界面的操作系统下,这个很简单,就是右键/拖拽 等等。
但是在Python的代码内该怎么做呢?
基本操作
用Python内置的os模块直接调用操作系统提供的接口函数:
import os
os.name
这里是通过OS告诉我们 我的操作系统的名字。 如果是posix,说明系统是#nix族,如果是nt,就是Windows
我们可以调用uname()来看看具体信息
环境变量
在操作系统中定义的环境变量,全部保存在os.environ这个dict中,可以直接查看:
# import os
# print(os.name)
# print(os.environ)
操作文件与目录
查看、创建和删除目录可以这么调用:
import os # # print(os.name) # # print(os.environ) # 获取当前目录的绝对路径 import os print(os.path.abspath('.')) #比如这里返回:E:\code\python\python_base\class_five #在某个目录下创建一个新的目录 #首先把新目录的完整路径表示出来: print(os.path.join('E:\code\python\python_base\class_five','Pictures')) # 这里得到的是一个字符串,代表了新的文件夹是这个位置:E:\code\python\python_base\class_five\Pictures # 自己也可以拼接起来,但是怕不同的操作系统下区分符号的问题,最好是用OS的接口 # 这个时候还没有创建任何的文件 # os.mkdir('E:\code\python\python_base\class_five\Pictures') # 同理,可以用同样的方法来删除文件夹 os.rmdir('E:\code\python\python_base\class_five\Pictures')
同样的道理,要拆分路径时,也不要直接去拆字符串,而要通过os.path.split()函数,这样可以把一个路径拆分为两部分,后一部分总是最后级别的目录或文件名:
拆分一个文件的路径 print(os.path.split('E:\code\python\python_base\class_five\Pictures')) #靠os.path.splittext()得到文件的扩展名 os.path.splitext('E:\code\python\python_base\class_five\Pictures.AJiao.avi') #文件的重命名: os.rename('JAV-001.avi','学习资料') #删除文件 os.remove('学习资料')
尴尬的是。。复制文件并不存在于os里。。(⊙﹏⊙)b
原因是复制这个操作,不是由操作系统提供的系统调用。
我们可以用上面的代码,读入一个文件,再写入一个文件,来达到复制的目的。
当然,Python作为一个Glue Language的调性,总有第三方库来帮我们stay lazy:
Shutil就是其中一个。基本上可以看做是os的补充。它提供copyfile()方法,来复制你的文件:
import shutil shutil.copyfile('E:/hello/hello.txt','e:/hello/a.txt')
这个库用起来比os爽很多。你们可以自己百度一下。看看文档。比较简单易用
小例子:
通过我们之前提过的方法,我们来看看怎么完成如下任务:
- 列出当前目录下的所有目录:
print([x for x in os.listdir('.') if os.path.isdir(x)]) #[]表示我们得到的结果是list #这个地方会得到一个list of文件夹
#这个地方os.listdir('.')表示将这个地方所有的目录都列举出来,如果是一个目录,将其输出到list()中去
#只想列出.py文件: print([x for x in os.listdir('.') if os.path.isfile(x) and os.path.splitext(x)[1]=='.py'])
#这个地方如果只想看到以.py为结尾的文件,那就将其列举出来,可以将目录做一些清理
四、序列化和反序列化
什么是序列化?
程序运行的过程中,所有变量都是在内存中操作的,当程序一旦执行完毕,结束退出后,变量占有的内存就被操作系统回收了。 因此我们需要将某些数据持久化存储到磁盘中,下次运行的时候从磁盘中读取相关数据。
我们将变量从内存中变成可以存储或传输的过程称之为序列化,在Python中叫做pickling,在其它语言中也称之为 serialization、marshaling、flattening等等,说的都是一个意思。 反之,则为反序列化,称之为unpickling,把变量内容从序列化的对象重新读取到内存中。
1.序列化:
#序列化 import pickle # 此处定义一个dict字典对象 d = dict(name='思聪',age=29,score=80) str = pickle.dump(d) #调用pickle的dump函数进行序列化处理 print(str) #你可以看看它长得什么样子 #定义和创建一个file文件对象,设定模式为wb f = open('dump.txt','wb') #将内容序列化写入到file文件中去 pickle.dump(d,f) f.close() #最后关闭掉文件资源
2.反序列化:
就是把刚刚的搞成的“序列化”的码,转成python看得懂的object:
#反序列化 # import pickle # #从之前序列化的dump.txt文件里面读取内容 # f = open('dump.txt','rb') #设定文件选项模式为rb # d = pickle.load(f) #调用load做反序列化的处理过程 # f.close() #关闭文件资源 # print(d) # print('name is %s'%d['name'])
3.用JSON实现序列和反序列化
同时,也许你们也会发现,这个pickle完的东西,是让人看不懂的。只有python自己可以把它unpickle回来。
如果我们有一个文件现在存下来,并且日后要在其他地方用到的话,
我们可以用JSON来做序列化。Python的数据结构跟Json有非常完美的兼容:
JSON类型 | Python类型 |
---|---|
{} | dict |
[] | list |
"string" | 'str'或者u'unicode' |
1234.56 | int或float |
true/false | True/False |
null | None |
如果你有一个比较结构化的数据想要序列化,并且想要别的地方别的语言也能看得懂。那么你可以用JSON来做:
import json #定义dict字典对象 d1 = dict(name='小王',age=20,score=80) str=json.dumps(d1) #调用json的dumps函数进行json序列化 print(str) #调用json的loads函数进行反序列化处理 d2 = json.loads(str) #字典类型 json类型 print(d2)
五、高阶函数
可以把别的函数作为参数传入的函数叫高阶函数。
举个例子:求绝对值的函数 abs()
print(abs(-10))
python里变量和函数都是object
那么就是说,abs这函数可以直接复制给另外一个变量:
f = abs f(-9)
看,现在f有了abs的功能!既然如此,我们知道,函数本身就是可以作为一个变量。那我们的变量是可以作为另一个函数的参数的,那么一个函数也可以作为另一个函数的参数。
我们来看一个简单的高阶函数:
def add(x,y,f): print( f(x)+f(y)) add(-5,6,abs)
当我们调用add(-5, 6, abs)时,参数x,y和f分别接收-5,6和abs:
六、匿名函数
python 使用 lambda 来创建匿名函数。
-
lambda只是一个表达式,函数体比def简单很多。
-
lambda的主体是一个表达式,而不是一个代码块。仅仅能在lambda表达式中封装有限的逻辑进去。
-
lambda函数拥有自己的命名空间,且不能访问自有参数列表之外或全局命名空间里的参数。
-
虽然lambda函数看起来只能写一行,却不等同于C或C++的内联函数,后者的目的是调用小函数时不占用栈内存从而增加运行效率。
-
最重要的一点,Lambda表达式可以体现你的逼格。华尔街和伦敦银行高街最逼的一群人都是自诩用且只用函数式编程的。什么是函数式编程?就是类似于全篇程序都用python中lambda这样的一行代码来解决问题。为什么他们逼?因为数学家们学编程的时候,脑子还在数学公式这条线上;他们不会写面对对象编程,只会死想出一条条公式来解决问题;其实这是智商堪忧的体现;但是因为投行基金们很倾向于聘用这群数学家转型的半吊子程序员;他们的使用习惯于是成了圈内高逼的体现
语法
Lambda函数的语法只包含一个语句,如下:
lambda [arg1 [,arg2,.....argn]]:expression
比如,我写个相加函数:
sum = lambda arg1,arg2:arg1+arg2 print(sum(10,20))
除了Lambda本身,Python还提供了其他几个辅助工具,让你的函数式代码块更加牛逼:
reduce
Python中的reduce内建函数是一个二元操作函数,他用来将一个数据集合(列表,元组等)中的所有数据进行如下操作:传给reduce中的函数func() (必须是一个二元操作函数)先对集合中的第1,2个数据进行操作,得到的结果再与第三个数据用func()函数运算,最后得到一个结果。
顾名思义,reduce就是要把一个list给缩成一个值。所以你必须用二元操作函数。
reduce(function, sequence[, initial]) -> value
Apply a function of two arguments cumulatively to the items of a sequence,
from left to right, so as to reduce the sequence to a single value.
For example, reduce(lambda x, y: x+y, [1, 2, 3, 4, 5]) calculates
((((1+2)+3)+4)+5). If initial is present, it is placed before the items
of the sequence in the calculation, and serves as a default when the
sequence is empty.
reduce(function,sequence [,initial]) - > value
将两个参数的函数累加应用于序列的项目,
从左到右,以便将序列减少到单个值。
例如,reduce(lambda x,y:x + y,[1,2,3,4,5])计算
((((1 + 2)3)4)5)。 如果存在initial,则将其放在项目之前
计算中的序列,并作为默认值
序列是空的。
from functools import reduce
l= [1,2,3,4,5]
# print(reduce(lambda x,y:x+y,l))
#
# #这里代表着,把list中的值,一个个的放进lambda的x和y中去
# 如果给出一个初始值,可以放在list后面,这样,x开始的时候就被赋值为10,然后依次进行相加
print(reduce(lambda x,y:x+y,l,10))
map
map函数应用于每一个可迭代的项,返回的是一个结果list。如果有其他的可迭代参数传进来,map函数则会把每一个参数都以相应的处理函数进行迭代处理。map()函数接收两个参数,一个是函数,一个是序列,map将传入的函数依次作用到序列的每个元素,并把结果作为新的list返回。
格式:map(func, seq1[, seq2...] )
Python函数式编程中的map()函数是将func作用于seq中的每一个元素,并用一个列表给出返回值。
map可以使用任何的lambda函数操作,本质上是把原有的list根据lambda法则变成另外一个list
#对于python3我们需要注意,外面需要套一个list,这里是为了让里面的值显示出来,要不然就是得到的一个map函数,而不是里面的值 # l = [1,2,3] # new_list=list(map(lambda i:i+1,l)) # print(new_list) #我们可以将两个数组搞成一个单独的数组 # l2=[4,5,6] # new_list=list(map(lambda x,y:x+y,l,l2)) # print(new_list)
filter
filter()函数可以对序列做过滤处理,就是说可以使用一个自定的函数过滤一个序列,把序列的每一项传到自定义的过滤函数里处理,并返回结果做过滤。最终一次性返回过滤后的结果。 和map()类似,filter()也接收一个函数和一个序列。和map()不同的时,filter()把传入的函数依次作用于每个元素,然后根据返回值是True还是False决定保留还是丢弃该元素。
语法
filter(func, seq)
l = [100,20,24,50,110] new = list(filter(lambda x:x<50,l)) print(new)
七、装饰器
装饰器就是函数的『包装』:
我们来看一个代码:
def hello(fn): def wraper(): print("hello,%s"%fn.__name__) fn() print("goodby,%s"%fn.__name__) return wraper() @hello def foo(): print("i am foo") foo()
你可以看到如下的东西:
1)函数foo前面有个@hello的“注解”,hello就是我们前面定义的函数hello
2)在hello函数中,其需要一个fn的参数(这就用来做回调的函数)
3)hello函数中返回了一个inner函数wrapper,这个wrapper函数回调了传进来的fn,并在回调前后加了两条语句。
所以,本质上来讲,用@decorator来装饰某个函数时,其实是做了下面这件事儿:
@decorator
def func():
pass
变成 =====》
func = decorator(func)
再简单点说,就是把一个函数传到另外一个函数中,再调回给自己。
所以:
hello(foo)返回了wrapper()函数,所以,foo其实变成了wrapper的一个变量,而后面的foo()执行其实变成了wrapper()
同理,我们也可以搞多个decorator:
@decorator_one
@decorator_two
def func():
pass
相当于:
func = decorator_one(decorator_two(func))
你还可以给这个decorator带个参数:
@decorator(arg1, arg2)
def func():
pass
相当于:
func = decorator(arg1,arg2)(func)
好了,讲这么多比较复杂,我们来看个网页编程的case:
def makeHtmlTag(tag,*args,**kwds): def real_decorator(fn): css_class =" class='{0}'".format(kwds["css_class"]) if "css_class" in kwds else "" def wrapped(*args,**kwds): return "<"+tag+css_class+">"+fn(*args,**kwds)+"</"+tag+">" return wrapped return real_decorator @makeHtmlTag(tag="b",css_class="bold_css") @makeHtmlTag(tag="i",css_class="italic_css") def hello(): return "hello world" print(hello())
在上面这个例子中,我们可以看到:makeHtmlTag有两个参数。所以,为了让 hello = makeHtmlTag(arg1, arg2)(hello) 成功,makeHtmlTag 必需返回一个decorator(这就是为什么我们在makeHtmlTag中加入了real_decorator()的原因),这样一来,我们就可以进入到 decorator 的逻辑中去了—— decorator得返回一个wrapper,wrapper里回调hello。
这里插一个知识,我们看到parameters里面有个`(*args, **kwargds)`,指的是:
一个星星,指的是这里可以随便放多少个参数,内部提及的时候,当做一个list看。 可变参数
两个星星指的也是随便多少个参数,但是这里可以带上参数的名字,比如(x='1', y='2'),内部提及的时候,当做一个dict看。字典类型的参数
Decorator这个东西,也可以写成class样式:
class myDecorator(object): def __init__(self,fn): #这里的fn就是afunction()这个函数 print("inside myDecorator.__init__()") self.fn=fn def __call__(self): self.fn() print("inside myDeractor.__call__()") @myDecorator def afunction(): print("inside afunction()") print("Finished decorating afunction()") afunction()#在这里才开始调用afunction()这个函数
这个class样式的看起来比函数样式看着清楚点儿,这样我们再把刚刚的网页编程那段改一下,就得到:
class makeHtmlTagClass(object): def __init__(self,tag,css_class=""): self._tag=tag self._css_class="class='{0}'".format(css_class) if css_class!="" else "" def __call__(self, fn): def wrapped(*args,**kwargs): return "<"+self._tag+self._css_class+">" +fn(*args,**kwargs)+"<"+self._tag+">" return wrapped @makeHtmlTagClass(tag="b",css_class="bold_css") @makeHtmlTagClass(tag="i",css_class="italic_css") def hello(name): return "Hello,{}".format(name) print(hello("Baby"))
装饰器的副作用:
因为decorator的因素,我们原本的函数其实已经变成了一个叫wrapper函数。
比如,你再调用__name__
的时候,他会告诉你,这是 wrapper, 而不是 foo 或者 hello。
当然,虽然功能效果不变,但是有些处女座的童鞋会觉得很不爽。
所以,Python的functool包中提供了一个叫wrap的decorator来消除这样的副作用:
from functools import wraps def hello(fn): @wraps(fn) def wrapper(): print("hello, %s" % fn.__name__) fn() print("goodby, %s" % fn.__name__) return wrapper @hello def foo(): '''foo help doc''' print("i am foo") pass foo() print(foo.__name__) print(foo.__doc__)
八、偏函数
Python的functools模块提供了很多有用的功能,其中一个就是偏函数(Partial function)。
偏函数又可以翻译成部分函数,大概意思就是说,只设置一部分参数。
举个例子,我们知道int()可以把字符串变成十进制数字:
print(int('123456'))
print('123456',8) #将八进制数123456转换成十进制的数值
但int()函数还提供额外的base参数,默认值为10。如果传入base参数,就可以做N进制的转换:
print(int('123456',base=8)) #将八进制数123456转换成十进制的数值
print(int('12345',base=16)) #将16进制数值转换为十进制数值 74565
假设要转换大量的二进制字符串,每次都传入int(x, base=2)非常麻烦,于是,我们想到,可以定义一个int2()的函数,默认把base=2传进去:
def int2(x,base=2): #将二进制数转换为十进制 return int(x,base) print(int2('1010'))
functools.partial就是帮助我们创建一个偏函数的,不需要我们自己定义int2(),可以直接使用下面的代码创建一个新的函数int2:
import functools int2 = functools.partial(int,base=2) print(int2('1000000'))