你是否对函数、类、方法、库和模块等花哨的编程术语感到困惑?你是否在变量的作用域方面遇到困难?无论你是自学成才的程序员还是受过正规培训的代码工人,代码的模块化都可能令人困惑。但是类和库鼓励模块化代码,而模块化代码可能意味着构建一个多用途代码块集合,你可以在许多项目中使用这些代码块来减少你的编码工作量。换句话说,如果你继续学习本文关于 Python 函数的内容,你将找到更智能的工作方式,而更智能的工作方式意味着更少的工作。
本文假设你已熟悉 Python,能够编写和运行简单的脚本。如果你尚未使用过 Python,请先阅读我的 Python 入门文章。
函数
函数是实现模块化的重要一步,因为它们是重复的形式化方法。如果你的程序中存在需要一遍又一遍完成的任务,你可以将代码分组到一个函数中,并根据需要多次调用该函数。这样,你只需要编写一次代码,但可以根据需要多次使用它。
这是一个简单函数的示例
#!/usr/bin/env python3
import time
def Timer():
print("Time is " + str(time.time() ) )
创建一个名为 mymodularity 的文件夹,并将函数代码保存为 timestamp.py。
除了这个函数之外,在 mymodularity 目录中创建一个名为 __init__.py 的文件。你可以在文件管理器或 Bash shell 中执行此操作
$ touch mymodularity/__init__.py
你现在已经在名为 mymodularity 的 Python 包中创建了自己的 Python 库(在 Python 术语中称为“模块”)。它不是一个非常有用的模块,因为它所做的只是导入 time 模块并打印时间戳,但这是一个开始。
要使用你的函数,请像对待任何其他 Python 模块一样对待它。这是一个小应用程序,用于测试 Python 的 sleep() 函数的准确性,使用你的 mymodularity 包作为支持。将此文件另存为 sleeptest.py,放在 mymodularity 目录之外(如果你将其放入 mymodularity,那么它将成为你的包中的一个模块,而你不想那样)。
#!/usr/bin/env python3
import time
from mymodularity import timestamp
print("Testing Python sleep()...")
# modularity
timestamp.Timer()
time.sleep(3)
timestamp.Timer()
在这个简单的脚本中,你正在从你的 mymodularity 包中调用你的 timestamp 模块(两次)。当你从包中导入模块时,通常的语法是从包中导入你想要的模块,然后使用模块名称 + 一个点 + 你要调用的函数名称(例如,timestamp.Timer())。
你正在调用你的 Timer() 函数两次,因此,如果你的 timestamp 模块比这个简单的示例更复杂,你将节省大量重复代码。
保存文件并运行它
$ python3 ./sleeptest.py
Testing Python sleep()...
Time is 1560711266.1526039
Time is 1560711269.1557732
根据你的测试,Python 中的 sleep 函数非常准确:在休眠三秒后,时间戳成功且正确地增加了三秒,微秒略有偏差。
Python 库的结构可能看起来令人困惑,但它不是魔法。Python 被编程为将一个装满 Python 代码的文件夹和一个 __init__.py 文件视为一个包,并且它被编程为首先在其当前目录中查找可用的模块。这就是语句 from mymodularity import timestamp 起作用的原因:Python 在当前目录中查找一个名为 mymodularity 的文件夹,然后查找一个以 .py 结尾的 timestamp 文件。
你在本示例中所做的在功能上与这个不太模块化的版本相同
#!/usr/bin/env python3
import time
from mymodularity import timestamp
print("Testing Python sleep()...")
# no modularity
print("Time is " + str(time.time() ) )
time.sleep(3)
print("Time is " + str(time.time() ) )
对于像这样的简单示例,实际上没有理由不以这种方式编写你的 sleep 测试,但是编写你自己的模块的最佳之处在于你的代码是通用的,因此你可以将其重用于其他项目。
你可以通过在调用函数时将信息传递到函数中来使代码更通用。例如,假设你想使用你的模块来测试不是计算机的 sleep 函数,而是用户的 sleep 函数。更改你的 timestamp 代码,使其接受一个名为 msg 的传入变量,该变量将是一个文本字符串,用于控制每次调用时时间戳的显示方式
#!/usr/bin/env python3
import time
# updated code
def Timer(msg):
print(str(msg) + str(time.time() ) )
现在你的函数比以前更抽象了。它仍然打印时间戳,但它为用户打印的内容是不确定的。这意味着你需要在调用函数时定义它。
你的 Timer 函数接受的 msg 参数是任意命名的。你可以将参数称为 m 或 message 或 text 或任何对你来说有意义的名称。重要的是,当 timestamp.Timer 函数被调用时,它接受一些文本作为其输入,将接收到的任何内容放入变量中,并使用该变量来完成其任务。
这是一个新的应用程序,用于测试用户正确感知时间流逝的能力
#!/usr/bin/env python3
from mymodularity import timestamp
print("Press the RETURN key. Count to 3, and press RETURN again.")
input()
timestamp.Timer("Started timer at ")
print("Count to 3...")
input()
timestamp.Timer("You slept until ")
将你的新应用程序另存为 response.py 并运行它
$ python3 ./response.py
Press the RETURN key. Count to 3, and press RETURN again.
Started timer at 1560714482.3772075
Count to 3...
You slept until 1560714484.1628013
函数和必需参数
新版本的 timestamp 模块现在需要 msg 参数。这很重要,因为你的第一个应用程序已损坏,因为它没有将字符串传递给 timestamp.Timer 函数
$ python3 ./sleeptest.py
Testing Python sleep()...
Traceback (most recent call last):
File "./sleeptest.py", line 8, in <module>
timestamp.Timer()
TypeError: Timer() missing 1 required positional argument: 'msg'
你能修复你的 sleeptest.py 应用程序,使其与更新版本的模块一起正确运行吗?
变量和函数
按照设计,函数限制变量的作用域。换句话说,如果在函数内部创建了一个变量,则该变量仅对该函数可用。如果你尝试在函数外部使用函数中出现的变量,则会发生错误。
这是 response.py 应用程序的修改版本,尝试打印来自 timestamp.Timer() 函数的 msg 变量
#!/usr/bin/env python3
from mymodularity import timestamp
print("Press the RETURN key. Count to 3, and press RETURN again.")
input()
timestamp.Timer("Started timer at ")
print("Count to 3...")
input()
timestamp.Timer("You slept for ")
print(msg)
尝试运行它以查看错误
$ python3 ./response.py
Press the RETURN key. Count to 3, and press RETURN again.
Started timer at 1560719527.7862902
Count to 3...
You slept for 1560719528.135406
Traceback (most recent call last):
File "./response.py", line 15, in <module>
print(msg)
NameError: name 'msg' is not defined
该应用程序返回 NameError 消息,因为 msg 未定义。这可能看起来令人困惑,因为你编写了定义 msg 的代码,但你对你的代码的了解比 Python 更多。调用函数的代码(无论函数出现在同一文件中还是打包为模块)都不知道函数内部发生了什么。函数独立执行其计算并返回它被编程为返回的内容。涉及的任何变量都仅是本地的:它们仅存在于函数内部,并且仅在函数完成其目的所需的时间内存在。
返回语句
如果你的应用程序需要仅包含在函数中的信息,请使用 return 语句使函数在运行后提供有意义的数据。
人们常说时间就是金钱,因此修改你的 timestamp 函数以允许使用虚构的收费系统
#!/usr/bin/env python3
import time
def Timer(msg):
print(str(msg) + str(time.time() ) )
charge = .02
return charge
timestamp 模块现在每次调用收费两美分,但最重要的是,它会在每次调用时返回收取的金额。
这是一个演示如何使用 return 语句的示例
#!/usr/bin/env python3
from mymodularity import timestamp
print("Press RETURN for the time (costs 2 cents).")
print("Press Q RETURN to quit.")
total = 0
while True:
kbd = input()
if kbd.lower() == "q":
print("You owe $" + str(total) )
exit()
else:
charge = timestamp.Timer("Time is ")
total = total+charge
在此示例代码中,变量 charge 被分配为 timestamp.Timer() 函数的终点,因此它接收函数返回的任何内容。在本例中,函数返回一个数字,因此使用一个名为 total 的新变量来跟踪已进行的更改次数。当应用程序收到退出信号时,它会打印总费用
$ python3 ./charge.py
Press RETURN for the time (costs 2 cents).
Press Q RETURN to quit.
Time is 1560722430.345412
Time is 1560722430.933996
Time is 1560722434.6027434
Time is 1560722438.612629
Time is 1560722439.3649364
q
You owe $0.1
内联函数
函数不必在单独的文件中创建。如果你只是编写一个特定于一项任务的简短脚本,那么在同一文件中编写你的函数可能更有意义。唯一的区别是你不必导入你自己的模块,但除此之外,函数的工作方式是相同的。这是时间测试应用程序的最新迭代版本,它位于一个文件中
#!/usr/bin/env python3
import time
total = 0
def Timer(msg):
print(str(msg) + str(time.time() ) )
charge = .02
return charge
print("Press RETURN for the time (costs 2 cents).")
print("Press Q RETURN to quit.")
while True:
kbd = input()
if kbd.lower() == "q":
print("You owe $" + str(total) )
exit()
else:
charge = Timer("Time is ")
total = total+charge
它没有外部依赖项(time 模块包含在 Python 发行版中),并且产生与模块化版本相同的结果。优点是所有内容都位于一个文件中,缺点是你无法在你正在编写的其他脚本中使用 Timer() 函数,除非你手动复制和粘贴它。
全局变量
在函数外部创建的变量没有任何限制其作用域的因素,因此它被视为全局变量。
全局变量的一个示例是 charge.py 示例中用于跟踪当前费用的 total 变量。运行总计是在任何函数外部创建的,因此它绑定到应用程序而不是特定函数。
应用程序中的函数可以访问你的全局变量,但是要将变量放入你导入的模块中,你必须以与发送 msg 变量相同的方式将其发送到那里。
全局变量很方便,因为它们似乎在你需要它们时随时随地可用,但是很难跟踪它们的作用域并了解哪些变量在系统内存中仍然存在很长时间,即使它们不再需要(尽管 Python 通常具有非常好的垃圾回收机制)。
然而,全局变量很重要,因为并非所有变量都可以是函数或类的局部变量。现在你已经知道如何将变量发送到函数并取回值,这很容易了。
函数总结
你已经学到了很多关于函数的知识,所以开始将它们放入你的脚本中——如果不是作为单独的模块,那么就作为你无需在一个脚本中多次编写的代码块。在本系列的下一篇文章中,我将介绍 Python 类。
6 条评论