python之旅:函数基础

一、引子

1、函数是什么

用函数与不用函数

#1、代码的组织结构不清晰,可读性差
#2、遇到重复的功能只能重复编写实现代码,代码冗余
#3、功能需要扩展时,需要找出所有实现该功能的地方修改之,无法统一管理且维护难度极大 

函数是带名字的代码块,用于完成具体的工作。

函数是组织好的,可重复使用的,用来实现单一,或相关联功能的代码段。

函数能提高应用的模块性,和代码的重复利用率。你已经知道Python提供了许多内建函数,比如print()。但你也可以自己创建函数,这被叫做用户自定义函数。

现实比拟:

想象生活中的例子,修理工需要实现准备好工具箱里面放好锤子,扳手,钳子等工具,然后遇到锤钉子的场景,拿来锤子用就可以,而无需临时再制造一把锤子。

修理工===>程序员
具备某一功能的工具===>函数 要想使用工具,需要事先准备好,然后拿来就用且可以重复使用
要想用函数,需要先定义,再使用

2、函数分类

#内置函数

对于一些常见,常用的功能,python都给我门内置好了,直接拿起来用就好了。

python解释器已经为我们定义好了的函数即内置函数。对于内置函数,我们可以拿来就用而无需事先定义,如len(),sum(),max()

#自定义函数

很明显内置函数所能提供的功能是有限的,这就需要我们自己根据需求,事先定制好我们自己的函数来实现某种功能,以后,在遇到应用场景时,调用自定义的函数即可。

例如:

def greet_user():
'''显示简单的问候语'''
print('Hello')
greet_user()

3、用函数是为解决以下问题

1、代码的组织结构不清晰,可读性差

2、遇到重复的功能只能重复编写实现代码,代码冗余

3、功能需要扩展时,需要找出所有实现该功能的地方修改之,无法统一管理且维护难度极大

二、定义函数

1、定义一个函数

你可以定义一个由自己想要功能的函数,以下是简单的规则:

  • 函数代码块以 def 关键词开头,后接函数标识符名称和圆括号 ()
  • 任何传入参数和自变量必须放在圆括号中间,圆括号之间可以用于定义参数。
  • 函数的第一行语句可以选择性地使用文档字符串—用于存放函数说明。
  • 函数内容以冒号起始,并且缩进。
  • return [表达式] 结束函数,选择性地返回一个值给调用方。不带表达式的return相当于返回 None。

2、函数语法

  

#语法
def 函数名(参数1,参数2,参数3,...):
'''注释'''
函数体
return 返回的值 #函数名要能反映其意义
def auth(user:str,password:str):
'''
auth function
:参数 user: 用户名
:参数 password: 密码
:return: 认证结果
'''
if user == 'allen' and password == '':
return 666
# print(auth.__annotations__) #{'user': <class 'str'>, 'password': <class 'str'>, 'return': <class 'int'>} user=input('用户名>>: ').strip()
pwd=input('密码>>: ').strip()
res=auth(user,pwd)
print(res)
#结果如下
用户名>>: allen
密码>>: 123
666

实例

3、向函数传递信息

eg:

def greet_user():
'''显示简单的问候语'''
print('Hello')
greet_user()

只需要稍作修改,就可以让函数greet_user()不仅向用户显示Hello!,还将用户的名字用作抬头。为此,可在函数定义def greet_user()的括号内添加username。通过在这里添加username,就可以让函数接受你给username指定的任何值。

eg:

def greet_user(username):
'''显示简单的问候语'''
print('Hello '+username.title()+' !')
name=input('请输入你的用户名 >>> ')
greet_user(name) #结果如下
请输入你的用户名 >>>  allen
Hello Allen

4、函数使用的原则:先定义,再调用

便于理解:函数类似“变量”,“变量”必须先定义后引用。未定义而直接引用函数,就相当于在引用一个不存在的变量名
#test 1
def greet_user():
'''显示简单的问候语'''
print('Hello ')
password()
greet_user() #报错 因为password()没有定义 却调用了 #test 2
def greet_user():
'''显示简单的问候语'''
print('Hello ')
password()
def password():
'''密码'''
print('password ')
greet_user() # 正常 #结论:函数的使用,必须遵循原则:先定义,后调用
#我们在使用函数时,一定要明确地区分定义阶段和调用阶段
#定义阶段
def greet_user():
'''显示简单的问候语'''
print('Hello ')
password()
def password():
'''密码'''
print('password ')
#调用阶段
greet_user()

 5、函数在定义阶段都干了哪些事?

#只检测语法,不执行代码
也就说,语法错误在函数定义阶段就会检测出来,而代码的逻辑错误只有在执行时才会知道

6、定义函数的三种形式

1、无参:应用场景仅仅只是执行一些操作,比如与用户交互,打印
2、有参:需要根据外部传进来的参数,才能执行相应的逻辑,比如统计长度,求最大值最小值
3、空函数:设计代码结构
#定义阶段
def tell_tag(tag,n): #有参数
print(tag*n) def tell_msg(): #无参数
print('hello world') #调用阶段
tell_tag('*',12)
tell_msg()
tell_tag('*',12) '''
************
hello world
************
''' #结论:
#1、定义时无参,意味着调用时也无需传入参数
#2、定义时有参,意味着调用时则必须传入参数

有参 and 无参

def auth(user,password):
'''
auth function
:param user: 用户名
:param password: 密码
:return: 认证结果
'''
pass def get(filename):
'''
:param filename:
:return:
'''
pass def put(filename):
'''
:param filename:
:return:
'''
def ls(dirname):
'''
:param dirname:
:return:
'''
pass #程序的体系结构立见 空函数

空函数

三、调用函数

1、调用函数

定义一个函数:给了函数一个名称,指定了函数里包含的参数,和代码块结构。

这个函数的基本结构完成以后,你可以通过另一个函数调用执行,也可以直接从 Python 命令提示符执行。

如下实例调用了 printme() 函数:

函数的调用:函数名加括号 
1 先找到名字
2 根据名字调用代码 # 定义函数
def printme( str ):
"打印任何传入的字符串"
print (str);
return; # 调用函数
printme("我要调用用户自定义函数!");
printme("再次调用同一函数");

以上实例输出结果:

我要调用用户自定义函数!
再次调用同一函数

2、函数返回值

无return->None
return 1个值->返回1个值
return 逗号分隔多个值->元组
什么时候该有返回值?
    调用函数,经过一系列的操作,最后要拿到一个明确的结果,则必须要有返回值
    通常有参函数需要有返回值,输入参数,经过计算,得到一个最终的结果
什么时候不需要有返回值?
    调用函数,仅仅只是执行一系列的操作,最后不需要得到什么结果,则无需有返回值
    通常无参函数不需要有返回值

3、函数调用的三种形式

1 语句形式:foo()
2 表达式形式:3*len('hello')
3 当中另外一个函数的参数:range(len('hello'))

四、函数的参数

1、实参与形参

方便于理解:形参类似于变量名,实参类似于变量值,函数调用时,将值绑定到变量名上,函数调用结束,解除绑定

形参(形式参数):指的是在定义函数时,括号内定义的函数,形参其实就变量名   (位置形参,默认形参)

实参(实际参数):指的是在调用函数时,括号内传入的值,实参其实就变量的值

python之旅:函数基础

2、位置参数

位置参数:位置即顺序,位置参参数指的就是按照从左到右的顺序依次定义的参数
位置形参:必选参数
位置实参:按照位置给形参传值

位置参数分两种:

  在定义函数时,按照位置定义的形参,称为位置形参

  在定义函数时,按照位置定义的实参,称为位置实参

#在定义函数时,按照位置定义的形参,称为位置形参
def foo(x,y,z):
print(x,y,z)
#注意:
#位置形参的特性是:在调用函数时必须为其传值,而且多一个不行,少一个不行
foo(1,2) #TypeError: foo() missing 1 required positional argument: 'z'
#TypeError: foo()缺失1个必需的位置参数:“z”
foo(1,2,3,4) #TypeError: foo() takes 3 positional arguments but 4 were given
#foo(1、2、3、4)#TypeError: foo()接受3个位置参数,但4个是给定的。
#在调用函数时,按照位置定义的实参,称为位置实参
#注意:位置实参与形参一一对应
foo(1,3,2) #return:1 3 2

位置参数 示例

3、关键字实参

在调用函数时,按照key=value的形式定义的实参,称为关键字参数

因为关键字实参将key=value(名称-值对)将名称和值关联起来了,因此向函数传递参数时不会混淆。所以关键字实参不需要考虑函数调用中的实参顺序

注意的问题:

      1. 关键字实参必须在位置实参右面

      2. 对同一个形参不能重复传值

  

#1、相当于指名道姓地为形参传值,意味着即便是不按照顺序定义,仍然能为指定的参数传值
def foo(x,y,z):
print(x,y,z)
foo(x=1,y=3,z=6) #return:1 3 6
#2、在调用函数时,位置实参与关键字实参可以混合使用,但必须遵守
#1:必须遵循形参的规则
# foo(1,z=3,y=2) #return: 1 2 3
foo(1,z=3) # TypeError: foo() missing 1 required positional argument: 'y'
#2:不能为同一个形参重复传值
foo(1,x=1,z=2,y=3) # TypeError: foo() got multiple values for argument 'x'
#3:位置实参必须放到关键字实参的前面
foo(z=3,y=2,1) #SyntaxError: positional argument follows keyword argument

关键字 示例

4、默认参数(默认值)

在定义阶段,已经为某个形参赋值,那么该形参就称为默认参数

可以传值也可以不传值,经常需要变得参数定义成位置形参,变化较小的参数定义成默认参数(形参)

注意的问题:

      1. 只在定义时赋值一次

      2. 默认参数的定义应该在位置形参右面

      3. 默认参数通常应该定义成不可变类型

应用:
   对于经常需要变化的值,需要将对应的形参定义成位置形参
   对于大多数情况值都一样的情况,需要将对应的形参定义成默认形参

#在定义阶段已经有值,意味着在调用阶段可以不传值
def register(name,age,sex):
print(name,age,sex) register('egon',18,'mele')
register('dai',36,'female')
register('wxx',16,'male')
register('allen',15,'male') #输出如下
egon 18 mele
dai 36 female
wxx 16 male
allen 15 male #我门可以看出基本都是性别是男人,所以我们可以为性别sex设置默认值
def register(name,age,sex='male'):
print(name,age,sex) register('egon',18,)
register('dai',36,'female')
register('wxx',16,)
register('allen',15,) #输出如下
egon 18 male
dai 36 female
wxx 16 male
allen 15 male #位置形参必须在默认参数前面
def register(name,sex='male',age): #SyntaxError: non-default argument follows default argument
print(name,age,sex) #默认参数的值只在定义阶段赋值一次,也就是说默认参数的值在定义阶段就固定死了
m=10
def foo(x,y=m):
print(x,y)
m='aaaaaa'
foo(1) #return: 1 10 #按照变量的定义应该是1 aaaaa,所以说明了默认参数的值只在定义阶段赋值一次
foo(1,11) #1 11 #记住:默认参数的值应该设置为不可变类型
def register(name,hobby,l=[]): #name='wxx',hobby='play'
l.append(hobby) #l=['play']
print(name,l) # wxx ['play'] register('wxx','play') # wxx ['play']
register('allen','read') # alex ['read']
register('egon','music') # alex ['music'] #输出如下 显然输出的结果是不正确的,所以默认参数的值应该设置为不可变类型
wxx ['play']
allen ['play', 'read']
egon ['play', 'read', 'music'] #解决上述办法如下
def register(name,hobby,l=None): #name='wxx',hobby='play'
if l==None:
l=[]
l.append(hobby) #l=['play']
print(name,l) # wxx ['play'] register('wxx','play') # wxx ['play']
register('allen','read') # alex ['read']
register('egon','music') # alex ['music'] #输出如下
wxx ['play']
allen ['read']
egon ['music']

默认参数 示例

5、可变长参数

可变长指的是实参值的个数不固定
而实参有按位置和按关键字两种形式定义,针对这两种形式的可变长,形参对应有两种解决方案来完整地存放它们,分别是*args,**kwargs

什么是可变长度参数

  可变长度值的参数的个数可以不固定,实参有按位置定义的实参和按关键字定义的实参

  所以可变长度的实参指的就是按照两种形式定义的实参个数可以不固定,然而实参终究是要给形参传值的

  所以形参必须有两种对应的解决方案来分别处理以上两种形式可变长度的实参

形参里包含*与**:

  *

  **

#==========================形参里包含*与**
# * 会将溢出的 位置实参 全部接受,然后保存成 元组 的形式赋值给args
#args程序员约定俗称的规定
def foo(x,y,z,*args):
print(x,y,z,args)
foo(1,2,3,4,5,6,7,8)
#输出结果
1 2 3 (4, 5, 6, 7, 8) # ** 会将溢出的 关键字实参 全部接受,然后保存成 字典 的形式赋值给kwargs
#kwargs程序员约定俗称的规定,用其他命名不会报错
def foo(x,y,z,**kwargs):
print(x,y,z,kwargs)
foo(x=1,y=2,z=3,a=4,b=5,c=6,d=7,f=8)
#输出结果
1 2 3 {'a': 4, 'b': 5, 'c': 6, 'd': 7, 'f': 8} #==========================实参里包含*与**
#一旦碰到实参加*,就把该实参的值打散
def foo(x,y,z,*args):
print(x,y,z,args) foo(1,2,3,*[4,5,6,7,8]) #列表 1 2 3 (4, 5, 6, 7, 8)
foo(1,2,3,*(4,5,6,7,8)) #元组 1 2 3 (4, 5, 6, 7, 8)
foo('ni','hao','a',*'hello') #字符串 ni hao a ('h', 'e', 'l', 'l', 'o')
foo('ni','hao',*'hello') #参数不够时 自动补位 ni hao h ('e', 'l', 'l', 'o')
#测试的几组数据
def foo(x,y,z,):  #这里没有*args哦!
print(x,y,z) foo(*[1,2,3]) #1 2 3
foo(*[1,2,3,4]) #TypeError: foo() takes 3 positional arguments but 4 were given
foo(*[1,2]) #TypeError: foo() missing 1 required positional argument: 'z'

#一旦碰到实参加*,就把该实参的值打散
def foo(x,y,z,**kwargs):
print(x,y,z,kwargs) foo(1,2,3,**{'a':1,'b':2}) # 1 2 3 {'a': 1, 'b': 2}
def foo(x,y,z,):
print(x,y,z,) foo(1,**{'y':1,'z':2}) # 1 1 2
def foo(x,y,z,):
print(x,y,z,) foo(1,**{'y':1,'z':2,'x':111}) #TypeError: foo() got multiple values for argument 'x'
                   #翻译:TypeError: foo()有多个值用于参数“x”
#组合使用
def index(name,age,gender): #index n. 指标;指数;索引;指针
print('welcome %s %s %s' %(name,age,gender))
def wrapper(*args,**kwargs): #wrapper n. 包装材料;[包装] 包装纸;书皮
# print(args)
# print(kwargs)
index(*args,**kwargs) #三种实参其实都是一个结果
wrapper('allen',age='16',gender='male') #egnder n. 性;性别
wrapper('allen',18,gender='male') #male 男人
wrapper('allen',18,'male') #输出结果 以上三种实参其实都是一个结果
welcome allen 16 male
welcome allen 18 male
welcome allen 18 male
*的应用场景

def sum2(*args): #args=(1,2,3)
    res=0
    for num in args:
        res+=num
    return res print(sum2(1,2,3,4,5,6,7)) ** 的应用场景
def auth(name,pwd,**kwargs):
    print(name)
    print(pwd)
    print(kwargs) # auth('egon','123')
auth(name='egon',pwd='123',group='group1') ############################### def foo(x,y=1,*args,z,**kwargs):
    pass def foo(x,y=1):
    pass def foo(x,*args,**kwargs):
    pass

6、命名关键字参


命名关键字参数:*后定义的参数,必须被传值(有默认值的除外),且必须按照关键字实参的形式传递
        可以保证,传入的参数中一定包含某些关键字 def foo(x, y, *args, a=1, b, **kwargs):
print(x, y)
print(args)
print(a)
print(b)
print(kwargs) foo(1, 2, 3, 4, 5, b=3, c=4, d=5)
# 结果:
1 2
(3, 4, 5)
1
3
{'c': 4, 'd': 5} ##################
def auth(*args,**kwargs):
'''
使用方式auth(name='egon',pwd='123')
:param args:
:param kwargs:
:return:
'''
if len(args) != 0:
print(args)
print('必须使用关键字的形式传参')
return
if 'name' not in kwargs:
print(kwargs)
print('必须用指定的key名name')
return
if 'pwd' not in kwargs:
print('必须用指定的key名pwd')
return
name=kwargs['name']
pwd=kwargs['pwd']
print(name,pwd) auth('egon',123)
auth(pwd=123,name='egon')
#结果如下
('egon', 123)
必须使用关键字的形式传参
egon 123
上一篇:MySQL -- 外键创建失败


下一篇:BZOJ4383/LuoGuP3588 Pustynia/PUS 线段树建图优化