目录
一、函数的定义
二、函数的调用
三、函数的参数
3.1 可变与不可变对象
3.2 函数参数传递
3.3 参数类型
四、匿名函数
五、函数的return语句
六、作用域
七、python的模块化
八、 main 函数
一、函数的定义
函数是经过精心组织、可重复使用的代码片段,用于实现单一功能或相关联的一系列功能。
通过使用函数,可以提升应用程序的模块化程度,并增强代码的复用性。你已经熟悉了Python提供的众多内置函数,例如 print()
。此外,你还可以自行创建函数,这些函数被称为用户自定义函数。
定义一个函数
你可以根据自身需求定义一个具有特定功能的函数。以下是定义函数的基本规则:
-
函数代码块以
def
关键字开头,后跟函数标识符名称和圆括号()
。 -
任何传入的参数和自变量必须放置在圆括号内。圆括号内可用于定义参数。
-
函数的第一行语句可以选择性地使用文档字符串——用于存放函数说明。
-
函数内容以冒号
:
起始,并且需要缩进。 -
return [表达式]
语句用于结束函数,并选择性地返回一个值给调用方。不带表达式的return
语句相当于返回None
。
示例:
def greet(name):"""这是一个简单的函数,用于打印问候语。:param name: 接收一个字符串参数,表示名字。"""print(f"Hello, {name}!")
二、函数的调用
定义一个函数仅仅是赋予了函数一个名称,明确了函数所包含的参数,以及代码块的结构。
一旦这个函数的基本结构构建完毕,你便可以通过在另一个函数中调用来执行它,或者直接从Python的交互式提示符(如IDLE或命令行)中执行。
函数调用示例:
#!/usr/bin/python
# -*- coding: UTF-8 -*-# 定义函数
def printme( str ):"打印任何传入的字符串"print strreturn# 调用函数
printme("我要调用用户自定义函数!")
printme("再次调用同一函数")
三、函数的参数
3.1 可变与不可变对象
在 Python 中,类型是与对象相关联的,而变量本身并不具有类型。例如:
a = [1, 2, 3]
a = "Runoob"
在这段代码中,`[1, 2, 3]` 是一个列表(List)类型对象,`"Runoob"` 是一个字符串(String)类型对象,而变量 `a` 本身并不具有类型,它只是对象的一个引用(或者说是一个指针),可以指向列表类型的对象,也可以指向字符串类型的对象。
### 可变与不可变对象
在 Python 中,字符串(strings)、元组(tuples)和数字(numbers)是不可变对象,而列表(list)、字典(dict)等则是可变对象。
- **不可变类型**:当变量被赋值 `a = 5` 后,再赋值 `a = 10`,实际上是新生成了一个整数值对象 `10`,然后让 `a` 指向这个新对象,而原来的值 `5` 被丢弃。这并不是改变 `a` 的值,而是相当于新生成了 `a`。
- **可变类型**:当变量被赋值 `la = [1, 2, 3, 4]` 后,再赋值 `la[2] = 5`,这是将列表 `la` 的第三个元素的值更改,列表 `la` 本身并没有被替换,只是其内部的一部分值被修改了。
3.2 函数参数传递
### 函数参数传递
在 Python 中,函数的参数传递可以分为两种情况:
- **不可变类型**:类似于 C++ 中的值传递,例如整数、字符串、元组。如 `fun(a)`,传递的只是 `a` 的值,不会影响 `a` 对象本身。如果在 `fun(a)` 内部修改 `a` 的值,只是修改了另一个复制的对象,不会影响 `a` 本身。
- **可变类型**:类似于 C++ 中的引用传递,例如列表、字典。如 `fun(la)`,则是将 `la` 真正的传过去,修改后函数外部的 `la` 也会受到影响。
在 Python 中,一切都是对象,严格意义上我们不能简单地说值传递还是引用传递,而应该说传递的是不可变对象还是可变对象。
实参:在调用函数时,可以通过参数将一些值传递给函数处理,这些在调用函数时提供给函数的值称为实参。
形参:在定义函数时,函数名后面括号中的变量称为形参。如果形参个数超过1个,各个参数之间用逗号隔开。
Python 传递不可变对象的实例
以下是一个演示 Python 传递不可变对象的示例(适用于 Python 2.0+):
#!/usr/bin/python
# -*- coding: UTF-8 -*-def ChangeInt(a):a = 10b = 2
ChangeInt(b)
print b # 结果是 2
在这个示例中,有一个整数对象 `2`,变量 `b` 指向这个对象。当 `b` 传递给 `ChangeInt` 函数时,实际上是按值传递的方式复制了变量 `b`,使得 `a` 和 `b` 都指向同一个整数对象。当在函数内部执行 `a = 10` 时,新生成一个整数值对象 `10`,并让 `a` 指向它,而原来的整数对象 `2` 并未改变。
传递可变对象的实例
以下是一个演示 Python 传递可变对象的示例(适用于 Python 2.0+):
#!/usr/bin/python
# -*- coding: UTF-8 -*-# 可写函数说明
def changeme(mylist):"""修改传入的列表"""mylist.append([1, 2, 3, 4])print "函数内取值: ", mylistreturn# 调用changeme函数
mylist = [10, 20, 30]
changeme(mylist)
print "函数外取值: ", mylist
在这个示例中,传入函数和在末尾添加新内容的对象使用的是同一个引用。因此,输出结果如下:
函数内取值: [10, 20, 30, [1, 2, 3, 4]]
函数外取值: [10, 20, 30, [1, 2, 3, 4]]
这表明,当传递可变对象(如列表)时,函数内部对对象的修改会影响到函数外部的对象。
3.3 参数类型
Python 函数参数类型
在调用 Python 函数时,可以使用以下几种正式参数类型:
必需参数
关键字参数
默认值参数
可变长度参数
1)必需参数
必需参数必须按照函数声明时的顺序传递。调用时提供的参数数量必须与声明时一致。例如,调用 `printme()` 函数时,必须提供一个参数,否则会导致语法错误:
示例(Python 2.0+)
#!/usr/bin/python
# -*- coding: UTF-8 -*-# 函数定义
def printme(str):"打印传入的字符串"print strreturn# 调用 printme 函数
printme()
上述代码的输出结果:
Traceback (most recent call last):
File "test.py", line 11, in <module>
printme()
TypeError: printme() takes exactly 1 argument (0 given)
2)关键字参数
关键字参数与函数调用紧密相关,允许在调用函数时通过参数名指定参数值。
使用关键字参数时,参数的顺序可以与声明时不一致,因为 Python 解释器能够通过参数名匹配参数值。以下示例展示了在调用 `printme()` 函数时使用参数名:
示例(Python 2.0+)
#!/usr/bin/python
# -*- coding: UTF-8 -*-# 函数定义
def printme(str):"打印传入的字符串"print strreturn# 调用 printme 函数
printme(str="My string")
上述代码的输出结果:
My string
进一步说明关键字参数顺序不重要:
示例(Python 2.0+)
#!/usr/bin/python
# -*- coding: UTF-8 -*-# 函数定义
def printinfo(name, age):"打印传入的字符串"print "Name: ", nameprint "Age ", agereturn# 调用 printinfo 函数
printinfo(age=50, name="miki")
上述代码的输出结果:
Name: miki
Age 50
3)默认值参数
如果在调用函数时未提供默认值参数的值,则使用默认值。以下示例展示了如果未传入 `age` 参数,则使用默认值 35:
示例(Python 2.0+)
#!/usr/bin/python
# -*- coding: UTF-8 -*-# 函数定义
def printinfo(name, age=35):"打印传入的字符串"print "Name: ", nameprint "Age ", agereturn# 调用 printinfo 函数
printinfo(age=50, name="miki")
printinfo(name="miki")
上述代码的输出结果:
Name: miki
Age 50
Name: miki
Age 35
4)可变长度参数
有时可能需要一个函数能够处理比声明时更多的参数。这些参数称为可变长度参数,声明时不命名。基本语法如下:
def functionname([formal_args,] *var_args_tuple):"函数文档字符串"function_suitereturn [expression]
带有星号(*)的变量名会存储所有未命名的变量参数。以下是可变长度参数的示例:
示例(Python 2.0+)
#!/usr/bin/python
# -*- coding: UTF-8 -*-# 函数定义
def printinfo(arg1, *vartuple):"打印传入的参数"print "输出: "print arg1for var in vartuple:print varreturn# 调用 printinfo 函数
printinfo(10)
printinfo(70, 60, 50)
上述代码的输出结果:
输出:
10
输出:
70
60
50
四、匿名函数
在Python中,匿名函数是一种没有名字的函数,通常用于需要一个简单函数但不想为其定义一个完整函数的情况。匿名函数使用`lambda`关键字来创建,其语法如下:
lambda 参数1, 参数2, ...: 表达式
`lambda`函数可以有任意数量的参数,但只能有一个表达式。这个表达式的结果就是`lambda`函数的返回值。
以下是一些使用`lambda`函数的示例:
1. **基本用法**:
add = lambda x, y: x + yresult = add(3, 5)print(result) # 输出: 8
2. **作为参数传递给其他函数**:
numbers = [1, 2, 3, 4, 5]squared_numbers = list(map(lambda x: x**2, numbers))print(squared_numbers) # 输出: [1, 4, 9, 16, 25]
3. **在列表推导式中使用**:
numbers = [1, 2, 3, 4, 5]squared_numbers = [x**2 for x in numbers]print(squared_numbers) # 输出: [1, 4, 9, 16, 25]
4. **作为排序的关键函数**:
pairs = [(1, 'one'), (2, 'two'), (3, 'three'), (4, 'four')]pairs.sort(key=lambda pair: pair[1])print(pairs) # 输出: [(4, 'four'), (1, 'one'), (3, 'three'), (2, 'two')]
5. **结合`filter`函数使用**:
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]even_numbers = list(filter(lambda x: x % 2 == 0, numbers))print(even_numbers) # 输出: [2, 4, 6, 8, 10]
`lambda`函数的主要优点是简洁和方便,特别适合用于需要一个简单函数但不想为其定义一个完整函数的情况。然而,对于复杂的逻辑,建议使用常规的`def`定义函数,以提高代码的可读性和可维护性。
由此总结:
`lambda`是一种简洁的表达式形式,其函数体相较于传统的`def`定义更为精简。
`lambda`的核心是一个单一的表达式,而非复杂的代码块,这限制了它在表达逻辑上的复杂性。
`lambda`函数具有独立的命名空间,无法访问其参数列表之外或全局命名空间中的变量。
尽管`lambda`函数通常以单行形式呈现,但这并不意味着它等同于C或C++中的内联函数。内联函数的目的是通过减少函数调用的开销来提高运行效率,而`lambda`函数则主要用于简化代码和提供临时的函数定义。
五、函数的return语句
在Python中,`return`语句用于从函数中返回一个值,并终止函数的执行。`return`语句可以返回任意类型的对象,包括但不限于整数、字符串、列表、字典等。如果函数中没有`return`语句,或者`return`语句没有指定返回值,函数将默认返回`None`。
以下是一些关于`return`语句的详细说明和示例:
1. **基本用法**:
def add(a, b):return a + bresult = add(3, 5)print(result) # 输出: 8
2. **返回多个值**:
Python允许使用`return`语句返回多个值,这些值会被自动打包成一个元组。
def get_name_and_age():name = "Alice"age = 30return name, agename, age = get_name_and_age()print(name) # 输出: Aliceprint(age) # 输出: 30
3. **返回None**:
如果函数没有`return`语句,或者`return`语句后面没有指定返回值,函数将返回`None`。
def say_hello():print("Hello!")result = say_hello()print(result) # 输出: None
4. **提前终止函数**:
`return`语句可以用于提前终止函数的执行。
def check_age(age):if age < 0:return "Invalid age"elif age < 18:return "Minor"else:return "Adult"result = check_age(-5)print(result) # 输出: Invalid age
5. **返回函数**:
`return`语句还可以用于返回另一个函数,这在函数式编程中很常见。
def make_adder(x):def adder(y):return x + yreturn adderadd_5 = make_adder(5)result = add_5(3)print(result) # 输出: 8
`return`语句是Python函数中非常重要的组成部分,它不仅用于返回值,还用于控制函数的执行流程。合理使用`return`语句可以使代码更加清晰和高效。
六、作用域
Python变量作用域
在Python中,并非所有位置都能访问程序中的所有变量。变量的可访问性取决于其赋值的位置。
变量的作用域定义了在程序的哪一部分可以访问特定的变量名称。Python中最基本的两种变量作用域是:
全局变量
局部变量
全局变量与局部变量
在函数内部定义的变量具有局部作用域,而在函数外部定义的变量具有全局作用域。局部变量仅在其被声明的函数内部可访问,而全局变量则在整个程序范围内均可访问。当调用一个函数时,该函数内声明的所有变量名称都会被纳入其作用域。以下是一个示例:
示例(Python 2.0+)
#!/usr/bin/python
# -*- coding: UTF-8 -*-total = 0 # 这是一个全局变量# 函数定义
def sum(arg1, arg2):# 返回两个参数的和total = arg1 + arg2 # total在这里是局部变量print "函数内是局部变量 : ", totalreturn total# 调用sum函数
sum(10, 20)
print "函数外是全局变量 : ", total
上述代码的输出结果:
函数内是局部变量 : 30
函数外是全局变量 : 0
在这个示例中,`total`在函数内部被重新赋值,因此它是一个局部变量,仅在函数内部有效。而在函数外部,`total`保持其全局变量的值,不受函数内部赋值的影响。
总结:
在Python中,函数内部定义的变量仅在该函数范围内有效,这些变量被称为局部变量。
局部变量与函数外部具有相同名称的变量是相互独立的,即局部变量的名称仅在其函数内部有效。
局部变量的作用域从其被定义的位置开始,直到函数结束。
未在任何函数内部定义的变量是全局变量。
如果需要在函数内部修改一个在函数外部定义的变量,该变量不能是局部变量,而必须是全局变量。全局变量的作用域涵盖函数内外,可以通过`global`关键字来声明。
全局变量的值在函数内部被修改后,该修改会反映到函数外部;同样,在函数外部修改全局变量的值,也会影响到函数内部。
`global`语句的格式如下:
global 变量名1, 变量名2, ..., 变量名n
`global`语句有两个主要作用:
1) 如果一个变量在函数外部已经定义,但在函数内部需要对其进行赋值,并且希望这个赋值结果在函数外部也生效,可以在函数内部使用`global`声明该变量,将其定义为全局变量;例如:
在函数内部使用外部定义的全局变量。
2) 在函数内部直接声明一个变量为全局变量,即使该变量在函数外部未被声明,在调用该函数后,该变量将成为新的全局变量。
七、python的模块化
Python的模块化编程是一种将代码组织成可重用、可维护的模块的方法。模块是包含Python定义和语句的文件,其文件名是模块名加上`.py`后缀。通过模块化编程,可以将复杂的程序分解成更小、更易于管理的部分,每个部分负责特定的功能。
以下是Python模块化编程的一些关键概念和实践:
1. **创建模块**:
一个模块就是一个包含Python代码的文件。例如,创建一个名为`mymodule.py`的文件,其中包含一些函数和变量。
# mymodule.pydef greet(name):print(f"Hello, {name}!")def add(a, b):return a + b
2. **导入模块**:
使用`import`语句可以在其他Python文件中导入模块。
import mymodulemymodule.greet("Alice")result = mymodule.add(3, 5)print(result)
3. **使用`from...import`语句**:
可以使用`from...import`语句从模块中导入特定的函数或变量,这样可以避免使用模块名作为前缀。
from mymodule import greet, addgreet("Alice")result = add(3, 5)print(result)
4. **模块搜索路径**:
Python解释器在导入模块时会搜索一系列目录,这些目录存储在`sys.path`变量中。可以通过修改`sys.path`或设置环境变量`PYTHONPATH`来添加自定义的模块搜索路径。
5. **包(Packages)**:
包是一种组织模块的方式,它是一个包含`__init__.py`文件的目录。包可以包含子包和模块。
mypackage/
__init__.py
module1.py
module2.py
subpackage/
__init__.py
module3.py
导入包中的模块:
from mypackage import module1from mypackage.subpackage import module3
6. **内置模块和第三方模块**:
Python有许多内置模块(如`sys`、`os`、`math`等),可以直接使用。此外,还可以通过`pip`安装第三方模块,如`requests`、`numpy`等。
7. **模块的文档字符串**:
每个模块应该包含一个文档字符串,用于描述模块的功能和使用方法。文档字符串可以通过`__doc__`属性访问。
# mymodule.py"""This module contains functions for greeting and adding numbers."""def greet(name):"""Print a greeting message."""print(f"Hello, {name}!")def add(a, b):"""Return the sum of two numbers."""return a + b
通过模块化编程,可以提高代码的可读性、可维护性和可重用性,使大型项目更易于管理和扩展。
八、 main 函数
在Python中,并没有像C或Java那样的显式`main`函数作为程序的入口点。Python程序的执行从脚本的第一行开始,按顺序执行每一行代码。然而,为了组织代码和明确程序的入口点,通常会在脚本中定义一个`main`函数,并在脚本的最后调用它。
以下是一个典型的Python脚本结构,包含一个`main`函数:
def main():# 这里是程序的主要逻辑print("Hello, world!")# 其他代码...if __name__ == "__main__":main()
在这个结构中:
1. `main`函数包含了程序的主要逻辑。
2. `if __name__ == "__main__":` 这一行检查当前模块是否是作为主程序运行,而不是被其他模块导入。如果是主程序运行,则调用`main`函数。
这样做的好处包括:
- **代码组织**:将主要逻辑放在`main`函数中,使代码结构更清晰。
- **可测试性**:可以单独测试`main`函数,而不必运行整个脚本。
- **模块化**:如果需要,可以将脚本作为模块导入到其他脚本中,而不会自动执行`main`函数中的代码。
例如,如果将上述代码保存为`script.py`,并在命令行中运行:
python script.py
输出将是:
Hello, world!
如果将`script.py`作为模块导入到另一个脚本中:
import script
则不会输出任何内容,因为`main`函数没有被调用。