Python文件读写和函数编程

文件读写

在Python中,文件的操作应用非常多,比如大数据领域,涉及许多数据处理请求,基本上都是从一个文件对数据进行分析、抽取、重写进行梳理数据

文件的读写分几步?如何把大象放冰箱
《Python文件读写和函数编程》

下面我们来拆分下:

  1. 打开文件:python中打开文件使用open函数

            open(filename,[access_mode],[buffering]
            filename:要看打开的文件路径
            access_mode:选填,打开文件的模式,r只读,w只写,a读写 等..
            buffering:选填,缓冲大小 0为不寄存 1 寄存行 大于1 为寄存区的缓冲大小
            例:f=open("song.txt","r",encoding="utf-8")
            理解:open函数返回的是一个File对象,有file对象才能进行后续的读写操作
  2. 通过句柄对文件进行操作

            读:fileObject.read([count]) #count为字节数,不传递默认读取整个文件,当文件太大的时候我们不建议直接通过read一次读取完成
            读:fileObject.readline() #读取下一行,需要配合for in 来使用
            读:fileObject.readlines() #读取整个文件到一个迭代器以供我们遍历
            写:fileObject.write("啦啦\n啦啦啦") 
            注意:读写操作需要看下open函数设置的打开模式,是读还是写
            
  3. 关闭文件

            显示关闭:fileObject.close();
            注意:一般情况下一个文件对象在退出程序后会自动关闭,但是为了安全起见,我们通过close方法关闭文件
            觉得太过繁琐?python也这么觉得,所以给我们提供了另外一种方式,自动帮你close(): 
            with open("song.txt","r",encoding="utf-8") as f:    
                    for line in f:
                            print(line)
       
    

函数

函数是组织好的、可重复使用的、用来实现单一或相关联功能的代码段,能够提高应用的模块性和代码的重复利用率

如何定义函数

#sayhi为函数名,小括号里面用来定义函数所需要的参数
def sayhi():
    #函数体
    print("hello world");

sayhi(); #调用函数,如果没有调用函数体不会执行

函数的参数

#-----------第一种:必须参数------------------
#calc参数我们称为形参 (x , y)
def calc(x,y):
    print(x+y);

#1 ,2 我们称为实参,形参和实参必须要一一对应
calc(1,2);

#-----------第二种:默认参数------------------
def calc(x,y=10):
    print(x+y);
calc(2);#如果不传递第二个参数,则默认值为10,如果传递了以你传递的为准

#-----------第三种:关键参数------------------
def calc(x,y):
    print("x:",x);
    print("y:", y);
calc(y=2,x=1);#注意当通过指定参数名传递的时候,名字必须一致不能写错

#-----------第四种:可变参数------------------
def calc(x,y,*arg): #加了*号的变量会存放所有未命名的变量参数,如果没有多余的参数,这种arg是一个空元组
    print("x:",x);
    print("y:", y);
    print("arg:", arg);
calc(1,2,"zhangsan",20) #输出结果为x: 1   y: 2   arg: ('zhangsan', 20)

函数的返回值
函数我们可以理解为一个独立的盒子,那么我们的外界和盒子里面如何进行沟通呢,我们可以通过返回值的方式,把想要的结果从盒子里面返回回来。

def calc(x,y):
    return x+y; #注意:在函数体内出现return会停止后续代码的执行
    print("here") #这一句永远不会被执行到,因为上一句已经return 终止了

sum=calc(1,2);#calc函数有返回值,需要有东西接收返回回来的结果
print(sum)

局部变量和全局变量

  1. 局部变量:在函数内部定义的变量只能在函数内部使用,不能在函数外使用,这个变量的作用域是局部的
  2. 全局变量:在函数外,一段代码最开始赋值的变量可以被多个函数引用。
count=1;#全局变量
def calc(x,y):
   #count=2; #这个count并不会改到全局的count
   #如何修改全局变量count?
   global count
   count=4; #修改到全局的变量
   sum=x+y; #sum为局部变量
   print(sum)
calc(1,2)
print(sum) #无法输出,因为sum为局部变量
print(count)#输出结果为4 

注意:虽然我们在函数内部可以操作全局变量,但是我们不建议这样修改,因为函数的通用性,你不知道什么地方改了全局变量。

递归函数

在一个函数的内部调用自己,这个函数称为递归函数,形象点想想其实我们早都知道递归,比如从前有座山,山里有座庙,庙里有个老和尚在讲故事,在讲什么呢,从前有座山…

我们来用代码看下

def say():
    print("从前有座山,山里有座庙,庙里有个老和尚在讲故事,在讲什么呢")
    return say()
say()
#通过递归计算1-100的和
def Calc(i):
    if i==1:
        return 1
    else:    
        return i+Calc(i-1)
print(Calc(100))  
#通过递归进行字符串反转
str = input('请输入若干字符:')
def f(x):
    if x == -1:
        return ''
    else:
        return str[x] + f(x-1)
print(f(len(str)-1))  

常用算法

#顺序查找
def search(arr,n):
    for s in arr:
        if s==n:
            print("找到")

search([1,4,7,8,10,39,2],7)

#二分查找
arr=[1,4,7,9,10,20,50]

def FindNumber(low,high,n):
    mid = (low + high)// 2
    if arr[mid] == n:
        return mid
    if n > arr[mid]: #你要找的值大于中间数的时候
        return FindNumber(mid+1,high,n)
    if n < arr[mid]: #你要找的值小于中间数的时候
        return FindNumber(low,mid-1,n)
    
print(FindNumber(0,len(arr),7))
#优化后的版本
# arr=[1,3,6,9,10,20,30]
# def binary_search(data_source,find_n):
#     #取中位数
#     mid=int(len(data_source)/2)
#     if len(data_source)>=1:
#         if data_source[mid]>find_n:  #中位数大于要查找的数,则要查找的数在左半部分,继续调用二分算法进行查找
#             binary_search(data_source[:mid],find_n)
#         elif data_source[mid]<find_n:  #中位数小于要查找的数,则要查找的数在右半部分
#             binary_search(data_source[mid:],find_n)
#         else:   #中位数等于要查找的数
#             print("找到了:",data_source[mid])

#     else:
#         print("没有找到")
# binary_search(arr,10)
# 二分查找的优化(二)
# arr=[1,3,6,9,10,20,30]
# def findnumber(l,h,number):
#     mid=(l+h)//2
#     if arr[mid]==number:
#         print("找到了"+str(mid))
#     elif arr[mid]<number:
#         l=mid
#         return findnumber(mid+1,h,number)   
#     elif arr[mid]>number:
#         h=mid
#         return findnumber(l,mid-1,number) 
#     else:
#         print("没有找到")

# findnumber(0,len(arr)-1,30)        

#冒泡排序
arr=[1,4,2,10,33,99,20,0,100]
#外层循环
for i in range(0,len(arr)-1):
    for j in range(0,len(arr)-1-i):
        if arr[j]>arr[j+1]:
            n=arr[j]
            arr[j]=arr[j+1]
            arr[j+1]=n
          
print(arr)

匿名函数

匿名函数其实很好理解,匿名的话就是没有名字的函数,定义的时候也不用def

Python使用lambda创建匿名函数,lambda只是一个表达式,语法也很简单

#正常函数
def calc(x,y):
    return x+y;
#使用lambda表达式
n=lambda x,y:x+y    #x,y为所需要的参数,:为分割符 x+y 则是返回值
print(n(2,3))

通过上面的代码我们发现使用lambda表达式定义匿名函数编写的代码比使用def要少
那么什么情况下使用匿名函数:

  1. 程序一次使用
  2. 用匿名函数可以节省内存中变量定义的空间
  3. 让代码更加简洁

函数嵌套

嵌套我们都知道,就是在一个函数里面再通过def定义一个函数

#函数嵌套
def outer():
  def inner():
    print('inner')
  print('outer')
  inner()
outer()
inner() #这句会报错,因为inner的作用域只在outer内
#函数调用
def inner():
  print('inner')
def outer():
  print('outer')
    inner();

outer()

函数嵌套这块有个注意事项就是作用域的问题,第二个就是只有通过def在内部定义才是嵌套,直接写函数名是函数调用

高阶函数

一个函数接收另一个函数作为参数,这种函数就称之为高阶函数

def sayhi():
    print("hello")
def outer(func):
    func();#执行传递进来的函数
outer(sayhi)  #运行结果输出hello      

闭包

我们先不急着定义什么是闭包,先来看看一段代码

def outer():
     x = 1
     def inner():
         print(x) #输出1
     return inner
foo = outer()

我们来解读下,x是函数outer里的一个局部变量。当函数inner在#1处打印x的时候,python解释器会在inner内部查找相应的变量,当然会找不到,所以接着会到封闭作用域里面查找,并且会找到匹配

但是从变量的生存周期来看,该怎么理解呢?我们的变量x是函数outer的一个本地变量,这意味着只有当函数outer正在运行的时候才会存在。根据我们已知的python运行模式,我们没法在函数outer返回之后继续调用函数inner,在函数inner被调用的时候,变量x早已不复存在,可能会发生一个运行时错误。

万万没想到,返回的函数inner居然能够正常工作。Python支持一个叫做函数闭包的特性,用人话来讲就是,嵌套定义在非全局作用域里面的函数能够记住它在被定义的时候它所处的封闭命名空间。这能够通过查看函数的func_closure属性得出结论,这个属性里面包含封闭作用域里面的值(只会包含被捕捉到的值,比如x,如果在outer里面还定义了其他的值,封闭作用域里面是不会有的)

记住,每次函数outer被调用的时候,函数inner都会被重新定义。现在变量x的值不会变化,所以每次返回的函数inner会是同样的逻辑,假如我们稍微改动一下呢?

def outer(x):
     def inner():
         print x # 1
     return inner
print1 = outer(1)
print2 = outer(2)
print1()
1
print2()
2

装饰器

装饰器本质上来说也是函数(装饰其他函数)就是为其他函数添加附加功能。装饰器其实就是一个闭包,把一个函数当做参数然后返回一个替代版函数。

使用装饰器有两个原则:

  1. 不能修改被装饰的函数的源代码
  2. 不能修改被装饰的函数的调用方式

根据上面我们高阶函数和嵌套函数的理解,我们先试着写一个装饰器出来

#定义一个函数
def test1():
    print("in the test 1 ");
#现在需求给test1添加一些扩展功能,同时满足装饰器的两个使用原则
def main(func): #传进来的test1=func
    def deco():
        print("我是新加的功能");
        func();  
        print("我也是");  
    return deco; #deco为main函数的嵌套函数    
#怎么使用,注意test1不能加小括号
test1=main(test1)   
test1() 

上面代码我们通过高阶函数+嵌套函数完成了装饰器,有没有简洁一点的写法,python给我们提供了一个语法糖

@main  #这个地方的main就是你装饰的新功能,等同上面代码里面的  test1=main(test1)  
def test1():
    print("in the test 1 ");
test1()    

但是如果我的函数需要参数该如何使用呢,在deco加上*args 非固定参数即可。

user,pwd='admin','123'
#login为一个装饰器
def login(func):
    def wrapper(*args):
        username=input("请输入账号:").strip()
        password=input("请输入密码:").strip()
        if(user==username and pwd==password):
            print("登录成功")
            func(*args)
        else:
            print("验证失败")  
    return wrapper;
@login    
def home():
    print("in the home")
    
home() #模拟首页访问                  

最后补充一个知识点,如果装饰器带参数该如何使用呢?@login(参数)即可

生成器

只有在调用的时候才会生成相应的数据

#生成器
generator1 = ( i*i for i in range(10) )
for i in generator1:
    print(i)
#生成器函数:斐波那契数列
def fib(max):
    n, a, b = 0, 0, 1
    while n < max:
        yield b #因为使用了yield,它便不能再称之为函数,而应该称之为生成器
        a, b = b, a + b
        n += 1
    return 'done'
fib_generator = fib(20)
print(fib_generator)   

内存中如何存储

我们通过一幅图来理解下python中的变量、函数等对应内存中的存储
《Python文件读写和函数编程》

我们可以看到,当我们定义一个变量的时候,会在内存中开辟一块空间,存放我们的值,可以把这个内存空间理解为一个大厦中的小房间,那么每个房间都有一个门牌号,就是我们上图中的name

我们要如何找到这个变量呢,通过name(门牌号),函数也是一样,把函数体存放在一个空间内,匿名函数不同,没有门牌号,所以匿名函数运行完会被系统回收,那么其他的name和函数在没有引用的时候也会被回收(也就是门牌号被摘掉了的时候,没人引用了,就会被系统回收)

    原文作者:离岛
    原文地址: https://segmentfault.com/a/1190000012393665
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞