CSDN时雨h PYTHON笔记汇总(不定时会有更新)

简介: 来自github python100天的笔记总结

来自github python100天的笔记总结

Python的优缺点


Python的优点很多,简单的可以总结为以下几点。


简单明了,学习曲线低,比很多编程语言都容易上手。


开放源代码,拥有强大的社区和生态圈,尤其是在数据分析和机器学习领域。


解释型语言,天生具有平台可移植性,代码可以工作于不同的操作系统。


对两种主流的编程范式(面向对象编程和函数式编程)都提供了支持。


代码规范程度高,可读性强,适合有代码洁癖和强迫症的人群。


Python的缺点主要集中在以下几点。


执行效率稍低,对执行效率要求高的部分可以由其他语言(如:C、C++)编写。


代码无法加密,但是现在很多公司都不销售卖软件而是销售服务,这个问题会被弱化。


在开发时可以选择的框架太多(如Web框架就有100多个),有选择的地方就有错误。


Python的应用领域


目前Python在Web应用后端开发、云基础设施建设、DevOps、网络数据采集(爬虫)、自动化测试、数据分析、机器学习等领域都有着广泛的应用。


玩转PyCharm


用PyCharm创建项目


启动PyCharm之后会来到一个欢迎页,在欢迎页上我们可以选择“创建新项目”(Create New Project)、“打开已有项目”(Open)和“从版本控制系统中检出项目”(Get from Version Control)。


如果选择了“Create New Project”来创建新项目就会打一个创建项目的向导页。下图所示是PyCharm专业版创建新项目的向导页,可以看出专业版支持的项目类型非常的多,而社区版只能创建纯Python项目(Pure Python),没有这一系列的选项。


接下来,我们要为项目创建专属的虚拟环境,每个Python项目最好都在自己专属的虚拟环境中运行,因为每个项目对Python解释器和三方库的需求并不相同,虚拟环境对不同的项目进行了隔离。在上图所示的界面在,我们可以选择新建虚拟环境(New environment using Virtualenv),这里的“Virtualenv”是PyCharm默认选择的创建虚拟环境的工具,我们就保留这个默认的选项就可以了。


项目创建完成后就可以开始新建各种文件来书写Python代码了,如下图所示。左侧是项目浏览器,可以看到刚才创建的项目文件夹以及虚拟环境文件夹。我们可以在项目上点击鼠标右键,选择“New”,在选择“Python File”来创建Python代码文件,下图中我们创建了两个Python文件,分别是poker_game.py和salary_system.py。当然,如果愿意,也可以使用复制粘贴的方式把其他地方的Python代码文件复制到项目文件夹下。


在工作窗口点击鼠标右键可以在上下文菜单中找到“Run”选项,例如要运行salary_system.py文件,右键菜单会显示“Run 'salary_system'”选项,点击这个选项我们就可以运行Python代码啦,运行结果在屏幕下方的窗口可以看到,如下图所示。


常用操作和快捷键


PyCharm为写Python代码提供了自动补全和高亮语法功能,这也是PyCharm作为集成开发环境(IDE)的基本功能。PyCharm的“File”菜单有一个“Settings”菜单项(macOS上是在“PyCharm”菜单的“Preferences…”菜单项),这个菜单项会打开设置窗口,可以在此处对PyCharm进行设置,如下图所示。


PyCharm的菜单项中有一个非常有用的“Code”菜单,菜单中提供了自动生成代码、自动补全代码、格式化代码、移动代码等选项,这些功能对开发者来说是非常有用的,大家可以尝试使用这些菜单项或者记住它们对应的快捷键,例如在macOS上,格式化代码这个菜单项对应的快捷键是alt+command+L。除此之外,“Refactor”菜单也非常有用,它提供了一些重构代码的选项。所谓重构是在不改变代码执行结果的前提下调整代码的结构,这也是资深程序员的一项重要技能。还有一个值得一提的菜单是“VCS”,VCS是“Version Control System”(版本控制系统)的缩写,这个菜单提供了对代码版本管理的支持。版本控制的知识会在其他的课程中为大家讲解。


下表列出了一些PyCharm中特别常用的快捷键,当然如果愿意,也可以通过设置窗口中“Keymap”菜单项自定义快捷键,PyCharm本身也针对不同的操作系统和使用习惯对快捷键进行了分组。


快捷键


作用


command + j


显示可用的代码模板


command + b


查看函数、类、方法的定义


ctrl + space


万能代码提示快捷键,一下不行按两下


command + alt + l


格式化代码


alt + enter


万能代码修复快捷键


ctrl + /


注释/反注释代码


shift + shift


万能搜索快捷键


command + d / command + y


复制/删除一行代码


command + shift + - / command + shift + +


折叠/展开所有代码


F2


快速定位到错误代码


command+ alt + F7


查看哪些地方用到了指定的函数、类、方法


说明:Windows系统下如果使用PyCharm的默认设置,可以将上面的command键换成ctrl键即可,唯一的例外是ctrl + space那个快捷键,因为它跟Windows系统切换输入法的快捷键是冲突的,所以在Windows系统下默认没有与之对应的快捷键。


语言元素


指令和程序


计算机的硬件系统通常由五大部件构成,包括:运算器、控制器、存储器、输入设备和输出设备。其中,运算器和控制器放在一起就是我们通常所说的中央处理器,它的功能是执行各种运算和控制指令以及处理计算机软件中的数据。我们通常所说的程序实际上就是指令的集合,我们程序就是将一系列的指令按照某种方式组织到一起,然后通过这些指令去控制计算机做我们想让它做的事情。


今天的计算机也是冯诺依曼结构:


“冯·诺依曼结构”有两个关键点,一是指出要将存储设备与中央处理器分开,二是提出了将数据以二进制方式编码。


进制转换有关问题:


了解1?二进制以及它与我们生活中的十进制之间的转换关系,以及2?二进制与八进制和十六进制的转换关系还是有必要的。


量子计算机:


变量和类型:


变量是一种存储数据的载体。计算机中的变量是实际存在的数据或者说是存储器中存储数据的一块内存空间,变量的值可以被读取和修改,这是所有计算和控制的基础。计算机能处理的数据有很多种类型,除了数值之外还可以处理文本、图形、音频、视频等各种各样的数据,那么不同的数据就需要定义不同的存储类型。


整型:在Python 3.x中整数只有int这一种了(而且支持二进制(如0b100,换算成十进制是4)、八进制(如0o100,换算成十进制是64)、十进制(100)和十六进制(0x100,换算成十进制是256)的表示法。)


浮点型:浮点数也就是小数,之所以称为浮点数,是因为按照科学记数法表示时,一个浮点数的小数点位置是可变的,浮点数除了数学写法(如123.456)之外还支持科学计数法(如1.23456e2)。


字符串型:字符串是以单引号或双引号括起来的任意文本,比如'hello'和"hello",字符串还有原始字符串表示法、字节字符串表示法、Unicode字符串表示法,而且可以书写成多行的形式(用三个单引号或三个双引号开头,三个单引号或三个双引号结尾)。


布尔型:布尔值只有True、False两种值,要么是True,要么是False,在Python中,可以直接用True、False表示布尔值(请注意大小写),也可以通过布尔运算计算出来(例如3 < 5会产生布尔值True,而2 == 1会产生布尔值False)。


复数型:形如3+5j,跟数学上的复数表示一样,唯一不同的是虚部的i换成了j。实际上,这个类型并不常用,了解一下就可以了。


变量命名


每个人都需要自己的一个姓名


在Python中,变量命名需要遵循以下这些必须遵守硬性规则和强烈建议遵守的非硬性规则。


硬性规则:


变量名由字母(广义的Unicode字符,不包括特殊字符)、数字和下划线构成,数字不能开头。


大小写敏感(大写的a和小写的A是两个不同的变量)。


不要跟关键字(有特殊含义的单词,后面会讲到)和系统保留字(如函数、模块等的名字)冲突。


PEP 8要求:


用小写字母拼写,多个单词用下划线连接。


受保护的实例属性用单个下划线开头(后面会讲到)。


私有的实例属性用两个下划线开头(后面会讲到)。


Python里的关键字:


Python 包含的保留字可以执行如下命令进行查看:


>>> import keyword

>>> keyword.kwlist

['False', 'None', 'True', 'and', 'as', 'assert', 'break', 'class', 'continue', 'def', 'del', 'elif', 'else', 'except', 'finally', 'for', 'from', 'global', 'if', 'import', 'in', 'is', 'lambda', 'nonlocal', 'not', 'or', 'pass', 'raise', 'return', 'try', 'while', 'with', 'yield']


需要注意的是,由于 Python 是严格区分大小写的,保留字也不例外。所以,我们可以说 if 是保留字,但 IF 就不是保留字。

在实际开发中,如果使用 Python 中的保留字作为标识符,则解释器会提示“invalid syntax” 的错误信息,如图 2 所示。


在Python中可以使用type函数对变量的类型进行检查。


a = 100b = 12.345c = 1 + 5jd = 'hello, world'e = Trueprint(type(a))    # <class 'int'>print(type(b))    # <class 'float'>print(type(c))    # <class 'complex'>print(type(d))    # <class 'str'>print(type(e))    # <class 'bool'>


可以使用Python中内置的函数对变量类型进行转换。


int():将一个数值或字符串转换成整数,可以指定进制。


float():将一个字符串转换成浮点数。


str():将指定的对象转换成字符串形式,可以指定编码。


chr():将整数转换成该编码对应的字符串(一个字符)。


ord():将字符串(一个字符)转换成对应的编码(整数)。


对print函数的说明:


print函数中输出的字符串使用了占位符语法,其中%d是整数的占位符,%f是小数的占位符,%%表示百分号(因为百分号代表了占位符,所以带占位符的字符串中要表示百分号必须写成%%),字符串之后的%后面跟的变量值会替换掉占位符然后输出到终端中


运算符

运算符的优先级指的是多个运算符同时出现时,先做什么运算然后再做什么运算。除了我们之前已经用过的赋值运算符和算术运算符,我们稍后会陆续讲到其他运算符的使用。


运算符


描述


[] [:]


下标,切片


**


指数


~ + -


按位取反, 正负号


* / % //


乘,除,模,整除


+ -


加,减


>> <<


右移,左移


&


按位与


^ |


按位异或,按位或


<= < > >=


小于等于,小于,大于,大于等于


== !=


等于,不等于


is is not


身份运算符


in not in


成员运算符


not or and


逻辑运算符


= += -= *= /= %= //= **= &= `

= ^= >>= <<=`


说明: 在实际开发中,如果搞不清楚运算符的优先级,可以使用括号来确保运算的执行顺序。


a += b        # 相当于:a = a + b


a *= a + 2    # 相当于:a = a * (a + 2)


比较运算符唯一需要提醒的是比较相等用的是==,请注意这个地方是两个等号,因为=是赋值运算符,我们在上面刚刚讲到过,==才是比较相等的比较运算符。比较运算符会产生布尔值,要么是True要么是False。


逻辑运算符


and字面意思是“而且”


or字面意思是“或者”


not运算符的后面会跟上一个布尔值,它的作用是得到与该布尔值相反的值,也就是说,后面的布尔值如果是True运算结果就是False,而后面的布尔值如果是False则运算结果就是True。


分支结构:


if语句的使用


username = input('请输入用户名: ')


password = input('请输入口令: ')# 用户名是admin且密码是123456则身份验证成 功否则身份验证失败


if username == 'admin' and password == '123456':


   print('身份验证成功!')else:


print('身份验证失败!')


Tip:需要说明的是和C/C++、Java等语言不同,Python中没有用花括号来构造代码块而是使用了缩进的方式来表示代码的层次结构,如果条件成立的情况下需要执行多条语句,只要保持多条语句具有相同的缩进就可以了。换句话说连续的代码如果又保持了相同的缩进那么它们属于同一个代码块,相当于是一个执行的整体。缩进可以使用任意数量的空格,但通常使用4个空格,建议大家不要使用制表键或者设置你的代码编辑工具自动将制表键变成4个空格。


当然如果要构造出更多的分支,可以使用结构或者嵌套的结构,下面的代码演示了如何利用多分支结构实现分段函数求值。if...elif...else...if...else...


x = float(input('x = '))if x > 1:

   y = 3 * x - 5elif x >= -1:

   y = x + 2else:

y = 5 * x + 3print('f(%.2f) = %.2f' % (x, y))


当然根据实际开发的需要,分支结构是可以嵌套的,例如判断是否通关以后还要根据你获得的宝物或者道具的数量对你的表现给出等级(比如点亮两颗或三颗星星),那么我们就需要在的内部构造出一个新的分支结构,同理和中也可以再构造新的分支,我们称之为嵌套的分支结构,也就是说上面的代码也可以写成下面的样子。


x = float(input('x = '))if x > 1:

   y = 3 * x - 5else:

   if x >= -1:

       y = x + 2

   else:

      y = 5 * x + 3print('f(%.2f) = %.2f' % (x, y))


循环结构


应用场景


我们在写程序的时候,一定会遇到需要重复执行某条或某些指令的场景。例如用程序控制机器人踢足球,如果机器人持球而且还没有进入射门范围,那么我们就要一直发出让机器人向球门方向移动的指令。在这个场景中,让机器人向球门方向移动就是一个需要重复的动作,当然这里还会用到上一课讲的分支结构来判断机器人是否持球以及是否进入射门范围。再举一个简单的例子,如果要实现每隔1秒中在屏幕上打印一次“hello, world”并持续打印一个小时,我们肯定不能够直接把这句代码写3600遍,这里同样需要循环结构。print('hello, world')


循环结构就是程序中控制某条或某些指令重复执行的结构。在Python中构造循环结构有两种做法,一种是循环,一种是循环。for-inwhile


for-in循环


如果明确的知道循环执行的次数或者要对一个容器进行迭代(后面会讲到),那么我们推荐使用循环,例如下面代码中计算1~100求和的结果


"""用for循环实现1~100求和Version: 0.1Author: 骆昊"""


sum = 0for x in range(101):


sum += xprint(sum)


依次取出从1到100的整数。当然的用法非常灵活,下面给出了一个例子(取随机数的不同表示法):


range(101):可以用来产生0到100范围的整数,需要注意的是取不到101。

range(1, 101):可以用来产生1到100范围的整数,相当于前面是闭区间后面是开区间。

range(1, 101, 2):可以用来产生1到100的奇数,其中2是步长,即每次数值递增的值。

range(100, 0, -2):可以用来产生100到1的偶数,其中-2是步长,即每次数字递减的值

知道了这一点,我们可以用下面的代码来实现1~100之间的偶数求和。


"""用for循环实现1~100之间的偶数求和Version: 0.1Author: 骆昊"""


sum = 0for x in range(2, 101, 2):

   sum += xprint(sum)


当然,也可以通过在循环中使用分支结构的方式来实现相同的功能,代码如下所示。


"""用for循环实现1~100之间的偶数求和Version: 0.1Author: 骆昊"""


sum = 0for x in range(1, 101):

   if x % 2 == 0:

       sum += xprint(sum)


说明:相较于上面直接跳过奇数的做法,下面这种做法很明显并不是很好的选择。


(代码的运行时间也很重要!!!,不要冗杂!!!)


Zen of Python(Python之禅)(拓展)


while循环


如果要构造不知道具体循环次数的循环结构,我们推荐使用循环。循环通过一个能够产生或转换出值的表达式来控制循环,表达式的值为则继续循环;表达式的值为则结束循环。


While  while  bool  True  False


下面我们通过一个"猜数字"的小游戏来看看如何使用循环。猜数字游戏的规则是:计算机出一个1到100之间的随机数,玩家输入自己猜的数字,计算机给出对应的提示信息(大一点、小一点或猜对了),如果玩家猜中了数字,计算机提示用户一共猜了多少次,游戏结束,否则游戏继续。while


"""猜数字游戏Version: 0.1Author: 骆昊"""import random

answer = random.randint(1, 100)counter = 0while True:

   counter += 1

   number = int(input('请输入: '))

   if number < answer:

       print('大一点')

   elif number > answer:

       print('小一点')

   else:

       print('恭喜你猜对了!')

       breakprint('你总共猜了%d次' % counter)if counter > 7:

print('你的智商余额明显不足')

上面的代码中使用了关键字来提前终止循环,需要注意的是只能终止它所在的那个循环,这一点在使用嵌套的循环结构(下面会讲到)需要引起注意。除了之外,还有另一个关键字是,它可以用来放弃本次循环后续的代码直接让循环进入下一轮。Break  break  break  continue


和分支结构一样,循环结构也是可以嵌套的,也就是说在循环中还可以构造循环结构。下面的例子演示了如何通过嵌套的循环来输出一个九九乘法表。


"""输出乘法口诀表(九九表)Version: 0.1Author: 骆昊"""


for i in range(1, 10):

   for j in range(1, i + 1):

       print('%d*%d=%d' % (i, j, i * j), end='\t')

   print()


构造程序逻辑:


但是这些内容已经足够我们来构建程序中的逻辑。对于编程语言的初学者来说,在学习了Python的核心语言元素(变量、类型、运算符、表达式、分支结构、循环结构等)之后,必须做的一件事情就是尝试用所学知识去解决现实中的问题,换句话说就是锻炼自己把用人类自然语言描述的算法(解决问题的方法和步骤)翻译成Python代码的能力,而这件事情必须通过大量的练习才能达成。


我们在本章为大家整理了一些经典的案例和习题,希望通过这些例子,一方面帮助大家巩固之前所学的Python知识,另一方面帮助大家了解如何建立程序中的逻辑以及如何运用一些简单的算法解决现实中的问题。


经典的例子


寻找水仙花数


说明:水仙花数也被称为超完全数字不变数、自恋数、自幂数、阿姆斯特朗数,它是一个3位数,该数字每个位上数字的立方之和正好等于它本身,例如:$1^3 + 5^3+ 3^3=153$。


"""找出所有水仙花数Version: 0.1Author: 骆昊"""

for num in range(100, 1000):

   low = num % 10

   mid = num // 10 % 10

   high = num // 100

   if num == low ** 3 + mid ** 3 + high ** 3:

       print(num)


在上面的代码中,我们通过整除和求模运算分别找出了一个三位数的个位、十位和百位,这种小技巧在实际开发中还是常用的。用类似的方法,我们还可以实现将一个正整数反转,例如:将12345变成54321,代码如下所示。


正整数的反转


Version: 0.1Author: 骆昊"""

num = int(input('num = '))reversed_num = 0while num > 0:

   reversed_num = reversed_num * 10 + num % 10

   num //= 10print(reversed_num)


百钱百鸡问题。


说明:百钱百鸡是我国古代数学家张丘建在《算经》一书中提出的数学问题:鸡翁一值钱五,鸡母一值钱三,鸡雏三值钱一。百钱买百鸡,问鸡翁、鸡母、鸡雏各几何?翻译成现代文是:公鸡5元一只,母鸡3元一只,小鸡1元三只,用100块钱买一百只鸡,问公鸡、母鸡、小鸡各有多少只?


"""《百钱百鸡》问题Version: 0.1Author: 骆昊"""

for x in range(0, 20):

   for y in range(0, 33):

       z = 100 - x - y

       if 5 * x + 3 * y + z / 3 == 100:

           print('公鸡: %d只, 母鸡: %d只, 小鸡: %d只' % (x, y, z))


上面使用的方法叫做穷举法,也称为暴力搜索法,这种方法通过一项一项的列举备选解决方案中所有可能的候选项并检查每个候选项是否符合问题的描述,最终得到问题的解。这种方法看起来比较笨拙,但对于运算能力非常强大的计算机来说,通常都是一个可行的甚至是不错的选择,而且问题的解如果存在,这种方法一定能够找到它。


CRAPS赌博游戏。


说明:CRAPS又称花旗骰,是美国拉斯维加斯非常受欢迎的一种的桌上赌博游戏。该游戏使用两粒骰子,玩家通过摇两粒骰子获得点数进行游戏。简单的规则是:玩家第一次摇骰子如果摇出了7点或11点,玩家胜;玩家第一次如果摇出2点、3点或12点,庄家胜;其他点数玩家继续摇骰子,如果玩家摇出了7点,庄家胜;如果玩家摇出了第一次摇的点数,玩家胜;其他点数,玩家继续要骰子,直到分出胜负。


"""Craps赌博游戏我们设定玩家开始游戏时有1000元的赌注游戏结束的条件是玩家输光所有的赌注Version: 0.1Author: 骆昊"""from random import randint

money = 1000while money > 0:

   print('你的总资产为:', money)

   needs_go_on = False

   while True:

       debt = int(input('请下注: '))

       if 0 < debt <= money:

           break

   first = randint(1, 6) + randint(1, 6)

   print('玩家摇出了%d点' % first)

   if first == 7 or first == 11:

       print('玩家胜!')

       money += debt

   elif first == 2 or first == 3 or first == 12:

       print('庄家胜!')

       money -= debt

   else

       needs_go_on = True

   while needs_go_on:

       needs_go_on = False

       current = randint(1, 6) + randint(1, 6)

       print('玩家摇出了%d点' % current)

       if current == 7:

           print('庄家胜')

           money -= debt

       elif current == first:

           print('玩家胜')

           money += debt

       else:

           needs_go_on = Trueprint('你破产了, 游戏结束!')

###有用的练习


生成斐波那契数列的前20个数。


说明:斐波那契数列(Fibonacci sequence),又称黄金分割数列,是意大利数学家莱昂纳多·斐波那契(Leonardoda Fibonacci)在《计算之书》中提出一个在理想假设条件下兔子成长率的问题而引入的数列,所以这个数列也被戏称为"兔子数列"。斐波那契数列的特点是数列的前两个数都是1,从第三个数开始,每个数都是它前面两个数的和,形如:1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, ...。斐波那契数列在现代物理、准晶体结构、化学等领域都有直接的应用。


找出10000以内的完美数。


说明:完美数又称为完全数或完备数,它的所有的真因子(即除了自身以外的因子)的和(即因子函数)恰好等于它本身。例如:6($6=1+2+3$)和28($28=1+2+4+7+14$)就是完美数。完美数有很多神奇的特性,有兴趣的可以自行了解


输出100以内所有的素数。


说明:素数指的是只能被1和自身整除的正整数(不包括1)。


函数和模块的使用


函数的作用


不知道大家是否注意到,在上面的代码中,我们做了3次求阶乘,这样的代码实际上就是重复代码。编程大师Martin Fowler先生曾经说过:“代码有很多种坏味道,重复是最坏的一种!”,要写出高质量的代码首先要解决的就是重复代码的问题。对于上面的代码来说,我们可以将计算阶乘的功能封装到一个称之为“函数”的功能模块中,在需要计算阶乘的地方,我们只需要“调用”这个“函数”就可以了。


定义函数


在Python中可以使用def关键字来定义函数,和变量一样每个函数也有一个响亮的名字,而且命名规则跟变量的命名规则是一致的。在函数名后面的圆括号中可以放置传递给函数的参数,这一点和数学上的函数非常相似,程序中函数的参数就相当于是数学上说的函数的自变量,而函数执行完成后我们可以通过return关键字来返回一个值,这相当于数学上说的函数的因变量。


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


"""输入M和N计算C(M,N)Version: 0.1Author: 骆昊"""

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中,函数的参数可以有默认值,也支持使用可变参数,所以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))


我们给上面两个函数的参数都设定了默认值,这也就意味着如果在调用函数的时候如果没有传入对应参数的值时将使用该参数的默认值,所以在上面的代码中我们可以用各种不同的方式去调用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 m2

m1.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():

   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__


练习


练习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 factor

def 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, int(num ** 0.5) + 1):

       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 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等都属于内置作用域。


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


def foo():

   a = 200

   print(a)  # 200

if __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)  # 200

if __name__ == '__main__':

   a = 100

   foo()

   print(a)  # 200


我们可以使用global关键字来指示foo函数中的变量a来自于全局作用域,如果全局作用域中没有a,那么下面一行的代码就会定义变量a并将其置于全局作用域。同理,如果我们希望函数内部的函数能够修改嵌套作用域中的变量,可以使用nonlocal关键字来指示变量来自于嵌套作用域,请大家自行试验。


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


(  垃圾回收这一步对于jvm十分重要,内存管理虽然不用手动管理,但是对于查错确实,排错确是十分重要哦!


大致流程:允许GC之后(注意,发动GC也是需要一定的要求步骤,不详细展开,会另写博客进行说明)-> 开始查找那些允许被回收的(两个算法)-> 开始回收(四个算法)


)http://t.csdn.cn/rufI7


事实上,减少对全局变量的使用,也是降低代码之间耦合度的一个重要举措,同时也是对迪米特法则(http://t.csdn.cn/sm8NA)的践行。减少全局变量的使用就意味着我们应该尽量让变量的作用域在函数的内部,但是如果我们希望将一个局部变量的生命周期延长,使其在定义它的函数调用结束后依然可以使用它的值,这时候就需要使用闭包(闭包函数:声明在一个函数中的函数,叫做闭包函数。


闭包:内部函数总是可以访问其所在的外部函数中声明的参数和变量,即使在其外部函数被返回(寿命终结)了之后。),这个我们在后续的内容中进行讲解。


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


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


def main():


   # Todo: Add your code here


   pass


if __name__ == '__main__':


main()


Zen of Python(Python之禅)(拓展)


在python的编译界面中输入import this


Beautiful is better than ugly. (优美比丑陋好)


Explicit is better than implicit.(清晰比晦涩好)


Simple is better than complex.(简单比复杂好)


Complex is better than complicated.(复杂比错综复杂好)


Flat is better than nested.(扁平比嵌套好)


Sparse is better than dense.(稀疏比密集好)


Readability counts.(可读性很重要)


Special cases aren't special enough to break the rules.(特殊情况也不应该违反这些规则)


Although practicality beats purity.(但现实往往并不那么完美)


Errors should never pass silently.(异常不应该被静默处理)


Unless explicitly silenced.(除非你希望如此)


In the face of ambiguity, refuse the temptation to guess.(遇到模棱两可的地方,不要胡乱猜测)


There should be one-- and preferably only one --obvious way to do it.(肯定有一种通常也是唯一一种最佳的解决方案)


Although that way may not be obvious at first unless you're Dutch.(虽然这种方案并不是显而易见的,因为你不是那个荷兰人[这里指的是Python之父Guido])


Now is better than never.(现在开始做比不做好)


Although never is often better than *right* now.(不做比盲目去做好[极限编程中的YAGNI原则])


If the implementation is hard to explain, it's a bad idea.(如果一个实现方案难于理解,它就不是一个好的方案)


If the implementation is easy to explain, it may be a good idea.(如果一个实现方案易于理解,它很有可能是一个好的方案)


Namespaces are one honking great idea -- let's do more of those!(命名空间非常有用,我们应当多加利用)


函数重载


版权声明:本文为CSDN博主「程序猿编码」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。


原文链接:https://blog.csdn.net/chen1415886044/article/details/100941004


是 C++ 编程的一个特性,它允许我们有多个具有相同名称但不同参数列表的函数,当我说参数列表时,它表示参数的数据类型和顺序,例如函数myfuncn(int a, float b)的参数列表是(int, float),它与函数myfuncn(float a, int b)参数列表(float, int)不同。函数重载是编译时多态。


记住这条规则的最简单方法是参数应符合以下任何一个或多个条件,它们应具有不同的类型,数量或顺序的参数。


例如:


这两个函数有不同的参数类型:


print(int num1, int num2)


print(double num1, double num2)


这两个的参数数量不同:


print(int num1, int num2)


print(int num1, int num2, int num3)


这两个有不同的参数顺序:


print(int num1, double num2)


print(double num1, int num2)


以上三种情况都是有效的重载情况。我们可以有任意数量的函数,只需记住参数列表应该是不同的。例如:


int print(int, int)


double print(int, int)


由于参数列表相同,因此不允许这样做。尽管它们具有不同的返回类型,但它无效。

目录
相关文章
|
4天前
|
存储 设计模式 算法
|
3月前
|
Python
小笔记:Python 使用字符串调用函数
小笔记:Python 使用字符串调用函数
42 0
|
3月前
|
JavaScript 前端开发 测试技术
[小笔记]TypeScript/JavaScript模拟Python中的Range函数
[小笔记]TypeScript/JavaScript模拟Python中的Range函数
27 0
|
4天前
|
存储 索引 Python
|
3月前
|
JavaScript 前端开发 测试技术
[小笔记]TypeScript/JavaScript模拟Python中的zip(不使用map)
[小笔记]TypeScript/JavaScript模拟Python中的zip(不使用map)
19 0
|
3月前
|
C# Python
C# 笔记3 - 重载一系列像python那样的print()方法
C# 笔记3 - 重载一系列像python那样的print()方法
29 1
|
3月前
|
关系型数据库 MySQL 数据库
MySQL命令笔记+Python案例
MySQL命令笔记+Python案例
47 0
|
3月前
|
开发者 Python
【备战蓝桥杯】如何使用Python 内置模块datetime去计算我与CSDN相遇的天数
【备战蓝桥杯】如何使用Python 内置模块datetime去计算我与CSDN相遇的天数
32 1
|
3月前
|
存储 Python
Python:核心知识点整理大全16-笔记-2
Python:核心知识点整理大全16-笔记
18 0
|
3月前
|
存储 Python
Python:核心知识点整理大全16-笔记-1
Python:核心知识点整理大全16-笔记
27 0