slogan: 我学编程就是为了赚钱,后来发现它不只是能赚钱…
1. 函数
函数的作用
编程大师Martin Fowler先生曾经说过:“代码有很多种坏味道,重复是最坏的一种!”,要写出高质量的代码首先要解决的就是重复代码的问题。对此,我们可以把需要重复执行的代码封装到一个称为“函数”的功能模块中,在需要的时候,直接调用该函数,这样大大提升了代码的编写和维护效率。
1.1 函数的定义和调用
在Python中可以使用def
关键字来定义函数,函数的命名规则跟变量的命名规则是一致。在函数名后面的圆括号中可以放置传递给函数的参数,函数体以冒号起始,并且缩进,而函数执行完成后我们可以通过return
关键字来返回一个值。
具体格式如下:
def 函数名(参数列表):
函数体
举个例子:
def hello() :
print("Hello World!")
hello() #Hello World!
上面的例子定义了一个hello
的函数,由于这个函数没有参数,所以直接通过函数名加括号来实现对函数的调用。而对于有参数的函数,要调用它,就需要知道函数的名称和参数。
1.2 函数的参数
函数是绝大多数编程语言中都支持的一个代码的"构建块",但是Python中的函数与其他语言中的函数还是有很多不太相同的地方,其中一个显著的区别就是Python对函数参数的处理。
Python函数的参数灵活性非常大,它可以是必要参数、默认参数、可变参数和关键字参数等。
- 必要参数
必要参数须以正确的顺序传入函数。调用时的数量必须和声明时的一样。
例如:在调用 printme() 函数,你必须传入一个参数,不然会出现语法错误:
#输出任何传入的字符串
def printme( str ): #定义了一个名叫`str`的参数
print (str)
return
printme() # 调用 printme 函数,不加参数会报错
printme('字符串') #删除上一行代码,给printme加入参数
- 关键字参数
关键字参数允许你传入0个或任意个含参数名的参数,允许函数调用时参数的顺序与声明时不一致。
def printinfo( name, age ):
print ("名字: ", name)
print ("年龄: ", age)
return
#调用printinfo函数
printinfo( age=50, name="runoob" )
- 默认参数
用函数时,如果没有传递参数,则会使用默认参数。
def printinfo( name, age = 35 ):
print ("名字: ", name)
print ("年龄: ", age)
return
#调用printinfo函数
printinfo( age=50, name="runoob" )
print ("------------------------")
printinfo( name="runoob" ) #没有传入age参数的值,输出默认值35
- 可变参数
在不确定参数个数的时候,我们可以使用可变参数,代码如下所示。
def printinfo( arg1, *vartuple ):
print ("输出: ")
print (arg1)
print (vartuple)
# 调用printinfo 函数
printinfo( 70, 60, 50 )
说明: 加了星号 * 的参数会以元组(tuple)的形式导入,存放所有未命名的变量参数
如果在函数调用时没有指定参数,它就是一个空元组。我们也可以不向函数传递未命名的变量。
def printinfo( arg1, *vartuple ):
print ("输出: ")
print (arg1)
for var in vartuple: #依次打印 vartuple 参数(元祖)里的元素
print (var)
return
# 调用printinfo 函数
printinfo( 10 )
printinfo( 70, 60, 50 )
2. 模块
对于任何一种编程语言来说,给变量、函数这样的标识符起名字都是一个让人头疼的问题,因为我们会遇到命名冲突这种尴尬的情况。最简单的场景就是在同一个.py文件中定义了两个同名函数,由于Python没有函数重载的概念,那么后面的定义会覆盖之前的定义,也就意味着两个函数同名函数实际上只有一个是存在的。
def foo():
print('hello, world!')
def foo():
print('goodbye, world!')
foo() #输出第二个 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()
也可以按照如下所示的方式来区分到底要使用哪一个foo
函数。
test.py
import module1 as m1
import module2 as m2
m1.foo()
m2.foo()
但是如果将代码写成了下面的样子,那么程序中调用的是最后导入的那个foo
,因为后导入的foo覆盖了之前导入的foo
。
test.py
from module1 import foo
from module2 import foo
# 输出goodbye, world!
foo()
test.py
from module2 import foo
from module1 import foo
# 输出hello, world!
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__
说明: 类似
__main__
的写法是由单词前后分别两个连贯的下划线组成