今天主要讲了文件操作,函数与装饰器,装饰器比较烧脑,需要多做练习,逐步分解来进行理解! 加油!
一 文件操作
操作系统 提供文件的概念
可以操作磁盘。
文件的只读模式: 注意如果是windows 前面要加个r:open(r"C:\a.txt") . 或者分隔符是 /
f = open("D:/python21期/L002-老男孩教育-Python20期VIP视频-mp4/b.txt","r",encoding="utf-8")
data = f.read()
print(data)
f.close() # 文件关闭,回收操作系统的资源
print(f) 这个f变量还是存在的因为是python的变量。 python变量是在程序结束后自动释放
f.read() 这个文件已经被操作系统关闭了,所以就无法read读取了。
另一种方法: with open
with open("a.txt","r",encoding="utf-8")
pass
这个是可以帮你自动关闭文件
其他读方法
1 f.readline() 只读取一行
print(f.readline(),end="")
print(f.readline(),end="")
print(f.readline(),end="")
2 readlines() 吧文件都读出来,并全部放在一个列表list里面。
注意:如果文件过大,比如1T文件 就无法进行操作,这个只适用于小文件
文件的只写模式: 默认是wt文本写,如果文件不存在就创建文件,如果存在文件就清空原来的文件!!
f = open("b.txt","w",encoding="utf-8")
f.write(1111\n)
f.write(222222\n)
f.close()
其他写方法:
1 f.write("11111\n")
2 f.writelines 可以通过列表的形式 或元祖的形式 吧多行内容写到文件。
f.writelines(["name\n","age\n","hobbie"])
结果是
name
age
hobbie
########## 注意 如果是wt文本格式打开的那么就只能写字符串格式
1 文件处理
补充
print(f.writable()) 看一下是否是可写的 可写返回True 不可写False
新的写模式:
a 追加模式, 整个文件末尾追加写
f = open("b.txt","a",encoding="utf-8") 文件不存在就新建一个,如果存在就打开文件吧光标移动到文件末尾追加写,原文件内容不影响
读取多行的话 可以使用for
whit open("a.txt","r",encoding="utf-8") as f:
for line in f:
print(line)
b 模式 可以理解为而二进制
rb 二进制读
with open("a.txt","rb') as f:
print(f.read().decode("utf-8")) ## 如果是文本内容也存为b二进制模式,可以使用decode解码查看文本
wb 二进制写
with open("a.txt","wb") as f:
res = "添加".encode("utf-8") 二进制写需要进行一下编码编码为二进制写
f.write(res)
ab 追加写
跟上面一样 就是需要encode("utf-8")
练习 ; 实现cp命令 可以拷贝任何类型的文件
import sys
_,src_file,dst_file = sys.argv # sys.argv是获取运行的参数,第一个是cp.py程序文件 第二个是源文件,第三个是目标文件,
# 所以第一个不用获取,_,就可以空出第一个参数,src_file是第二个参数源文件
# dst_file 是第三个参数目标文件。
with open("src_file","rb") as read_f, open("dst_file","wb") as write_f:
for line in read_f:
write_f.write(line)
# 注意内存的数据往硬盘中存建议设置个缓存区,积攒一定的大小猴在往磁盘中写入,减少IO压力
# wirte_f.flush() 这个是告诉操作系统赶快写到磁盘中
文件的修改:
import os
with open("1.txt","r",encoding="utf-8") as read_f,open(".1.txt.swap","w",encoding="utf-8") as write_f:
data = read_f.read()
write_f.write(read_f.replace("alex","SB"))
os.remove("1.txt")
os.rename(".1,txt.swap","1.txt")
这样是吧整个文件都写入到内存中修改,如果文件过大,会导致卡顿,所以可以一行一行的改:
for i in read_f:
if "alex" in i:
i = i.replace("alex","SB")
write_f.write(i)
文件内光标移动
只有一种情况光标指的是以字符为单位移动
with open("c.txt","rt","")as f:
f.read(3)
只有read方式才能以字符方式移动
print(f.tell) 告诉光标在第几个字节。 注意 如果有中文的话 一个中文是3个字节。比如
hello你好
f.read(6)
print(f.tell())
这个结果就是8 因为一个英文是一个字节,中文是三个字节 所以读到第六个字符后,这个光标就在5+3=8的字节上
光标的移动
f.seek
with open("a.txt","rt",encoding="utf-8") as f:
f.read(6)
print(f.tell())
这个时候是光标已经移动到了第6个字符(第8个字节)
f.seek(8,0) # 第一个参数是移动几个字节, 第二个参数有三个模式:0,1,2. 0代表相对的位置参照物,参照文件的最开头开始移动
# seek(8,0) 就是从开头0开始移动 往后移动8个字节。
# 0模式在字符t模式与二进制b模式下都可以使用。另外的 1 2 模式需要在bytes模式才能使用。他们的移动单位都是字节
print(f.read())
结果是打印第8个字节后面的内容。
1模式:
从当前光标开始往后移动。
f.read(6) # 已经移动了6个
f.seek(2,1) # 从当前第6个光标继续往后移动2个
print(f.read())
结果是6+2个字节,也是打印第8个字节后面的内容。
2模式:
从文件最末尾为参考开始移动。倒着移动
f.seek(-3,2)
print(f.read())
从文件最后往前移动3个字节,如果文件最后是一个中文,那就只打印这个中文
实现 tail -f access.log
with open("access.log","rb") as f:
f.seek()
while True:
line = f.readline()
if line:
print(line)
else:
time.sleep(0.5)
注意 二进制b模式不翻译\n换行符。 字符t模式翻译\n 如果文本内容有\n换行符,二进制b模式print(line.decode(),end="")
end="" 就是把print自带的空格取消。 如果文本内容中没有\n换行符,哪就直接使用print(line) 就可以了
截断文件
截断模式只能是 a 模式下使用:
with open("a.txt","a",encoding="utf-8") as f:
f.truncate(3) # 只有从文件开头为参照物。3代表截取3个字节
#就是只截断前三个字符
2 函数
1函数基础
为何要用函数?没有函数带来的困扰
1 组织结构不清晰,可读性差
2 代码冗余
3 可扩展性差
什么是函数?
具备某一个功能的工具-->函数
函数就是事先准备的工具
工具准备好了 ,拿来就可以用。重复使用
先定义,后调用。
函数的分类?
内置函数:
help(函数名) 这个就是查看函数下面的注释信息 参考语法1
len
max
mix
count
自定义函数:def 自定义函数
定义函数
语法:
def 函数名(参数1,参数2):
""" 注释信息""" # 可选
函数体,功能体
return 返回值
(其实函数就是一个变量,只不过可以调用)
所以执行函数分为两步,第一步是通过函数名找到这个函数变量,找到函数就可以用()来调用。
定义阶段:
定义函数只检测语法是否问题,不定义代码。
调用函数:
先定义后调用。
调用函数,如果函数中定义的变量等功能没有或其他问题就会报错。
列子:
def foo():
print('from foo")
bar()
foo()
第一次调用会出现问题,因为bar函数没有定义,所以需要再定义一个bar函数
def bar():
print("from bar")
foo()
这次就可以了。 注意先定义后调用原则。
定义函数的三种形式:
1:无参函数
def foo()
print("我是无参数函数")
2:有参函数
def auth(u,p):
if u =="root" and p == "123":
print("login successfull")
else:
print("user or password err")
def interactive()
name = input("请输入账号:").strip()
password = input("请输入密码:").strip()
auth(name,password)
3:空函数
def auth():
pass
在写应用的时候,针对每个功能定义一个函数。空函数就是先一个框架。站个位置。
函数的返回值:
# 函数内可以有多个return 但是只能执行一个return。
def foo(x,y)
print("first")
return 1
print("second")
return 2
print("third")
return 3
res=foo()
print(res) 结果是first 1 因为执行遇到return后就退出了。先运行程序,然后在输出返回值
# 执行return函数就立刻结束,并且return后的值当做本次调用的结果返回。
def foo(x,y):
retrue x+y
res = foo(1,2)
可以返回函数:
def bar();
print("from bar")
def foo():
return bar()
print(foo())
没有return 没有return 返回的是 None print(foo)是打印函数内存地址, print(foo())是返回执行结果
返回一个值return 就返回值本身。
return多个值 返回的是一个元祖格式。
函数调用的三种形式:
def my_mix(x,y):
if x >=y:
return x
else:
reture y
第一种: 单独使用
res1 = my_max
第二种: 把函数返回值的结果进行运算
res2 = my_max(1,2)*10
第三种: 函数调用可以当作另外一个函数的参数
res3 = foo(my_max(1,2),aa)
函数参数的使用
1.参数种类:两种
1 形参 def foo(x,y) 在定义阶段在括号内是形参, 形参在定义阶段是不占内存空间的
2 实参 foo(1,2) 在调用阶段括号内传入的值叫实参, 相当于值
在调用阶段,实参的值会绑定给形参,在调用结束后解除绑定 。
2.位置参数
按照从左到右的顺序依次定义的参数。
1 位置形参:必须被传值,多一个少一个都不行。
2 位置实参:与形参一一对应传值。
def foo(x,y)
print(x,y)
foo(y=1,x=1)
3.关键字参数
在函数调用时,按照key=value的形式定义的实参
特点:指名道姓地给形参传值,不再依赖于位置
注意:
def foo(name,age,sex):
print(name,age,sex)
注意: 1关键字实参必须在位置实参的后面。
2不能为同一个参数赋多次值。
4.默认参数
1 指的是在函数定义阶段,就已经为形参复制。
def foo(x,y=1):
print(x,y)
默认参数的好处就是可以不用传值,当然也可以传值来修改默认参数的值
2 默认参数的值只在定义时被复制一次
res = 1
def foo(x,y=res):
print(x,y)
res = 10
foo("aaaa")
结果是 "aaaa" 1
3 默认参数的值通常应该是不可变类型。 要么是数字,要吗是元祖
5.可变长参数*args, **kwargs
在调用函数时,实参值得个数不固定
实参的形式有:位置实参和关键字实参。
就是实参两个形式的长度个数是不固定。
形参的解决方案: * 和 **
* 是位置实参解决方案
** 是关键字key value解决方案
1 *args
位置的实参会保存为元祖的形式保存
def foo(x,y,*z):
print(x,y)
print(z)
foo(1,2,3,4,5,6)
结果是吧1给x 2给y *接收后面的然后以元祖形式赋值给z3,4,5,6给z
1,2
( 3,4,5,6 )
另一种
def foo(x,y):
print(x,y)
foo(*(1,2))
结果是1,2
2 **kwargs
def foo(x,y,**kwargs):
print(x,y)
foo(x=1,y=2,i=3,l=4)
这种情况*会接收i=3,l=4 并赋值给kwargs 保存为字典 格式。
另一种
def foo(x,y,**kwargs)
foo(y=2,**{"c":5,"b":6,"x":2})
3 应用场景
def bar(x,y,z):
print(x,y,z)
def wrapper(*args.**kwargs):
bar(*args,**kwargs)
wrapper(1,2,3,a=1,b=2,c=2)
这样就会报错,因为wrapper接收参数后,传给bar,然后bar 通过* 与**原封不动的解析回
一开始传给wrapper的参数,所以bar就会报错。无法赋值。
6.命名关键字参数
指的是定义在*后的参数,该参数必须被传值(除非他有默认值),而且必须按照key=value的形式传值。
三 函数进阶
(1)名称空间:
1 名称空间
2 局部名称空间
3 内置名称空间
1 名称空间,或命名空间
运行时开辟出来,里面存的是变量与值内存地址的关系
2 临时名称空间
比如定义了一个函数
def func():
name = "wustr"
age = 23
函数名存放到内存空间中
但是函数体中的变量没有管。
当遇到函数执行的时候,他会通过函数名找到函数体里面的变量然后
临时开辟出一个内存空间,就是临时命名空间,也叫局部名称空间,然后
吧函数里面的变量与值得关系存入到临时命名空间里面
然后随着函数的执行结束,临时名称空间的内存就消失。释放。
3 内置名称空间
只要创建了py文件 解释器就会吧内置名称空间加载到内存中了。内置函数就可以使用了
比如:len()
(2)作用域:
1 全局作用域:全局名称空间,内置名称空间
2 局部作用域:局部名称空间
(3)加载顺序,取值顺序
加载顺序:内置名称空间先加载到内存。然后是全局名称空间加载到内存,最后是局部名称空间(只有函数执行时才加载)
取值顺序:
函数先从局部作用域取值,如果没有在找全局作用域,如果还没有就找内置名称空间。也就是从内向外找。
而且只是单向的,找到最后没有就没了
globals locals 内置函数
globals 将全局命名空间打印出来
locals 将局部命名空间打印出来
global nonlocal 关键字
global
函数里面可以使用全局命名空间的变量。但是不能改变。如果要改变需要使用global关键字声明全局变量
nonlocal 声明全局变量,如果存在就更改全局变量。
引用并改变上一层的局部变量。上一层同层的变量都会改变。
(4)函数名:
1 可以相互赋值
def fun1():
print(666)
f1 = func1
f1()
2 可以当成函数的参数
def func2():
print(111)
def func3(argv):
argv()
print(222)
func3(func2)
3 可以当成容器类数据类型的参数
def func11():
print(222)
def func22():
print(333)
def func33():
print(444)
ll = ["func11","func22","func33"]
for i in ll:
i()
4 可以当作函数的参数和返回值
def func():
print(666)
def func1(argv)
print(777)
return argv
res = func1(func)
res()
(5)闭包:
内层函数对外层函数非全局变量的引用
def wrapper():
name = "alex"
def inner():
print((name)
inner()
wrapper()
判断是不是闭包 inner.__closure__
如果是返回cell 不是返回 None
好处是:如果python检测是闭包。有一个机制,你的局部作用域不会随着函数的结束而结束。
比如爬虫,爬下来的数据,使用闭包可以吧爬下来的数据保存到内存中,下次再爬直接从内存中取
import urlopen,url
def index():
url = "http://www.xiaohua100.com/index.html"
def get():
return urlopen(url).read()
return get
index()()
四 装饰器
函数对象
函数是第一类对象:指的是函数可以当作数据传递
1 可以被引用 x=1,x=y
def func(x,y):
print(x,y)
f=func
f(1,2)
2 可以当作函数的参数传入
def foo():
print("form foo")
def bar(x):
x():
bar(foo)
3 可以当作函数的返回值
4 可以当作容器类型的元素
后续内容待补。。。