python学习-函数

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介: 目录python学习-函数一、文件处理1、读取文件2、写入文件3、文件操作函数4、注意事项二、函数基础1、函数体2、输入3、输出4、局部变量/变量作用域5、匿名函数6、函数调用7、命名空间LEGB8、函数参数解包9、递归三、内置函数四、函数进阶1、闭包2、装饰器函数3、生成器函数五、几个技术问题1、解包2、值交换3、默认参数陷阱4、生成器深入理解python学习-函数标签: python 函数目录一、文件处理python的内置函数open提供了对文件的处理功能。

目录

python学习-函数

标签: python 函数


目录

一、文件处理

python的内置函数open提供了对文件的处理功能。open函数会调用os的系统接口,得到一个类文件对象f,此f对象将作为对文件操作的标识符。从行为动作划分,文件处理分为读取和写入。从操作的数据类型划分,文件处理分为操作字符串和操作二进制数据。

1、读取文件

with open(file_path, 'r', encoding='utf-8') as f:
    data = f.read()

注意:
: 1、使用with来妥善处理文件的关闭问题。
2、给定的file_path是文件的路径,python默认当前目录(相对路径),也可以给出绝对路径,不过在windows下的路径要注意 \ 的问题。
3、'r'表示了读取模式,如果使用'rb'则意味着使用读取二进制模式,两者的区别在于,'r'模式会使用encoding参数指定的字符编码格式对文件进行解码,并转换成unicode到内存中作为str类型存在,而'rb'则不会解码,而是直接将硬盘上的二进制数据读取到内存中作为bytes类型存在。
4、使用'r'更适用于需要对文件内容执行显式的字符串操作,一般有需要展示内容的需求。使用'rb'更适用于对数据进行非显式操作,如直接传输、压缩等等,不需要展示内容给用户。
5、readreadlinereadlines分别对应:读取整个文件作为一个字符串、读取一行作为一个字符串、读取所有行并返回一个行列表。

2、写入文件

with open(file_path, 'w', encoding='utf-8') as f:
    f.write(msg)

注意:
: 1、使用'w'模式将会首先寻找此文件是否存在,如果不存在则新建,如果存在,则会清空原有文件的所有内容。
2、使用write函数对文件执行写入操作,msg应该是str类型。

3、文件操作函数

file_path = 'text.py'

with open(file_path, 'w+', encoding='utf-8') as f:
    print(f.fileno())  # 获取文件对象在内核中的索引值

    f.write('hello, world')  # 写入数据
    f.flush()  # 在不关闭文件的情况下,强制将缓存中的内容刷入硬盘
    f.seek(0)  # 移动seek到开头
    print('这里有数据', f.read())  # 读取所有内容

    print(f.readable())  # 是否可读
    print(f.writable())  # 是否可写
    print(f.seekable())  # 是否可移动seek

    f.seek(0)
    f.truncate()  # 从seek处开始截取到文件末尾
    print('这里没数据', f.read())  # 读取内容

    print(f.tell())  # 获取当前seek位置

4、注意事项

  • [x] 使用+来扩展原有的文件处理模式,如w+r+
  • [x] 文件的操作要特别注意当前的seek位置,seek位置会随着读取和写入而后移。
  • [x] 文件对象f可以被迭代,即:for line in f,可以每次获取一行,类似readline
  • [x] 可以使用charsetdetect函数对二进制数据的编码格式进行推测。
  • [x] 使用aab模式来对文件追加内容,使用此模式时seek将会自动移到文件末尾。
  • [x] 使用seek(0)函数配合truncate()函数完成文件清空操作。
  • [x] os模块提供了很多函数用于处理文件和目录。
  • [x] 所有直接对硬盘数据的修改都会覆盖当前内容,无法直接移动位置,必须通过读取到内容、修改内容、写入硬盘的方式。

二、函数基础

函数在python中是第一类对象,即函数可以像变量一样被赋值。函数名是函数对象的引用,函数对象和普通变量没有太大的区别,普通对象比如数字对象可以四则运算,集合类型对象可以迭代,函数对象则可以执行。函数对象保存了执行代码和执行时的上下文环境,函数每次被执行的时候都要在内存中开辟一个新的函数栈用于保存函数的执行上下文,然后执行函数对象中的代码,一旦函数执行完毕就会通过return返回函数执行的结果,并销毁此函数栈,从而结束一个函数的运行。

1、函数体

函数体用于定义函数执行时的代码,函数体中的代码会被函数对象所保存并在函数被调用的时候被执行。函数被()所调用。

2、输入

函数可以被认为是一个小型程序或者子例程,是一个计算机的微型模型,可以接受调用者提供的输入。调用者的输入不同,函数执行的过程和结果也不同,所以虽然函数体中的代码已经预先定义好了,但是调用者可以通过不同的输入来实现不同的执行效果。而函数的形参就是用于接收可变化的输入值的变量。

3、输出

函数一定有输出,即使没有显式的声明return语句,也会在执行代码的最后一行添加return None。函数的输出用于表示此函数的执行结果,不论此函数是否需要输出结果给调用者,输出都被要求必须提供,哪怕提供的是None。一般调用者通过函数的输出来判断执行的结果,并可以通过输出来对外部函数的执行后续流程做一定的决策。

4、局部变量/变量作用域

因为函数的执行上下文存在于函数栈,而函数执行完毕后将会销毁此函数栈,故在函数运行过程中定义的所有变量都被称为局部变量,因为这些变量都存活在此函数栈中。可以在函数运行过程中通过locals()函数来获取当前函数的函数栈中的局部变量命名空间内容。正因为变量仅存活在函数栈中,故这些变量的作用域也就只在函数执行过程中生效。一旦函数执行完毕,将无法访问函数栈中的变量地址,当然,除非你显式的return这些变量地址

5、匿名函数

所谓匿名函数,即是一个没有函数名的函数,如下两个声明是完全等价的:

a = 2  # 声明对象2,名字是a
2  # 声明无名对象2
#===============
def f():  # 声明有名对象函数, 名字是f
    print('hello')

lambda x:print('hello')  # 声明无名对象函数  --> 匿名函数

匿名函数一般配合mapfilterreduce等高阶函数使用,此时匿名函数作为其他函数的输入。

6、函数调用

每一个函数的执行都依赖于对应的函数栈,即使是python程序没有定义任何函数,此程序之所以会运行,也是依赖于顶层函数栈提供了全局命名空间。在函数发生调用的时候,cpu代码执行视角将会从当前函数跳转到另一个函数所在的内存地址,一旦被调函数执行完毕,cpu代码执行视角将会回到主调函数的下一条代码地址处,主调函数获得被调函数的返回值。

7、命名空间LEGB

local 当前函数所在的命名空间
enclosing 嵌套函数所在的命名空间,从儿子到父亲到爷爷的寻找链
global 全局命名空间,即模块的命名空间
builtin 内置命名空间,即模块的上一级,定义了所有的内置函数和变量
检测顺序是:L --> E --> G --> B --> 报错

8、函数参数解包

函数中的*args和**kw提供了解包,args代表元组,kw代表字典,可以这样使用:

def show(a, b):
    print(a, b)

x = (1,2)
show(*x)

#=========

def show(a=1, b=2):
    print(a, b)

x = {'a': 10, 'b': 20}
show(**x)

9、递归

所谓的递归,就是函数调用过程中调用自己,这样会产生和无限循环类似的无限调用自己。函数每一次调用都需要创建一个函数栈,而因为主调函数并没有执行完毕,所以主调函数的函数栈不会销毁,而是会等待被调函数返回结果。无限递归,就会无限产生新的被调函数栈,保留主调函数栈,而导致函数栈溢出python函数栈设置为最大1000层。

使用递归一定需要设置递归停止条件,就像使用循环一定要设置循环退出条件一样。

使用递归可以方便的解决某些需要不断调用自身算法的问题,但是递归效率本身并不高,因为需要不停的 创建新的函数栈和销毁函数栈。

三、内置函数

print(dir(list))  # 查看对象的所有属性名称列表
print(sorted([233,34,54,56,56,67,67]))  # 对序列进行排序,返回新列表对象

print(eval('3 + 5'))  # 执行字符串代码,得到返回值,无法执行赋值等更改程序内容的语句
exec('a = 2')  # 执行多行字符串,返回值永远是None,但是可以执行赋值等更改程序内容的语句
print(a)

filter(lambda x: x%2==0, [34,34,54,54,56,6,2,2,3,34])  # 执行序列过滤
map(lambda x: str(x), [2334,34,4,5])  # 执行序列各元素统一处理

from functools import reduce
reduce(lambda x, y: x+y, [23,34,34,45,5,56])  # 执行序列回归累计操作

print('msg is here', 'another msg', sep='!', end='\n!!!!\n')  # print函数的参数使用

f = lambda x: 2
callable(f)  # 对象是否可调用判断

print(list(zip([23,23,32,43], [4,34,43,34])))  # 执行多个序列的打包操作,会截断序列以最短的为准,多个序列元素打包成元组

四、函数进阶

1、闭包

函数一旦定义完毕,就会将执行代码和上下文环境保存到函数对象中。上下文环境除了保留当前层次的环境之外,还会保留外层嵌套的上下文环境。一旦此函数被作为变量返回给外部函数,外部函数可以随时调用,被调用时,内层函数即可访问之前保留的嵌套函数中的上下文。此即:闭包,即,将执行代码和执行环境一起包裹,一旦被执行,就可以使用嵌套的上下文环境。闭包可以用于保留嵌套函数的变量状态。

def outer():
    a = 20
    def inner():
        print(a)

    return inner

f = outer()
f()  # 执行inner的时候,可以访问到a = 20

2、装饰器函数

正因为闭包的性质,可以对函数进行功能扩展,在不修改原有函数代码的前提下,增加额外的功能。装饰器也是一种高阶函数,将原函数作为输入,对原函数进行功能扩展并返回与原函数同名的新函数。新函数一旦被执行,将会把原函数功能和扩展功能一起执行。

func_map = {}

def register(key):
    def wrapper_outer(func):

        def wrapper_inner(*args, **kw):
            result = func(*args, **kw)
            print('扩展功能')
            return result

        func_map[key] = wrapper_inner

        return wrapper_outer
    return wrapper_outer

@register('show')
def show(msg):
    print('this is show msg:', msg)

msg = 'hello, world'
func_map['show'](msg)

3、生成器函数

生成器函数可以被设计成永远不会销毁函数栈,但是却可以和主调函数通过yield进行函数栈的切换。生成器函数最大的好处有如下几个:

1.延迟计算,生成器需要手工执行next或者send才会执行生成器函数代码

2.可以和主函数进行函数栈切换,效率高,串行无锁,无变量安全问题

3.生成器函数可以通过yield返回值,主调函数通过send发送值,两者可以交互

4.可以用于实现协程

log_path = 'xxx.log'

def logger_generator(log_path):
    log_count = 0
    with open(log_path, 'w', encoding='utf-8') as f:  # 准备log文件,预先清空内容
        pass
    msg = yield 'ok'

    while True:  # 永远执行
        if msg == 'stop':
            return 'stop'  # 引发stopiteration,主调函数要try
        else:
            log_count += 1
            msg = '# [{log_count}] --> {msg}\n'.format(log_count=log_count, msg=msg)  # 准备日志信息

            with open(log_path, 'a', encoding='utf-8') as f:  # 记录日志到文件
                f.write(msg)

            log_summary = '%d informations has been logged!' %log_count  # 准备日志汇总信息
            msg = yield log_summary  # 返回截止目前的日志汇总信息,同时等待主调发送新的日志消息

logger = logger_generator(log_path)
logger.send(None)

print(logger.send('用户登录'))
print(logger.send('用户密码修改'))

五、几个技术问题

1、解包

li = [1,2,3,4,5]
first, *mid, last = li
print(first, mid, last)

first, second, *_ = li
print(first, second, _)

a, b, *c = range(5)
print(a, b, c)

dic1 = {'a':1}
dic2 = {'b':2}
dic3 = {**dic1, **dic2}
print(dic3)

dic = {
    'name': 'hz',
    'age': 26,
}
msg = 'name is: {name}, age is: {age}'.format(**dic)
print(msg)

1.使用解包可以让代码更简洁

2.***分别用于解包序列和字典

3.使用*_来承载不需要的值,_变量也可以被使用

2、值交换

如下两者是等价的:

a = 2
b = 33

temp = a
a = b
b = temp
print(a, b)

#=========

a = 3
b = 44
a, b = b, a
print(a, b)

python的值交换会自动帮你处理temp临时变量

3、默认参数陷阱

a = 20

def show(x=a):
    print(x)

a = 30
show()
show(a)

print('#=========')

def show(x=[]):
    x.append(99)
    print(x)

show()
show()
show()

结果是:

20
30
#=========
[99]
[99, 99]
[99, 99, 99]

1.默认参数在函数被编译的时候就固定了引用对象

2.如果不显式的提供show(a)就会打印原来的固定对象20

3.如果默认参数指向可变对象,因为可变对象可以修改值,会导致非期望的结果如[99,99,99]

4.默认参数一定要指向不可变对象,而且调用函数的时候尽量显式的提供值

4、生成器深入理解

生成器的好处:

  • [x] 保存计算规则、算法、节省内存空间,随时计算。
  • [x] 高效的函数栈切换,生成器函数还可以嵌套多层,多个yield提供更灵活的切换控制
  • [x] 保存生成器函数中的上下文环境不会被销毁,只要切换进去就可恢复使用这些变量值
  • [x] 可以和主调函数或者其他子层生成器函数相互协作完成程序执行
  • [x] 使用sendnextyield来提供消息、信号交互
  • [x] 可以实现协程,高效串行执行,无变量安全问题,不需加锁
相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
相关文章
|
2月前
|
机器学习/深度学习 Python
堆叠集成策略的原理、实现方法及Python应用。堆叠通过多层模型组合,先用不同基础模型生成预测,再用元学习器整合这些预测,提升模型性能
本文深入探讨了堆叠集成策略的原理、实现方法及Python应用。堆叠通过多层模型组合,先用不同基础模型生成预测,再用元学习器整合这些预测,提升模型性能。文章详细介绍了堆叠的实现步骤,包括数据准备、基础模型训练、新训练集构建及元学习器训练,并讨论了其优缺点。
106 3
|
1月前
|
Python 容器
Python学习的自我理解和想法(9)
这是我在B站跟随千锋教育学习Python的第9天,主要学习了赋值、浅拷贝和深拷贝的概念及其底层逻辑。由于开学时间紧张,内容较为简略,但希望能帮助理解这些重要概念。赋值是创建引用,浅拷贝创建新容器但元素仍引用原对象,深拷贝则创建完全独立的新对象。希望对大家有所帮助,欢迎讨论。
|
2月前
|
搜索推荐 Python
利用Python内置函数实现的冒泡排序算法
在上述代码中,`bubble_sort` 函数接受一个列表 `arr` 作为输入。通过两层循环,外层循环控制排序的轮数,内层循环用于比较相邻的元素并进行交换。如果前一个元素大于后一个元素,就将它们交换位置。
148 67
|
26天前
|
Python
Python学习的自我理解和想法(10)
这是我在千锋教育B站课程学习Python的第10天笔记,主要学习了函数的相关知识。内容包括函数的定义、组成、命名、参数分类(必须参数、关键字参数、默认参数、不定长参数)及调用注意事项。由于开学时间有限,记录较为简略,望谅解。通过学习,我理解了函数可以封装常用功能,简化代码并便于维护。若有不当之处,欢迎指正。
|
17天前
|
数据可视化 数据挖掘 大数据
1.1 学习Python操作Excel的必要性
学习Python操作Excel在当今数据驱动的商业环境中至关重要。Python能处理大规模数据集,突破Excel行数限制;提供丰富的库实现复杂数据分析和自动化任务,显著提高效率。掌握这项技能不仅能提升个人能力,还能为企业带来价值,减少人为错误,提高决策效率。推荐从基础语法、Excel操作库开始学习,逐步进阶到数据可视化和自动化报表系统。通过实际项目巩固知识,关注新技术,为职业发展奠定坚实基础。
|
15天前
|
Python
[oeasy]python057_如何删除print函数_dunder_builtins_系统内建模块
本文介绍了如何删除Python中的`print`函数,并探讨了系统内建模块`__builtins__`的作用。主要内容包括: 1. **回忆上次内容**:上次提到使用下划线避免命名冲突。 2. **双下划线变量**:解释了双下划线(如`__name__`、`__doc__`、`__builtins__`)是系统定义的标识符,具有特殊含义。
26 3
|
18天前
|
JSON 监控 安全
深入理解 Python 的 eval() 函数与空全局字典 {}
`eval()` 函数在 Python 中能将字符串解析为代码并执行,但伴随安全风险,尤其在处理不受信任的输入时。传递空全局字典 {} 可限制其访问内置对象,但仍存隐患。建议通过限制函数和变量、使用沙箱环境、避免复杂表达式、验证输入等提高安全性。更推荐使用 `ast.literal_eval()`、自定义解析器或 JSON 解析等替代方案,以确保代码安全性和可靠性。
28 2
|
3天前
|
存储 人工智能 Python
[oeasy]python061_如何接收输入_input函数_字符串_str_容器_ 输入输出
本文介绍了Python中如何使用`input()`函数接收用户输入。`input()`函数可以从标准输入流获取字符串,并将其赋值给变量。通过键盘输入的值可以实时赋予变量,实现动态输入。为了更好地理解其用法,文中通过实例演示了如何接收用户输入并存储在变量中,还介绍了`input()`函数的参数`prompt`,用于提供输入提示信息。最后总结了`input()`函数的核心功能及其应用场景。更多内容可参考蓝桥、GitHub和Gitee上的相关教程。
8 0
|
1月前
|
存储 索引 Python
Python学习的自我理解和想法(6)
这是我在B站千锋教育学习Python的第6天笔记,主要学习了字典的使用方法,包括字典的基本概念、访问、修改、添加、删除元素,以及获取字典信息、遍历字典和合并字典等内容。开学后时间有限,内容较为简略,敬请谅解。
|
1月前
|
存储 程序员 Python
Python学习的自我理解和想法(2)
今日学习Python第二天,重点掌握字符串操作。内容涵盖字符串介绍、切片、长度统计、子串计数、大小写转换及查找位置等。通过B站黑马程序员课程跟随老师实践,非原创代码,旨在巩固基础知识与技能。