函数和模块的使用

在讲解本章节的内容之前,我们先来研究一道数学题,请说出下面的方程有多少组正整数解。

事实上,上面的问题等同于将8个苹果分成四组每组至少一个苹果有多少种方案。想到这一点问题的答案就呼之欲出了。

可以用Python的程序来计算出这个值,代码如下所示。

"""输入M和N计算C(M,N)"""m = int(input('m = '))n = int(input('n = '))fm = 1for num in range(1, m + 1):    fm *= numfn = 1for num in range(1, n + 1):    fn *= numfmn = 1for num in range(1, m - n + 1):    fmn *= numprint(fm // fn // fmn)

函数的作用

不知道大家是否注意到,在上面的代码中,我们做了3次求阶乘,这样的代码实际上就是重复代码。

编程大师Martin Fowler先生曾经说过:“代码有很多种坏味道,重复是最坏的一种!”,要写出高质量的代码首先要解决的就是重复代码的问题。

对于上面的代码来说,我们可以将计算阶乘的功能封装到一个称之为“函数”的功能模块中,在需要计算阶乘的地方,我们只需要“调用”这个“函数”就可以了。

定义函数

在Python中可以使用def关键字来定义函数,和变量一样每个函数也有一个响亮的名字,而且命名规则跟变量的命名规则是一致的。

在函数名后面的圆括号中可以放置传递给函数的参数,这一点和数学上的函数非常相似,程序中函数的参数就相当于是数学上说的函数的自变量,而函数执行完成后我们可以通过return关键字来返回一个值,这相当于数学上说的函数的因变量。

在了解了如何定义函数后,我们可以对上面的代码进行重构,所谓重构就是在不影响代码执行结果的前提下对代码的结构进行调整,重构之后的代码如下所示。

def factorial(num):    """求阶乘"""    result = 1    for n in range(1, num + 1):        result *= n    return resultm = int(input('m = '))n = int(input('n = '))# 当需要计算阶乘的时候不用再写循环求阶乘而是直接调用已经定义好的函数print(factorial(m) // factorial(n) // factorial(m - n))

说明: Python的math模块中其实已经有一个factorial函数了,事实上要计算阶乘可以直接使用这个现成的函数而不用自己定义。下面例子中的一些函数在Python中也都是现成的,我们这里是为了讲解函数的定义和使用才把它们又实现了一遍,实际开发中不建议做这种低级的重复性的工作。

函数的参数

函数是绝大多数编程语言中都支持的一个代码的"构建块",但是Python中的函数与其他语言中的函数还是有很多不太相同的地方,其中一个显著的区别就是Python对函数参数的处理。

在Python中,函数的参数可以有默认值,也支持使用可变参数,所以Python并不需要像其他语言一样支持函数的重载,因为我们在定义一个函数的时候可以让它有多种不同的使用方式,下面是两个小例子。

from random import randintdef roll_dice(n=2):    """摇色子"""    total = 0    for _ in range(n):        total += randint(1, 6)    return totaldef 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))

我们给上面两个函数的参数都设定了默认值,这也就意味着如果在调用函数的时候如果没有传入对应参数的值时将使用该参数的默认值。

所以在上面的代码中我们可以用各种不同的方式去调用add函数,这跟其他很多语言中函数重载的效果是一致的。

其实上面的add函数还有更好的实现方案,因为我们可能会对0个或多个参数进行加法运算,而具体有多少个参数是由调用者来决定,我们作为函数的设计者对这一点是一无所知的,因此在不确定参数个数的时候,我们可以使用可变参数,代码如下所示。

# 在参数名前面的*表示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))

用模块管理函数

对于任何一种编程语言来说,给变量、函数这样的标识符起名字都是一个让人头疼的问题,因为我们会遇到命名冲突这种尴尬的情况。

最简单的场景就是在同一个.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()

也可以按照如下所示的方式来区分到底要使用哪一个foo函数。

test.py

import module1 as m1import module2 as m2m1.foo()m2.foo()

但是如果将代码写成了下面的样子,那么程序中调用的是最后导入的那个foo,因为后导入的foo覆盖了之前导入的foo。

test.py

from module1 import foofrom module2 import foo# 输出goodbye, world!foo()

test.py

from module2 import foofrom module1 import foo# 输出hello, world!foo()

需要说明的是,如果我们导入的模块除了定义函数之外还中有可以执行代码,那么Python解释器在导入这个模块时就会执行这些代码,事实上我们可能并不希望如此。

因此如果我们在模块中编写了执行代码,最好是将这些执行代码放入如下所示的条件中,这样的话除非直接运行该模块,if条件下的这些代码是不会执行的,因为只有直接执行的模块的名字才是"__main__"。

module3.py

def foo():    passdef 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__

练习

练习1:实现计算求最大公约数和最小公倍数的函数。

参考答案:

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 factordef lcm(x, y):    """求最小公倍数"""    return x * y // gcd(x, y)

练习2:实现判断一个数是不是回文数的函数。

参考答案:

def is_palindrome(num):    """判断一个数是不是回文数"""    temp = num    total = 0    while temp > 0:        total = total * 10 + temp % 10        temp //= 10    return total == num

练习3:实现判断一个数是不是素数的函数。

参考答案:

def is_prime(num):    """判断一个数是不是素数"""    for factor in range(2, num):        if num % factor == 0:            return False    return True if num != 1 else False

练习4:写一个程序判断输入的正整数是不是回文素数。

参考答案:

if __name__ == '__main__':    num = int(input('请输入正整数: '))    if is_palindrome(num) and is_prime(num):        print('%d是回文素数' % num)

注意:通过上面的程序可以看出,当我们将代码中重复出现的和相对独立的功能抽取成函数后,我们可以组合使用这些函数来解决更为复杂的问题,这也是我们为什么要定义和使用函数的一个非常重要的原因。

最后,我们来讨论一下Python中有关变量作用域的问题。

def foo():    b = 'hello'    # Python中可以在函数内部再定义函数    def bar():        c = True        print(a)        print(b)        print(c)    bar()    # print(c)  # NameError: name 'c' is not definedif __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等都属于内置作用域。

再看看下面这段代码,我们希望通过函数调用修改全局变量a的值,但实际上下面的代码是做不到的。

def foo():    a = 200    print(a)  # 200if __name__ == '__main__':    a = 100    foo()    print(a)  # 100

在调用foo函数后,我们发现a的值仍然是100,这是因为当我们在函数foo中写a = 200的时候,是重新定义了一个名字为a的局部变量,它跟全局作用域的a并不是同一个变量,因为局部作用域中有了自己的变量a。

因此foo函数不再搜索全局作用域中的a。如果我们希望在foo函数中修改全局作用域中的a,代码如下所示。

def foo():    global a    a = 200    print(a)  # 200if __name__ == '__main__':    a = 100    foo()    print(a)  # 200

我们可以使用global关键字来指示foo函数中的变量a来自于全局作用域,如果全局作用域中没有a,那么下面一行的代码就会定义变量a并将其置于全局作用域。

同理,如果我们希望函数内部的函数能够修改嵌套作用域中的变量,可以使用nonlocal关键字来指示变量来自于嵌套作用域,请大家自行试验。

在实际开发中,我们应该尽量减少对全局变量的使用,因为全局变量的作用域和影响过于广泛,可能会发生意料之外的修改和使用,除此之外全局变量比局部变量拥有更长的生命周期,可能导致对象占用的内存长时间无法被垃圾回收。

事实上,减少对全局变量的使用,也是降低代码之间耦合度的一个重要举措,同时也是对迪米特法则的践行。

减少全局变量的使用就意味着我们应该尽量让变量的作用域在函数的内部,但是如果我们希望将一个局部变量的生命周期延长,使其在定义它的函数调用结束后依然可以使用它的值,这时候就需要使用闭包,这个我们在后续的内容中进行讲解。

说明: 很多人经常会将“闭包”和“匿名函数”混为一谈,但实际上它们并不是一回事,如果想了解这个概念,可以看看维基百科的解释或者知乎上对这个概念的讨论。

说了那么多,其实结论很简单,从现在开始我们可以将Python代码按照下面的格式进行书写,这一点点的改进其实就是在我们理解了函数和作用域的基础上跨出的巨大的一步。

def main():    # Todo: Add your code here    passif __name__ == '__main__':    main()

python 用if判断一个数是不是整数_五天学会Python基础02(下)相关推荐

  1. python 用if判断一个数是不是整数_Python基础教程07-函数和模块的使用

    在讲解本章节的内容之前,我们先来研究一道数学题,请说出下面的方程有多少组正整数解. x1+x2+x3+x4=8 事实上,上面的问题等同于将8个苹果分成四组每组至少一个苹果有多少种方案.想到这一点问题的 ...

  2. python判断一个数是整数、浮点数还是字符串

    python判断一个数是整数.浮点数还是字符串 示例代码:

  3. 判断一个数是不是整数

    <?php //判断一个数是整数吗? function check($num){if($num>1){return (check($num-1));//采用递归,一直减到0到1之间 }el ...

  4. python 如何判断一个数为整数?(判断整数,没有小数)(取余)判断整型 isinstance()

    方法1 判断是否为整数(即使不为整型) # -*- coding: utf-8 -*- """ @File : test.py @Time : 2020/6/25 11: ...

  5. # 关于如何判断一个数是不是整数的方法

    问题是从一个题目里发现的,当时感觉很奇怪,一个数开根号以后,判断它是不是一个整数: 代码如下: #include"stdio.h" #include"math.h&quo ...

  6. 【JavaScript 教程系列第 10 篇】判断一个数是整数还是小数

    这是[JavaScript 教程系列第 10 篇],如果觉得有用的话,欢迎关注专栏. 思路 如果一个数是整数,那么 parseInt() 函数和 parseFloat() 函数的返回值是相同的,反之返 ...

  7. js判断字符串是否为空_每日一课 | Python 如何判断一个字符串是否包含另一个字符串?...

    在Python中,我们可以使用in运算符或str.find()来检查一个字符串是否包含另一个字符串. 1.运算符 name = "mkyong is learning python 123& ...

  8. python对浮点类型的数据进行格式化_(自用)Python Log2 数据类型、字符编码、格式化...

    数据类型 1.整数 十六进制可以使用0x+数字0-9(字母a-f). 2.浮点数 一般使用科学计数法,用e代替10,比如1.2e5,为1.2×10^5. 3.字符串 可以使用单引号' ',或者双引号& ...

  9. 用python内置函数算复杂度吗_番外篇: Python 面试感受

    醒来有人说我刁难面试者, 我来解释一下, 我的面试环节一般是最后一个环节才会进行 python 基础的面试. 之前都是项目交流, 进入到最后这个环节的, 都是我认为项目 OK 的, 我对他的能力也是认 ...

最新文章

  1. FATE 集群部署 step3
  2. leetcode算法题--Pyramid Transition Matrix
  3. python垃圾回收 (GC) 机制
  4. 2017腾讯编程题----游戏人物标记
  5. c++ 条码打印机句柄画图_FBA发货前期需要准备哪些工作?FBA发货准备工作、打印机要求介绍...
  6. H5新增的标签和属性
  7. python爬虫开发数据库设计入门经典_Python3实现的爬虫爬取数据并存入mysql数据库操作示例...
  8. android gone动画_Android动画之淡入淡出
  9. 企业微信:腾讯的“佛系”办公江湖
  10. Leetcode算法题(C语言)3--买卖股票的最佳时机 II
  11. 手把手教你使用 Clion 开发 Linux C++ 项目
  12. 如何解决layui下select组件大数据卡顿问题
  13. ArcGIS自动矢量化~
  14. matlab音频指纹识别_指纹识别算法matlab实现.doc
  15. GTK+的编译还真麻烦
  16. 汇编语言与接口技术——期末设计
  17. erp系统不能连接服务器配置,erp系统云服务器怎么配置
  18. Centos 7 matlab2018a安装及键盘无法输入的问题
  19. 运动目标检测--背景减法
  20. 灰狼优化算法(Grey Wolf Optimizer, GWO)

热门文章

  1. 企业办公自动化系统_办公自动化怎么理解?如何实现?
  2. 常用的php函数,PHP常用函数整理(上)
  3. 美国伊利诺伊大学香槟分校计算机专业,伊利诺伊大学香槟分校计算机科学排名第7(2020年TFE美国排名)...
  4. 怎么设置电脑屏幕一直亮着_电脑屏幕出现条纹是什么原因 花屏怎么解决?
  5. Sublime中增加格式化代码的快捷键
  6. 确认了!西湖大学将开始招收本科生:首批五个专业
  7. GitHub上3天1W赞的程序员学习路线!入门进阶都非常实用
  8. 知乎上高赞的40个有趣回复,很精辟!
  9. 爬取《哪吒》豆瓣短评,我得到了什么?
  10. html中的特性是什么,数据库的四大特性是什么