Python开发【第三篇】:文件操作与函数

内容概要

  1. 文件操作
  2. 初始函数
    1. 函数定义与调用
    2. 函数的返回值
    3. 函数的参数
  3. 函数进阶
    1. 函数参数--动态传参
    2. 名称空间
    3. 作用域
    4. 函数的嵌套
    5. 函数名的运用
    6. gloabal,nonlocal 关键字

 

1、文件操作

        使用python来读写文件是非常简单的操作. 我们使用open()函数来打开一个文件, 获取到文件句柄. 然后通过文件句柄就可以进行各种各样的操作了. 根据打开方式的不同能够执行的操作也会有相应的差异.
        打开文件的方式: r, w, a, r+, w+, a+, rb, wb, ab, r+b, w+b, a+b 默认使用的是r(只读)模式

 

 

1.1 只读操作

1. read()  将文件中的数据全部读取出来, 弊端: 占内存,如果文件过大,容易导致内存崩溃

f = open("文件练习",mode="r",encoding="utf-8")    #encoding 表示编码集,即文件用什么编码写入的,就用什么编码获取到数据
content = f.read()    
print(content)

#执行结果:
民谣很好听,
但是很费烟啊。

 

 

read(n) 读取n个字符. 如果再次读取. 那么会在当前位置继续去读取不是从头读

f = open("文件练习",mode="r",encoding="utf-8")
content1 = f.read(3)
content2 = f.read(3)
print(content1)
print(content2)

#执行结果:
民谣很
好听,


2. readline() 每次读取一行数据, 注意: readline()结尾, 注意每次读取出来的数据都会有一个 \n 所以呢. 需要我们使用strip()方法来去掉\n或者空格 

f = open("文件练习",mode="r",encoding="utf-8")
content1 = f.readline()    # 读取一行数据,光标会移动一行
content2 = f.readline()    # seek(0) 移动光标到最开始位置
print(content1)     
print(content2)

# 执行结果:
民谣很好听,

但是很费烟啊。

#print打印默认也带有 \n 换行,所以会有空行出来

 

3. readlines()  将每一行形成一个元素, 放到一个列表中. 将所有的内容都读取出来. 所以也是. 容易出现内存崩溃的问题

f = open("文件练习",mode="r",encoding="utf-8")
lit = f.readlines()
print(lit)
# 执行结果: ['民谣很好听,\n', '但是很费烟啊。']

# 循环打印
for line in lit:
    print(line.strip())

 

4.  循环读取. 这种方式是最好的. 每次读取一行内容.不会产生内存溢出的问题. 

# #重点:::: 文件句柄可以进行迭代
 for line in f: # 每一行内容
     print(line.strip())
f.close()
执行结果: 民谣很好听, 但是很费烟啊。

 

 

  注意: 读取完的文件句柄一定要关闭 f.close()

 

 

1.2  写操作

# w(write) 写   如果文件不存在. 创建新文件, 如果文件存在. 清空文件

f = open("葫芦娃",mode="w",encoding="utf-8")
f.write("蛇精")
f.write("小金刚")   

print(f.read())  # io.UnsupportedOperation: not readable   # 模式是w 不能执行读操作

f.flush()   # 刷新,养成好习惯
f.close()   # 关闭

 

  

# a(append) 追加

只要是a或者ab, a+ 都是在文件的末尾写入. 不论光标在任何位置

f = open("葫芦娃",mode="a",encoding="utf-8")
f.write("大娃,二娃")
f.flush() # 刷新,养成好习惯 f.close() # 关闭

 

 

1.3 其他模式: rb, wb, ab  b:bytes 不需要encoding
带b的用来操作非文本文件(图片, 视频, 音频)

# # 文件复制
# f1 = open("c:/hyf.jpg", mode="rb")
# f2 = open("d:/hyf.jpg", mode="wb")
# for b in f1: # 一部分一部分复制
#     f2.write(b)
#
# f1.close()
# f2.flush()
# f2.close()

 

1.4 其他模式: r+, w+, a+ +:扩展
一个文件. 要么读, 要么写
r+   在读的基础上扩展. 读写操作
w+  写读操作
a+  追加写读操作

# f = open("电影", mode="r+", encoding="utf-8") # r+ 读写:先读后写
# print(f.read())
# f.write("欢乐喜剧人") # 把原来的数据覆盖了
# # print(f.read())
# f.flush()
# f.close()

 

其他模式:r+b, w+b, a+b 操作的是字节

 

 

1.5 文件修改操作

1. 读取源文件中的内容
2. 修改内容
3. 把新内容写入到副本文件
4. 删除源文件
5. 把副本文件重命名成源文件
  import os
  os.remove() 删除文件
  os.rename() 重命名文件

文件内容:
10.2.12.17
10.2.12.40   
10.2.12.17
10.2.12.13

把10.2.12.17 替换成 10.2.12.156
import os
f1 = open("ip.txt",mode="r",encoding="utf-8")
f2 = open("ip.txt.tmp",mode="w",encoding="utf-8")
for content in f1:
    s = content.replace("10.2.12.17","10.2.12.156")
    f2.write(s)
f1.close()
f2.flush()
f2.close()
os.remove("ip.txt")
os.rename("ip.txt.tmp","ip.txt")


使用with
import os
with open("ip.txt",mode="r",encoding="utf-8") as f1, \
    open("ip.txt.tmp",mode="w",encoding="utf-8") as f2:
    for content in f1:
        s = content.replace("10.2.12.156","10.2.12.17")
        f2.write(s)
os.remove("ip.txt")
os.rename("ip.txt.tmp","ip.txt")

 

1.6 文件操作补充:
  f.seek(0) 光标移动到开头
  f.seek(0, 2) 光标移动到末尾

    第二个参数表示模式:
    0: 文件的开头,默认值
    1: 光标当前位置
    2: 文件的末尾

  f.tell() 告诉你光标当前的位置
  f.truncate() 截断文件

 

2、函数

1、 定义函数,函数是对功能或者动作的封装。

def 函数名(形参):
     函数体

调用函数
函数名(实参)

例如:
def func1(x):
    print("测试",x)

func1("哈哈")

 

2、函数的返回值

  执行完函数之后,可以使用 return来返回结果。

2.1 函数如果执行到return. 结束该函数

def func1():
    print("测试")
    return                  
    print("测试")         # 这句不会执行

func1()

 

2.2 return 值. 返回一个结果

def func1():
    print("测试")
    return  "ok"

res = func1()
print(res)
执行结果:
ok

 

2.3 只写return或者不写return. 返回None

def func1():
    print("测试")
    return

res = func1()
print(res)
#None

 

2.4 return 值1, 值2, 值3..... 返回多个结果

def func1():
    print("测试")
    return "OK" ,"太好了"

res = func1()
print(res)
print(type(res))
('OK', '太好了')
<class 'tuple'>

 

3、函数的参数

  参数, 函数在调用的时候指定具体的一个变量的值. 就是参数. 语法:

def 函数名(参数列表):
函数体

 

 关于参数:

形参
写在函数声明的位置的变量叫形参. 形式上的一个完整. 表示这个函数需要xxx

def func1(name):  #形参
    print("你的名字 %s" % name)

 

实参
在函数调用的时候给函数传递的值. 叫实参, 实际执⾏的时候给函数传递的信息. 表示给函数xxx

func1("周杰伦")  # 实参

 

参数的分类

实参有三种: 位置参数、关键字参数、混合参数

 

位置参数

def func1(name,age,gender):  
    print("%s %s %s" % (name,age,gender))

func1("周杰伦","18","")   # 一个值对应一个变量传上去

 

关键字参数

def func1(name,age,gender):
    print("%s %s %s" % (name,age,gender))

func1(gender="",age=19,name="MM")  #指定变量赋值

 

混合参数

 两种参数混合着使用. 也就是说在调用函数的时候即可以给出位置参数, 也可以指定关键字参数. 

def func1(name,age,gender):
    print("%s %s %s" % (name,age,gender))

func1("MM",gender="",age=19)

注意: 在使用混合参数的时候, 关键字参数必须在位置参数后面     即,位置参数  --> 关键字参数

 

 

形参也分三种: 位置参数、默认值参数、动态参数

位置参数:按照位置来赋值

def func1(name,age,gender):  
    print("%s %s %s" % (name,age,gender))

func1("周杰伦","18","")   # 一个值对应一个变量传上去

 

默认值参数:在函数声明的时候, 就可以给出函数参数的默认值. 在调用的时候可以给出具体的值, 也可以不给值, 使用默认值. 

def func1(name,age,gender=""):
    print("%s %s %s" % (name,age,gender))

func1("周杰伦","18")

注意, 必须先声明位置参数, 才能声明默认值参数

 

动态参数:

 动态参数分为两种

1. *args 接收位置参数的动态传参

def func1(*args):    # 接收所有位置参数,聚合为元祖
    print(args)
    print("姓名:%s,年龄:%s,性别:%s" % args)  # 元祖解包

func1("周杰伦","18","")
# 执行结果: # (
'周杰伦', '18', '') # 姓名:周杰伦,年龄:18,性别:男

 

2. **kwargs 接收关键字参数的动态传参

def func1(**kwargs):   # 接收所有关键字参数,聚合为字典
    print(kwargs)

func1(姓名="周杰伦",年龄=18,性别="")
# 执行结果:
# {'姓名': '周杰伦', '年龄': 18, '性别': ''}

 

动态接收所有参数,无敌的传几个参数,都可以接收:

def func1(*args,**kwargs):
    print(args)
    print(kwargs)

func1(1,2,3,4,姓名="李世民")
# 执行结果:
(1, 2, 3, 4)
{'姓名': '李世民'}

 

 形参列表顺序:

  位置 -> *args -> 默认值 -> **kwargs

 

动态参数的另一种传参方式:

在实参位置上给一个序列,列表,可迭代对象前面加个*表示把这个序列按顺序打散. 

在形参的位置上的* 表示把接收到的参数组合成一个元组

def fun(*args):  # 聚合
  print(args)
lst
= [1, 4, 7] fun(lst[0], lst[1], lst[2]) fun(*lst) # * 把一个列表按顺序 打散 s = "臣妾做不到" fun(*s) # 字符串也可以打散 (可迭代对象)

执行结果:
(1, 4, 7)
(1, 4, 7)
('臣', '妾', '做', '不', '到')

 

如果是一个字典, 那么也可以打散. 不过需要用两个*

def fun(**kwargs):   # 聚合为字典
 print(kwargs)
dic
= {'a':1, 'b':2} fun(**dic) # 打散为一个个关键字参数

执行结果:
{'a': 1, 'b': 2}

 

 函数的注释: 

def chi(food, drink):
 """
 这⾥是函数的注释, 先写⼀下当前这个函数是⼲什么的, ⽐如我这个函数就是⼀个吃
 :param :param food: 参数food是什么意思
 :param :param drink: 参数drink是什么意思
 :return :return: 返回的是什么东东
 """
 print(food, drink)
 return "very good"

 

2、命名空间

        在python解释器开始执行之后, 就会在内存中开辟一个空间, 每当遇到一个变量的时候, 就把变量名和值之间的关系记录下来, 但是当遇到函数定义的时候, 解释器只是把函数名读入内存, 表示这个函数存在了, 至于函数内部的变量和逻辑, 解释器是不关心的. 也就是说一开始的时候函数只是加载进来, 仅此而已, 只有当函数被调用和访问的时候, 解释器才会根据函数内部声明的变量来进行开辟变量的内部空间. 随着函数执行完毕, 这些函数内部变量占用的空间也会随着函数执行完毕而被清空.

def fun():
    a = 10
    print(a)
fun()
print(a)   # a 不存在了

     

我们给存放名字和值的关系的空间起一个名字叫: 命名空间. 我们的变量在存储的时候就是存储在这片空间中的. 

  • 命名空间分类:
  1. 内置命名空间 --> 存放python解释器为我们提供的名字, list, tuple, str, int这些都是内置命名空间
  2. 全局命名空间 --> 我们直接在py文件中, 函数外声明的变量都属于全局命名空间
  3. 局部命名空间 --> 在函数中声明的变量会放在局部命名空间

 

  • 加载顺序:
  1. 内置命名空间
  2. 全局命名空间
  3. 局部命名空间(函数被执行的时候)

 

  • 取值顺序:
  1. 局部命名空间
  2. 全局命名空间
  3. 内置命名空间
a = 10
def func1():
  a = 20
  print(a)   #执行结果:20  会先在局部中找

def func2
  print(a) #执行结果:10 一样先在局部找,局部没有 去全局找 func1()
func2()   

执行结果:
#
20
# 10

 

3、作用域

作用域: 作用域就是作用范围, 按照生效范围来看分为 全局作用域和局部作用域
  全局作用域: 包含内置命名空间和全局命名空间. 在整个文件的任何位置都可以使用(遵循从上到下逐行执行).
  局部作用域: 在函数内部可以使用.

作用域命名空间:
  1. 全局作用域: 全局命名空间 + 内置命名空间
  2. 局部作用域: 局部命名空间


我们可以通过globals()函数来查看全局作用域中的内容, 也可以通过locals()来查看局部作用域中的变量和函数信息

a = 10
def func():
    a = 40
    b = 20
    def abc():
        print("哈哈")
    print(a, b)     # 这里使用的是局部作用域
    print(globals())  # 打印全局作用域中的内容
    print(locals())   # 打印局部作用域中的内容
    
func()

 

 

 

4、函数的嵌套

1. 只要遇见了()就是函数的调用. 如果没有()就不是函数的调用
2. 函数的执行顺序

def fun1():
    print(111)
def fun2():
    print(222)
    fun1()
 
fun2()
print(111)

 

# 函数的嵌套

def fun2():
    print(222)
    def fun3():
        print(666)
    print(444)
    fun3()
    print(888)
    
print(33)
fun2()
print(555)

 

5、函数名

 函数名就是变量,指向函数体的内存地址。

def f1():
    print("f1")

def f2():
    print("f2")

def f3():
    print("f3")

# fn 代理了f1 f2 f3函数的执行
def fn(x):     # 函数名可以像变量一样作为参数进行传递
    x()

fn(f1)
fn(f2)
fn(f3)

 

函数名作为函数返回值。

def func():
    def inner():
        print("111")
        print(inner)
    return inner

ret = func()  # 把 inner 函数 返回给 ret变量
ret()     # 此时 ret 存的就是inner函数体的内存地址
print(ret)

#执行结果:
111
<function func.<locals>.inner at 0x00000000021FF158>  
<function func.<locals>.inner at 0x00000000021FF158>

# 精简一下就是
def func():
    def inner():
        print("111")
    return inner

func()()  # 执行 inner()

 

函数名还可以作为列表的元素,字典的 value

def f1():
    print("f1")

def f2():
    print("f2")

def f3():
    print("f3")

func_list = [f1,f2,f3]

for i in func_list:
    i()
#执行结果:
f1
f2
f3

 

# 作为字典的value

def f1():
    print("f1")

def f2():
    print("f2")

def f3():
    print("f3")

func_list = {
    1: f1,
    2: f2,
    3: f3
}
func_list[1]()
func_list[2]()
func_list[3]()

# 执行结果:
f1
f2
f3

 

 

 

 

 

 

 

6、关键字global和nonlocal

首先我们写这样一个代码, 首先在全局声明一个变量, 然后再局部调用这个变量, 并改变这个变量的值

a = 100
def func():
    global a     # 加了个global表示不再局部创建这个变量了. 而是直接使用全局的a
    a = 28
    print(a)
func()
print(a)

global表示. 不再使用局部作用域中的内容了. 而改变全局作用域中的变量

lst = ["麻花藤", "刘嘉玲", "詹姆斯"]
def func():
    lst.append("马云云")     # 对于可变数据类型可以直接进行访问. 但是不能改地址. 说白了. 不能赋值
    print(lst)

func()
print(lst)

 

nonlocal 表示在局部作用域中, 调用父级命名空间中的变量. (不能调用全局命名空间中的变量)

a = 10
def func1():
    a = 20
    def func2():
        nonlocal a
        a = 30
        print(a)
    func2()
    print(a)

    
func1()

结果:
加了nonlocal
30
30
不加nonlocal
30
20

 

再看, 如果嵌套了很多层, 会是一种什么效果:

a = 1
def fun_1():
    a = 2
    def fun_2():
        nonlocal a
        a = 3
        def fun_3():
            a = 4
            print(a)
        print(a)
        fun_3()
        print(a)
    print(a)
    fun_2()
    print(a)
 
print(a)
fun_1()
print(a)

 

执行结果:

 

 

global
  把全局中的变量引入到局部

a = 2
def wrapper():
        global a
    a += 1
    print(a)
wrapper()

 


nonlocal  
  在局部...把离他最近的外层函数中的局部变量引入到当前.

 

def wrapper():    
    a = 1
    def inner():
        nonlocal a
        a += 1
        print(a)
    inner()
wrapper()

 

posted @ 2019-01-16 16:10  LiShiChao  阅读(202)  评论(0)    收藏  举报