3. 函数基础

1.1 为何要用函数之不用函数的问题

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

1.2 函数分类

1、内置函数
针对一些简单功能,python解释器已经定义好了的函数即内置函数,拿来就用而无需事先定义,如len(),max(10,11),help(函数名)

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

2. 定义函数

2.1 如何自定义函数?

语法

def 函数名(参数1,参数2,参数3,…):
”’注释”’ # help(函数名) 显示此段注释信息
函数体
(可选)return 返回的值

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

函数即“变量”,“变量”必须先定义后引用。未定义而直接引用函数,就相当于在引用一个不存在的变量名

'''定义阶段(def bar() -> def foo()正常顺序)'''
def foo():
    print('from foo')
    bar()

def bar():
    print('from bar')

'''调用阶段:bar()已经存在,正常执行'''
foo()

我们在使用函数时,一定要明确地区分定义阶段和调用阶段

3. 函数在定义阶段只检测语法,不执行代码

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

'''定义认证函数'''
def auth():
    sex      # 定义函数阶段只检测语法,不执行代码;'print(' 报语法错误
    name=input('name>>: ').strip()
    password=input('password>>: ').strip()
    if name =='egon' and password == '123':
        print('login successfull')
    else:
        print('user or password error')

print(auth)     # ==> <function auth at 0x101e61ea0>
'''调用阶段'''
auth()    # 调用阶段执行代码,name 'sex' is not defined

4. 定义函数的三种形式

定义时无参,意味着调用时也无需传入参数;定义时有参,意味着调用时则必须传入参数

4.1 无参:应用场景仅仅只是执行一些操作,比如与用户交互,打印

4.2 有参:需要根据外部传进来的参数,才能执行相应的逻辑,比如统计长度,求最大值最小值

'''功能区分,便于今后扩展'''
def auth(name,password):
    if name =='egon' and password == '123':
        print('login successfull')
    else:
        print('user or password err')

def interactive():
    name=input('name>>: ').strip()
    password=input('password>>: ').strip()
    auth(name,password)

interactive()

4.3 空函数:设计代码结构

5. 调用函数

5.1 函数的返回值

通常有参函数需要有返回值,输入参数,经过计算,得到一个最终的结果
通常无参函数不需要有返回值,仅仅只是执行一系列的操作,最后不需要得到结果
返回的值没有类型限制,return返回值的个数

无return -> None
return 1个值-> 返回1个值
return 逗号分隔多个值 -> 返回元组

return的特点:

1、 函数内可以有多个return,但是只能执行一次return
2、 执行return函数就立刻结束,并且return的后值当做本次调用的结果返回

5.2 函数调用的三种形式

def my_max(x,y):
    if x >= y:
        return x
    else:
        return y

'''语句形式'''
res1=my_max(1,2)

'''表达式形式'''
res2=my_max(1,2)*10

'''当成另外一个函数的参数'''
res3=my_max(my_max(1,2),3)     # range(len([1,2,3]))

print(res1,res2,res3)

6. 函数的参数

6.1 python中参数的分类:

形参:在定义阶段括号内指定的参数,相当于变量名
实参:在调用阶段括号内传入的值称之为实参,相当于值;==在调用阶段,实参的值会绑定给形参,在调用结束后解除绑定==
位置参数:按照从左到右的顺序依次定义的参数
关键字参数:函数调用时,按照key=value的形式定义的==实参==
'''
**位置形参**:必须被传值,多一个少一个都不行
**位置实参**:按照位置给形参传值
**关键字参数**: 指名道姓地给形参传值,无需按照位置为形参传值
'''
def foo(x,y,z):    # 行参在定义阶段由于并未赋值,不占用内存空间
    print(x,y,z)

foo(1,2,3)        # x=1,y=2,z=3;foo(1,2,3,4)报错
foo(x=1,y=2,z=3)
foo(1,y=2,z=3,x=4) # 关键字实参必须在位置实参右面;对同一个形参不能重复传值(foo() got multiple values for argument 'x')
默认参数:在函数定义阶段,就已经为其赋值的==行参==
'''
定义阶段已经有值意味着调用阶段可传值也可不传值,经常需要变的参数定义成位置形参,变化较小的参数定义成默认参数(形参)
1. 默认参数的值只在定义时被赋值一次
2. 默认参数的定义应该在位置形参后面,`def register(name,sex='male',age,): pass` 定义阶段报语法错误
3. 默认的参数的值通常应该是不可变类型
'''
def register(name,age,sex='male'):
    print(name,age,sex)

res=1
def foo(x,y=res):
    print(x,y)

res=10    # 默认参数的值只在定义时被赋值一次,不影响默认参数
foo('aaaaaaaa')    # ==> aaaaaaaa 1
register('alex',38,'female')
register(sex='female',name='egon1',age=18)
register('egon2',18,sex='female')
可变长参数:在调用函数时,实参值的个数不固定;实参的形式有:位置实参和关键字实参
'''形参的解决方案:*args 处理位置参数,**kwargs 处理关键字参数'''
def foo(x,y,*args): # args=(3,4,5,6),args就是普通变量名,约定俗称写成 *args
    print(x,y)
    print(args)

def bar(x,y,**kwargs): # kwargs={'c':3,'a':1,'b':2}
    print(x,y)
    print(kwargs)

foo(1,2,3,4,5)
foo(1,2,*[3,4,5])  # 等同于 foo(1,2,3,4,5)
foo(*(1,2,3,4,5))  # 等同于 foo(1,2,3,4,5)
bar(1,y=2,a=1,b=2,c=3)
bar(y=2,**{'c':3,'x':1,'b':2,'a':1}) #foo(y=2,x=1,a=1,c=3,b=2)
def foo(name,age):
    print(name,age)

foo(**{'name':'egon','age':18})    # 等同于 foo(name='egon',age=18)
foo({'name':'egon','age':18})    # foo() missing 1 required positional argument: 'age'
'''
*args + **kwargs 
在一个函数当中,把它接收到的参数的形式原封不动的传给内部另外一个函数
'''
def bar(x,y,z):
    print(x,y,z)

def wrapper(*args,**kwargs): # 可接收任意长度参数;args=(1,2,3),kwargs={'a':1,'b':2,'c':3}
    print(args,kwargs)
    bar(*args,**kwargs) # 调用阶段使用 * & **,bar(*(1,2,3),**{'a':1,'b':2,'c':3}) = bar(1,2,3,a=1,b=2,c=3)

wrapper(1,2,3,a=1,b=2,c=3)    # 报错bar() got an unexpected keyword argument 'a';
wrapper(1,2,3)
wrapper(1,z=2,y=3)   # 关键字实参必须在位置实参右面,所以**kwargs必须在*args右边
命名关键字参数:*后定义的参数,必须被传值(有默认值的除外),且必须按照关键字实参的形式传递(key=value)
'''可以保证,传入的参数中一定包含某些关键字'''
def foo(x,y,*args,a=1,b,**kwargs):   # a 为带默认值的命名关键字参数
    print(x,y)      # ==> 1,2
    print(args)     # ==> (3,4,5)
    print(a)        # ==> 1
    print(b)        # ==> 3
    print(kwargs)     # ==> {'c': 4, 'd': 5}

foo(1,2,3,4,5,b=3,c=4,d=5)
foo(1,2)     # foo() missing 1 required keyword-only argument: 'b'

函数对象、函数嵌套、名称空间与作用域、装饰器

1. 函数对象:函数是第一类对象,即函数可当作数据传递

1.1 可以被引用 x=1,y=x

def func(x,y): print(x,y)

f=func    # 赋值
f(1,2)    # 调用

1.2 可当做函数的参数传入

def foo(): print('from foo')
def bar(func): func()

bar(foo)

1.3 可以当做函数的返回值

1.4 可以当做容器类型的元素

def foo(): print('from foo')
def bar(): return foo

f=bar()
f()
l=[foo,bar]     # print(l),==> [<function foo at 0x101d61ea0>, <function bar at 0x103ca1510>]
l[0]()
'''ftp功能 函数定义'''
def get(): print('get')
def put(): print('put')
def ls(): print('ls')
def auth(): print('auth')

# '''LOW 方法'''
# cmd=input('>>: ').strip()
# if cmd == 'get': get()       # cmd() ==> 'get'(),无意义
# elif cmd == 'put': put()
# elif cmd == 'ls': ls()

'''推荐方法:利用该特性,优雅取代多分支的if'''
func_dic={'get':get,'put':put,'ls':ls,'auth':auth}    # 加一个新功能,加一对key/value即可
cmd = input('>>: ').strip()
if cmd in func_dic:
    func_dic[cmd]()

2. 函数嵌套

2.1 函数的嵌套调用

def max(x,y): return x if x > y else y
def max4(a,b,c,d):     # 求四个值的最大值
    res1=max(a,b)
    res2=max(res1,c)
    res3=max(res2,d)
    return res3

print(max4(1,2,3,4))

2.2 函数的嵌套定义

def f1():
    def f2():
        def f3():
            print('from f3')
        f3()
    f2()

f1()     # ==> from f3

3. 名称空间与作用域

3.1 什么是名称空间?

名称空间:存放名字的地方,三种名称空间
(x=1,1存放于内存中,那名字x存放在哪里呢?名称空间正是存放名字与值绑定关系的地方)(x: 值对应的内存地址,同样存在内存空间内)

  • 内置名称空间:python解释器内置的名字,max,len,print(python解释器启动生效)
  • 全局名称空间:文件级别定义的名字:x,func,x,y;(执行python文件时生效)

    x=1
    def func():pass
    import time
    if x == 1:
    y=2

  • 局部名称空间:函数内部定义的名字(函数调用时生效,调用结束失效)

名称空间加载顺序
  1. python解释器先启动,因而首先加载的是:内置名称空间
  2. 执行test.py文件,然后以文件为基础,加载全局名称空间
  3. 在执行文件的过程中如果调用函数,则临时产生局部名称空间
访问查找顺序

局部名称空间(在局部可以查看全局的) —> 全局名称空间(在全局无法查看局部的) —> 内置名称空间

LEGB 代表名字查找顺序: locals -> enclosing function -> globals -> builtins
locals 是函数内的名字空间,包括局部变量和形参
enclosing 外部嵌套函数的名字空间(闭包中常见)
globals 全局变量,函数定义所在模块的名字空间
builtins 内置模块的名字空间

'''
全局作用域(全局范围):内置名称空间与全局名称空间的名字,全局存活,全局有效,globals()查看全局作用域
局部作用域(局部范围):局部名称空间的名字,临时存活,局部有效,locals()查看局部作用域
(x='gobal',f1(x=1,f2(x=2,f3(x=3))))
'''
x='gobal'
def f1():
    x=1
    def f2():
       x=2
       def f3():
           x=3
           print(x)
       f3()
    f2()

f1()
f2()    # 全局名称空间 --> 内置名称空间,无法找到
xxxxxxxxxxxxxxx=111111111111111111111
print(globals())     # 全局名称空间,python优化,以字典形式打印值与名字绑定关系
print(dir(globals()['__builtins__']))     # 内置名称空间
print(locals() is globals())     # True;全局的局部仍然是全局本身

def func():
    yyyyyyyyyyyyyyyyyyyyyyyy=22222222
    print(globals())    # 全局名称空间
    print(locals())      # yyyyyyyyyyyyyyyyyyyyyyyy

func()


x=100
def func():
    # global x   声明x为全局名字,print(x) ==> 1
    x=1

func()
print(x)     # 局部不会影响全局



x='global'
def f1():
    x=1
    def f2():
        nonlocal x    # 改当前层函数外层的x,找到为止,找不到报错,但不会在全局空间找x
        x=0
    f2()
    print('===f1 innter--->',x)

f1()    # ==> 0
print(x)     # ==> 'global'
强调两点:

1、打破函数层级限制来调用函数

def outter():
    def inner():
        print('inner')
    return inner     # 利用函数对象概念,把定义在函数内部的函数当作返回值返回出来,赋值给一个全局变量

f=outter()
print(f)     # ==> <function outter.<locals>.inner at 0x1043a1510>

def bar(): f()    # 全局空间内找到f

bar()       # ==> inner

2、函数的作用域关系在函数定义阶段就已经固定了,与调用位置无关

x=1
def outter():
    x=2
    def inner():
        print('inner',x)
    return inner

f=outter()
x=111     # 如果注释掉x=2,最终在全局名称空间找到 x 新赋值 111,bar() ==> 'inner' 111
def bar():
    x=3
    f()
bar()    # 'inner' 2;inner的值与函数的调用位置无关,回到定义位置去找作用域关系

4. 闭包函数

4.1 什么是闭包?

1 定义在函数内部的函数
2 该函数的函数体代码包含对外部作用域(而非全局作用域)名字的引用
3 通常将闭包函数用return返回,然后可以在任意使用
之前我们都是通过参数将外部的值传给函数,闭包提供了另外一种思路,

4.2 闭包的意义与应用

5. 装饰器(闭包函数的一种应用场景)

1、开放封闭原则:对扩展开放,对修改封闭(不修改被装饰对象的源代码和调用方式)
2、装饰器:装饰他人的器具,本身可以是任意可调用对象,被装饰者也可以是任意可调用对象。现在的场景装饰器-》函数,被装饰的对象也是-》函数
装饰器的目的:在遵循1,2的前提下为被装饰对象添加上新功能

5.1 装饰器语法

import time
def timmer(func):
    def inner(*args,**kwargs):
        start=time.time()
        res=func(*args,**kwargs)
        stop=time.time()
        print('run time is %s' %(stop-start))
        return res
    return inner

@timmer #index=timmer(index)
def index(name):
    time.sleep(1)
    print('welecome %s to index' %name)
    return 1111

res=index('egon')     # res=inner('egon')
print(res)

@timmer #home=timmer(home)
def home(name):
    print('welcome %s to home page' %name)

home('egon') #inner('egon')

5.1 装饰器的使用

无参装饰器
import time
def timmer(func):
    def wrapper(*args,**kwargs):
        start_time=time.time()
        res=func(*args,**kwargs)
        stop_time=time.time()
        print('run time is %s' %(stop_time-start_time))
        return res
    return wrapper

@timmer
def foo():
    time.sleep(3)
    print('from foo')
foo()
有参装饰器
def auth(driver='file'):
    def auth2(func):
        def wrapper(*args,**kwargs):
            name=input("user: ")
            pwd=input("pwd: ")

            if driver == 'file':
                if name == 'egon' and pwd == '123':
                    print('login successful')
                    res=func(*args,**kwargs)
                    return res
            elif driver == 'ldap':
                print('ldap')
        return wrapper
    return auth2

@auth(driver='file')
def foo(name):
    print(name)

foo('egon')
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值