函数

* 和 ** 分别在形参和实参中的用法

  • 形参
    • *参数:函数接收任意长度的非关键字参数,系统将这些参数用tuple元组组合起来表示
    • **参数:函数接收任意长度的关键字参数,系统将这些参数用dict字典组合起来表示
    • 混合使用:关键字必须在位置参数后面,否则报错
  • 实参
    • *参数:
      • 实参中的 * 后面跟的是可迭代对象,获取的是可迭代对象的 key,此时形参为位置参数或可变参数都可以
      • 除了list, str, tuple等,还可以用dict作为实参,获取的是dict中的key值
    • ** 参数:实参中的 ** 后面跟的是可迭代映射,获取的是可迭代映射的 value。此时,当形参为位置参数时,获取的是可迭代映射的value,当形参为关键字参数时,获取的是dict

命名关键字参数(了解)

  • 需要形参传实参的时候必须按照关键字
    • 在形参 * args的后面
    • 如果形参中还有** kwargs,那必须在它的前面

名称空间

  • 定义:用来存放变量名与数据值之间绑定关系的地方
  • 分类:
    • 内置名称空间:python解释器运行就会立刻创建的空间(代码过程中可以直接使用的名字都在该空间中)
    • 全局名称空间:py文件运行代码过程中产生的名字都会存入该空间(普通代码分支结构循环结构里的变量名、定义函数的函数名、定义类的类名)
  • 存活周期:
    • 内置名称空间:解释器运行(创建),解释器关闭(销毁)
    • 全局名称空间:py文件运行(创建),py文件结束(销毁)
    • 局部名称空间:函数体代码运行(创建),函数体代码结束(销毁)

名字查找顺序

  • 当前在全局名称空间:全局名称空间 → 内置名称空间

  • 当前在局部名称空间:局部名称空间 → 全局名称空间 → 内置名称空间

  • 名字的查找顺序默认情况下不能颠倒只能是:局部名称空间 → 全局名称空间 → 内置名称空间

名称空间的作用域

  • 内置名称空间/全局名称空间:在程序任意位置都可以使用(全局有效)
  • 局部名称空间:在各自的局部空间可以使用(局部有效)

局部名称空间复杂情况

  1. 各自局部名称空间默认情况下不能彼此共享名字
def func1():
    name = 'jason'
    print(age)

def func2():
    age = 18
    print(name)

func1()
func2()
  1. 特殊情况
x = 1
def func1():
    x = 2
    def func2():
        x = 3
        def func3():
            print(x)  # 特例
            # x = 4
        func3()
    func2()
func1()
  1. 函数在定义阶段其实名字的查找顺序就已经固定死了
name = 'jason'
def func():
    print(name) # 报错
    name = 'jasonNB'
func()

global与nonlocal关键字

  • 正常情况下,局部名称空间里面出现新的名字会在局部名称空间中存储,但是有时候需要在局部名称空间中修改全局名称空间的名字
money = 999

def func():
    money = 1000
    print(money) # 1000
func()
print(money) # 999

def func():
    global money  # 声明 局部名称空间中的money操作的是全局的money
    money = 1000
    print(money) # 1000
func()
print(money) # 1000
  • 局部修改全局名称空间中不可变类型的数据,需要使用关键字global声明,如果是可变类型,则无需关键字声明
l1 = [1, 2, 3, 4, 5]
s = '$jason$'

def func():
    s = 'jason'
    res = s.strip('$')
    l1.append(113123)
    l1.append(666)
func()
print(l1) # [1, 2, 3, 4, 5, 113123, 666]

def func():
    global s
    s = 'jason'
    res = s.strip('$')
    l1.append(113123)
    l1.append(666)
func()
print(l1) # [1, 2, 3, 4, 5, 113123, 666]
  • nonlocal 在内存局部名称空间修改外层局部名称空间中的不可变类型
def func1():
    x = 1
    l1 = [1,2]
    def func2():
        # nonlocal x
        x = 999
        l1.append(666)
    func2()
    print(x) # 1
    print(l1) # [1, 2, 666]
func1()

def func1():
    x = 1
    l1 = [1,2]
    def func2():
        nonlocal x
        x = 999
        l1.append(666)
    func2()
    print(x) # 999
    print(l1) # [1, 2, 666]
func1()

函数名的多种使用方式

  • 函数名也可以被用来多次赋值(函数名与变量名使用一致)

  • 函数名还可以当做函数的实参

  • 函数名还可以当做函数的返回值

  • 数名还可以当做容器类型里面的数据值

闭包函数

  • 定义:在一个内部函数中,对外部作用域的变量进行引用,(并且一般外部函数的返回值为内部函数),那么内部函数就被认为是闭包

  • 特征:

    • 定义在函数内部的函数
    • 内部函数使用了外部函数
  • 实际应用:

# 给函数体代码传值的方式一:通过形参传值
def func(xxx):
	print(xxx)
# 给函数体代码传值的方式2:闭包函数
def index():
	username = 'jason'
	def func():
		print(username)
	return func
res = index('jason')
res()

装饰器

简介

  • 概念:函数知识整合到一块的产物
  • 本质:在不改变装饰对象原来的‘调用方式’和‘内部代码’的情况下给被装饰的对象添加功能
  • 原则:对修改封闭,对扩展开放

装饰器固定模板

from functools import wraps
def outer(func_name):
    @wraps(func_name)  # 仅仅是为了让装饰器不容易被别人发现 做到真正的以假乱真
    def inner(*args, **kwargs):
        print('执行被装饰对象之前可以做的额外操作')
        res = func_name(*args, **kwargs)
        print('执行被装饰对象之后可以做的额外操作')
        return res
    return inner

装饰器语法糖

import time

from functools import wraps
def outer(func_name):
    @wraps(func_name)  # 仅仅是为了让装饰器不容易被别人发现 做到真正的以假乱真
    def inner(*args, **kwargs):
        print('执行被装饰对象之前可以做的额外操作')
        res = func_name(*args, **kwargs)
        print('执行被装饰对象之后可以做的额外操作')
        return res
    return inner


@outer  #  home = outer(真正的函数名home)
def home():
    '''我是home函数 我要热死了!!!'''
    time.sleep(1)
    print('from home')
    return 'home返回值'
home()

多层装饰器

  • 语法糖功能:会自动将下面紧挨着的函数名当做参数传递给@符号后面的函数名(加括号调用)
  • 多个语法糖装饰一个函数名:从下往上执行最后一个语法糖才会做重命名操作
def outter1(func1): # func1 = wrapper2函数名
    print('加载了outter1')
    def wrapper1(*args, **kwargs):
        print('执行了wrapper1')
        res1 = func1(*args, **kwargs)
        return res1
    return wrapper1

def outter2(func2): # func2 = wrapper3函数名
    print('加载了outter2')
    def wrapper2(*args, **kwargs):
        print('执行了wrapper2')
        res2 = func2(*args, **kwargs)
        return res2
    return wrapper2

def outter3(func3): # func3 = 真正的index函数名
    print('加载了outter3')
    def wrapper3(*args, **kwargs):
        print('执行了wrapper3')
        res3 = func3(*args, **kwargs)
        return res3
    return wrapper3

# 语法糖功能:会自动将下面紧挨着的函数名当做参数传递给@符号后面的函数名(加括号调用)
@outter1 # index = outter1(wapper2)   index是wrapper1
@outter2 # wrapper2 = outter2(wrapper3)
@outter3 # wrapper3 = outter3(真正的index函数名)
def index():
    print('from index')

index()
# 加载了outter3
# 加载了outter2
# 加载了outter1
# 执行了wrapper1
# 执行了wrapper2
# 执行了wrapper3
# from index

有参装饰器

  • 有参装饰器:编写一个装饰器时,当这个装饰器内部需要外界传入额外的数据来控制代码的分支,就需要给原来的装饰器模板再套用一层,加语法糖时就直接添加额外的数据
def outer(condition,type_user): # 给装饰器传参数(有几个参数就传几个参数)
    def login_auth(func_name):  # 这里不能再填写其他形参
        def inner(*args, **kwargs):  # 这里不能再填写非被装饰对象所需的参数
            username = input('username>>>:').strip()
            password = input('password>>>:').strip()
            # 应该根据用户的需求执行不同的代码
            if type_user =='jason':print('VIP')
            if condition == '列表':
                print('使用列表作为数据来源 比对用户数据')
            elif condition == '字典':
                print('使用字典作为数据来源 比对用户数据')
            elif condition == '文件':
                print('使用文件作为数据来源 比对用户数据')
            else:
                print('目前只有上面几种方式')
        return inner
    return login_auth
@outer('文件','jason') # 函数名加括号 优先级最高
def index():
    print('from index')
index()

递归函数

  • 概念:在函数内部直接或间接调用自己的函数
  • python中允许函数最大递归调用次数,官方给出的限制是1000,代码验证可能会有偏差(996 997 998...都有可能)
  • 应用场景:
    • 递推:层层往下寻找答案(每一次都是基于上一次进行下一次的执行)
    • 回溯:遇到终止条件,则从最后往回返一级一级的把值返回来
  • 特点:
    • 必须要有一个明确的结束条件
    • 一层比一层更简单
l1 = [1, [2, [3, [4, [5, [6, [7, [8, [9, [10,]]]]]]]]]]

def get_num(l1):
	for i in l1: # for循环自带结束条件,不用手动加结束条件
		if isinstance(i, int): # 判断i绑定的数据值是不是整型
			print(i)
		else:
			get_num(i)
get_num(l1)

算法之二分法

算法

  • 定义:解决问题的方法

  • 算法永远都在精进,但是很少有最完美的算法

二分法

  • 定义:二分法是所有算法里最简单的算法

  • 前提:数据集必须有顺序(升序/降序)

  • 原理:获取数据集中间的元素,比对大小

l1 = [11, 23, 32, 45, 65, 78, 90, 123, 432, 467, 567, 687, 765, 876, 999, 1131, 1232]

def get_num(l1, target_num):
    # 添加递归函数的结束条件
    if len(l1) == 0:
        print('不好意思 找不到')
        return
    # 1.先获取数据集中间那个数
    middle_index = len(l1) // 2
    middle_value = l1[middle_index]
    # 2.判断中间的数据值与目标数据值孰大孰小
    if target_num > middle_value:
        # 3.说明要查找的数在数据集右半边  如何截取右半边
        right_l1 = l1[middle_index + 1:]
        # 获取右半边中间那个数
        # 与目标数据值对比
        # 根据大小切割数据集
        # 经过分析得知 应该使用递归函数
        print(right_l1)
        get_num(right_l1, target_num)
    elif target_num < middle_value:
        left_l1 = l1[:middle_index]
        # 获取左半边中间那个数
        # 与目标数据值对比
        # 根据大小切割数据集
        # 经过分析得知 应该使用递归函数
        print(left_l1)
        get_num(left_l1, target_num)
    else:
        print('找到了', target_num)


# get_num(l1, 999)
get_num(l1, 1000)
  • 缺陷:
    • 数据集必须是有序的
    • 查找的数如果在开头或结尾而分发效率更低

三元表达式

  • 语法结构:值1 if 条件 else 值2注意:没有冒号
    • 如果if后面的条件成立,则使用if前面的值
    • 如果if后面的条件不成立,则使用else后面的值
# 编写一个函数,比较两个数的大小,返回较大的数
# 一般编写
def max_num(a, b):
    if a > b:
    	return a
    else:
        return b
res = max_num(1, 10)
print(res) # 10

# 三元表达式编写
def max_num(a, b):
    return a if a > b else b
res = max_num(1, 10)
print(res) # 10
  • 注意:

    • 仅限于二选一的情况并且不建议嵌套使用
    res = 123 if 10 > 5 else (222 if 0 else (666 if 1 == 2 else 666)) # 不建议嵌套使用
    
    • 在python中代码不是精简的越少越好,在精简的过程中还要保证代码的可读性

生成式

列表生成式

  • 概念:列表生成式是python内置的一种创建列表的方法,通过在[]内部执行一行for循环语句,将for循环所遍历到的元素添加到列表中

  • 语法结构:[表达式 for 变量 in 列表][表达式 for 变量 in 列表 if 条件]

  • 注意:列表生成式中只能出现for和if

# 给列表中所有的数据值加上_NB的后缀
name_list = ['jason', 'kevin', 'oscar', 'jerry', 'tony']
# 1.定义一个新的列表
new_list = []
# 2.循环原列表中所有的数据值
for name in name_list:
    # 3.拼接_NB后缀
    new_name = name + '_NB'
    # 4.追加到新列表中
    new_list.append(new_name)
    print(new_list)



new_list = [name + '_NB' for name in name_list]
print(new_list)

new_list = [name + '_NB' for name in name_list if name != 'jason']
print(new_list)

字典生成式

  • 概念:通过在大括号{} 内执行一行for循环语句创建一个新的字典,大括号内的语句需要指定键值对的key与value,这两项值都是for循环语句中变量
  • 语法结构:{key: values for 变量名 in 数据集}{key: values for 变量名 in 数据集 if 条件}
new_dict = {i: 'jason' for i in range(10)}
print(new_dict) # {0: 'jason', 1: 'jason', 2: 'jason', 3: 'jason', 4: 'jason', 5: 'jason', 6: 'jason', 7: 'jason', 8: 'jason', 9: 'jason'}

new_dict = {i: 'jason' for i in range(10) if i == 6}
print(new_dict)  # {6: 'jason'}

集合生成式

  • 概念:python集合生成式与列表生成式几乎是一模一样的,只需要将[]替换为{} 即可,在{}内执行一个for循环语句,for循环所遍历的元素自动添加到集合之中
  • 语法结构:{表达式 for 变量 in range()}{表达式 for 变量 in range() if 条件}
new_set = {i for i in range(10)}
print(new_set) # {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}

new_set = {i for i in range(10) if i == 6}
print(new_set) # {6}

注意:元组没有生成式,它是后面重点讲解的迭代器知识(生成器)

匿名函数

  • 概念:匿名函数,也称为lambda函数,是没有定义名称的函数
  • 语法结构:lambda 形参:返回值(lambda函数可以具有任意数量的参数,但只能有一个表达式,不用写return,返回值就是该表达式的结果。Lambda函数可在需要函数对象的任何地方使用)
  • 注意:lambda 表达式创建的函数只能包含一条表达式
  • 案例
(lambda x: x + 1)(123) # 直接调用
res = lambda x: x + 1 # 命名调用
print(res(123))
  • 应用场景:通常都需要配合其他函数一起使用,用于减少代码

重要内置函数

max函数

  • max():最大值。如果值是字符串,则按字母顺序进行比较。如果值是字符串,则按字母顺序进行比较
# max函数与lambda函数(匿名函数)集合使用
dic = {
    'jason': 100,
    'aj': 123,
    'Bason': 9999999,
    'zero': 888
}
def index(k):
    return dic.get(k)
# res = max(dic, key=lambda k: dic.get(k))
res = max(dic, key=index)
print(res)  # Bason

min函数

  • min():最小值。如果值是字符串,则按字母顺序进行比较。如果值是字符串,则按字母顺序进行比较
# min函数与lambda函数(匿名函数)集合使用
dic = {
    'jason': 100,
    'aj': 123,
    'Bason': 9999999,
    'zero': 888
}

def index(k):
    return dic.get(k)
# res = min(dic, key=lambda k: dic.get(k))
res = min(dic, key=index)
print(res)  # jason

map函数

  • map():映射函数
# map函数与lambda函数(匿名函数)集合使用
l1 = [11, 22, 33, 44, 55, 66]
res = map(lambda x: x + 20, l1)
print(res)  # <map object at 0x000001954B581100>
print(list(res)) # [31, 42, 53, 64, 75, 86]

filter函数

  • 定义和用法:过滤器,用法和map类似
# filter函数与lambda函数(匿名函数)集合使用
l1 = ['jason', 'kevin', 'oscar', 'tony']
res = filter(lambda a: a != 'jason', l1)
print(res)  # <filter object at 0x00000195F21E6C70>
print(list(res))  # ['kevin', 'oscar', 'tony']

reduce函数

  • 定义和用法:会对参数序列中元素进行累积
# reduce函数与lambda函数(匿名函数)集合使用
l2 = [1, 2, 3]
from functools import reduce

res = reduce(lambda x, y: x + y, l2, 100)
res = reduce(lambda x, y: x + y, l2, 100)
print(res) # 106

zip函数

  • 定义和用法:组合对象,将对象逐一配对
n1 = [1, 2, 3]
n2 = ['jason', 'kevin', 'oscar']
res = zip(n1, n2)
print(res)  # <zip object at 0x000002A9E38C7F40>
print(list(res)) # [(1, 'jason'), (2, 'kevin'), (3, 'oscar')]

n1 = [1, 2, 3, 4]
n2 = [5, 6, 7, 8]
n3 = 'jack'
res = zip(n1, n2, n3)
print(list(res)) # [(1, 5, 'j'), (2, 6, 'a'), (3, 7, 'c'), (4, 8, 'k')]


n1 = [1, 2, 3, 4, 5, 6, 7]
n2 = [5, 6, 7, 8]
n3 = 'jason'
res = zip(n1, n2, n3)
print(list(res)) # [(1, 5, 'j'), (2, 6, 'a'), (3, 7, 's'), (4, 8, 'o')]

练习题

1.编写一个用户认证装饰器
  基本要求
   执行每个函数的时候必须先校验身份 eg: jason 123
  拔高练习(有点难度)
   执行被装饰的函数 只要有一次认证成功 那么后续的校验都通过
     函数:register login transfer withdraw 
   提示:全局变量 记录当前用户是否认证
is_login = {'is_login': False, }
# 2.编写装饰器
def login_auth(func_name):
    def inner(*args, **kwargs):
        # 2.3判断全局字典is_login对应的值是否是True
        if is_login.get('is_login'):
            # 2.4直接执行被装饰函数即可
            res = func_name(*args, **kwargs)
            return res
        # 2.1假设是第一次调用 肯定需要校验用户身份
        username = input('username>>>:').strip()
        password = input('password>>>:').strip()
        if username == 'jason' and password == '123':
            res = func_name(*args, **kwargs)
            # 2.2保存用户登录的状态
            is_login['is_login'] = True
            return res
        else:
            print('权限不够 无法执行')
    return inner

# 1.定义函数
@login_auth
def register():
    print('注册功能')

@login_auth
def login():
    print('登录功能')

@login_auth
def transfer():
    print('转账功能')

@login_auth
def withdraw():
    print('提现功能')

register()
login()
transfer()
withdraw()

2.尝试编写有参函数将多种用户验证方式整合到其中
	直接获取用户数据比对
 	数据来源于列表
 	数据来源于文件
data_list = ['jason|123', 'kevin|321', 'oscar|222']

def login_auth(condition):
    def outer(func_name):
        def inner(*args, **kwargs):
            username = input('username>>>:').strip()
            password = input('password>>>:').strip()
            if condition == 'absolute':
                if username == 'jason' and password == '123':
                    res = func_name(*args, **kwargs)
                    return res
                else:
                    print('你不是jason 不允许执行')
            elif condition == 'list_type':
                for user_data in data_list:  # 'jason|123'
                    real_name, real_pwd = user_data.split('|')
                    if real_name == username and real_pwd == password:
                        res = func_name(*args, **kwargs)
                        return res
            elif condition == 'file_type':
                with open(r'userinfo.txt','r',encoding='utf8') as f:
                    for line in f:
                        real_name, real_pwd_n = line.split('|')
                        if real_name == username and password == real_pwd_n.strip('\n'):
                            res = func_name(*args, **kwargs)
                            return res
        return inner
    return outer

@login_auth('absolute')
def func1():
    print('from func1')
    return 1
@login_auth('list_type')
def func2():
    print('from func2')
    return 2
@login_auth('file_type')
def func3():
    print('from func3')
    return 3


func1()
func2()
func3()

3.尝试编写递归函数
	推导指定某个人的正确年龄
    	eg: A B C D E  已知E是18 求A是多少
def get_age(n):
    if n == 1:
        return 18
    return get_age(n-1) + 2

res = get_age(5)
print(res)
 posted on 2022-07-10 19:10  念白SAMA  阅读(30)  评论(0)    收藏  举报