Python函数讲解

python函数讲解

函数三问:

什么是函数?

函数就是可以完成特定功能的代码块,函数就是存放代码的容器可以理解为一个装有一些工具的箱子

为什么有函数?

1.减少了代码的冗余
2.让程序增加了可扩展性
3.让程序变得更容易维护

函数怎么用?

# 函数必须先定义后引用!!!!

# 定义函数
def 函数名(参数们):
    函数体
    return '返回值','返回值2'
# 可以有多个返回值

"""
函数名:拿到函数的地址
函数名():拿到函数的地址,且执行函数中存放的代码块(函数体)
函数名(参数):执行函数且传入参数

注:函数名加括号是调用,会得到函数的返回值,返回值和普通的变量一样可以赋值,直接打印,使用运算,函数内没有return默认返回None,return空也返回None
"""
def fu(num):
    print('传入的num值:%s'% num)
    return '收到了'
  
res = fu(10)
# 这一个步骤调用了函数fu且传入了参数10,并把返回值赋值给变量res
# 控制台会打印:传入的num值为:10
# res的值为:收到了

组成函数的四部分

"""
1.函数名:使用该函数的依据
2.函数体:完成特定功能的代码块
3.返回值:函数执行完成的返回结果
4.参数:执行程序所需要的条件信息
"""

函数分类

根据参数列表划分

  • 无参函数:不需要外界传入条件信息
def start():
    print('程序已启动')
    return '关闭'
start()
  • 有参函数:需要外界资源
def login(usr, pwd):
    if usr == 'lmd' and pwd == '123':
        print('登录成功')
    return '登录失败'

login('lmd', '123')

根据返回值划分

  • 空返回:返回None
def demo(x, y):
    print(x + y)


def demo(x, y):
    print(x + y)
        return  # 用来强制结束函数类似于(break)
  • 一值返回
def demo(x, y):
    return x + y
  • 返回多值
def demo(x, y):
    return x, y # 返回装有多个值的元祖
  
print(demo(10, 20))
(10, 20)

函数的嵌套调用

# 循环调用就是在一个函数的内部调用了另外一个函数

# 求俩个数的最大值
def max2(a1, a2):
    if a1 > a2:
        return a1
    return a2

# 三个
def max3(a1, a2, a3):
    m2 = max2(a1, a2)
    return max2(a3, m2)

# 四个
def max4(a1, a2, a3, a4):
    m3 = max3(a1, a2, a3)
    return max2(m3, a4)


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

参数分类

先抛出两个概念:

  • 实参:调用函数,在括号内传入的实际值,值可以为常量,变量,表达式,函数等
  • 形参:定义函数,在括号内声明的变量名,用来接受外界传来值
  • 注:形参随着函数的调用而产生,只在函数内部有效,随着程序的调用结束而销毁

实参

  • 位置实参:按照顺序给形参传值,顺序乱了,接受的结果也乱的

  • 关键字实参:明确形参名和值给形参传值,告诉系统你传的值是给谁用的,可以不按照顺序!但是一定要再位置实参之后!

    """
    混用:
    1.关键字实参出现必须在位置实参后
    2.多个位置实参还是按照位置传参
    3.关键字传参可以不按照顺序给形参传值
    """
    def fn(n1, n2, n3, n4, n5):
        return n1, n2, n3, n4, n5
    
    print(fn(30, 10, 20, n5=100, n4=99))
    (30, 10, 20, 99, 100)

位置形参的范畴

  • 位置形参
def fn(a, b, c):
    print(a, b, c)
    
# 位置形参可以由 位置实参 与 关键字实参 来传值
fn(10, 20, 30)
fn(a=10, b=20, c=30)
  • 默认形参
def fu(a=10, b=20):
    return a, b

# 默认形参可以由 位置实参 与 关键字实参 来传值,还可以不用传值(采用自身默认值)
print(fu())
# (10, 20) ,不传默认自己本身
print(fu(20, 10))
# (20, 10),可以位置实参传值
print(fu(b=1, a=2))
# (2, 1)
print(fu(a=1000))
# (1000, 20),可以只传一个
print(fu(a=1000 * 100, b=10 * 20))
# (100000, 200),可以运算传值


# 混用
# 位置形参与默认形参同时存在,默认形参必须在后
def fn1(a,b,c=10,d=20):
    print(a, b, c, d)

# 位置形参必须传值,默认形参分情况传值
fn1(100, 200, d=1000)
  • 可变长形参
# 可变长形参会以 元组 形式接受 位置形参与默认形参未接受完的 所有传入的位置实参,用索引来取第几个
# 可变长形参只能由 位置实参 来传值 
# *代表接收所有没有被接收的位置实参,可以不用args来接收写什么都一样前提是遵循命名规范,约定俗称用*args

# 案例1:
def fu(n1, n2, *args):
    return n1, n2, args

print(fu(10, 20, 30, 40, 50))
# (10, 20, (30, 40, 50))

# 案例2:
def fu(n1, n2, *args):
    return n1, n2, args[0]

print(fu(10, 20, 30, 40, 50))
# (10, 20, 30)


# 小细节:可变长形参只能接受位置实参的值,位置实参还必须在关键字实参前,
#       导致默认形参只能由位置实参来传值
fn(1, 20, 100, 200) # 1给a  20给b,不能用b=20传  100,200给args

关键字形参范畴

# 前提:出现在*之后的形参称之为关键字形参

#案例1:
def fu(n1, n2, *args, a, b=100):
    return n1, n2, args, a, b
  
# a,b都是出现在*之后的,都是关键字形参,必须通过关键字实参来传值,顺序无所谓,没有被接收的位置实参都是被args接收

print(fu(10, 20, 30, 40, 50, b=11, a=9))
# (10, 20, (30, 40, 50), 9, 11)


# 可变长关键字形参:用来接收没有被关键字形参接收完的关键字形参,也只能由关键字实参来传值
# 用字典来存放数据
# 案例2:
def fu(**kwargs):
    return kwargs

print(fu(b=11, a=9, q=100, w=11, e=222))
# {'b': 11, 'a': 9, 'q': 100, 'w': 11, 'e': 222}

# 案例3:
def fn(**kwargs):
    print(kwargs)
fn(a=10,b=20)  # {'a': 10, 'b': 20}

dic = {'x': 100, 'y': 200}
fn(**dic)  # {'x': 100, 'y': 200}

# 拓展:
dic = {'b': 11, 'a': 9, 'q': 100, 'w': 11, 'e': 222}
print(*dic)
# b a q w e  得到的是key的值

补充:args整体赋值

参数总结

def fn(a, b, c=10, *args, d, e=20, f, **kwargs):
    pass
# 位置形参:a、b
# 默认形参:c
# 可变长位置形参:args
# 无初值关键字形参:d、f
# 有初值关键字形参:e
# 可变长关键字参数:kwargs


# 1.位置形参与默认形参: 能用 位置实参 关键字实参 传值
# 2.可变长位置形参:只能 位置实参 传值
# 3.所以关键字形参:只能 关键字实参 传值

函数对象

# 函数名就是存放了函数的内存地址,存放了内存地址的变量都是对象,即 函数名 就是 函数对象
# 函数对象的应用
# 1 可以直接被引用  fn = cp_fn
# 2 可以当作函数参数传递 computed(cp_fn, 100, 20)
# 3 可以作为函数的返回值  get_cp_fn(cmd): return add
# 4 可以作为容器类型的元素  method_map: 对应关系中的值
def add(a, b):
    return a + b
def low(a, b):
    return a - b
def jump(a, b):
    return a * b
def full(a, b):
    return a / b
def quyu(a, b):
    return a % b
def computed(fn, n1, n2):
    res = fn(n1, n2) # 调用了函数且传递了参数n1,n2
    return res
method_map = {
    'add': add,
    'low': low,
    'jump': jump,
    'full': full,
    'quyu': quyu,
}
# 根据指令获取计算方法
def get_cp_fn(cmd):
    if cmd in method_map:
        return method_map[cmd] # 取到功能字典中对应的value
    return add  # 输入有误用默认方法处理


while True:
    cmd = input('cmd: ')
    if cmd == 'quit':
        break
    cp_fn = get_cp_fn(cmd)
    result = computed(cp_fn, 100, 20) # 也就等于result = computed(加减乘除, 100, 20)
    print(result)

名称空间

# 名称空间:存放名字与内存空间地址对应关系的容器
# 作用:解决由于名字有限,导致名字重复发送冲突的问题

# 三种名称空间
# Built-in:内置名称空间;系统级,一个;随解释器执行而产生,解释器停止而销毁
# Global:全局名称空间;文件级,多个;随所属文件加载而产生,文件运行完毕而销毁
# Local:局部名称空间;函数级,多个;随所属函数执行而产生,函数执行完毕而销毁

# 注:
# del 名字:可以移除查找的名字与内存空间地址的对应关系
# 加载顺序:Built-in > Global > Local

global关键词

num = 10

def fn():
    global num
    num = 20
    print(num)

print(num)
fn()  # 注:一定要调用函数,才能产生名字,并提升
print(num)
# global关键词可以将Local的名字提升为Global的名字
# 一个文件中的Global名字就是一个,所以函数内部外部使用的名字都是一个
# 函数为执行的时候找的是全局的

函数的嵌套定义

def outer():
    num = 20
    def inner():
        print(num)  # inner就可以直接使用outer中的名字
    inner()
outer()

作用域

# 作用域:名字起作用的范围
# 作用:解决同名字可以共存问题

# 四种作用域
# Built-in:内置作用域,所有文件所有函数
# Global:全局作用域,当前文件所有函数
# Enclosing:嵌套作用域,当前函数与当前函数的内部函数
# Local:局部作用域,当前函数

# 注:
# 不同作用域之间名字不冲突,以达到名字的重用
# 查找顺序:Local > Enclosing > Global > Built-in
len = 10  #全局作用域
def outer():
    len = 20  # 外层函数的局部变量:Enclosing - 嵌套作用域
    def inner():
        len = 30
        print('1:', len)  # 30, inner -> outer -> global -> built-in 自己本身有找自己
    inner()
    print('2:', len)  # 20, outer -> global -> built-in 上一层outer
outer()
print('3:', len)  # 10, global -> built-in # 找全局

del len
print('4:', len)  # len地址, built-in ,全局没有就找内置

闭包

# closure:被包裹的函数,称之为闭包
# 完整的闭包结构:1.将函数进行闭包处理;2.提升函数名的作用域,将内部函数对象作为外部函数的返回值

def outer(url):
    def get_html():
        html = requests.get(url)
        print(html.text)
    return get_html
# 先预定义多个爬虫方法,爬页面操作并未执行
baidu = outer('https://www.baidu.com')
python = outer('https://www.python.org')
sina = outer('https://www.sina.com.cn')
# 什么时候想爬什么页面就调用指定页面的爬虫方法
baidu()
sina()
baidu()

nonlocal关键字

# 作用:将 L 与 E(E中的名字需要提前定义) 的名字统一

# 应用场景:如果想在被嵌套的函数中修改外部函数变量(名字)的值

# 案例:

def outer():
    num = 10
    print(num)  # 10 
    def inner():
        nonlocal num
        num = 20
        p77rint(num)  # 20
    inner()
    print(num)  # 20

装饰器

# 把要被装饰的函数作为外层函数的参数通过闭包操作后返回一个替代版函数
# 被装饰的函数:fn
# 外层函数:outer(func)  outer(fn) => func = fn
# 替代版函数: return inner: 原功能+新功能

def fn():
    print("原有功能")

# 装饰器
def outer(tag):
    def inner():
        tag()
        print(新增功能")
    return inner
fn = outer(fn)              
              
fn()
  • @语法糖: @外层函数
def outer(f):
    def inner():
        f()
        print("新增功能1")
    return inner
              
def wrap(f):
    def inner():
        f()
        print("新增功能2")
    return inner              

@wrap  # 被装饰的顺序决定了新增功能的执行顺序
@outer  # <==> fn = outer(fn): inner      
def fn():
    print("原有功能")
  • 有参有返的函数被装饰
def check_usr(fn):  # fn, login, inner:不同状态下的login,所以参数是统一的
    def inner(usr, pwd):
        # 在原功能上添加新功能
        if not (len(usr) >= 3 and usr.isalpha()):
            print('账号验证失败')
            return False
        
        # 原有功能
        result = fn(usr, pwd)
        
        # 在原功能下添加新功能
        # ...
        
        return result
    return inner


@check_usr
def login(usr, pwd):
    if usr == 'abc' and pwd =='123qwe':
        print('登录成功')
        return True
    print('登录失败')
    return False

# 总结:
# 1.login有参数,所以inner与fn都有相同参数
# 2.login有返回值,所以inner与fn都有返回值

"""
inner(usr, pwd):
    res = fn(usr, pwd)  # 原login的返回值
    return res


login = check_usr(login) = inner

res = login('abc', '123qwe')  # inner的返回值
"""
  • 装饰器的最终写法
def wrap(fn):
    def inner(*args, **kwargs):
        print('前增功能')
        result = fn(*args, **kwargs)
        print('后增功能')
        return result
    return inner

@wrap
def fn1():
    print('fn1的原有功能')
@wrap
def fn2(a, b):
    print('fn2的原有功能')
@wrap   
def fn3():
    print('fn3的原有功能')
    return True
@wrap
def fn4(a, *, x):
    print('fn4的原有功能')
    return True

fn1()
fn2(10, 20)
fn3()
fn4(10, x=20)
  • 带参装饰器
# 了解
def outer(input_color):
    def wrap(fn):
        if input_color == 'red':
            info = '\033[36;41mnew action\33[0m'
        else:
            info = 'yellow:new action'

        def inner(*args, **kwargs):
            pass
            result = fn(*args, **kwargs)
            print(info)
            return result
        return inner
    return wrap  # outer(color) => wrap


color = input('color: ')
@outer(color)  # @outer(color) ==> @wrap  # func => inner
def func():
    print('func run')

func()

运算

  • 三目运算
# 三目(元)运算符:就是 if...else...语法糖
# 前提:简化if...else...结构,且两个分支有且只有一条语句
# 注:三元运算符的结果不一定要与条件直接性关系

cmd = input('cmd: ')
print('可以转化为数字') if cmd.isdigit() else print('不可以转化为数字')


a = 20
b = 30
res = a if a > b else b  # 求大值,如果a大于b返回a否则返回b
print(res)


res = 'b为小值' if a > b else 'a为小值'  # 求小值
print(res)
  • 推导式
# 列表推导式:[v for v in iterable]
dic = {'a': 1, 'b': 2, 'c': 3}  # => [('a', 1), ('b', 2), ('c', 3)]
res = [(k, v) for k, v in dic.items()]

# 字典推导式:{k: v fro k, v in iterable}
ls = [('a', 1), ('b', 2), ('c', 3)]  # => {'a': 1, 'b': 2, 'c': 3}
res = {k: v for k, v in ls}

递归

# ***
# 递归:回溯与递推 
# 回溯:询问答案的过程
# 递推:推出答案的过程

# 本质:函数的自我调用
# 直接:自己调自己
# 间接:自己调别人,别人最终由调回自己

匿名函数

# 匿名函数:没有名字的函数
# 语法:lambda 参数列表: 一个返回值表达式

# 重点:
# 1.匿名函数:没有函数名,没有函数体,只有一个返回值
# 2.关键字:lambda  |  参数列表省略()  |  返回值return关键字也被省略

# 应用场景:
# 1.匿名函数函数地址可以被一个变量接受,该变量就可以作为函数名来使用,但就违背了匿名初衷
# 2.结合内置函数来使用: 内置函数某些参数需要一个函数地址,
#       -- 可以赋值一个有名函数名,也可以直接赋值匿名函数

补充

深拷贝 和 浅拷贝

浅拷贝:指向的都是同一内存地址

深拷贝:开辟新的内存空间来存放

开放封闭原则:

不改变调用方式与源代码上增加功能
1.不能修改被装饰对象(函数)的源代码(封闭)
2.不能更改被修饰对象(函数)的调用方式,且能达到增加功能的效果(开放)

猜你喜欢

转载自www.cnblogs.com/limengda/p/10800455.html
今日推荐