学到中年的python学习笔记04--函数
目录
1.函数的作用
在熟悉了python编程基础知识后,大家肯定会尝试进行更深入的学习。我记得我刚开始学习python的时候,在了解一些基础后,老师出了一道题,我是真的写了一屏幕,现在想一想当时的自己真的是有一点点的憨。坚持学习的好处就是能及时的防止你一直憨下去。对于代码而言能用一句的就不要多写一句,能用一次循环的就不要去嵌套。那如果在学习中我们遇到需要重复的动作时该怎么解决呢?在学习任何一门技术知识的时候我们都要明白我们学习的所有知识其实都是工具,就好比一把斧头,要是生锈了就要找打铁的师傅,要是斧头钝了就需要找磨刀石。那你要明白你的“斧头”有什么用,应该怎么用。扯回来继续对于一些重复的代码我们可以将计算重复数据的代码封装到一个叫“函数”的功能中,在需要的时候我们在调用。
2.函数的定义
python中可以使用def定义函数,和变量类似每个函数也需要有一个名字,而且有固定的命名规则,在函数名后的括号内是需要传递给函数的参数,在函数执行完成后通过return关键字来返回一个值。
通过以下代码对比
# 输入m和n计算c(m,n)
m = int(input('m = '))
n = int(input('n = '))
fm = 1
for num in range(1, m + 1):
fm *= num
fn = 1
for num in range(1, n + 1):
fn *= num
fm_n = 1
for num in range(1, m - n + 1):
fm_n *= num
print(fm // fn // fm_n)
# 输入m和n计算c(m,n)
def f(num):
result = 1
for n in range(1,num+1):
result *= n
return result
m = int(input("m="))
n = int(input("n="))
print(f(m)//f(n)//f(m-n))
其实在python中已经实现了阶乘,可以直接调用math模块中的factorial函数实现阶乘。
3.函数的参数
函数是绝大多数编程语言都支持的一个代码的“构建块”,在python中的函数与其他的函数有一些区别,其中一个肯定就是对函数参数的处理上。python中,函数的参数可以有默认值,也支持可变参数,所以python并不像其他语言一样支持函数重载,因此我们在定义函数时可以让它有不同的使用方式。
from random import randint
def roll_dice(n=2):
# 摇骰子
total = 0
for _ in range(n):
total += randint(1,6)
return total
def sum(a=0,b=0,c=0):
# 累加
return a+b+c
# 如果参数为0,默认摇两次色子
# print(roll_dice())
# 指定摇的次数
# print(roll_dice(4))
print(sum(2,3,4))
# 可以指定参数的值
print(sum(c=2,b=3,a=4))
在上面的例子中我们对两个函数都设定了默认值,那么我们在调用这个函数时如果没有传入参数值将使用默认的值,所以在上面的代码中我们可以使用不同的方式去调用sum函数,也就类似于函数重载。
当然在python里sum函数还有更好的实现方式,在使用这个函数时我们可能会传入一个或者多个参数进行运算,而具体有多少个参数是由室友者来决定的,而在编辑时python设计者为了更方便就提供了可变参数。
def roll_dice(*args):
total = 0
for var in args:
total += var
return total
# 如果参数为0,默认摇两次色子
print(roll_dice(2,3,4,5))
print(roll_dice(2,3,4,5,6))
print(roll_dice(2,3,4,5,6,7))
4.用函数管理模块
在编程语言里给函数命名是非常重要且很繁琐的事,真的比如没有百度翻译我真的不知道我会不会直接上拼音,如果命名重复了会怎样,那就是后面定义的函数会覆盖掉先前定义的函数,在编程的世界里不会出现两个同样优秀的人你没法选择的问题,它往往会更倾向与后者。
那不知道有没有同学想到要是团队开发,你说那么多人肯定不会先开个会讨论一下会出现多少函数,以及如何命名等。所以如何避免这类问题产生呢?在python里就引入了模块这个概念,那什么是模块呢?每个文件就代表了一个模块(module),我们在不同的模块中可以定义有同名的函数,在使用函数时我们通过import关键字导入指定的模块来区分到底要使用哪个模块对应的哪个函数。
dome01.py

demo01.py

demo03.py

当然也可以使用其他的办法来区分使用哪个foo函数
如果按照图3的方式处理在程序中调用foo函数时后者依旧会覆盖前者
from module1 import foo
from module2 import foo
# 输出world
foo()
from module2 import foo
from module1 import foo
# 输出hello
foo()
需要我们注意的是,如果我们导入的模块中除了定义的函数之外还有其余可以执行的代码,那么python解释器在导入模块时可能会执行这些代码,而往往我们并不需要它执行。因此如果要在模块编写执行代码,最好是将这些代码放入如下所示的条件中,这样的话除非直接运行该模块,if条件下的这些代码是不会执行的,只有被python解释器直接执行的模块名才是“__main__”
5.练习
练习1:求两个数的最大公约数和最小公倍数
def commomDivisor(x,y):
"""最大公约数"""
(x,y) = (y,x) if x > y else (x,y)
for minNum in range(x,0,-1):
if x % minNum == 0 and y % minNum == 0:
return minNum
def commomMultiple(x,y):
"""最小公倍数"""
return x * y / commomDivisor(x,y)
num1 = int(input("输入整数1:"))
num2 = int(input("输入整数1:"))
print(num1,"和",num2,"两数的最大公约数为:",commomDivisor(num1,num2))
print(num1,"和",num2,"两数的最小公倍数为:",commomMultiple(num1,num2))
练习2:实现判断一个数是不是回文数的函数
def palindromeNumber(x):
"""判断一个数是不是回文数"""
number = x
total = 0
while number > 0:
total = total * 10 + number % 10
number //= 10
return total == x
y = 1
palindromeNum = []
while y < 10000:
if palindromeNumber(y) == True:
palindromeNum.append(y)
y += 1
print(palindromeNum)
练习3:实现判断一个数是不是素数的函数
def primeNumber(x): ======= def is_prime(num):
"""判断一个数是不是素数""" for factor in range(2,int(num**0.5)+1):
TrueNum = True if num % factor == 0:
for y in range(2,x): return False
if x % y == 0: return True if num != 1 else False
TrueNum = False
break
else:
TrueNum = True
return TrueNum
number = []
for oneHun in range (1,100):
if primeNumber(oneHun) is True:
number.append(oneHun)
print(number)
练习4:实现判断输入的数是不是回文素数
def palindromeNumber(x):
"""判断一个数是不是回文数"""
number = x
total = 0
while number > 0:
total = total * 10 + number % 10
number //= 10
return total == x
def is_prime(num=83):
for factor in range(2,11):
if num % factor == 0:
return False
return True if num != 1 else False
if __name__ == "__main__":
num = int(input("输入一个整数:"))
if palindromeNumber(num) and is_prime(num):
print('%d是回文素数' % num)
通过上面的例子我们将代码中重复和独立的功能写成函数,我们可以组合这些函数来解决更为复杂的问题,这也就是为什么要使用函数的重要原因。
6.变量的作用域
接下来让我们熟悉变量的作用域,可以理解为如果我们给a赋值为1,假设我在接下来的编程中都要用到1,那是不是可以在最开始就表明a的值呢?还是用的时候在定义呢?加入我在一个函数内定义了a,在此函数外我想用a是不是可以直接调用呢?如果我们这么做了会有什么后果呢?那就一探究竟吧。
def foo():
b = 'hello'
def bar():
c = True
print(a)
print(b)
print(c)
bar()
# print(c) NameError: name 'c' is not defined
if __name__ == '__main__':
a = 100
# print(b) NameError: name 'b' is not defined
foo()
上面的例子能够顺利打印出“100、hello、True”,但我们并未给在函数内部定义函数bar的a和b两个变量,那么a和b是从哪里来的呢?我们在代码中if的分支里定义了一个a,这是一个全局变量(global variable),属于全局作用域,它不单独的定义在其他的函数里。在函数foo中我们定义了函数变量b,这是定义在函数内部的局部变量(local variable),属于局部作用,在foo函数的外部均不能使用它,但对于foo函数内部的bar函数来说,变量b属于嵌套作用域,在bar函数中我们是可以访问它。bar函数中的变量c属于局部作用域,在bar函数之外是没法访问到它。事实上,Python查找一个变量时会按照“局部作用域、嵌套作用域、全局作用域、内置作用域”的顺序进行搜索。前三者通过上面的例子应该已经能明白了,那所谓的“内置作用域”就是Python那些内置的标识符,我们之前用过input、print、int等都属于内置作用域。
那上面这么大一段话说变量作用域到底有什么用呢?当然有用了,例
def aoo():
a = 100
print(a)
if __name__ == '__main__':
a = 200
aoo() # 100
print(a) # 200
上面的例子是希望能通过函数调用修改全局变量a的值,但结果发现好像并不能修改掉a的值。在调用函数aoo后我们发现a的值依旧是200,这是因为在函数aoo中我们定义的a=100的时候是重新定义了一个局部变量,它跟全局变量a=200并非同一个作用域,因此aoo函数在执行的时候不会去搜索全局作用的a。那如果我们希望在aoo函数中修改全局作用域中的a呢?使用什么方法。
def aoo():
global a
a = 100
print(a)
if __name__ == '__main__':
a = 200
aoo() # 100
print(a) # 100
通过例子发现使用了global关键字来修改函数aoo中变量a的作用域,在全局作用域中没有a那么在接下来执行的代码中就会将函数中的a变量置于全局变量作用域。那么在函数内部如果存在函数嵌套并且希望修改嵌套函数中嵌套作用域中的变量,可以使用nonlocal关键字来指示变量来自于嵌套作用域。请看下面的例子
a = 1
def outer():
a = 2
def inner():
nonlocal a
a = 3
def inner2():
print(a) # 3 此处的a使用上层局部作用域
inner2()
print(a) # 3 此处使用a本层局部组用域
inner()
print(a) # 3 此处本该打印2,但是通过nonlocal关键字将变量a指示到嵌套作用域
outer()
print(a) # 1
作为新手在此处分享一个小坑,请注意如下报错结果:SyntaxError: no binding for nonlocal 'a' found,
这个报错的意思是说他没有找到变量a,就无法使用nonlocal关键字将指示变量来自嵌套作用域,因为该关键字只作用于局部变量,且始终当前最近的上层局部变量。
在实际的开发环境里,我们应当尽量减少对全局变量的使用,因为全局变量的作用域和影响太广泛了,可能会发生意料之外的情况,当然还是因为全局变量比局部变量拥有更长的生命周期,可能导致对象占用的内存长时间无法被垃圾回收,应该减少对全局变量的使用,也是降低代码之间耦合度的一个重要措施,减少全局变量就要意味着尽量将变量的作用域在函数的内部。那如何延续局部变量的周期呢?使其在定义它的函数结束以后依然可以使用它的值,这个时候就需要闭包。
为了养成良好的习惯建议按照以下编写python的格式进行编写python。
def main():
pass
if __name__ == '__main__':
main()