第003天
函数和模块的使用
定义函数
在python中可以使用def关键字来定义函数,和变量一样每个函数也有一个名字,而且命名规则和变量的命名规则是一致的。在函数名后面的圆括号中可以放置传递给函数的参数,程序中函数的参数就是相当于数学上说的函数的自变量,函数执行完成后可以通过return关键字来返回一个值,这相当于数学上说的函数的因变量。
例:
排列组合,输入M和N计算C(M,N)
有一个经典算法:C(m, n) = (m!) / n! / (m-n)!
def fac(num):
result = 1
for n in range(1, num + 1):
result *= n
return result
m = int(input('m = '))
n = int(input('n = '))
# 当需要计算阶层的时候不用再写循环求阶乘而是直接调用已经定义好的函数
print(fac(m) // fac(n) // fac(m - n))
说明:python的math模块中其实已经有一个名为factorial函数实现了阶乘运算,事实上求阶乘并不用自己定义函数。
函数的参数
函数是绝大多数变成语言中都支持的一个代码的“构建块”,但是python中函数与其它语言中的函数还是有很多不太相同的地方,其中一个显著的区别就是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 add(a=0, b=0, c=0):
"""三个数相加"""
return a + b + c
# 如果没有指定参数那么使用默认值摇两个色子
print(roll_dice())
# 摇三个色子
print(roll_dice(3))
print(add())
print(add(1))
print(add(1, 2))
print(add(1, 2, 3))
# 传递参数时可以不按照设定的顺序进行传递
print(add(c=50, a=100, b=200))
执行结果
2
16
0
1
3
6
350
上面两个函数都设定了默认值,这就意味着如果在调用函数的时候如果没有传入对应参数的值时将使用该参数的默认值,所以在上面的代码中可以使用各种不同的方式去调用add函数。
在不确定参数个数的时候,可以使用可变参数
# 在参数名前面的*args是一个可变参数
def add(*args):
total = 0
for val in args:
total += val
return total
# 在调用add函数时可以传入0个或多个参数
print(add())
print(add(1))
print(add(1, 2))
print(add(1, 2, 3))
print(add(1, 3, 5, 7, 9))
执行结果如下:
0
1
3
6
25
*args
*args 不定参数(不定的意思是指,预先并不知道,函数使用者会传递多少个参数给你)
*args是用来发送一个非键值对的可变数量的参数列表给一个函数。
*args的用法:当传入的参数个数未知,且不需要知道参数名称时。
用模块管理函数
由于python中没有函数重载的概念,如果在同一个.py文件中定义了两个同名函数,后面的定义会覆盖之前的定义,也就是意味着两个函数同名函数实际上只有一个时存在的
def foo():
print('hello, world!')
def foo():
print('goodbye, world')
foo()
执行结果:
goodbye, world
(精通各种语言写的hello,world)
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函数
test2.py
import module1 as m1
import module2 as m2
m1.foo()
m2.foo()
输出结果:
hello, world!
goodbye, world!
注意上述代码在引用时,要么是引用该模块化文件以后立即执行该函数,要么就是在引用中利用不同的名字将其区分开,如果按照下面的方式,会导致后导入的foo覆盖之前导入的foo
test3.py
from module1 import foo
from module2 import foo
foo()
输出结果:
goodbye, world!
test4.py
from module2 import foo
from module1 import foo
foo()
输出结果:
hello, world!
说明:
如果我们导入的模块除了定义函数之外还有可执行代码,那么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()
test5.py
import module3
# 导入module3时,不会执行模块中if条件成立时的代码,因为模块的名字时module3而不是__main__
补充:
1、在python中,凡是以两个下划线开头,两个下划线结尾的变量叫做“魔法变量”,可以简单理解为魔法变量是python对象内置天生就有的属性变量。
首先创建一个名为 demo.py的python文件,里面只有一句代码
print(__name__)
直接在某个模块打印它自己的__name__属性,值就是__main__,这代表当前模块正在以“直接运行”的方式在运行
接下来,将这个模块导入到别的python文件中(本人创建的为study.py)
study.py
import demo
执行
当模块A被导入到模块B时,一旦运行模块B,模块A中的语句会自动被执行一遍,以便加载模块A中所有的函数定义、类定义等语句到内存中等待被使用。所以,当运行study.py文件时,其实就相当于自动运行了一次demo.py这个文件,第二点,当模块是以“被导入”的方式运行时,它的__name__属性会自动变成该模块的名字,所以study.py打印出来的是demo。
所以:if name == "main"的含义:
这个if语句只有当这个模块被直接运行时才会满足,当这个模块被导入到别的模块时是不会被满足的。所以,凡是想让某些代码只在当前模块下运行时,就把该模块的代码放在该if语句下面。
练习
1、实现计算最大公约数和最小公倍数的函数
import math
x = int(input('x = '))
y = int(input('y = '))
# 返回最大公约数函数
def max_factor():
m = max(x, y)
n = min(x, y)
r = m % n
while r != 0:
m = n
n = r
r = m % n
return n
# 返回最小公倍数
def min_multiple():
min_num = x * y / max_factor()
return min_num
print('最大公因数是%d,最小公倍数是%d' % (min_multiple(), min_multiple()))
或者:
def gcd(x, y):
"""求最大公约数"""
(x, y) = (y, x) if x > y else (x, y)
for factor in range(x, 0, -1):
if x % factor == 0 and y % factor == 0:
return factor
def lcm(x, f):
"""求最小公倍数"""
return x * y // gcd(x, y)
求最小公约数见的还挺多的,这里再写一下它的思想吧。
辗转相除法
1)找出两个数的大值和小值
2)定义一个临时变量等于大值%小值
3)重复1)直到临时变量为0,本次计算中的小值就是最大公约数
暴力法
1)找打两个数的大值和小值
2)从小值-1开始循环,步长为-1
3)当大值和小值%某数都为0时,该数为最大公约数
练习2、实现判断一个数是不是回文数的函数
def is_palindrome(num):
"""判断一个数是不是回文数"""
temp = num
total = 0
while temp > 0:
total = total * 10 + temp % 10
temp //= 10
return total == num
算法思想:
回文,是指顺序和反序是一样的。类似于12321、反过来也是12321。这个时候就需要考虑如何将它的每位翻转过来。通过每次取到最低位,并且将每次×10加上新取到的最低位来达到该效果。(num%10可以取到最低位),然后此时不知道它判断多少次,所以选用while循环,每次取了最低位以后需要将该数字除以10,当个位时,它除以10就是0。所以判断条件可以考虑temp > 0。
练习3:实现判断一个数是不是素数的函数
def is_prime(num):
for x in range(2, int(num ** 0.5) + 1):
if num % x == 0:
return False
return True if num != 1 else False # 要把1排除
在考虑二元对立的问题时,可以从两个方向出发考虑。不是XX便是XX
练习4、写一个程序判断输入的正整数是不是回文素数
def is_palindrome(num):
temp = num
total = 0
while temp > 0:
total = total * 10 + temp % 10
temp //= 10
return total == num
def is_prime(num):
for x in range(2, int(num ** 0.5) + 1):
if num % x == 0:
return False
return True if num != 1 else False
if __name__ == '__main__':
num = int(input('请输入你想判断的数字: '))
if is_palindrome(num) and is_prime(num):
print('是的,它是一个回文素数')
变量的作用域
def foo():
b = 'hello'
# python中可以在函数内部再定义函数
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
我们在上面代码的if分支中定义了一个变量a,这是一个全局变量(global variable),属于全局作用域,因为它没有定义在任何一个函数中,在上面的foo函数中我们定义了变量b,这是一个定义在函数中的局部变量(local varible),属于局部作用域,在foo函数的外部并不能访问到它;但对于foo函数内部的bar函数来说,变量b属于嵌套作用域,在bar函数中我们是可以访问到它的。bar函数中的变量c属于局部作用域,在bar函数之外是无法访问的。事实上,python查找一个变量时会按照“局部作用域”“嵌套作用域”“全局作用域”和“内置作用域”的顺序进行搜索,我们之前使用的 input、print、int 等都属内置作用域
从现在开始可以按照如下格式进行书写
def main():
# Todo: Add your code here
pass
if __name__ == '__main__':
main