函数引⼊
前⾯我们写过九九乘法表,但如果我要七七乘法表或五五乘法表的话,你会看到三者代码极其类似,只是循环变量不同,那么如何做到代码重⽤,⽽不是简单拷⻉黏贴修改呢,其实可是使⽤函数完成这⼀功能
def table(row,col):
for i in range(1, row + 1):
for j in range(1, col + 1):
if j <= i:
print("%d*%d = %2d" % (i, j, i * j), end=' ')
print('') #⼀次编码,到处运⾏
table(8,8)
table(5,5,8)
函数的优点:
1.函数定义
函数就是完成特定功能的代码块,本质上是对代码的封装。 语法格式
def 函数名([参数1],[参数2]....[参数n]):
函数体
2 函数参数
2.1 实参和形参
2.2 参数分类
def say_hello(name,age,home):
print('⼤家好,我是{},我今年{}岁了,我来⾃
{}'.format(name,age,home))
say_hello('王⼆妮',18,'湖北武汉') #实参个数、顺序必须和形参⼀致
def say_hello(name,age,home):
print('⼤家好,我是{},我今年{}岁了,我来⾃
{}'.format(name,age,home))
say_hello(name='王⼆傻',home='⼤连',age=20) #三个关键字参数
say_hello('⼤傻',home='美国',age=30) #两个关键字参数
sya_hello('⼆傻',24,home='何⽅') #⼀个关键字参数
def my_power(x,n=2):
return (x) ** n
my_power(3)
my_power(4,0.5)
def test(a=[]):
a.append('end')
print(a)
test([1,2,3])
test() #['end']
test() #['end','end']
#使⽤*接收任意数量的位置参数
#注意:*的不定⻓参数被当做元组处理
def demo(a,b,*args):
print(a,b,args)
demo(12,33,90)
demo(1,2,3,4,5)
a=(1,2,3)
demo(*a)
#使⽤**接收任意数量的关键字参数
#注意:**的不定⻓参数被当做字典处理
def demo1(a,**args):
print(a,args)
demo1(1,name='kk',age=3)
b = {'a':20,'b':12,'c':32}
demo(**b)
2.3 参数组合
2.4 命令⾏参数(了解)
如果要获取命令⾏下传给python⽂件的参数可以使⽤体统模块sys的argv来获取
参数个数:len(sys.argv)
⽂件名:sys.argv[0]
参数:sys.argv[1],sys.argv[2]…
# ⽂件名: 1.py
import sys
print(len(sys.argv))
print(sys.argv[0])
print(sys.argv[1])
3 函数调⽤
def demo(a,b,c=0,*arg1,**arg2):
print(a,b,c,arg1,arg2)
demo(1,3,k=4)
demo(1,2,3,4,5)
demo(1,b=3,c=3,d=5)
demo(*(1,2,3),**{'name':12}) #任何函数都可通过这种形式传递参数
4 返回值
可以通过return语句返回计算结果。语法: return 表达式
def demo2():
return 1
def demo3():
return 1,2,3
print(demo2())
print(demo3()) #(1,2,3)
5 ⽂档字符串
函数⽂档字符串documentation string (docstring)是在函数开头,⽤来解释其接⼝的字符串。简⽽⾔之:帮助⽂档
包含函数的基础信息
包含函数的功能简介
包含每个形参的类型,使⽤等信息
⽂档字符串书写规则:
必须在函数的⾸⾏
使⽤三引号注解的多⾏字符串(’’’ ‘’’) 或(""" “”")
函数⽂档的第⼀⾏⼀般概述函数的主要功能,第⼆⾏空,第三⾏详细描述。
def test():
"""
函数名:test
功能:测试
参数:⽆
返回值:⽆
"""
print("函数输出成功") #使⽤__doc__属性查看⽂档字符串
print(test.__doc__)
6.参数传递()**
python的参数传递是简单的值传递,当然这⾥的值是指变量的引⽤(地址),不是变量的值。不存在值传递和引⽤传递的区分。简⽽⾔之,python的参数传递可以称之为对象引⽤传递,对象可以分为:
def test():
pass # 占位符
函数(2)
1.函数类型
函数也是⼀种类型,我们⾃定义的函数就是函数对象,函数名保存了函数对象的引⽤(地址)
def test():
print('我是测试函数')
print(test) #函数名是变量,指向了函数对象
pf = test #pf变量也指向了函数对象,所以也可以通过pf调⽤test函数
pf()
2. 匿名函数
不再使⽤def 函数名()这种形式定义函数,⽽是使⽤lambda来创建匿名函数
特点:
3.传⼊函数(理解)
⼀个函数就可以接收另⼀个函数作为参数,这种函数就称之为⾼阶函数,也可以称之为传⼊函数。可以实现通⽤编程,排序等复杂功能
#能被2整除数的和
def sum_even(n):
sum = 0
for i in range(1,n+1):
if i % 2 == 0:
sum += i
return sum
#能被7整除的数的和
def sum_seven(n):
sum = 0
for i in range(1,n+1):
if i % 7 == 0:
sum += i
return sum
#能被3和5整除,但不能7整除的数的和
def sum_fifteen(n):
sum = 0
for i in range(1,n+1):
if i % 15 == 0 and i % 7 != 0:
sum += i
return sum
#通⽤求和函数
def sum1(n,callback):
'''
功能:求满⾜callback规定条件的数的和
:param n: ⼤于0的整数
:param callback: ⽤于判断⼀个数是否满⾜指定条件,由调⽤者传⼊,有⼀个参
数,形如:def callback(n)
:return: 求和的结果
'''
sum = 0
for i in range(1,n+1):
if callback(i):
sum += i
return sum
print(sum1(100,lambda x:x%2==0))
print(sum1(100,lambda x:x%7==0))
print(sum1(100,lambda x:x%15==0 and x % 7 != 0))
4.闭包
我们可以在⼀个函数中再定义⼀个函数,在函数内部定义的函数称之为内部函数,内部函数只能在函数内使⽤,不会污染外部空间。定义内部函数的函数称之为外部函数,这样的定义构成函数的嵌套
def outter(a): #外部函数
x = 10
def inner(y): #内部函数
print(x + y)
inner(a)
outter(20)
在⼀个外部函数中定义了⼀个内部函数,内部函数⾥引⽤了外部函数的变量,并且外部函数的返回值是内函数的引⽤。这样内部函数和其执⾏所需的环境变量就构成了⼀个闭包。
⼀般情况下,如果⼀个函数结束,函数的内部所有东⻄都会释放掉,局部变量都会消失。但是闭包是⼀种特殊情况,如果外函数在结束的时候发现有⾃⼰的局部变量将来会在内部函数中⽤到,就把这个局部变量绑定给了内部函数,然后⾃⼰再结束。
def outter(a): #外部函数
x = a
def inner(y): #内部函数
return x + y #引⽤外部变量
return inner #返回内部函数(闭包)
pf = outter(20)
print(pf(10)) #30
print(pf(20)) #40
在闭包中⽆法直接修改外部变量x的值
def outter(a): #外部函数
x = a
def inner(y): #内部函数
# x += 10 #UnboundLocalError: local variable 'x' referenced
before assignment
return x + y
return inner
在python3中可以通过nonlocal关键字声明⼀下x,表示这个变量不是局部变量,需要向上⼀层变量空间找这个变量。
def outter(a): #外部函数
x = a
def inner(y): #内部函数
nonlocal x
x += 10
return x + y
return inner
5.偏函数
当⼀个函数有⼤量参数,调⽤的时候⾮常不⽅便,可以使⽤偏函数技术,将⼀些参数固定(给默认值),达到简化函数调⽤的⽬的。
import functools
def test(a,b,c,d):
print(a, b, c, d)
#从前⾯固定参数,使⽤位置参数就⾏,1=>a,2=>b
test1 = functools.partial(test,1,2)
test1(3,4) #3=>c 4=>d
#从后⾯固定参数,需要使⽤关键字参数
test2 = functools.partial(test,c=3,d=4)
test2(1,2) #1=>a 2=>b
#如果固定的参数不连续,则需使⽤关键字参数固定
test3 = functools.partial(test,b=2,d=4)
test3(a=1,c=3) #需要使⽤关键字参数,否则会报错
6.变量的作⽤域
程序中的变量并不是在任意的位置都可以随意访问,在哪⾥可以访问取决于这个变量的作⽤域,变量的作⽤域指的是变量在那段代码中可以使⽤,可以使⽤变量的那段代码就是变量的作⽤域。在python中,只有函数/类/模块才引⼊作⽤域,if/elif/else , while/for,try/except等并不会引⼊新的作⽤域
#if语句不引⼊新作⽤域,msg在外⾯可以使⽤
if True:
msg = "message"
print(msg)
6.1 变量作⽤域的分类
按照作⽤域划分,可以分为
#1 局部作⽤域
#局部变量只能在函数内部使⽤,外部⽆法引⽤
#局部变量的作⽤域从定义开始到函数体结束
def demo():
num = 20 #局部变量
print(num)
demo()
#print(num) 错误
#闭包作⽤域
def outter():
x = 10 #函数作⽤域,从定义开始到本函数结束
def inner():
y = x #在闭包中可以引⽤
print(y)
return inner
pf = outter()
pf() #执⾏闭包
print(pf.__closure__)
#全局作⽤域
x = 100 #全局作⽤域 从定义开始到本⽂件结束
def demo():
print(x)
print(x)
#内建作⽤域,是指系统内建的函数或常量,在系统载⼊时加载,在所有模块中都可以
直接引⽤
#⽐如说系统函数
print(max(1,2,3)) #max函数就是内建作⽤域 哪⾥都可以引⽤
def demo():
x = 30
y = 50
print(max(x, y))
6.2 变量作⽤域查找规则
以 L --> E --> G -->B 的规则查找,即:在局部找不到,便会去局部外的局部找(例如闭包),再找不到就会去全局找,最后到内建作⽤域中找。
6.3 全局变量和局部变量
定义在函数内部的变量拥有⼀个局部作⽤域,被称为局部变量
定义在函数外⾯的变量拥有⼀个全局作⽤域,被称为全局变量
局部变量和全局变量同名,局部优先
total = 0 #全局变量
def sum(arg1,arg2):
total = arg1 + arg2 #局部变量
print("函数内部:",total)
return total
sum(10,20)
#print(total1)
print("函数外部:",total)
num = 1
def fun1():
print(num) #UnboundLocalError: local variable 'num' referenced
before assignment
num = 123
print(num)
fun1()
6.4 global和nonlocal
#1.在Python中,当内部作⽤域想修改全局变量的时候,则就要使⽤global关键字进⾏
声明
num = 1
def fun1():
global num #告诉编译器,此处的num是全局变量
print(num) #1
num = 123
print(num) #123
fun1()
a = 10
def test():
global a
a = a + 1
print(a)
test()
#2.如果要修改函数作⽤域中的变量,则使⽤nonlocal
#需要明确的是,nonlocal关键字定义在闭包⾥⾯
x = 0 #全局作⽤域
def outer():
x = 1 #闭包作⽤域
def inner():
nonlocal x
x = 2 #局部作⽤域
print("inner:",x) #2
inner()
print("outer:",x) #1---->2
outer()
print("全局:",x) #0
#nonlocal关键字:声明了该变量不只是在outer函数中有效,在inner函数内部也有效
7.闭包
我们可以在⼀个函数中再定义⼀个函数,在函数内部定义的函数称之为内部函数,内部函数只能在函数内使⽤,不会污染外部空间。定义内部函数的函数称之为外部函数,这样的定义构成函数的嵌套
def outter(a): #外部函数
x = 10
def inner(y): #内部函数
print(x + y)
inner(a)
outter(20)
def outter(a): #外部函数
x = a
def inner(y): #内部函数
return x + y #引⽤外部变量
return inner #返回内部函数(闭包)
pf = outter(20)
print(pf(10)) #30
print(pf(20)) #40
在闭包中⽆法直接修改外部变量x的值
def outter(a): #外部函数
x = a
def inner(y): #内部函数
# x += 10 #UnboundLocalError: local variable 'x' referenced
before assignment
return x + y
return inner
在python3中可以通过nonlocal关键字声明⼀下x,表示这个变量不是局部变量,需要向上⼀层变量空间找这个变量。
def outter(a): #外部函数
x = a
def inner(y): #内部函数
nonlocal x
x += 10
return x + y
return inner
8. 装饰器(重点,难点)
软件开发中有⼀条⾮常重要的规则就是:对修改封闭,对扩展开放。 对于⼀个现有的函数,如果想要增强此函数的功能,但是不允许修改此函数源代码的时候,使⽤装饰器来解决这个问题
#被修饰的函数
def say_hello(name):
print('我就是⼈⻅⼈爱,花⻅花开的%s'%name)
# 参数是被修饰函数
def wrapper(func): #1.定义装饰器
def inner(name): #2.定义闭包 在闭包中增加功能
print('-' * 50)
func(name) #3.调⽤原函数实现原来的功能
print('*' * 50)
return inner #4.返回增强功能的函数
say_hello = wrapper(say_hello) #5.获取增强功能的函数
say_hello('⻛流⼩王⼦') #6. 调⽤增强函数,
8.2使⽤@语法糖将装饰器应⽤到指定函数上,简化使⽤
#⽤法:在需要被装饰的函数前⾯加上: @装饰器的名字 【外层函数的名字】
def wrapper(func):
def inner(age1):
#增加的功能
if age1 < 0:
age1 = 0
#调⽤原函数
func(age1)
return inner
@wrapper #相当于 getAge = wrapper(getage)
def getAge(age):
print(age)
getAge(18) #调⽤增强的函数
注意:使⽤@的时候,如果装饰器和需要被装饰的函数在同⼀个.py⽂件中的时候,装饰
器⼀定要出现在被装饰函数的前⾯【Python中的代码是从上往下依次执⾏的】
8.3 带有不定⻓参数的装饰器
同⼀个装饰器可以应⽤于多个函数
def wrapper(func):
def inner(*tup,**kw): #变⻓参数
func(*tup,**kw)
print('-'*50)
return inner
@wrapper
def test1(a,b):
print(a,b)
test1(1,2)
@wrapper
def test1(a):
print(a)
8.4 多个装饰器作⽤在⼀个函数上(不重要)
#多个装饰器同时作⽤于⼀个函数的时候,要注意⼀下装饰器执⾏顺序
def wrapper1(func):
print("wrapper1~~~~外部函数")
def inner(a,b):
print('wrapper1-----内部函数')
func(a,b)
return inner
def wrapper2(func):
print("wrapper2~~~~外部函数")
def inner(a, b):
print("wrapper2~~~~内部函数")
func(a, b)
return inner
@wrapper1
@wrapper2
def text(num1,num2):
print(num1 + num2)
text(10,20)
8.5 装饰器的另⼀种⽤法(flask中的⽤法)
def wrapper(rule):
def inner(func):
pattern = (rule,func)
#注册路由
urlpatterns.append(pattern)
return func
return inner
@wrapper(r'/login/')
def index():
return "⾸⻚"
9.递归函数
1 嵌套调⽤
在函数A中可以调⽤函数B,在函数B中可以调⽤函数C,这种调⽤⽅式称为函数的嵌套调⽤
2 递归调⽤
⼀个函数直接或间接的调⽤⾃⼰则称为递归调⽤。
def fac(n):
if n ==0: #递归终⽌条件,如果n为0,则结束递归调⽤,返回
return 1
else:
tmp = fac(n-1) #调⽤⾃⼰计算n-1的阶乘
return n * tmp #返回n * (n-1)!
print(factorial(5)) #120
3 递归适⽤条件
如果⼀个问题规模缩减后,求解⽅式和原来⼀样,⼩规模问题解决后导致问题的最终解决,则可适⽤递归
递归的写法:
⼀个递归程序必须包含两部分:
1) 递归终⽌条件
2) 递归调⽤⾃⼰
def recurve(*args,**kw):
if 递归终⽌条件: #递归终⽌条件必须在递归调⽤前
# to do
else:
#to do
recurve(参数)
#to do