一百天入职AI开发岗__进阶Python_[6]
本文介绍了Python函数定义与导入的基础知识。主要内容包括:1.自定义函数的定义格式、参数类型(形式参数、默认参数、可变参数等)及作用域规则;2.函数导入方法,包括第三方库和自定义模块的导入方式;3.__name__属性的作用;4.面向对象编程基础,包括类的定义、实例化、方法及继承机制。文章通过实例演示了如何创建和使用函数与类,并强调了代码组织、调试技巧等实践要点。
#Day6.函数定义及其导入
一、自定义函数
1)基础形式
def solve_problem(a,b):
y = b /2 - a
x = a - y
return f'鸡有{x}只,兔有{y}只'
print(solve_problem(35, 94))
现在回到了最初的第一章,当时我们为了演示,囫囵吞枣的定义了一次函数。现在我们来详细拆解。函数定义的范式如下:
def 函数名(参数):
代码块
return 返回值
我们以def开头,声明将要定义一个函数,函数名是solve_problem()。创立的参数为a, b。使用两个赋值语句作为代码块实现了求解的操作。最后通过格式化字符串返回了问题的解。
整个流程并不高深,需要注意的有两点:
1、缩进。
在Python里对缩进要求颇为严格。至少要保持相同的缩进距离,这里推荐使用Tab键自动缩进四格,美观方便。
2、特殊的参数。
在这里,函数定义时被建立的变量a,b是形式参数,意即形式上的参数。因为函数定义的时候本身并不会赋予变量a,b具体的值,这两个变量所指向的存储空间是空的。
此外他们还被称之为局部变量。因为a,b这两个变量仅在执行函数内部代码块时生效。一旦函数执行完毕,变量将不存在。函数外查询该变量时会报错显示变量不存在。相应的,函数外部的变量被称为全局变量,从始至终存在——除了在函数内部。同样的函数内部不可调用全局变量,若要强行调用必须在函数内部,调用之前,显式的声明。
global global_var
1.1可变数据
当然也有部分数据存在例外,这种数据称为可变数据。事实上,就是我们在day3里面介绍的数据结构中除了元组之外的类型。
什么是可变?所谓的可变,其实就是我们已经介绍过的“可变”——内含的元素可改变。
怎么例外?在函数内部,即使不使用global声明,也可以直接对这些变量直接操作。
ls = [1, 2, 3]
def fun():
ls.reverse()
print(ls)
fun()
#[3, 2, 1]
Python为了便利共享数据而做了如此设定。但是实际应用时要多加注意以防意料外的情况。
我们对这些变量应用global时效果将会变更为创建新变量并赋值。
2)默认参数
有时,某些形参会有一些高频出现的参数。比如print()函数中,end(结尾)默认都会换行,也就是end="\n"的情况经常会出现。这种时候,Python为了提供方便,允许用户在定义参数时建立默认参数。对于默认参数,如果用户调用函数时未向默认参数传递值,那么该参数自动使用设置好的默认值。
def 函数名(参数1, 默认参数1 = 值1):
代码块
return 返回值
当然,默认参数的使用也很简单,只需要我们在写形参的时候加上一个“=”,并给予其具体的值即可。需要注意的是不论实在定义的时候还是调用的时候,默认参数必须位于必填参数(非默认参数)后方以便于区分。此外,考虑到复杂函数一般有多个参数待传入,方便起见当我们调用函数的时候若显式的指定则可以不按照顺序。
print(solve_problem(b = 94, a = 35))
3)可变参数和关键字参数
有时我们可能不确定到底要传入几个参数,这时,我们就用到了可变参数/关键字参数。
def 函数名(参数, *参数): #可变参数
代码块
return 返回值
def 函数名(参数, **参数): #关键字参数
代码块
return 返回值
事实上print()使用的就是可变/关键字参数。我们在调用print()的时候,我们不论传入多少个值、变量、表达式,print()都能正确处理完成就是这个原理。
在实际应用上,这两种参数作用相近。仅有的不同是可变参数会以元组的形式接受数据,而关键字参数以字典形式接受。
def fun(a, *b): #可变参数
print(b)
c = 0
for i in b:
c += a*i
returnc
print(fun(3,1,2,3))
def fun1(a,**b): #关键字参数
print(a,'课程考试成绩:')
Print(b)
fun4('Python',zhangsan=90,lisi=95)
在位置上,可变/关键字参数还要在默认参数之后。
4)匿名函数lambda
为了实现一些复杂的功能,有些时候我们需要临时创建一个简单函数使用。为了迎合这种需求,Python提供了另一种函数——匿名函数lambda。
让我们以sorted()排序功能的函数为例。
#sorted(iterable, key = None, reverse = False)
a =[3,7,9,4,0,2]
b = sorted(a,key=lambda x:abs(x-5))
print(b)
#[4, 3, 7, 2, 9, 0]
让我们来具体拆解:第一句,我们通过赋值语句创建了一个列表;第二句我们调用了函数sorted(),其中第一位必填参数,我们填入了a,表示处理对象是a内含的列表数据。接着我们显示的指定了要赋值的默认参数key,其值为使用lambda创建的匿名函数。然后通过赋值语句将结果返回给b;最后打印出来。
现在让我们看看它是怎么工作的。
x作为形参,挨个去读取列表中的值,第一次读取到了3;
lamda在形参获取到传递的数字后,执行运算abs(3-5),得到值2(abs()作用是返回绝对值);
函数sorted()获取到了返回值2后建立一个映射关系:3~2;
按照这个步骤依次操作,将列表中每一个数字都获取一个映射:
[3,7,9,4,0,2] ~ [2, 2, 4, 1, 5, 3]
函数sorted()检查revers(反转)的值发现为默认的False,于是按照升序排列。映射的数据变为:
[1, 2, 2, 3, 4, 5]
逆转映射:
[4, 3, 7, 2, 9, 0]
这样我们就实现了在sorted()函数中进行复杂排序。
除了这种方式外,Python还允许直接用lamda快速建立一个函数,就是不太常用。
fun = lambda x, y:y + x
#等效于
def fun(x, y):
return y+x
二、导入函数
1)导入其他人的函数——第三方库
一如在开篇中所说的,指望全流程手操实现AI是不现实的。重要的是学会利用前人的工作。其中之一就是前人写好的函数以及函数集合成的——第三方库。(虽然事实上更加复杂一些,但目前姑且这样理解)
#以math库为例
import math #运行后Python会将其导入缓存中,当具体调用其中的函数时Python会到缓存中查找
print(math.sqrt(16)) #调用库里的具体函数时,以moudle.function()方式
import math as m #Python允许为导入的库起别称,以方便调用
print(m.sqrt(16))
form math import sqrt #运行后Python会直接将函数本身导入缓存中,可以像内置函数print()一样直接调用
print(sqrt(16))
form math import sqrt as s #函数也能起别名
print(s(16))
from math import * #运行后Python会直接将库里的所有函数导入缓存中,非常不建议这种方式
print(sqrt(16))
2)导入自己的的函数——从其他文件中
有时,一项复杂的工作可能需要你成百乃至上千行。这种时候将所有内容都写在同一个py文件(我们书写的代码保存后就是一个py文件)里显然不合适,这会导致耦合度奇高——说人话就是所有东西凑一起,过于混乱——往往需要把实现不同功能的代码块分文件存放,单独测试、封装以提高效率。当然也不能单纯的把代码分开,我们必须让他们在分开后也能以某种方式一起工作。这种时候我们采取的往往是把一个实现完整功能的代码块独立存放入一个文件并将其封装为函数。也就是我们需要一个方法从自己书写的其他文件中导入自定义函数。
如果文件正好就在我们当前工作空间会简单一些
form 文件名 import 函数名
如果文件在其他路径会麻烦一些,. 代表着当前文件夹,该方法可以找到当前文件夹中包含着目标文件的文件夹的里面去。
from .subpackage import moudle
说起来有些绕,但是举个例子就很好理解了。
假设我们当前工作空间处于
C:\Users\username\Desktop
目标文件在
C:\Users\username\Desktop\demo\fun.py
这种时候,这种方法就起效了。如果有需要,可以再加一个. 索引到上级目录。其余方法就不多加介绍了,几乎不会用到。
需要注意的是Python在导入文件或库时有优先级顺序,先检查自带的标准库,接着是当前工作空间,最后是Python安装目录下的site-packages目录,即用户手动安装的第三方库。这意味着如果我们将文件命名与其他库若是重名则会导致冲突,无法调用第三方库。
#关于什么是第三方库什么是标准库后续会有更多介绍。
3)__name__
当我们为了方便,将一个能够独立实现功能的代码块单独封装为一个函数存放进其他文件时,我们还会遇到一个新问题。没有代码是绝对没问题的,想让一个代码更好必须经过多轮调试。但是当一个程序真正run起来的时候我们并不需要单独测试某个函数的调试代码,而若是简单粗暴的调完就删又不可能,因为一项复杂的工程往往需要反反复复的调试。于是为了解决这个问题就有了__name__这个内置属性。如果当前文件是正在运行的文件,那么__name__返回的值就是__main__,如果当前文件只是一个被引用的模块,那么__name__返回的值就是当前的文件名。这样我们只需要将调试代码块放入条件判断语句中就可以解决这个问题了。
if __name__ == '__main__':
调试代码块
当我们调试这个函数的时候,当前文件就是运行的文件,判断成立,调试代码块就会运行。当这只是作为一个模块被引用时,条件不成立,调试代码块就被忽略。为我们提供了方便。
更多推荐



所有评论(0)