函数是组织好的,可重复使用的,用来实现单一,或相关联功能的代码段。
函数能提高应用的模块性,和代码的重复利用率。
编程大师Martin Fowler先生曾经说过:“代码有很多种坏味道,重复是最坏的一种!”,
Python提供了许多内建函数,比如print()
,我们可以直接调用。
但你也可以自己创建函数,这被叫做用户自定义函数。
自定义函数
在Python
中可以使用def
关键字来定义函数,和变量一样每个函数也有一个响亮的名字,而且命名规则跟变量的命名规则是一致的。
在函数名后面的圆括号中可以放置传递给函数的参数,这一点和数学上的函数非常相似,程序中函数的参数就相当于是数学上说的函数的自变量,而函数执行完成后我们可以通过return
关键字来返回一个值,这相当于数学上说的函数的因变量。
即,在Python
中,定义一个函数要使用def
语句,依次写出函数名、括号、括号中的参数和冒号:
,
然后,在缩进块中编写函数体,函数的返回值用return
语句返回。
语法
Python
定义函数使用 def
关键字,一般格式如下:
def 函数名(参数列表):
函数体
默认情况下,参数值和参数名称是按函数声明中定义的顺序匹配起来的。
自定义一个函数maxValue
,用来比较两个数,并返回较大的数:
def maxValue(a, b):
if a > b:
return a
else:
return b
a = 4
b = 5
print(maxValue(a, b))
以上代码运行输出
5
函数的参数
定义函数的时候,我们把参数的名字和位置确定下来,函数的接口定义就完成了。对于函数的调用者来说,只需要知道如何传递正确的参数,以及函数将返回什么样的值就够了,函数内部的复杂逻辑被封装起来,调用者无需了解。
Python的函数定义非常简单,但灵活度却非常大。除了正常定义的必选参数外,还可以使用默认参数、可变参数和关键字参数,使得函数定义出来的接口,不但能处理复杂的参数,还可以简化调用者的代码。
参数
参数类型分为:
- 位置参数
这是最常见的参数传递方式,根据参数的位置进行传递。
def greet(name: str, age: int):
print(f"Hello, {name}! You are {age} years old.")
- 默认参数
在定义函数时为参数指定默认值。
def greet(name: str, age: int = 30):
print(f"Hello, {name}! You are {age} years old.")
- 可变位置参数
允许函数接受任意数量的位置参数。
def calculate_total(*args: int):
total = sum(args)
return total
- 关键字参数
在函数调用时通过键值对的方式传递参数。
def person(name, age, **kw):
print('name:', name, 'age:', age, 'other:', kw)
函数person
除了必选参数name
和age
外,还接受关键字参数kw
。在调用该函数时,可以只传入必选参数:
person('Michael', 30)
# name: Michael age: 30 other: {}
也可以传入任意个数的关键字参数:
person('Bob', 35, city='Beijing')
# name: Bob age: 35 other: {'city': 'Beijing'}
person('Adam', 45, gender='M', job='Engineer')
# name: Adam age: 45 other: {'gender': 'M', 'job': 'Engineer'}
- 必需参数
必需参数须以正确的顺序传入函数。调用时的数量必须和声明时的一样。
调用printme()
函数,你必须传入一个参数,不然会出现语法错误:
def printme( str ):
"打印任何传入的字符串"
print (str)
return
# 调用 printme 函数,不加参数会报错
printme()
函数是绝大多数编程语言中都支持的一个代码的"构建块",但是Python
中的函数与其他语言中的函数还是有很多不太相同的地方,其中一个显著的区别就是Python
对函数参数的处理。
在Python
中,函数的参数可以有默认值,也支持使用可变参数,所以Python并不需要像其他语言一样支持函数的重载,因为我们在定义一个函数的时候可以让它有多种不同的使用方式。
用模块管理函数
对于任何一种编程语言来说,给变量、函数这样的标识符起名字都是一个让人头疼的问题,因为我们会遇到命名冲突这种尴尬的情况。
最简单的场景就是在同一个.py
文件中定义了两个同名函数,由于Python
没有函数重载的概念,那么后面的定义会覆盖之前的定义,也就意味着两个函数同名函数实际上只有一个是存在的。
def foo():
print('hello, world!')
def foo():
print('goodbye, world!')
# 下面的代码会输出什么呢?
foo()
当然上面的这种情况我们很容易就能避免,但是如果项目是由多人协作进行团队开发的时候,团队中可能有多个程序员都定义了名为foo
的函数,那么怎么解决这种命名冲突呢?
答案其实很简单,Python
中每个文件就代表了一个模块(module),我们在不同的模块中可以有同名的函数,在使用函数的时候我们通过import
关键字导入指定的模块就可以区分到底要使用的是哪个模块中的foo
函数,代码如下所示。
module1.py
def foo():
print('hello, world!')
module2.py
def foo():
print('goodbye, world!')
test.py
from module1 import foo
# 输出hello, world!
foo()
from module2 import foo
# 输出goodbye, world!
foo()
或者
test.py
import module1 as m1
import module2 as m2
m1.foo()
m2.foo()
如果我们导入的模块除了定义函数之外还有可以执行代码,那么Python
解释器在导入这个模块时就会执行这些代码,事实上我们可能并不希望如此。
因此如果我们在模块中编写了执行代码,最好是将这些执行代码放入如下所示的条件中,这样的话除非直接运行该模块,if
条件下的这些代码是不会执行的,因为只有直接执行的模块的名字才是"__main__
"。
module3.py
def foo():
pass
def bar():
pass
# __name__是Python中一个隐含的变量它代表了模块的名字
# 只有被Python解释器直接执行的模块的名字才是__main__
if __name__ == '__main__':
print('call foo()')
foo()
print('call bar()')
bar()
test.py
import module3
# 导入module3时 不会执行模块中if条件成立时的代码 因为模块的名字是module3而不是__main__