第三课 文件处理和函数

时间:2022-07-28 20:07:05

一、文件处理:

1、文件操作的必要条件:

  1,打开文件,产生文件句柄。
  2,操作文件句柄。
  3,关闭文件。

例子:
护士主妇空姐老师联系方式.txt
        1,文件路径
        2,编码方式:utf-83,动作mode,读,读写,写读.....

    f1 = open('D:\空姐护士老师主妇.txt', encoding='utf-8', mode='r')
    content = f1.read()
    print(content)
  f1.close() f1,文件句柄,文件对象,file,f_handle,file_handle,f_obj open打开的指令,windows的指令, windows 默认编码方式gbk,linux默认编码方式utf
-8,mac utf-8
当打开绝对路径的文件时会出现的问题:
   问题:1、 SyntaxError: (unicode error) 'unicodeescape' codec
     can't decode bytes in position 2-3: truncated \UXXXXXXXX escape
     f1 = open(r'D:\空姐护士老师主妇.txt', encoding='utf-8', mode='r')
     # f1 = open('D:\\空姐护士老师主妇.txt', encoding='utf-8', mode='r')
解决方法:在文件前加r或者加\\   
2、EncodeDecodeErorr: 编码错误。
    windows下的文件保存时gbk,修改保存方式为utf-8,因为python用的是utf-8

注:在读取文件时最好里面都加上:encoding='utf-8',因为不同的项目可能用的编码方式不一样

2、读文件 r  rb r+,r+b(read  read(n)  readline  readlines  for 循环)

  2.1、r 只读

    read  read(n)  readline  readlines  for 循环 这些方法都能用

例子:
# read 全部读出
# f1 = open('log', encoding='utf-8')
# content = f1.read()  #
# print(content)
# f1.close()

#read(n)
# f1 = open('log', encoding='utf-8')
# content = f1.read(5)  # r 模式 按照字符读取。
# print(content)
# f1.close()
#readline()按行读取
# f1 = open('log', encoding='utf-8')
# print(f1.readline())
# print(f1.readline())
# print(f1.readline())
# print(f1.readline())#当没有也不报错,读出空行
# f1.close()

#readlines() 将每一行作为列表的一个元素并返回这个列表,返回值是列表
# f1 = open('log', encoding='utf-8')
# print(f1.readlines())
# f1.close()

#for循环
# f1 = open('log', encoding='utf-8')
# for i in f1:
#     print(i)
# f1.close()

总结:1、for循环是最好的读取方式,因为只有一个句柄,每次读一行,占内存小;用read读取,如果文件很大会直接卡死,readline读取慢
   2、for循环,循环句柄,得到的是每一行的值,为字符串。
    3、读r默认可以省略
    4、read 、read(n) 、readline 返回的都是字符串;readlines 返回的是列表

  2.2、rb: 非文字类的文件的操作

    以字节的方式进行读取,当如果存在中文,中文是三个字节,当read(n)读取在decode()转换就有可能报错。

    unicode ---> bytes encode()   rb 方式就直接转成了bytes方式读取
    bytes---> unicode decode()  b.decode('utf-8') 转换

例子:
#rb
# f1 = open('log', mode='rb')
# content = f1.read()  # rb模式 按照字节读取。
# print(content)
# f1.close()

#rb 读取部分就会出问题
# f1 = open('log', mode='rb')
# content = f1.read(5)  # rb模式 按照字节读取。
# print(content.decode('utf-8'))#当已二进制读取5个字节时,转换成utf-8就会报错,因为三个字节是一个字符。
# f1.close()

  2.3、r+,r+b

    r+  读写模式,先读在写

#r+ 读写 只能先读后写,先写后读,如果存在中文(三个字节)如果写入的字节小于三或大于三就会有问题
# f1 = open('log', encoding='utf-8', mode='r+')
# print(f1.read())
# f1.write('\n666')
# f1.close()
# r+ 如果想在这种模式下进行先写后读,需要用seek方法
f1 = open('log', encoding='utf-8', mode='r+')
f1.seek(0,2) #表示把光标调到最后
f1.write('6666')
f1.seek(0)#调整光标到开始
print(f1.read())
#光标 按照字节去运转 seek
f1.close()

  2.4、gbk不能和utf-8相互之间转换,先rb读取,在gbk转中文,在转utf-8

#编码的补充:
# s1 = b'\xd6\xd0\xb9\xfa'#windows下gbk‘中国’占四个字节;gbk的中文不能直接转成utf-8
# s2 = s1.decode('gbk') #将s1转成中文
# s3 = s2.encode('utf-8')#在讲中文转成utf-8下的字节方式。
# print(s2)
# print(s3)  # b'\xe4\xb8\xad\xe5\x9b\xbd'
# s1 = b'\xd6\xd0\xb9\xfa'.decode('gbk').encode('utf-8') 可以直接合成一行来写
# print(s1)

3、 写文件

    1,没有文件,创建文件,写入内容

    2,将原文件所有内容清空,写入新内容。

  3.1、w模式 及wb

    w 按照设置写的模式往文件中写,wb是以字节的方式写

例子:
#
f1 = open('log2', encoding='utf-8', mode='w') # f1.write('alex是披着高富帅外衣的纯屌丝.....') # f1.close() # f1 = open('log2', mode='wb') # # f1.write('alex是披着高富帅外衣的纯屌丝.....')#报错:TypeError: a bytes-like object is required, not 'str' # f1.write('alex是披着高富帅外衣的纯屌丝.....'.encode('utf-8')) # f1.close()

  3.2 w+ 写读

#w+ 写读模式
# f1 = open('log2', encoding='utf-8', mode='w+')
# print(f1.read())
# f1.write('666')
# f1.close()

4、追加写:a   ab    a+    a+b

  4.1、a 按照设置的编码方式追加写文件,ab按照自己的方式追加写,a+追加后读取

#a ab
# f1 = open('log2', encoding='utf-8', mode='a')
# f1.write('\n老男孩')
# f1.close()

#a+
# f1 = open('log2', encoding='utf-8', mode='a+')
# f1.write('fdsafdsafdsagfdg')
# f1.seek(0)
# print(f1.read())
# f1.close()

5、其他操作方法:

  5.1、readable是否可读     writable是否可写

  5.2、tell 告诉指针的位置(作用:可以实时记录指针指向的位置,然后可以打印位置后面的日志,后再在当前位置写入等操作)

f1 = open('log2', encoding='utf-8', mode='w')
f1.write('fdsafdsafdsagfdg')
print(f1.tell())
f1.close()

  5.3、seek(参数),seek(0,2) 调至最后 按照字节去调整光标

  5.4、with 方法,使用with不用使用close()关闭

# with open() as:
with open('log', encoding='utf-8') as f1,\
        open('log2', encoding='utf-8', mode='w')as f2:
    print(f1.read())
    f2.write('777')

  5.5、修改文件

    1、打开原文件,产生文件句柄。

    2、创建新文件,产生文件句柄。

    3、读取原文件,进行修改,写入新文件。

    4、将原文件删除

    5、新文件重命名原文件

import os   #当文件大的时候容易卡死
with open('file_test', encoding='utf-8') as f1,\
    open('file_test.bak', encoding='utf-8', mode='w') as f2:
    old_content = f1.read()
    new_content = old_content.replace('sb','SB')
    f2.write(new_content)
os.remove('file_test')
os.rename('file_test.bak','file_test')

import os   #节省内存
with open('file_test', encoding='utf-8') as f1,\
    open('file_test.bak', encoding='utf-8', mode='w') as f2:
    for line in f1:
        new_line = line.replace('SB','alex')
        f2.write(new_line)
os.remove('file_test')
os.rename('file_test.bak','file_test')

 

二、函数 

1、函数的初识:
  函数的定义,函数体,函数名。
  函数的返回值return。
  函数的参数。
    实参:
      三种(位置参数,关键字参数,位置和关键字混合参数)

    形参:
      三种(位置参数,关键字参数,位置和关键字混合参数)
    动态参数。

   1.1、从一个计数器开始了解函数

 原始计算方法:缺点:重复代码多、可读性差

#计算一个文件的长度 ,如果没有函数,只能每次都写一遍代码来计算
# li = [1, 2, 3, 43, 'fdsa', 'alex']
# count = 0
# for i in li:
#     count += 1
# print(count)
#字符串
# s1 = 'fdsgdfkjlgdfgrewioj'
# count = 0
# for i in s1:
#     count += 1
# print(count)

简单的函数定义:

  def 关键字 函数名(设定与变量相同):

      函数体
    #函数的返回值 return
# s1 = 'fdsgdfkjlgdfgrewioj'
# def my_len():
#     count = 0
#     for i in s1:
#         count += 1
#     # print(count)

my_len()
# 函数名+() 执行函数

  注意点:

'''
    1,遇到return,结束函数。
    def func1():
        print(11)
        print(22)
        return
        print(333)
        print(444)
    func1()
    2,给函数的调用者(执行者)返回值。
        无 return 返回None
        return 不写 或者 None 返回None
        return 返回单个数.
        return 返回多个数,将多个数放在元组中返回。
'''

  函数的其他功能点:

#s1='dsadfsafdsgfdsgfsa'
#
def my_len(): # count = 0 # for i in s1: # count += 1 # return 666 # print(my_len(),type(my_len()))#长度、类型 # def my_len(): # count = 0 # for i in s1: # count += 1 # return 666,222,count,'老男孩' # print(my_len(),type(my_len())) #返回对个值,多个值都传给函数my_len(),然后以元组的方式存储。 # def my_len(): # count = 0 # for i in s1: # count += 1 # return 666,222,count # ret1,ret2,ret3 = my_len() # (666, 222, 19,)#分别赋值给定义的每一个变量 # print(ret1) # print(ret2) # print(ret3) # def my_len(): # count = 0 # for i in s1: # count += 1 # return count #函数中最好不要用print 打印,演示的时候可用 # print(my_len()) #返回值是count,将count返回给my_len()函数 # print(len(s1))
#函数的传参
# li = [1, 2, 3, 43, 'fdsa', 'alex']
# s1 = 'fdsgdfkjlgdfgrewioj'

# def my_len(a):  # 函数的定义()放的是形式参数,形参
#     count = 0
#     for i in a:
#         count += 1
#     return count
# ret = my_len(li)  # 函数的执行() 实际参数,实参
# print(ret)
# print(len(s1))

实参:

# 从实参角度
    #1,位置参数。 必须一一对应,按顺序
# def func1(x,y):
#     print(x,y)
# func1(1, 2)
    #2,关键字参数。必须一一对应,不分顺序。
# def func1(x,y,z):
#     print(x,y,z)
# func1(y=2,x=1,z=5,)

# def max(a,b):
#     if a > b:
#         return a
#     else:
#         return b
# print(max(100,102))
# def max(a,b):return a if a > b else b  三元条件表达式
# print(max(100,102))
    #3,混合参数。一一对应 且 关键字参数必须在位置参数后面。
# def func2(argv1,argv2,argv3):
#     print(argv1)
#     print(argv2)
#     print(argv3)
# func2(1,2,argv3=4)#关键字参数必须在位置参数后面

形式参# 从形参角度

    #1,位置参数。 必须一一对应,按顺序
# def func1(x,y):
#     print(x,y)
# func1(1,2)
    #2,默认参数。 必须在位置参数后面。
# def register(name,sex='男'):
#     with open('register',encoding='utf-8',mode='a') as f1:
#         f1.write('{} {}\n'.format(name,sex))
#
# while True:
#     username = input('请输入姓名:/q 或者 Q 退出')
#     if username.upper() == 'Q':break
#     if 'a' in username:
#         sex = input('请输入性别:')
#         register(username,sex)
#     else:
#         register(username)

    #3,动态参数 *args,**kwargs 万能参数

# def func2(*args,**kwargs):
#     print(args)  # 元组(所有位置参数)
#     print(kwargs) #字典(关键字形式的参数都以键值对存储)
# func2(1,2,3,4,5,6,7,11,'alex','老男孩',a='ww',b='qq',c='222')
# 参数定义的顺序:位置参数,*args,默认参数
# def func3(a,b,*args,sex='男'):
#     print(a)
#     print(b)
#     print(sex)
#     print(args)
# func3(1,2,'老男孩','alex','wusir',sex='女')
# 参数定义的顺序:位置参数,*args,默认参数,**kwargs
# def func3(a,b,*args,sex='男',**kwargs):
#     print(a)
#     print(b)
#     print(sex)
#     print(args)
#     print(kwargs)
# func3(1,2,'老男孩','alex','wusir',name='alex',age=46)
# def func1(*args,**kwargs):  # 函数的定义 * 聚合。
#     print(args)
#     print(kwargs)
# l1 = [1,2,3,4] # l11 = (1,2,3,4) # l2 = ['alex','wusir',4] # func1(*l1,*l2,*l11) # 函数的执行:* 打散功能。 # func1(1,2,3,4,'alex','wusir',4,1,2,3,4) # 函数的执行:* 打散功能。 # dic1 = {'name1':'alex'} # dic2 = {'name2':'laonanhai'} # func1(**dic1,**dic2)

2、函数的进阶

  2.1、命名空间一共分为三种:

      全局命名空间

      局部命名空间

      内置命名空间:*内置命名空间中存放了python解释器为我们提供的名字:input,print,str,list,tuple...

  2.2、临时名称空间:

      临时名称空间,局部名称空间,存入函数里面的变量与值的关系,随着函数的执行结束,临时名称空间消失。

  2.3、加载顺序、取值顺序

    加载顺序:内置命名空间(程序运行前加载)->全局命名空间(程序运行中:从上到下加载)->局部命名空间(程序运行中:调用时才加载)

    取值顺序:

        在局部调用:局部命名空间->全局命名空间->内置命名空间

        在全局调用:全局命名空间->内置命名空间

       综上所述,在找寻变量时,从小范围,一层一层到大范围去找寻。

  2.4、作用域

     作用域就是作用范围,按照生效范围可以分为全局作用域和局部作用域。

    全局作用域:包含内置名称空间、全局名称空间,在整个文件的任意位置都能被引用、全局有效

    局部作用域:局部名称空间,只能在局部范围生效

    globals和locals方法

# name1 = 'wusir'
# def func1():
#     name2 = 'laonanhai'
#     print(globals())
#     print(locals())
# func1()

  2.5、global关键字,nonlocal关键字

     global关键字:   

      1,声明一个全局变量。

      2,在局部作用域想要对全局作用域的全局变量进行修改时,需要用到 global(限于字符串,数字)

#global 声明全局变量
# count = 1 # def func1(): # count = count + 1#这个会报错,不能修全局变量的值 # print(count) #global 1,声明一个全局变量 #2,更改一个全局变量 # name = 'wusir' # def func1(): # global name #修改全局变量 # name = 'alex' # return # func1() # print(name)

      ps:对可变数据类型(list,dict,set)可以直接引用不用通过global。

li = [1,2,3]
dic = {'a':'b'}

def change():
    li.append('a')
    dic['q'] = 'g'
    print(dic)
    print(li)
change()
print(li)
print(dic)

     nonlocal关键字:

       1,不能修改全局变量。

       2,在局部作用域中,对父级作用域(或者更外层作用域非全局作用域)的变量进行引用和修改,并且引用的哪层,从那层及以下此变量全部发生改        变。

#nonlical
# def func1(): # name1
= 'alex' # print('+',name1) # def inner(): # nonlocal name1 # name1= 'wusir' # print('*',name1) # def inner1(): # pass # inner() # print('%',name1) # func1()

3、函数的嵌套:

  1、嵌套的定义及嵌套的使用

  2、函数的作用域链:小范围作用域可以使用大范围的变量,但是反之不行,他是单向的。

4、函数名的本质

    函数名本质上就是函数的内存地址

  4.1、可以被引用(可以互相赋值

# def func1():
#     print(666)
#
# f1 = func1
# f1()

  4.2、函数名可以当成函数的参数

# def func1():
#     print(666)
#
# def func2(argv):
#     argv()
#     print(777)
#
# func2(func1)

  4.3、可以当成容器类数据类型的参数

# def func1():
#     print(666)
#
# def func2():
#     print(777)
#
# def func3():
#     print(888)
#
# l1 = [func1, func2, func3]
# for i in l1:
#     i()

  4.4、函数名可以当成函数的返回值

# def func1():
#     print(666)
#
# def func2(argv):
#     print(777)
#     return argv
#
# ret = func2(func1)
# ret()

注:

第一类对象(first-class object)指
1.可在运行期创建
2.可用作函数参数或返回值
3.可存入变量的实体。

*不明白?那就记住一句话,就当普通变量用

5、闭包

  5.1、闭包函数:

    内部函数包含对外部作用域而非全句作用域变量的引用,该内部函数称为闭包函数
    #函数内部定义的函数称为内部函数

    由于有了作用域的关系,我们就不能拿到函数内部的变量和函数了。如果我们就是想拿怎么办呢?返回呀!

    我们都知道函数内的变量我们要想在函数外部用,可以直接返回这个变量,那么如果我们想在函数外部调用函数内部的函数呢?

    是不是直接就把这个函数的名字返回就好了?

    这才是闭包函数最常用的用法

# def wrapper():
#     name1 = '老男孩'
#     def inner():
#         print(name1)
#     inner()
# wrapper()

# def wrapper():
#     name1 = '老男孩'
#     def inner():
#         print(name1)
#     inner()
#     print(inner.__closure__)  # cell
# wrapper()

  5.2、判断闭包函数的方法__closure__

# name1 = '老男孩'
# def wrapper():
#     def inner():
#         print(name1)
#     inner()
#     print(inner.__closure__)  # None不是闭包,因为name1是全局变量
# wrapper()

# name = 'alex'
# def wrapper(argv):
#     def inner():
#         print(argv)
#     inner()
#     print(inner.__closure__)  # cell
# wrapper(name)

  5.3、闭包嵌套

def wrapper():
    money = 1000
    def func():
        name = 'eva'
        def inner():
            print(name,money)
        return inner
    return func
f = wrapper()
i = f()
i()
爬虫的例子:
from urllib.request import urlopen
def index():
    url = "http://www.cnblogs.com/jin-xin/articles/8259929.html"
    def get():
        return urlopen(url).read()
    return get
name1 = 'alex'
content1 = index()()
content2 = index()()
print(content1)
他有一个机制,你的局部作用域不会随着函数的结束而结束

6、装饰器

  6.1、装饰器的定义:

    装饰器本质上就是一个python函数,他可以让其他函数在不需要做任何代码变动的前提下,增加额外的功能,装饰器的返回值也是一个函数对象。

  6.2、使用场景:

    装饰器的应用场景:比如插入日志,性能测试,事务处理,缓存等等场景。

  6.3、装饰器的形成:

现在我有一个需求,我想让你测试这个函数的执行时间,在不改变这个函数代码的情况下:
import time

def func1():
    print('in func1')

def timer(func):
    def inner():
        start = time.time()
        func()
        print(time.time() - start)
    return inner

func1 = timer(func1)
func1()

 注:但是如果有多个函数,我都想让你测试他们的执行时间,你每次是不是都得func1 = timer(func1)?这样还是有点麻烦,因为这些函数的函数名可能是不相同,有func1,func2,graph,等等,所以更简单的方法,python给你提供了,那就是语法糖。

  6.4、语法糖:

import time
def timer(func):
    def inner():
        start = time.time()
        func()
        print(time.time() - start)
    return inner

@timer   #==> func1 = timer(func1)
def func1():
    print('in func1')

func1()

  6.5、带参数的装饰器:(是指被测试的函数带参数)

def timer(func):
    def inner(a):
        start = time.time()
        func(a)
        print(time.time() - start)
    return inner

@timer
def func1(a):
    print(a)

func1(1)

装饰器——带参数的装饰器

  带多个参数的装饰器

#被装饰函数带参数
# def timer(f1):  # f1 = func1
#     def inner(*args,**kwargs):
#         start_time = time.time()
#         f1(*args,**kwargs)  # func1()
#         end_time = time.time()
#         print('此函数的执行效率%s' %(end_time-start_time))
#     return inner
#
# @timer  # func1 = timer(func1)  inner
# def func1(a,b):
#     print(a,b)
#     print('晚上回去吃烧烤....')
#     time.sleep(0.3)
# func1(111,222)  # inner(111,222)

#被装饰函数带参数
# def timer(f1):  # f1 = func1
#     def inner(*args,**kwargs):
#         start_time = time.time()
#         ret = f1(*args,**kwargs)  # func1()
#         end_time = time.time()
#         print('此函数的执行效率%s' %(end_time-start_time))
#         return ret
#     return inner
#
# @timer  # func1 = timer(func1)  inner
# def func1(a,b):
#     print(a,b)
#     print('晚上回去吃烧烤....')
#     time.sleep(0.3)
#     return 666
# ret2 = func1(111,222)  # inner(111,222)
# print(ret2)

  6.6、装饰器的基本构成:

def wrapper(f1):
    def inner(*args,**kwargs):
        '''执行函数之前的操作'''
        ret = f1(*args,**kwargs)
        '''执行函数之后的操作'''
        return ret
    return f1

  6.7、开放封闭原则: 

    1.对扩展是开放的

      为什么要对扩展开放呢?

      我们说,任何一个程序,不可能在设计之初就已经想好了所有的功能并且未来不做任何更新和修改。所以我们必须允许代码扩展、添加新功能。

    2.对修改是封闭的

      为什么要对修改封闭呢?

      就像我们刚刚提到的,因为我们写的一个函数,很有可能已经交付给其他人使用了,如果这个时候我们对其进行了修改,很有可能影响其他已经在使用该函数的用户。

    装饰器完美的遵循了这个开放封闭原则。

  6.8、取消已被引用的装饰器:

def outer(flag):
    def timer(func):
        def inner(*args,**kwargs):
            if flag:
                print('''执行函数之前要做的''')
            re = func(*args,**kwargs)
            if flag:
                print('''执行函数之后要做的''')
            return re
        return inner
    return timer

@outer(False)
def func():
    print(111)

func()

  6.9、多个装饰器装饰一个函数

def outer(flag):
    def timer(func):
        def inner(*args,**kwargs):
            if flag:
                print('''执行函数之前要做的''')
            re = func(*args,**kwargs)
            if flag:
                print('''执行函数之后要做的''')
            return re
        return inner
    return timer

@outer(False)
def func():
    print(111)

func()
复制代码
 

返回顶部
多个装饰器装饰同一个函数

有些时候,我们也会用到多个装饰器装饰同一个函数的情况。


复制代码
def wrapper1(func):
    def inner():
        print('wrapper1 ,before func')
        func()
        print('wrapper1 ,after func')
    return inner

def wrapper2(func):
    def inner():
        print('wrapper2 ,before func')
        func()
        print('wrapper2 ,after func')
    return inner

@wrapper2
@wrapper1
def f():
    print('in f')

f()

总结:多个装饰器时先打印最外的装饰器的之前执行前的语句,然后执行内层装饰器的语句,执行完后在执行最外层装饰器的之后的语句