总结3

文件操作

文件的定义

文件时操作系统提供给我们管理硬盘的方法,可以将硬盘上的一块区域划出,其内部的数据就是有效的文件,硬盘上的数据都是以机器语言1010的二进制形式存储的,将其与文本格式、视频格式等联系起来就需要靠软件程序进行转换。

文件在python中可以认为是一种数据类型,像字符串、列表类似可以使用一些内置函数。

打开文件的两种方式

打开文件的方式1

f = open('文件路径','打开模式',encoding='编码格式')  # 打开文本文件
- 进行文件操作 -
f.close()

利用open打开,将文件类型的数据赋值给变量f,这个过程只确定了文件的位置以及如何将硬盘上的数据加载到了内存里,但还没有加载。

在利用open打开文件后,我们可以对文件进行一系列操作。

在进行了一系列操作后,f.close()可以看做文件类型的内置函数,实现的功能就是释放文件类型的数据打开期间占用的所有内存,并且将内存中的文件写入硬盘。

打开文件的方式2

利用了with上下文的管理方法,此方法主要是防止我们忘了使用close函数释放内存。

with open('文件路径','打开模式',encoding='编码格式')as f:
    - 子代码块,进行一系列文件操作 -

as 关键字这里就相当于赋值符号,将文件类型数据给了变量f。

在with的子代码块执行完后,系统会自动执行close,释放内存,并将内容写入硬盘。

打开文件的模式

读写模式

r模式 - 只读

在这个模式下打开文件,文件只能执行读相关的操作。

with open('a.txt','r',encoding='utf8')as f:
    f.read()  # 读取文件内所有的数据

在open中,我们确定了文件路径和名称,确定了只读模式,确定了字符编码是utf8,于是在f执行read功能时,就会按照这个路径,按字符编码是utf8解码,读出硬盘中的数据,并加载到内存中。

所以,open决定加载的方式,而加载是在read这一步执行的。

  1. 打开文件如果文件路径不存在会报错
  2. 打开文件如果文件路径存在则按路径读

w模式 - 只写

在这个模式下,文件只能执行写相关的操作。

with open('a.txt','w',encoding='utf8')as f:
    f.write('我是中文')  # 将内容写到内存中

write按照字符编码utf8将字符串编码为二进制存储到硬盘的一个位置,这个位置的文件名是‘a.txt’

  1. 打开文件如果文件路径不存在,则新建文件,将内容存进去

  2. 打开文件如果文件路径存在,则会覆盖原文件

    a.txt原本存着   我是中文    这一句话,我们空执行open:
    with open('a.txt','w',encoding='utf8')as f:
        pass
    会发现,文件空了,说明是在打开文件时,就会清除掉原文件的数据。
    

a模式 - 追加

在这个模式下,文件也执行写相关的操作。

与write不同的是,打开已经存在文件时,它并不会覆盖掉原文件。

它会将光标停在文件的尾部,在尾部执行写操作。

with open(r'a.txt', 'a', encoding='utf8')as f:
    f.write('I am English')
    
# 文件内容
我是中文
I am English

数据形式

t模式 - 文本模式

是默认的打开模式,文件模式的完整形式为'rt'这样,但不写t就默认是t模式。

上述的所有代码都是以文本文件操作的。

文本模式顾名思义只能打开文本类型的文件。

b模式 - 二进制模式

二进制模式可以打开所有类型的文件,相当于直接加载硬盘上的二进制。

配合读写模式有'rb','wb','ab'

with open('a.txt','rb')as f:  # 以二进制只读方式打开文件
    f.read()  # 读取文件内所有的数据

b模式需要注意,因为读写是以二进制形式,所以不存在编码概念,一定不能加encoding。

文件类型的内置函数补充

刚才提到了close,read,write等文件类型的内置函数。

其中read是一次性将硬盘中的文件数据读到内存中。

for 循环读取数据

防止文件过大,用read太占用内存了。

with open('a.txt', 'r', encoding='utf8') as f:
    for line in f:  # line按行依次取f中的内容
        print(line, end='')  # 将行的内容打印出来

f的内容被for循环划分为一行行,line变量接收,并是以字符串类型存储的,可以被print打印出来。

readline() 和 readlines()

读一行和读所有行。

# 一次只读一行内容
with open('a.txt', 'r', encoding='utf8')as f:
	f.readline()  # 第一行
# 一次性读取文件内容,会按照行数组织成列表的一个个数据值
with open('a.txt', 'r', encoding='utf8')as f:
    f.readlines()  # ['第一行\n','第二行\n','第三行']

readable()

判断文件数据是否可读。

writeable()

判断文件数据是否可写。

writelines()

与 readlines() 相反。接收一个列表,依次写到f变量里。

flush()

将内存中文件数据立刻刷到硬盘,等价于我们平常的快捷键ctrl + s

之前的一系列的read和write都是对内存数据的读写。

光标操作

f.seek移动光标 和 f.tell拿到光标坐标。

函数

函数分定义和调用两个阶段。定义好的函数可以在后续任意位置调用。

函数的基本结构

定义阶段

def 函数名(形参):
    函数体代码
    return 

调用阶段

函数名(实参)  # 实参与形参的数量一一对应。

函数的返回值

函数体代码执行完后向外部产出的值。

默认返回值

如果没有return,则没有返回值,也可以说默认返回值为None。

return用法

  • return独自出现:直接结束函数体,没有返回值
  • return后跟一个数据值:结束函数体,返回值为这个数据值
  • return后跟多个数据值:结束函数体,将多个数据组为元组作返回值

函数的参数

形参和实参的使用

形参是在定义函数时同时定义的,它接收函数外部的输入,以变量的形式在函数体计算或操作。

实参是实际传入函数的参数,实参的数量需要和形参一一对应。

形参

一般形参

在函数名后的扩号内依次排列开来的变量名,这些变量名接收实参的数据值,并可以在函数体内引用。

默认形参

当变量名没能接收到实参时则取默认值。

def func(a = 10):
    print(a)
    
func(a)  # 10

可变长形参

可以接收多余的实参,防止报错,提高兼容性。装饰器经常用。

def func(a, b, *args, **kwargs):
    print(args, kwargs)


func(1, 2, 3, 4, 5, c=6, d=7)
# (3, 4, 5) {'c': 6, 'd': 7}
多余的位置实参被args接收,多余的关键字参数被kwargs接收。

命名关键字参数

跟在可变长位置形参的后面的默认参数,这样的结构让它只能通过关键字实参传入。

def func(a, b, *args, c=10, **kwargs):
    print(args, c, kwargs)
    
func(1, 2, 3, 4, 5, c=6, d=7)
# (3, 4, 5) 6 {'d': 7}

实参

位置实参

自动按定义时形参的位置,对应的将实参传过去。

def func(a, b):
    print(a, b)
    
func(1, 2)  # 1传给a,2传给b

关键字实参

突破位置的限制,按定义时的形参的变量名传入实参。

def func(a, b):
    print(a, b)
    
func(b=2, a=1)  # 1 2

可变长实参(打散)

  • *,将可迭代对象打散为位置实参

    func(*[1, 2]) # func(1, 2)
    func(*{'a', 1,'b': 2})  # func('a', 'b')
    func('hello')  # func('h', 'e', 'l', 'l', 'o')
    l1 = ('aaa', 'bbb')
    func(*l1)  # func(l1[0], l1[1])
    
  • **,将字典打散为关键字实参

    func(**{'a', 1,'b': 2})  # func(a=1, b=2)
    

    字典的key打散为实参的关键字,对应的值打散为对应的值。

名称空间

存关键字、变量名、函数名、类名的空间,数据值存在内存里,这些数据值的索引--变量名等也存在内存的某一个空间中。

三大名称空间

三个名称空间在内存中的存储是分开的。

内置名称空间

存储所有的python关键字,如print、input、len等

在python打开时,产生这个空间,运行python代码就可以用这个空间里面的名字。

全局名称空间

文件级别的名称空间,执行py文件时,产生这个空间,存储文件内所有可能产生的所有名字。

在py文件执行结束后消失。

局部名称空间

函数级别的名称空间,当调用函数时产生(定义时不产生),存储函数运行期间可能产生的所有名字,这里面的名字不会在全局名称空间产生(理解为全局的程序只定义函数,所以函数内的名字不归它管。)

名字查找顺序

在局部里引用某个名字,在局部优先找 ---> 全局其次 ----> 内置最后

在局部的局部引用某个名字,局部的局部 ----> 局部 ----> 全局 ----> 内置

总结:从当前空间找起,找不到就往高级的空间找。

global与nonlocal

用于解决局部定义和赋值语句冲突的问题。

在局部使用赋值语句想要为全局的变量赋值,会判定为局部名称空间的定义语句,而无法修改全局的值。

a = 111
def func():
    a = 222
    
    
func()
print(a)  # 111

而使用global事先声明全局变量,就可以在局部修改全局的值:

a = 111
def func():
    global a  # 函数内别定义a了,用全局的就OK了。
    a = 222
    
    
func()
print(a)  # 222

nonlocal则是声明外层函数的变量:

def func1():
    a = 111
    def func2():
        nonlocal a
        a = 222
    func2()
    print(a)
    
func1()  # 222

函数名的用法

与变量名一样

函数名和变量名一样是名字,变量存储各种数据值,函数存储函数体代码。

在python中,函数名可以像变量名一样用,如:

a = 10
b = a
# 实际上b也绑定了10
def funcxxx():
    print('funcxxx')
    
    
func = funcxxx
func()  # 相当于funcxxx()
# 因为func也绑定了funcxxx的代码地址,可以用func加括号的形式调用这部分代码

这里可以应用于功能字典,在一些场景下简化if-elif。

作实参传入函数和作为返回值传出函数

def wrapper(func):  # 作为参数传入一个函数名
    def inner():
        func()  # 函数名加括号可以执行
        print('转换了')
    return inner  # 将inner这个函数作为返回值返回了

闭包函数和装饰器

闭包函数

闭包函数指:

  • 一对嵌套的函数,分外层函数和内层函数
  • 内层函数用到了外层函数里的名称(变量名、函数名)
def outer():
    a = 111
    def inner():
        print(a)
    return inner

func = outer()  # 将outer中的inner返回给func
func()  # 111  # inner()
相当于inner函数的执行结果,inner中的a是outer空间内的

闭包函数实际上给我们提供了传递参数的第二种方式:

def outer(a):  # a不由outer内部定义,而由外部函数传入
    def inner():
        print(a)
    return inner

func = outer(1)  # 要求传入一个参数
func()  # 1
# 这样在全局拿到了一个状态被锁定的函数,不必再次传参

闭包函数这种传参方式被应用于装饰器中。

装饰器

装饰器是在不改变原函数调用方式、不改变原函数的内容的情况下给原函数添加新功能的方式。

装饰器的功能并不绑定某个原函数,而可以加装到所有的函数上,所以包含某种功能的装饰器要独立的写出来。

无参装饰器

def outer(func):  # 用闭包函数传递函数名
    def inner(*args, **kwargs):  # 用args和kwargs来传递所有的位置实参和关键字实参
        # 在内部书写添加的功能
        res = func(*args, **kwargs)
        return res  # 用中间变量传递原本的返回值
    return inner  # 将转换好的函数作为返回值传出

有参装饰器

就是装饰器本身的功能也可能需要参数的传入,但是func和args的位置都不能动,所以需要再包一层函数来给装饰器传参。

def outer(装饰器所需参数):  # 外层加一层参数层
    def inner(func):  # 内层所有不变
        def wrapper(*args, **kwargs):
             # 在内部书写添加的功能
        	res = func(*args, **kwargs)
        	return res  # 用中间变量传递原本的返回值
        return wrapper  # 用wrapper返回转换好的函数
    return inner # outer(参数)的结果就是原本的内层的无参装饰器

语法糖

装饰器加装到某个函数的语句为index = outer(index),不仅不美观,而且凌乱,python提供了语法糖,帮助我们更好的加装装饰器。

def decorate(func):...  # 定义了一个装饰器,内部代码参考上述模板

@decorate  # 语法糖 
def func():...  # 某函数

上述程序中@+装饰器名,就相当于帮我们在定义了某个函数后执行了index = outer(index)

而装饰器有参时,语法糖如下:

def outer(para):  # 定义了一个有参的装饰器,内部代码参考上述模板
    def decorate(func):...  # 内层结构如无参装饰器
    return decorate


@outer(para)  # 语法糖 
def func():...  # 某函数

注意函数名+括号的执行优先,所以outer(para)执行得到了decorate,语法糖再执行,就变成了执行@decorate

多层语法糖的执行

@deco1
@deco2
@deco3
def func():...

在装饰了多个装饰器时,会按照从近到远的顺序执行装饰。即deco3 ---> deco2 ----> deco1

递归函数

递归函数是指直接或者间接的调用本身的函数结构。

def func():
    print('coming')
    func()  # 内部调用自己

递归函数需要在调用到一定程度时,设置一个节点使这个过程停下来,因为不断递归会不断开辟名称空间占用内存。如:

def func(n):
    if n==0:
        return
    return func(n - 1)

func(10)  # 传入一个10 ,递归调用会变成func(9)\8\7...到0时停止

递归函数可以认为是一种循环结构,在实在没办法的时候才建议使用。

在下面的二分法中简单写一个递归函数练手。

算法及二分法

算法是针对某个问题而提出的有效方法,有高效的算法也有低效的。

二分法

对于有序列表进行查找,就要用到二分法,将目标查找值与中位数进行比对,目标大时下次寻找右半区(大的那边),否则寻找左半区;然后再次和半区的中位数比对确定目标值所在的更小的半区。最终划分到只有这个目标,从而得到目标值所在位置。

其程序实现可以用如下两段代码实现:

循环结构
l1 = [11, 23, 44, 64, 71, 78, 91, 107, 114, 125, 131, 201, 210, 212, 456, 678]


def bin_search(target, num_list):
    bin_list = num_list
    while True:
        if len(bin_list) == 0:
            return '没找到'
        mid_index = len(bin_list) // 2
        if target == bin_list[mid_index]:
            return '找到了'
        elif target > bin_list[mid_index]:
            bin_list = bin_list[mid_index + 1:]
        else:
            bin_list = bin_list[:mid_index]


print(bin_search(71, l1))
递归结构
l1 = [11, 23, 44, 64, 71, 78, 91, 107, 114, 125, 131, 201, 210, 212, 456, 678]


def bin_search(target, num_list):
    if not num_list:
        return '没找到'
    mid_index = len(num_list) // 2
    if target == num_list[mid_index]:
        return '找到了'
    elif target > num_list[mid_index]:
        return bin_search(target, num_list[mid_index + 1:])
    else:
        return bin_search(target, num_list[:mid_index])


print(bin_search(711, l1))

表达式、生成式

三元表达式

值1 if 条件表达式 else 值2
# 如果成立则得到值1,如果不成立则得到值2

列表生成式

[数据值表达式 for 变量名 in 可迭代对象]
如:
[i for i in range(10)]  # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

字典生成式

{k:v for k, v in 二元组迭代对象}
如:
keys = ['name', 'age', 'gender']
infos = ['leethon', 18, 'male']
print({k: v for k, v in zip(keys, infos)})  # {'name': 'leethon', 'age': 18, 'gender': 'male'}

集合生成式

{v for v in 可迭代对象}
如:
print({c for c in 'hello'})  #  {'o', 'e', 'l', 'h'}
# 去重且无序,集合的特点

匿名函数及内置函数

匿名函数

匿名函数没有函数名,即用即丢,其关键字为lambda

语法结构lambda 形参:返回值,相当于:

def func(形参):
    return 返回值

内置函数汇总

重要内置函数

map映射

l1 = [111, 222, 333]  # 将l1中的元素统一加10
print(map(lambda x: x + 10, l1))  # <map object at 0x000002762993A7C0>
print(list(map(lambda x: x + 10, l1)))  # [121, 232, 343]

max\min最大最小值

# 直接比较
print(max([1, 2, 3]))  # 3
# 通过映射关系比较
print(max([1, 2, 3], key=lambda x: 1 / x))  # 这里的key=是命名关键字参数,传函数类型参数

reduce多返一

from functools import reduce
print(reduce(lambda x, y: x + y, range(5)))  # 10

依次取出迭代器中的元素做参数,进行函数运算。reduce(func, 迭代器)

zip拉链

将多个迭代器对应的值缝合成一个元组。

anum = [chr(x) for x in range(65, 70)]
num = range(65, 70)
print(list(zip(anum, num)))
# [('A', 65), ('B', 66), ('C', 67), ('D', 68), ('E', 69)]

filter过滤

结构:filter(函数, 迭代器对象)
res = filter(lambda x: x > 40, range(20, 100, 10))
print(list(res))  # [50, 60, 70, 80, 90]

过滤掉不符合条件的元素值。

sorted排序

给列表排序(迭代器对象),默认升序

l1 = [13, 12, 533, 64, 766, 322, 131, 22]
res = sorted(l1)
print(res)  # [12, 13, 22, 64, 131, 322, 533, 766]

其他内置函数

abs —— 取绝对值
all —— 全与:只要有一个为真就为真
any —— 全或:只要有一个为真就为真
bytes —— 转换为bytes类型数据
	bytes(字符串, encoding='utf8')
callable —— 可执行判断:判断名字是否可执行(加括号调用)
chr\ord —— ASCII码值的数字字母对应转换
dir —— 查看对象内置方法:可以‘点’出来的函数
divmod —— 整除和取余
	divmod(除数, 被除数)  得到 (整数商,余数)
enumerate —— 枚举
	类似于zip(range(len(迭代器对象)),迭代器对象)
hash —— 加密:将字符串加密为一种定长的字符
isinstance —— 判断数据类型
	isinstance(数据,数据类型)
    isinstance(111, int)  # True
pow —— 幂指数
round —— 四舍五入取整
sum —— 求和

可迭代对象、迭代器对象

可迭代对象

可以被转换为迭代器对象的数据类型,用__iter__方法转换。

有字符串、元组、列表、集合、字典、文件。

而整型、浮点型、布尔值、函数都不是可迭代类型。

迭代器对象

迭代器对象是工厂,拥有__iter__方法和__next__方法,如range函数得到的就是迭代器对象。

迭代器操作

s1.__iter__()得到还是他自己

s1.__next__()依次往下遍历值

for循环本质

for循环的本质是遍历迭代器对象。
for i in 可迭代对象\迭代器对象:的本质是
# 可迭代对象\迭代器对象.__iter__()
# while循环
#     迭代器对象.__next__()
# 直到没值了结束循环

异常捕获

异常就是报错,俗称bug。语法错误和逻辑错误都会造成程序运行过程中出现异常。

在py报错时,会给我们一些提示信息。

提示信息由3部分组成:异常位置,异常类型和异常详细

习题

习题1
# 编写⽤户识别程序
#  要求:
#  可循环根据⽤户输⼊的姓名不同输出不同的身份信息
#  添加程序结束功能(如⽤户输⼊字⺟q直接结束识别程序)
#  jason:扫地僧 tony:洗碗⼯ kevin:服务员 jack:配菜员 其他:未识别
career_dict = {
    'jason': '扫地僧',
    'tony': '洗碗⼯',
    'kevin': '服务员',
    'jack': '配菜员',
}
while True:
    name = input('请输入你要搜索的名字(q退出)')
    if name == 'q':
        break
    print(career_dict.get(name))
习题2
# 2.利⽤for循环及range⽅法⾃动⽣成链家⼆⼿房多⻚数据⽹址(⼗条以
# 上即可)
#  初始⽹址:https://sh.lianjia.com/ershoufang/
# 第二页:https://sh.lianjia.com/ershoufang/pg2/
s1 = "https://sh.lianjia.com/ershoufang/pg{}/"
for i in range(1, 11):
    print(s1.format(i))
习题3
# 3.编写⽤户登录程序
#  温馨提示:
#  ⽤户名与密码来源于字符串source_data = 'jason|123'
#  想办法从中拆分出⽤户名和密码⽤于后续账户信息⽐对
#  普通要求:
#  1.验证失败情况下可⼀直循环验证 成功则直接退出
#  拔⾼练习:
#  1.只允许三次失败机会
#  2.登录成功后进⼊内层循环,⽤户输⼊任何指令利⽤格式化输出
#  打印正在执⾏该⽤户指令即可,直到⽤户输⼊字⺟q退出内层循环
source_data = 'jason|123'
count = 0
while count < 3:
    input_name = input('输入用户名:')
    input_pwd = input('输入密码:')
    if f'{input_name}|{input_pwd}' == source_data:
        print('登录成功')
        while True:
            order = input('请输入指令(q退出):')
            if order == 'q':
                break
            print(f'你在执行【{order}】指令')
        break
    count += 1
posted @ 2022-10-16 20:48  leethon  阅读(32)  评论(0编辑  收藏  举报