香中别有韵,清极不知寒-----------函数

函数

一、函数的定义

1、什么是函数
	函数就是封装了某一功能的代码
2、为什么要函数
	因为 不利于方便读
    	 代码冗余
         维护性差 拓展性也差
3、怎么用函数
	先定义(三种方式)
	后调用(三种方式)
	返回值(三种方式)
	

二、函数的定义与调用

1、函数的定义模板
	def 函数名(参数1, 参数2)
    	"""文档描述"""
        函数数据
        return 值
2、函数的调用    
def func():  # func 为内存的地址 func()则为函数的值
    print("666")


func()

三、定义函数的三种形式

3.1 无参函数

def func():  # func 为内存的地址 func()则为函数的值
    print("666")


func()

3.2 参数函数

def func(x, y):  # x = 1, y = 2
    print(x, y)


func(1, 2)

3.3 空函数

def func(x, y):  # func 为内存的地址 func()则为函数的值
    pass


func()

3.4 三大定义函数的应用场景

1、无参函数的应用场景
2、有参函数的应用场景
3、空函数的应用场景
	当你做个项目的时候,写了很多函数确定功能的时候,可以用空函数先放那,然后根据实际情况再去修改

四、调用函数的三种形式

4.1 语句的形式直接调用

# 只调用函数
func(x, y)

4.2 表达式调用

1、赋值表达式
def func(x, y):
    res = x + y
    return res

res = func(1, 2)
print(res)

2、算术表达式
def func(x, y):
    res = x + y
    return res

res = func(1, 2) * 8
print(res)

4.3 函数调用可以当作参数

def func(x, y):
    res = x + y
    return res
res = func(func(1, 2),3)
print(res)

五、返回值的三种形式

5.1 不返回

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

5.2 返回一个值

def func(x, y)
    return 10

res = func()
print(res)

5.3 返回多个值

def func(x, y):
    return 10, 8, [1, 2]

res = func()
print(res)
# 会以元组的方式返回

六、形参与实参

6.1 形参与实参

# 形参是函数定义时括号内的变量名,接受外界的值
# 实参则是函数调用时括号里的 可以是常量 变量 表达式的或者三者混合的传入的值

6.2 位置参数

1、什么是位置参数
位置参数实际上就是按照从左到右的顺序去传参,参数可以是形参也可以是实参
特点是 必须被传值 多一个不行 少一个不行
2、
def func(x, y) # x, y则是形参
	print("123")

    
func(1, 2)  # x = 1, y = 2,按照顺序传入了x, y

6.3 关键字参数

1、什么是关键字参数
关键字参数则是通过变量名 = 值 去准确传递,也可以说是关键字实参,这样就不用按照从左到右的顺序去传参数了
2、
def func(x, y)
	print(x, y)
    
    
func(y=1, x=2)
3、注意
	3.1 位置参数必须在关键字参数前,例如
        def func(x, y, z)
        print(x, y, z)


    func(1, z=2, y=1)
    3.2 不能重复给一个形参传值

6.4 默认形参

1、what
	默认形参则是在定义函数时,就已经为形参赋值,这类形参称之为默认参数,当函数有多个参数时,需要将值经常改变的参数定义成位置参数,而将值改变较少的参数定义成默认参数。例如编写一个注册学生信息的函数,如果大多数学生的性别都为男,那完全可以将形参sex定义成默认参数
2、定义时就已经为参数sex赋值,意味着调用时可以不对sex赋值,这降低了函数调用的复杂度
    # 需要注意:
        默认参数必须在位置参数之后
        默认参数的值仅在函数定义阶段被赋值一次
3、代码
    def student(name, age, sex='male'):
        print(name, age, sex)


    student('Ame', 18)
    student('Yuki', 18)
    student('Egon', 18, 'female')


6.5 可变长参数

1.what
	可变长参数分为两种 一是可变长形参 二是可变可变长实参
2.可变长形参
	我们可以用*args **kwargs来接受不固定的值的长度,比如我们不知道要传入多少参数,我们就可以用这两个来接受传入的参数
    # *args会传入后以元组的方式来保存
    # **kwargs参数传入后会以字典的方式去保存
3.可变长实参
	# 打散
    我们用元组传入后 元组的数据会以打散后位置参数传入形参
    字典方式传入后,字典就会以关键字实参模式传入形参
4.代码1
    def func(*args, **kwargs):
        print(args, kwargs)
    func(1, 2, 3, 4, 5, 4, x=1, y=2, z=3, c=4)
    """
    (1, 2, 3, 4, 5, 4) {'x': 1, 'y': 2, 'z': 3, 'c': 4}
    """
5.代码2
    def index(x, y, z):
        print(x, y, z)


    def wrapper(*args, **kwargs):
        index(*args, **kwargs)


    wrapper(1, z=2, y=3)
# 分析:首先将实参传值给形参:*args = (1,)  **kwargs = {'y':3,'z':2}
# 第二步:这里是直接把wrapper(*args,**kwargs)中的形参给到了index(*args,      **kwargs)的实参,
# 即index(*(1,),**{'y':3,'z':2}),这里再次将其打散,然后绑定给x,y,z

七、名称空间与作用域

7.1 名称空间

"""
	名称空间是  在内存栈区中分好不同的名称空间,这样就可以存放一样的变量名的空间,所以因为有名称空间的存在,我们才可以在不同的空间去使用相同的名字
"""

7.2 内置名称空间

"""
存放的名字: 从python解释器内置存放的名字
存活的周期: 从python打开即生成    python关闭则死亡	   
"""
# 例如
    print
    input

7.3 全局名称空间

"""
存放的空间: 既不在函数内,也不再解释器内置里,都为全局名称空间
存活的周期: 文件的执行即开始,文件的执行结束为死亡
"""
# 例如1
    def func()
        x = 5
        y = 1


    func()  # func既不是函数内 也不是内置内,所以为全局名称空间,其他的都一样
# 例如2
    x = 250
    y = 38
    """
    x和y也是全局名称空间里面的,全局也皆可调用
    """
    # 例如3
    if 3>1:
        c = 10
    """
    这个c也是全局名称空间里面的,虽然他不是顶格写的,但是也是全部皆可调用
    """

7.4 局部名称空间

"""
存放的空间: 是函数内创造的
存货的周期: 函数调用开始出生  函数调用结束死亡
"""
# 例如
    def func()  # x, y则在局部名称空间 定义时出生
        x = 5
        y = 1


    func()  # 调用时死亡

7.5 名称空间的出生顺序和销毁顺序和查找顺序

1.出生顺序
内置名称空间>全局名称空间>局部名称空间
2.销毁顺序
局部名称空间>全局名称空间>内置名称空间
3.查找顺序
# 如果在局部名称空间
局部名称空间--------全局名称空间--------内置名称空间
# 如果在全局名称空间
全局名称空间--------内置名称空间

7.6 全局作用域

"""
全局存活
全局有效: 即所有的函数都能调用他
"""

7.7 局部作用域

"""
局部存活
局部有效: 即只有函数内才能调用他
"""

八、函数对象

# 函数对象的特点精髓就是可以把函数看成变量去调用
比如
	def func(x):
    	print(x)


	res = func()

九、 函数嵌套

# 函数嵌套调用就是在一个函数内再调用一个函数
# 函数嵌套定义就是在一个函数内再定义一个函数
	def max1(x, y)
    	if x > y:
            return x
        else:
            return y
        
    def max(a, b, c, d):
        res1 = max1(a, b)
        res2 = max1(res1, c)
        res3 = max1(res2, d)
        print(res3)


    max(1, 156, 31, 456)

十、闭包函数

1.1.什么是闭包函数
	闭包函数 = 名称空间与作用域 + 函数对象 + 函数嵌套
    其实闭包函数就是一个传参方式
    
1.2.为什么要用闭包函数
	万一函数的参数被占用了,我们应该怎么传参就用闭包函数
    
1.3.怎么用闭包函数
    def func(x):
        x = 100

        def wrapper():
            print(x)

        return wrapper


    f = func()
    f()
    """
    这就是闭包函数,wrapper被包起来了,只能用到func的x,外界都没有用
    """

十一、装饰器

11.1、 装饰器的定义(what why how)

1.什么是装饰器
	装饰器就是装饰某一个东西的工具
2.为什么要用装饰器
	因为在源函数代码不改变调用方法&原代码给原函数添加新功能,装饰器是最好的解决工具
3.怎么用装饰器
	详见下面

11.2、 装饰器的实现思路

"""
	现有一个原函数index,想要为其添加一个时间差的功能,我们应该怎么做呢
"""

# 方案一  这就已经修改了他的原代码了
	import time


    def index(x):
        start_time = time.time()
        print(f'welcome to index {x}')
        time.sleep(3)
        print(time.time() - start_time)

    index(1)
# 方案二 没有修改原函数的代码&调用,但是代码冗余
    def index(x):
        print(f'welcome to index {x}')
        time.sleep(3)


    start_time = time.time()
    index(1)
    print(time.time() - start_time)

    start_time = time.time()
    index(1)
    print(time.time() - start_time)


# 方案三 解决了代码冗余问题,但是原函数的调用我们修改了,而且wrapper写死了,只能调用index
    def index(x):
        print(f'welcome to index {x}')
        time.sleep(3)


    def wrapper():
        start_time = time.time()
        index(1)
        print(time.time() - start_time)


    wrapper()


# 方案四 我们先搞定调用index函数写死问题
    def index(x):
        print(f'welcome to index {x}')
        time.sleep(3)


    def outter(func):
        def wrapper():
            start_time = time.time()
            func(1)
            print(time.time() - start_time)

        return wrapper


    index = outter(index)
    index()


# 方案五  然后在搞定index函数参数问题
    def index(x):
        print(f'welcome to index {x}')
        time.sleep(3)


    def outter(func):
        def wrapper(*args, **kwargs):
            start_time = time.time()
            func(*args, **kwargs)
            print(time.time() - start_time)

        return wrapper


    index = outter(index)
    index(11)


# 方案六  在搞定index返回值的问题,这样就把wrapper伪装成index伪装的很好了
    def index(x):
        print(f'welcome to index {x}')
        time.sleep(3)
        return 123


    def outter(func):
        def wrapper(*args, **kwargs):
            start_time = time.time()
            res = func(*args, **kwargs)
            print(time.time() - start_time)
            return res

        return wrapper


    index = outter(index)
    res = index(11)
	print(res)


# 方案七 在让wrapper更像index一点
    def index(x):
        """
        这是index的配置信息
        """

        print(f'welcome to index {x}')
        time.sleep(3)
        return 123


    def outter(func):
        def wrapper(*args, **kwargs):
            start_time = time.time()
            res = func(*args, **kwargs)
            print(time.time() - start_time)
            return res

        wrapper.__name__ = index.__name__
        wrapper.__doc__ = index.__doc__
        return wrapper


    index = outter(index)
    res = index(11)
    print(res)


# 语法糖
    def outter(func):
        from functools import wraps
        @wraps(func)
        def wrapper(*args, **kwargs):
            start_time = time.time()
            res = func(*args, **kwargs)
            print(time.time() - start_time)
            return res

        return wrapper


    @outter
    def index(x):
        """
        这是index的配置信息
        """

        print(f'welcome to index {x}')
        time.sleep(3)
        return 123


    res = index(11)
    print(res)

11.3、装饰器的总结模板

# 无参装饰器
def outter(func):
    def wrapper(*args, **kwargs):
        res = func(*args, **kwargs)
        return res
    return wrapper

# 有参装饰器
def outter(x):
    def foo(func):
        def wrapper(*args, **kwargs):
            res = func(*args, **kwargs)
            return res
        return wrapper
   	return foo

11.4 多个装饰器

import time


def deco1(func):
    print('----deco1')

    def wrapper(*args, **kwargs):
        start_time = time.time()
        res = func(*args, **kwargs)
        print(time.time() - start_time)
        return res

    return wrapper()


def deco2(func):
    print('----deco2')

    def wrapper(*args, **kwargs):
        start_time = time.time()
        res = func(*args, **kwargs)
        print(time.time() - start_time)
        return res

    return wrapper()


@deco1
@deco2
def index(x):
    print(f'{x}')
'''
	当多个装饰器叠加的时候 例如这样是先从上而下去执行,但是返回值则是从下而上的去返回 所以deco2的返回值是在deco1,deco1的返回值是在真正的index上
'''

十二、 有参装饰器

def login():
    name = input('name is :').strip()
    pwd = input('pwd is :').strip()

    if name == 'ycc' and pwd == '123':
        print('login successful')
    else:
        print('name or pwd error')

def auth(db_type):
    def deco(func):
        def wrapper(*args,**kwargs):

            if db_type == 'file':
                login()
                print('基于文件验证')
                # res = func(*args,**kwargs)
                # return res
            elif db_type == 'mysql':
                login()
                print('基于数据库验证')
                # res = func(*args, **kwargs)
                # return res

            elif db_type == 'ldap':
                login()
                print('基于ldap验证')
                # res = func(*args, **kwargs)
                # return res
            res = func(*args, **kwargs)
            return res
        return wrapper
    return deco


@auth(db_type='file')
def index(x, y):
    print(x, y)


@auth(db_type='mysql')
def home(name):
    # print('welcome %s to home page'%name)
    print(f'welcome {name} to home page')
    return name


@auth(db_type='ldap')
def transfer():
    print('transfer')


index(1, 2)
name = home('ycc')
print(name)

十三、可迭代对象与迭代器对象

1.什么是可迭代对象
	里面有内置的__iter__方法的就是可迭代对象
    'abc'.__iter__
    [1, 2, 3].__iter__
    {'x':1, 'y':2}.__iter__
# 调用一个可迭代对象的__iter__()会得到一个返回值,改返回值是迭代器对象
2.迭代对象对象的特点:
	2.1 内置有__next__方法
    iter_s = s.__iter__()  # iter_s = iter(s)
    print(iter_s.__next__())
    
    dic = {'x':1, 'y':2}
    iter_dic = iter(dic)
    print(iter_dic.__next__())

十四、迭代器

1.什么是迭代器
    迭代器就是用来迭代取值的工具

    什么是迭代?
        迭代就是一个重复的过程,但是每次重复都是基于上一次的结果进行的

        # 单纯的重复不叫迭代
        while True:
            1+1

        # 下面才是迭代的过程
        count = 1
        while count < 5:
            print(count)
            count += 1

2.为何要用迭代器
    优点
    (1) 找到一种通用的迭代取值方案->for循环
    (2)惰性计算,节省内存

    缺点:
        1、不能取指定的值,只能往后next
        2、迭代器对象是一次性的:一个迭代器对象值取干净了,不能继续取了

3.可迭代对象
    3.1 内置有__iter__方法的对象都是可迭代对象
            "abc".__iter__()
            [1, 2, 3].__iter__()
            (1, 2, 3).__iter__()
            {"x": 1, "y": 2}.__iter__()
            {1, 2, 3, 4}.__iter__()
            open('a.txt', mode='r').__iter__()
            open('a.txt', mode='r').__next__()


# 调用可迭代对象的__iter__()会得到一个返回值,该返回值是迭代器对象
迭代对象对象的特点:
          1、内置有__next__()方法
          2、内置有__iter__()方法

    s = "abc"
    iter_s = s.__iter__()  # iter_s = iter(s)
    print(iter_s.__next__())  # print(next(ites_s))
    print(iter_s.__next__())
    print(iter_s.__next__())
    print(iter_s.__next__())


    dic = {"x":1,"y":2}
    iter_dic = dic.__iter__()
    print(iter_dic.__next__())
    print(iter_dic.__next__())

4. for工作原理
    4.1 调用in后那个对象的__iter__方法,得到一个迭代器对象iter_names
    4.2 x=next(iter_names),然后运行循环体代码
    4.3 重复步骤2,直到取完值,抛出异常,for循环会帮我们捕捉异常结束循环
    
    
5.for循环里面的内部原理
	names = ['egon', 'tom', 'jack', 'lili', 'ttt']


    iter_s1 = iter(names)
    print(next(iter_s1))
    print(next(iter_s1))
    print(next(iter_s1))
    print(next(iter_s1))
    print(next(iter_s1))

    iter_s2=iter(names)

    print(next(iter_s2))
    print(next(iter_s2))
    print(next(iter_s2))
    print(next(iter_s2))
    print(next(iter_s2))

# 下面的第二个for循环其实是取不出值的
    iter_names = iter(names)
    for x in iter_names :
        print(x)

    print('='*50)

    for x in iter_names :
        print(x)

十五、生成器&yield

1. 什么是生成器
	生成器就是自定义的迭代器
2. 为什么要用生成器
	因为生成器也就是迭代器是惰性计算不浪费内存,所以我们有时候可以自己自定义一个迭代器
3. 如何定义生成器
	# 这个就得用到我们的秘籍-----yield
    def func():
    print('one...')
    yield 111
    print('two...')
    yield 222
    print('three...')
    yield 333
    print('fourth')

# 函数内但凡出现yield关键字,再调用函数不会立即执行函数体代码,而会返回一个
# 生成器对象,生成器对象本质就是一个自定义的迭代器对象

4. yield 对比 return
"""
相同点:都可以返回任意类型、任意个数的值
不同点:return只能返回值一次值,函数就立即结束
       yield可以返回多次值,yield可以暂停函数的运行
"""

十六、函数递归

1. 什么是函数递归
	就是定义一个函数,然后再函数内部调用自己,说白了就是个循环
2. 函数递归可以分为两个阶段
    2.1 回溯 就是去要的过程
    2.2 递推 要到了回来的过程
3. 函数递归应用
    l = [1, [2, [3, [4, [5, [6, [7, [8]]]]]]]]


    def func(l):
        for items in l:
            if type(items) is list:
                func(items)
            else:
                print(items)

    func(l)

十七、三元表达式

# 过于麻烦的普通代码
def func():
    if x > y:
        return x
    else:
        return y
# 三元表达式
  x if x > y else y
  
# 三元表达式的格式
达成条件的返回的值 if 条件 条件没有达成的返回值
     

十八、各种生成式

18.1 列表生成式

"""
列表生成式
"""
# 正常思路
l = ["xxx_dsb", "aaa_dsb", "bbb_dsb", "ccc_dsb", "yuki"]
new_l = []
for i in l:
    if i.endswith("dsb"):
        new_l.append(i)
print(new_l)
# 列表生成式
l = ["xxx_dsb", "aaa_dsb", "bbb_dsb", "ccc_dsb", "yuki"]
new_l = [i for i in l if i.endswith("dsb")]
print(new_l)

# 练习 1 把所有小写字母换成大写字母  2 把所有名字去掉后缀
l = ["xxx_dsb", "aaa_dsb", "bbb_dsb", "ccc_dsb", "yuki"]
new_1 = [name.upper() for name in l]
new_l = [i.replace("_dsb", '') for i in l if "_dsb" in i]

18.2 字典生成式

keys = {"aaa", 'bbb', 'ccc'}
{k: None for k in keys}

18.3 集合生成式

keys = {"aaa", 'bbb', 'ccc'}
{k for k in keys}

18.4 生成器生成式

	# 把一个文件所有的字符总和取出来
	with open("stock_data.txt", mode="rt", encoding="UTF-8") as file:
    print(sum(len(line) for line in file)  # 节约了内存 提高了效率

十九、 二分法

需求:如果一个列表储存着从小到大的数字
	然后从中需要找一个数字
    
1.for循环
	list = [-6, -1, 2, 5, 6, 10, 12, 16, 20, 25, 28, 30, 65, 98]
    need_num = 25
    for i in list:
        if i == need_num:
            break
    print(i)

"""
这样是可以找出来,但是这样假如列表有很多元素,比如一千万个
这样寻找会效率非常低,所以应该改进
"""
2.二分法  
# 思路模板
list = [-6, -1, 2, 5, 6, 10, 12, 16, 20, 25, 28, 30, 65, 98]
need_num = 25
def func():
    median = 中间值
    if 中间值 > need_num:
        就要从左边开始寻找
        list = 左边的列表
        def func()
    elif 中间值 < need_num:
        就要从右边开始寻找
        list = 右边的列表
        def func()
    else:
        print(find it)
        
# 实现
list = [-6, -1, 2, 5, 6, 10, 12, 16, 20, 25, 28, 30, 65, 98]
need_num = 30


def func(need_num, list):
    print(list)
    median = len(list) // 2
    if list[median] > need_num:
        list = list[0:median]
        print(list)
        func(need_num, list)
    elif list[median] < need_num:
        list = list[median:]
        print(list)
        func(need_num, list)
    else:
        print("find it")


func(need_num, list)

二十、匿名函数

匿名函数的应用场景
	任何只需要用到一次函数,用完即可不要 就可以用匿名函数
lambda x,y: x+y

匿名函数与有名函数有相同的作用域,但是匿名意味着引用计数为0,使用一次就释放,所以匿名函数用于临时使用一次的场景,匿名函数通常与其他函数配合使用,我们以下述字典为例来介绍它

salaries={
    'siry':3000,
    'tom':7000,
    'lili':10000,
    'jack':2000
}
要想取得薪水的最大值,我们可以使用内置函数max
>>> max(salaries)
'tom'
>>> min(salaries)
'jack'
内置max和min都支持迭代器协议,工作原理都是迭代字典,取得是字典的键,因而比较的是键的最大和最小值,而我们想要的是比较值的最大值与最小值,于是做出如下改动
# 函数max会迭代字典salaries,每取出一个“人名”就会当做参数传给指定的匿名函数,然后将匿名函数的返回值当做比较依据,最终返回薪资最高的那个人的名字
>>> max(salaries,key=lambda k:salaries[k]) 
'lili'
# 原理同上
>>> min(salaries,key=lambda k:salaries[k])
'jack'
同理,我们直接对字典进行排序,默认也是按照字典的键去排序的
>>> sorted(salaries)
['jack', 'lili', 'siry', 'tom']
posted @ 2021-06-30 15:36  Ame林汀  阅读(118)  评论(0)    收藏  举报