30天python基础(七,函数)

函数引⼊
前⾯我们写过九九乘法表,但如果我要七七乘法表或五五乘法表的话,你会看到三者代码极其类似,只是循环变量不同,那么如何做到代码重⽤,⽽不是简单拷⻉黏贴修改呢,其实可是使⽤函数完成这⼀功能

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]:
 函数体
  • 函数名命名规则同变量名,要满⾜标识符命名规则
  • 函数定义分两部分函数头和函数体
  • 函数体,就是实现功能的代码段,以:开头,必须缩进
  • 函数名的命名⻛格:⼀般建议⽤下划线分隔的⼩写单词组成:say_hello

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='何⽅') #⼀个关键字参数
  • 默认值,如果形参在定义的时候给定⼀个值,那么函数在调⽤时就可以不传实参,可以简化调⽤
  1. 默认值参数必须放到最右边
  2. 如果传了实参,那么实参优先,不会使⽤默认值
  3. 默认值只计算⼀次
  4. 默认值必须是不可变对象
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']
  • 可变参数,传⼊的参数个数是可变的,可以是1个、2个到任意个,还可以是0个。
#使⽤*接收任意数量的位置参数
 #注意:*的不定⻓参数被当做元组处理
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 参数组合

  • 形参顺序须按照以下顺序:位置参数、默认值参数、*args,**kwargs

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 表达式

  • return的作⽤⼀个是终⽌函数的执⾏,所有执⾏了return后,其后的语句不会被执⾏
  • 如果没有return语句,则默认返回的是None
  • 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的参数传递可以称之为对象引⽤传递,对象可以分为:

  • 不可变对象:int、float、None、complex、bool、tuple、str,range
    在函数内部不可能修改函数外部的变量
  • 可变对象: dict、list
    可以在函数内部修改
    7 空函数
    借助于pass语句实现,函数体不完成任何功能,只有⼀个pass语句
def test():
 pass # 占位符

函数(2)
1.函数类型
函数也是⼀种类型,我们⾃定义的函数就是函数对象,函数名保存了函数对象的引⽤(地址)

def test():
 print('我是测试函数')
print(test) #函数名是变量,指向了函数对象
pf = test #pf变量也指向了函数对象,所以也可以通过pf调⽤test函数
pf()

2. 匿名函数
不再使⽤def 函数名()这种形式定义函数,⽽是使⽤lambda来创建匿名函数
特点:

  1. ambda只是⼀个表达式,函数体⽐def定义的函数简单的多
  2. lambda的函数体不再是代码块,⽽是⼀个表达式
  3. 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 变量作⽤域的分类
按照作⽤域划分,可以分为

  • L:Local,局部作⽤域
  • E:Enclosing,闭包作⽤域【闭包的外部函数中定义的变量】
  • G:Global,全局作⽤域 在所有函数外定义的变量
  • B:Built-in,內建作⽤域【内置作⽤域】
#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. 装饰器(重点,难点)

软件开发中有⼀条⾮常重要的规则就是:对修改封闭,对扩展开放。 对于⼀个现有的函数,如果想要增强此函数的功能,但是不允许修改此函数源代码的时候,使⽤装饰器来解决这个问题

  • 本质:就是⼀个闭包,还是⼀个返回函数的⾼阶函数
  • 好处:就是在不⽤修改原函数代码的前提下给函数增加新的功能
    8.1装饰器写法
#被修饰的函数
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. ⼀个递归程序必须包含两部分:

    	1) 递归终⽌条件
    	2) 递归调⽤⾃⼰
    
def recurve(*args,**kw): 
 if 递归终⽌条件: #递归终⽌条件必须在递归调⽤前
 # to do
 else:
 #to do
 recurve(参数)
 #to do

你可能感兴趣的:(学习专栏)