zl程序教程

您现在的位置是:首页 >  其他

当前栏目

3-1python语法基础-函数-函数创建调用和参数,函数作用域,装饰器迭代器生成器,递归函数,匿名函数

基础迭代 函数 创建 参数 调用 语法 装饰
2023-09-14 08:59:04 时间

########

函数学习什么

1,函数定义

2,函数的参数

形参,实参,关键字参数,

3,函数作用域,

4,装饰器

5,迭代器生成器

6,递归函数,

7,匿名函数

########

函数的定义,函数调用,函数返回值和返回值接收

def func1():
    name = "andy"
    age = 18
    dict1 = {"name": "andy"}
    list1 = [1, 2, 3]

    return name,age,dict1,list1

print(func1())

name = func1()
print(name)

name,age,dict1,list1 = func1()
print(name,"-----",age,"-----",dict1,"-----",list1)

#####

1,函数可以有返回值,也可以没有返回值,只要执行了return就不会往后执行了,

2,函数的返回值可以有一个,也可以有多个,有多个返回值,return 1,2   多个参数使用逗号分隔,而且返回值的类型可以是任意的类型,

3,如果是一个变量接收,就是一个元组,如果多个变量接收,但是要求和返回值的数量一致,否则会报错,

######

函数参数,形参实参,无参数,默认参数,调用使用位置参数,关键字参数,

def hollo3(age, name="heihei"):
    print(age)
    print(name)


hollo3(name='andy', age=18)

####

理解函数的参数:

1,函数可以有参数,也可以没有参数,有参数一定要传递,否则会报错的,

2,定义函数的参数,叫做形参,调用的时候传入的参数,叫做实参,

3,定义函数的时候,定义参数的时候,必须先定义位置参数,最后定义默认参数,默认参数也叫做关键字参数,

4,函数调用的时候就使用了关键字参数,这样就可以不用管参数的顺序了,默认是位置参数,

5,有了默认参数之后,调用函数的时候,这个参数可以不写,则使用默认值,

 

####

函数参数-----不定长参数(动态参数)

def hollo3(name,*args,**kwargs):
    print("name",name)
    print("args",args)   # (60,70) # 这是返回一个元组
    print("kwargs",kwargs)  # {'a': 1, 'b': 2} 这是一个字典,

hollo3("xiaoxiao")
hollo3("xiaoxiao",60,70)
hollo3("xiaoxiao",60,70,a=1,b=2)

list1 = [60,70]
dict1 = {'a': 1, 'b': 2}
hollo3("xiaoxiao",*list1,**dict1)

####

一定要重视这种不定长参数,使用是非常的广泛的,

1,不定长参数,或者叫做动态参数

2,加了星号 * 的参数会以元组(tuple)的形式导入,加了两个星号 ** 的参数会以字典的形式导入。然后就可以像处理元组和字典一样处理参数了,

3,先后顺序:必须先定义args,然后定义kwargs,如果有关键字参数一定放在kwargs之前,

4,参数名加星就可以了,后面的args,不是必须是这个,但是星一定要有,一般使用args,这是一种编码习惯,

####

不定长参数的调用

1,这种参数,可以不传递任何的值,打印出来是一个空的元组,或者一个空的字典,

2,如果想要传递元组的形式,可以调用的时候,传入单个值,如果像要传入字典的方式,就是name = value的方式

3,还可以使用列表传入,使用字典传入,

调用方式很灵活,一定要注意,

######

函数的命名空间和作用域

a = 1

def func():
    a = 4
    print(a)

print(a)
func()

#####

1,命名空间,一个全局的,一个是局部的,函数内部定义的变量就是局部变量,

2,作用域和命名空间是分不开的,作用域分为,全局作用域和局部作用域,

#####

函数的命名空间和作用域----global,globals,locals,

a =1

def func():
    x = "aaa"
    global a
    a += 1
    print("函数内部的globals",globals())  # 这个还是全局的打印出的是一样的,
    print("函数内部的locals",locals())   # 这个是可以打印出x的,但是没有a,
func()
print("全局的globals", globals()) # 这里的globals = locals
print("全局的locals", locals())   # 这里就是没有x的,

####

1,对于不可变类型,你可以在局部查看全局的变量,但是不能修改,如果要修改,你就需要使用global进行声明,

2,自己的代码中应该尽量少使用global,这样会导致代码非常不安全,混乱,会污染全局变量,

 

#####

函数的嵌套和闭包,深刻体会函数名的使用

def func():
    name = 'python'
    def inner():
        # print(123)
        print(name)
    print(inner.__closure__)
    # inner()  # 这种是闭包,但是这种写法比较浪费资源,每次调用函数都要生成name变量,所以不这样写
    return inner  # return inner 在外部函数中返回内部函数的名字,这就是一个闭包最常用的方式,

f = func()  # f = func() = inner 
f()  # f() = inner
f()  # # 这种写法我调用这个函数100次,但是里面的name变量就只会生成一次了,不会反复生成了,节省资源,
# 输出结果:python

####

1,什么是简单的函数嵌套?什么是闭包?根本只有一个,就是内部函数是否使用了外部函数的变量,这也是闭包的定义!!!

2,看代码里面,inner.__closure__,这个就可以验证是否闭包,返回cell,就是闭包,返回none就不是闭包,

3,需要深刻体会函数名的意义,你看f接收了一个内部函数的名字,只需要f(),这个时候就是调用的内部函数了,

4,函数名,就是一个内存地址,可以赋值给变量,可以通过变量直接(),调用,可以通过作为容器中的元素,比如[func1,func2]

5,return inner 在外部函数中返回内部函数的名字,这就是一个闭包最常用的方式,

#####

函数的装饰器

def outer(func1):
    def inner(*args,**kwargs):
        print(123)
        return func1(*args,**kwargs)
    return inner

@outer
def say_hello():
    print("同学你好")

# say_hello2 = outer(say_hello)
# say_hello2()

say_hello()

###

深刻认识装饰器,

1,首先一定是一个闭包,也就是是一个函数嵌套,并且内部函数引用了外部函数的变量,

2,一定要传入的是一个函数,并且要在内部函数中,把这个函数调用

3,装饰器的意义就是在于可以在函数的开始之前,或者结束之后,增加一些东西,

4,必须要深刻理解这个函数的装饰器,这个应用是非常的广泛的,

5,有点像是hook函数,并且修改函数,

######

装饰器---带参数的装饰器

def info(value):
    def outer(func):
        def inner(*args, **kwargs):
            print(value)
            return func(*args, **kwargs)
        return inner
    return outer


@info("456")
def say_hello():
    print("同学你好")

say_hello()

####

所以带参数的装饰器,就是在装饰器外部再加一层函数嵌套,就可以实现了

###

装饰器---多个装饰器

####

def outer1(func):
    def inner(*args, **kwargs):
        print("outer 1 before")
        ret = func(*args, **kwargs)
        print("outer 1 after")
        return ret
    return inner


def outer2(func):
    def inner(*args, **kwargs):
        print("outer 2 before")
        ret = func(*args, **kwargs)
        print("outer 2 after")
        return ret
    return inner

@outer2
@outer1
def say_hello():
    print("同学你好")

say_hello()
"""
outer 2 before
outer 1 before
同学你好
outer 1 after
outer 2 after
"""

####

这样你就了解了,在函数前面的装饰器内容,是从上往下执行的,而在函数后面的装饰内容,是从下往上执行的,

就和套娃一样的概念,

###

内置装饰器

有三种我们经常会用到的装饰器, property、 staticmethod、 classmethod,他们有个共同点,都是作用于类方法之上。

#####

迭代器和可迭代对象的区别

from collections import Iterable
from collections import Iterator

print(isinstance([], Iterable))  # true
print(isinstance([], Iterator))  # false  列表不是迭代器,

print(dir([])) class A: def __iter__(self): pass def __next__(self): pass a = A() print(isinstance(a, Iterable)) # true print(isinstance(a, Iterator)) # true # 由此可以证明,只有有__iter__',__next__(),这两个方法就是迭代器,

认知:

1,迭代器协议,要同时具有__iter__',__next__(),这两个方法就是迭代器,,可迭代的不一定是迭代器,因为要有next方法, 

2,可迭代协议,只要能被for循环就是可迭代的,如果是迭代器一定是可迭代的,因为里面有iter方法, 

3,我们常见的可以迭代的对象,list,dict,元组,集合,文件,range(),enumerate

4,使用迭代方法的好处:1.可节省内存空间,2.会从容器里面挨个取值,直到取完为止

5,之前讲的iter方法和next方法,工作中基本不会用到,因为for循环已经做了这件事,这是为了讲解迭代器的原理,还有就是为了生成器做铺垫,

#####

生成器---使用生成器函数创建,yield,

def func1():
    print(1)
    yield "a"
    print(2)
    yield "b"

ret = func1()
print(ret)   # <generator object func1 at 0x7f8f6cabb6d0>
print(ret.__next__())  # 1 a
print(ret.__next__())  # 2 b

for i in ret:
    print(i)

###

1,这个生成器也有__iter__(),__next__())两个方法,所以生成器也是一个迭代器,

2,只要有yield关键字的函数就是生成器函数,

3,yield只能写到函数里面,外边不能写,yield不能和return共用,yield不会把代码结束,return会把代码结束,

4,生成器既然也是迭代器,所以可以进行for循环,

####

生成器---使用生成器表达式创建,

# 生成器表达式

# 列表推导式,
list1 = ["鸡蛋%d"%i for i in range(10)]
print(list1)

# 生成器表达式
g = (i for i in range(10))
print(type(g))  # <class 'generator'>
# g是一个生成器,
# 和列表推导式的不同点
# 1,括号不一样,
# 2,返回值不一样,
# 生成器表达式几乎不占用内容,这是它的优点,缺点就是返回值还是一个表达式,取值需要第二步,

# 生成器表达式只能做简单的事情,想要复杂的功能的生成器还是生成器函数,
# 这些推导式你不会没有任何影响,使用for循环和生成器函数都能解决,只是面试的时候会有,别人的代码会有,所以你要掌握

######

递归函数

###############    递归   ##############

# 递归的定义——在一个函数里再调用这个函数本身
# 递归的最大深度——998

# 二分查找算法
# 你观察这个列表,这是不是一个从小到大排序的有序列表呀?
# 如果这样,假如我要找的数比列表中间的数还大,是不是我直接在列表的后半边找就行了?
l = [2,3,5,10,15,16,18,22,26,30,32,35,41,42,43,55,56,66,67,69,72,76,82,83,88]
def search(num,l,start=None,end=None):
    start = start if start else 0
    end = end if end is not None else len(l) - 1
    mid = (end - start)//2 + start
    if start > end:
        return None
    elif l[mid] > num :
        return search(num,l,start,mid-1)
    elif l[mid] < num:
        return search(num,l,mid+1,end)
    elif l[mid] == num:
        return mid
 

#####

匿名函数

def func1(n):
    return n+n

func2 = lambda x:x+x
func3 = lambda x, y: x * y

print(func1(3))  # 6
print(func2(3))  # 6
print(func3(3,3))  # 9

####

认知:

1,匿名函数的定义,func = lambda x : x**x,,函数名 = lambda 参数 :返回值

2,参数  参数可以有多个,用逗号隔开,参数也可以使用默认值参数,

3,返回值和正常的函数一样可以是任意数据类型

4,匿名函数不管逻辑多复杂,只能写一行,且逻辑执行结束后的内容就是返回值,

5,这个匿名函数经常可以和filter和map函数进行结合使用队序列进行操作

 

#####