有效的函数

简介: 有效的函数

有效的函数
Python的函数是第一等级
在Python中,函数是被视为“第一等级”对象的。你可以将它们赋值给变量,存储在数据结构中,作为参数传递给其他函数,并且甚至可以将它们作为其他函数返回值来使用。Python中的函数具有很高的灵活性和可操作性,使得它们在编写代码和实现功能时扮演着至关重要的角色。

深入理解这些概念的直觉方式将使你更容易掌握Python中高级特性,如lambda表达式和装饰器。这也将引导你走向函数式编程技术的道路。接下来,我将为你提供一系列示例,帮助你逐步培养这种直觉理解。这些示例会逐个建立在前一个的基础上,因此建议你按照阅读顺序阅读它们,并且在过程中尝试在Python解释器中运行一些示例来加深理解。

理解和掌握我们将讨论的概念可能需要比你预期的更多时间。别担心,这完全正常。我自己也经历过类似的情况。你可能会觉得自己像是撞到了墙,然后突然间一切就绪了,当你准备好时, 我会使用这个“吼叫”函数来演示目标。这是一个简单的玩具示例,具有易于识别的输出:

>>> def yell(text):
...   return text.upper() + '!'

>>> yell('hello')
'HELLO!'

函数是对象
在Python程序中,所有数据都由对象或对象之间的关系表示。对象如字符串、列表、模块和函数等都是对象的实例。 Python中的函数虽有特殊性,但从本质上说它们也是对象。

由于"吼叫"函数是Python中的一个对象,因此你可以像操作其他对象一样将它赋值给另一个变量:

>>> bark = yell

这条代码并没有调用函数。它实际上是获取了被引用为"吼叫"的函数对象,并创建了一个新的名称"bark",这个"bark"实际上是指向原始的函数对象。所以你现在也可以通过调用"bark"来执行与原函数相同底层功能的对象:

>>> bark('woof')
'WOOF!'

函数对象和它们的名字确实是两个独立的概念。这里有一个更明确的例子来证明这一点:正如例子所示,即使删除了原始函数名"yell",因为另一个名字"bark"仍然可以调用这个函数。

>>> del yell
>>> yell('hello?')
NameError: name 'yell' is not defined
>>> bark('hey')
'HEY!'

顺便说一下,Python在每个函数创建时都会附加一个字符串标识符,这是出于调试目的。你可以通过name属性来访问这个内部标识:

>>> bark.__name__
'yell'

现在,尽管函数的name仍然是’yell’,但这并不影响你从代码中访问该函数对象的方式。名字标识符只是一个调试辅助工具。一个指向函数的变量和函数本身实际上是两个独立的关注点。

函数可以存储在数据结构中
既然函数是第一类公民,你可以像存储其他对象一样将它们存储在数据结构中。举个例子,你可以在一个列表中添加函数:

>>> funcs = [bark, str.lower, str.capitalize]
>>> funcs
[<function yell at 0x1174676a0>, <method 'lower' of 'str' objects>, <method 'capitalize' of 'str' objects>]

在列表内部存储的函数对象,其访问方式与其他类型的对象并无区别。

>>> for f in funcs:
...    print(f, f('hey there'))

<function yell at 0x1174676a0> HEY THERE!
<method 'lower' of 'str' objects> hey there
<method 'capitalize' of 'str' objects> Hey there

你甚至可以在不先将其赋值给变量的情况下,直接调用存储在列表中的函数对象。你可以进行查找操作,然后立即在一个表达式中调用得到的“独立”函数对象

>>> funcs[0]('heyho')
'HEYHO!'

函数可以作为参数传递给其他函数
因为函数也是对象,所以你可以将它们作为参数传递给其他函数。这里有一个greet函数,它使用传递给它的函数对象来格式化问候字符串,并打印输出。

def greet(func):
  greeting = func('Hi, I am a Python program.')
  print(greeting)

你可以通过传递不同的函数来影响最终问候的内容。例如,如果你将bark函数传递给greet函数,会发生什么?

>>> greet(bark)
HI, I AM A PYTHON PROGRAM.!

当然,你也可以定义一个新的函数来生成不同风格的问候。例如,以下的whisper函数可能更适合于不希望Python程序听起来像“Optimus Prime”的场景。

def whisper(text):
    return text.lower() + '...'
>>> greet(whisper)
hi, i am a python program....

将函数对象作为参数传递给其他函数的能力非常强大。这允许你抽象行为,并在你的程序中传递这些行为。例如,在这个例子中,greet函数保持不变,但你可以通过传递不同的问候行为来影响其输出。这种灵活性和可扩展性使得代码更加模块化和易于维护。

能够接受其他函数作为参数的函数也被称为高阶函数。在函数式编程(Functional Programming, FP)风格中,高阶函数是必不可少的一部分。这种函数不仅执行基本操作,还允许你通过传递其他函数来实现更复杂的行为。这种灵活性使得函数式编程可以更好地处理数据和逻辑,并且代码更加模块化和易于维护。

在Python中,高阶函数的经典例子是内置的map函数。它接受一个函数对象和一个可迭代对象(如列表、元组或集合),然后对可迭代对象中的每个元素应用这个函数,并将结果逐个返回。这种模式使得你可以将复杂的数据处理逻辑抽象为一个函数,然后通过map这样的高阶函数来遍历并处理整个数据集。

以下是如何通过映射bark函数到它们的方式,一次性格式化一系列的问候语:

>>> list(map(bark, ['hello', 'hey' 'hi']))
['HELLO!', 'HEYHI!']

正如你所看到的,map函数遍历了整个列表,并对每个元素应用了bark函数。结果,我们得到了一个新的列表对象,其中包含的是经过修改的问候语字符串。

函数可以嵌套

出人意料的是,Python确实允许在一个函数内部定义另一个函数。这些经常被称为嵌套函数或内层函数。如下例子:

def speak(text):
    def whisper(t):
        return t.lower() + '...'
    return whisper(text)
>>> speak('Hello, World')
'hello, world...'

这里发生了什么呢?每次你调用 speak,它都会定义一个新的内部函数 whisper,然后立即在其后调用这个函数。我感觉我的大脑开始有点小痒了,但是总体来说,这还算是相对简单的事情。

不过,关键在于—whisper 这个函数不存在于 speak 这个外部作用域中,它并不能在其他地方被访问或定义。

>>> whisper('Yo')
NameError: name 'whisper' is not defined

>>> speak.whisper
AttributeError: 'function' object has no attribute 'whisper'

但是,如果你真的想从speak外部访问到那个嵌套的whisper函数呢?别担心,函数本质上就是对象——你可以将内部函数作为对象返回给调用你父级函数(即定义两个内层函数的那个函数)的客户端。

举个例子,这里有一个函数,它定义了两个内部函数。根据传给最顶层函数(也就是这个包含两个内层函数的函数)的参数,它会选择并返回其中一个内部函数给调用者。

def get_speak_func(volume):
    def whisper(text):
        return text.lower() + '...'
    def yell(text):
        return text.upper() + '!'
    if volume > 0.5:
        return yell
    else:
        return whisper

注意这里get_speak_func并没有实际调用它的任何内层函数。它只是根据传入的音量参数,选择合适的内层函数,并返回该函数对象。这是通过设计和编程实现的,目的是在需要时,从外部访问到特定功能的内部实现。

>>> get_speak_func(0.3)
<function get_speak_func.<locals>.whisper at 0x11779e7a0>
>>> get_speak_func(0.7)
<function get_speak_func.<locals>.yell at 0x11779e8e0>

当然可以,然后你可以调用返回的函数,这可能是直接调用,或者先通过赋值给一个变量来操作。例如,你可能会这样做:

>>> speak_func = get_speak_func(0.7)
>>> speak_func('Hello')
'HELLO!'

让我花点时间消化一下这个信息……这意味着函数不仅可以通过参数接受行为,而且还能返回行为。这多么酷啊!你知道吗?我现在有点思维混乱了,我需要一个小憩,喝杯咖啡再继续写作(我建议你这样做)。

函数能够捕捉局部状态
你刚刚见识了函数如何包含内部函数,而且甚至有可能从父函数中返回这些隐藏的内部函数。这就像系上安全带,因为接下来我们将进入更深的函数式编程领域。(咖啡休息时间应该结束了,对吧?)

这意味着函数不仅可以返回其他函数,这些内部函数甚至能够在捕获和携带部分父函数状态的同时运行。简单来说,假设我们有一个get_speak_func函数,它能生成一个可以发声的函数。新版本的这个函数在接收volume(音量)和text(要读的文字)这两个参数时,就让返回的函数立即可用。这样做的好处在于提高了代码的灵活性,并且能够更好地管理和控制父函数的状态。

def get_speak_func(text, volume):
    def whisper():
        return text.lower() + '...'
    def yell():
        return text.upper() + '!'
    if volume > 0.5:
        return yell
    else:
        return whisper
>>> get_speak_func('Hello, World', 0.7)()
'HELLO, WORLD!'

仔细观察内部函数whisper和yell,你会发现它们不再需要text参数。然而,它们似乎能访问父函数中定义的text参数,并且能够捕获并‘记住’这个参数的值。

这样的函数被称为词法闭包(或简称为闭包), 它们的特点是即使程序执行流程离开了原来的词法作用域,闭包依然能够保留和使用其中存储的值。

从实际操作的角度来说,这意味着函数不仅可以返回行为,还可以预先配置这些行为。下面是一个简单示例来说明这个概念:

def make_adder(n):
    def add(x):
        return x + n
    return add
>>> plus_3 = make_adder(3)
>>> plus_5 = make_adder(5)
>>> plus_3(4)
7
>>> plus_5(4)
9

在这个例子中,make_adder充当了一个工厂,用来创建和配置名为add的函数。请注意,这些add函数能够访问make_adder工厂方法中定义的n参数(即它们所在作用域的父级)。

对象可以行为类似于函数
尽管在Python中所有函数都是对象,但反过来并不成立。对象并不是函数,它们可以被赋予可调性(Callable),这使得在许多情况下我们能将它们当作函数来使用。

如果一个对象是可调的,这意味着你可以使用圆括号函数语法调用它,并且甚至可以传递函数调用参数。这一切都依赖于call方法,它是底层实现的关键。

这里有一个类定义的可调对象的例子:

class Adder:
    def __init__(self, n):
        self.n = n
    def __call__(self, x):
        return self.n + x
>>> plus_3 = Adder(3)
>>> plus_3(4)
7

在幕后,当我们把一个对象实例当作函数来调用时,实际上是试图执行这个对象的call方法。
当然,并不是所有对象都能够被调用。这就是为什么Python提供了一个内置的可调用函数,用于检查一个对象是否看起来像是可以被调用的对象:

>>> callable(plus_3)
True
>>> callable(yell)
True
>>> callable('hello')
False
相关文章
|
机器学习/深度学习 存储 数据挖掘
Python图像处理实用指南:PIL库的多样化应用
本文介绍Python中PIL库在图像处理中的多样化应用,涵盖裁剪、调整大小、旋转、模糊、锐化、亮度和对比度调整、翻转、压缩及添加滤镜等操作。通过具体代码示例,展示如何轻松实现这些功能,帮助读者掌握高效图像处理技术,适用于图片美化、数据分析及机器学习等领域。
551 20
|
数据处理 索引 Python
用Python实现数据录入、追加、数据校验并生成表格
本示例展示了如何使用Python和Pandas库实现学生期末考试成绩的数据录入、追加和校验,并生成Excel表格。首先通过`pip install pandas openpyxl`安装所需库,然后定义列名、检查并读取现有数据、用户输入数据、数据校验及保存至Excel文件。程序支持成绩范围验证,确保数据准确性。
606 14
|
前端开发 计算机视觉 Python
浅蓝色代表什么颜色?——Python中的颜色表示与处理
本文介绍了浅蓝色在计算机图形和Web开发中的表示方法,包括RGB、十六进制和HSL三种常见格式,并详细说明了如何使用Python的Pillow和colorsys库来处理和转换这种颜色,最后给出了生成浅蓝色背景的CSS代码示例。
1283 6
|
搜索推荐 UED Python
实现一个带有昼夜背景切换的动态时钟:从代码到功能解析
本文介绍了一个使用Python和Tkinter库实现的动态时钟程序,具有昼夜背景切换、指针颜色随机变化及整点和半点报时功能。通过设置不同的背景颜色和随机变换指针颜色,增强视觉吸引力;利用多线程技术确保音频播放不影响主程序运行。该程序结合了Tkinter、Pygame、Pytz等库,提供了一个美观且实用的时间显示工具。欢迎点赞、关注、转发、收藏!
527 94
|
Python
使用OpenPyXL库实现Excel单元格其他对齐方式设置
本文介绍了如何使用Python的`openpyxl`库设置Excel单元格中的文本对齐方式,包括文本旋转、换行、自动调整大小和缩进等,通过具体示例代码展示了每种对齐方式的应用方法,适合需要频繁操作Excel文件的用户学习参考。
649 85
使用OpenPyXL库实现Excel单元格其他对齐方式设置
|
存储 小程序 Python
农历节日倒计时:基于Python的公历与农历日期转换及节日查询小程序
### 农历节日倒计时:基于Python的公历与农历日期转换及节日查询小程序 该程序通过`lunardate`库实现公历与农历的日期转换,支持闰月和跨年处理,用户输入农历节日名称后,可准确计算距离该节日还有多少天。功能包括农历节日查询、倒计时计算等。欢迎使用! (239字符)
947 86
|
Python
阿里云百炼大模型生成贪吃蛇小游戏
阿里云百炼大模型生成的贪吃蛇小游戏增加了背景音乐功能。通过Pygame的`mixer`模块,实现背景音乐的加载和播放。关键步骤包括:1. 安装Pygame;2. 准备音乐文件;3. 修改代码以初始化混音器并加载音乐。游戏开始时自动播放背景音乐,支持无限循环。此外,还可以根据需要调整游戏速度、难度及添加更多音效。
311 13
阿里云百炼大模型生成贪吃蛇小游戏
|
机器学习/深度学习 安全 网络安全
网络安全词云图与技术浅谈
### 网络安全词云图与技术浅谈 本文介绍了通过词云图展示网络安全关键术语的方法,并探讨了构建现代网络安全体系的关键要素。词云图利用字体大小和颜色突出高频词汇,如恶意软件、防火墙、入侵检测系统等。文中提供了生成词云图的Python代码示例,包括安装依赖库和调整参数。此外,文章详细讨论了恶意软件防护、加密技术、身份验证、DDoS防御、社会工程学防范及威胁情报等核心技术,强调了多层次、多维度的安全策略的重要性。
532 11
网络安全词云图与技术浅谈
|
监控 安全 网络协议
计算机端口:网络通信的桥梁
计算机端口是网络通信的逻辑通道,支持数据传输和服务识别。本文介绍端口定义、分类(知名、注册、动态端口)、作用及管理方法,涵盖常用知名端口如HTTP(80)、HTTPS(443)等,并强调端口安全配置的重要性,帮助读者全面理解这一关键组件。
1278 6
|
数据可视化 数据处理 Python
使用Pandas实现Excel中的数据透视表功能
本文介绍了如何使用Python的Pandas库实现Excel中的数据透视表功能,包括环境准备、创建模拟销售数据、代码实现及输出等步骤。通过具体示例展示了按地区和销售员汇总销售额的不同方法,如求和、平均值、最大值等,帮助读者掌握Pandas在数据处理上的强大能力。
517 12