Python(10)函数

函数的主要作用: 打包代码

最大程度实现代码的重用减少冗余代码

将不同代码进行封装、分解从而降低结构复杂度,提高代码可读性

创建和调用函数

# 创建
def my_func():
    pass


# 调用
my_func()

函数的参数

def my_func(name, times):
    for i in range(times):
        print(f"我喜欢{name}")


my_func(name="python", times=5)

形式参数和实际参数

形式参数(形参)

  • name和times

实际参数(实参)

  • "python"和5

关键字参数和位置参数

我们在传参的时候不仅可以这样

number = div(x=6, y=0)

还可以这样

number = div(6, 0)

那么可以混着用么?

# 这样是可以的
number = div(6, y=0)

# 但这样不行
number = div(x=6, 0)

关键字参数必须在,位置参数后面

默认参数

我们可以定义一个求圆面积的函数是这样的

def func(r,pai):
    print(2*pai*r)

我们都知道 pai 是3.1415926…

那么我们可以不可以让这个参数默认为 3.14 呢,如果需要要精细那么再让用户自己传入.

def func(r, pai=3.14):
    print(2 * pai * r)


# 可以传
# func(3, 3.1415926)

# 也可以不传
func(3)

收集参数

有时候我们不知道用户要输入多少参数

例如:print

这时我们就需要使用到我们的收集参数

def func(*args):
    print(args, type(args))  # (1, 2, 3, 4) <class 'tuple'>


func(1, 2, 3, 4)

我们发现,传入这么多值其实用的元组的打包性质

字典形式的收集参数

def func(**kwargs):
    print(kwargs)  # {'a': 1, 'b': 2, 'c': 3}


func(a=1, b=2, c=3)

变量的解包

收集参数就是 打包操作

那么我们 传入的时候还有一个变量解包操作

def func(a, b, c, d):
    print(a, b, c, d)  # 1 2 3 4


f = (1, 2, 3, 4)
g = {'a': 1, "c": 3, "b": 2, "d": 4}
func(*f)
func(**g)

函数的返回值

例如:

  • sum()函数回返回求和后的结果
  • len()函数回返回一个元素的长度

我们需要使用 return 让我们的自定义函数实现返回

实现一个除法的函数

  1. 实现基本的除法
  2. 除数不能为0
def div(x, y):
    if y == 0:
        return "除数不能为0"
    else:
        return x / y


number = div(x=6, y=1)

print(number)

return后代码将不会在继续向下执行,这样代码就可以简化成

def div(x, y):
    if y == 0:
        return "除数不能为0"
    return x / y


number = div(x=6, y=1)

print(number)

在想想我们学过的三目运算,代码就可以简化成这样

def div(x, y):
    return x / y if y != 0 else "除数不能为0"


number = div(x=6, y=0)

print(number)

如果我们不写return 那么函数也会返回一个 None

作用域

变量或函数起作用的范围

全局变量

变量定义的位置不在任何函数中

a = 0   # 全局变量


def func():
    print(a)


func()

我们想修改全局变量时,可能不如我们所意

a = 0  # 全局变量


def func1():
    a = 1  # 局部变量
    print(a)  # 输出的是局部变量 1


func1()
print(a)  # 输出的是全局变量 0

我们可以使用 global 从而实现我们修改的目的

a = 0  # 全局变量


def func1():
    global a
    a = 1
    print(a)


func1()
print(a)

局部变量

def func1():
    b = 1   # 局部变量

def func2():
    print(b)    # NameError: name 'b' is not defined

func1()
func2()

函数嵌套

  1. 函数内的函数,无法在外部被调用
  2. 函数内定义的变量,可以使用nonlocal关键字进行修改
def func1(x):
    print(x)

    def func2():
        nonlocal x
        x = 5
        print(x)

    func2()
    print(x)

func1(1)

闭包

我们都知道嵌套函数如果想要调用内层函数,可以在外层函数的内部调用,然后我们调用外部函数,从而实现内部函数的使用。

def func():
    x = 10

    def func2():
        print(x)

    func2()


func()

那么我们有没有什么方法,可以在调用外部函数的时候不执行内层函数,而在外部代码中选择执行位置呢?

我们可以将内部函数的引用返回

def func():
    x = 10

    def func2():
        print(x)

    return func2


f2 = func()
print("func调用结束")
f2()

注意 : 返回的是函数名,无需加小括号

工厂函数

那么通过这个特征我们可以实现什么呢

可以实现工厂函数

我们调用外部函数传入不同参返回内部函数时,内部函数将会一直受到外部参数的影响

def func(x):
    def func2(y):
        return y ** x

    return func2


square = func(2)
cube = func(3)

print(square(5))  # 25
print(cube(5))  # 125

闭包巧用

我们还能怎么用闭包

控制某个单位移动

def func(x, y):
    def func2(x1, y1):
        nonlocal x, y
        x += x1
        y += y1
        print(f"x = {x} , y = {y}")

    return func2


f1 = func(0, 0)
f1(1, 2)

f1(-2, 1)

装饰器

函数可以传参,函数本身也可以被当作参数被传入

def func1():
    print("hello world func1")


def func2(func):
    print("func2 start")
    func()
    print("func2 end")


func2(func1)

这样我们就可以实现一个计算函数耗时的函数

import time


def func1():
    time.sleep(1)
    print("hello world func1")


def func2(func):
    s = time.time()
    func()
    e = time.time()
    print(f"程序耗时:{e - s}")


func2(func1)

这样写是可以实现,但每次使用都得去专门调用这个函数。我们想要更加简洁可以使用装饰器

import time


def func1(func):
    def func2():
        s = time.time()
        print("开始")
        func()
        print("结束")
        e = time.time()
        print(f"程序耗时:{e - s}")

    return func2


def func3():
    time.sleep(1)
    print("hello world func3")


func = func1(func3)

func()

感觉还不如不用来的简便是么?我们可以使用语法糖 @func1 就可以直接调用装饰器了

import time


def func1(func):
    def func2():
        s = time.time()
        print("开始")
        func()
        print("结束")
        e = time.time()
        print(f"程序耗时:{e - s}")

    return func2


@func1
def func3():
    time.sleep(1)
    print("hello world func3")


func3()

装饰器的嵌套

def add(func):
    def inner():
        print("add")
        x = func()
        print(x, "add")
        return x + 1

    return inner


def cube(func):
    def inner():
        print("cube")
        x = func()
        print(x, "cube")
        return x ** 3

    return inner


def square(func):
    def inner():
        print("square")
        x = func()
        print(x, "square")
        return x + 2

    return inner

@add
@cube
@square
def test():
    print(1)
    return 3


print(test())

执行结果如下

add
cube
square
1
3 square
5 cube
125 add
126

装饰器传参

原本模样

import time


def func4(msg):
    def func1(func):
        def func2():
            print(f"msg = {msg}")
            s = time.time()
            print("开始")
            func()
            print("结束")
            e = time.time()
            print(f"程序耗时:{e - s}")

        return func2

    return func1


def func3():
    time.sleep(1)
    print("hello world func3")


func3 = func4(msg="A")(func3)

func3()

使用语法糖

import time


def func4(msg):
    def func1(func):
        def func2():
            print(f"msg = {msg}")
            s = time.time()
            print("开始")
            func()
            print("结束")
            e = time.time()
            print(f"程序耗时:{e - s}")

        return func2

    return func1


@func4(msg="B")
def func3():
    time.sleep(1)
    print("hello world func3")


func3()

幂名函数 lambda

我们正常写一个函数是这样的

# 求平方
def square(x):
    return x * x

print(square(2))

而lambda是这样的

# 求平方
square = lambda x: x*x
print(square(2))

lambda 和 map、filter

我们看出来了lambda是比较简洁的,但好像并没有那么简洁那么我们来看看这个

求出列表中所有值的平方

正常写法

list_x = [1, 2, 3, 4, 5, 6, 7, 8, 9]

def square(x):
    return x * x

list_y = []
for x in list_x:
    list_y.append(square(x))

print(list_y)

使用map

list_x = [1, 2, 3, 4, 5, 6, 7, 8, 9]

def square(x):
    return x * x

print(list(map(square,list_x)))

使用lambda和map

list_x = [1, 2, 3, 4, 5, 6, 7, 8, 9]

print(list(map(lambda x: x * x, list_x)))

生成器

小练习 斐波那契数列

斐波那契数列 由0和1开始,之后的斐波那契数就是由之前的两数相加而得出例如:

0,1,1,2,3,5,8…

解决方法

init_number1 = 0
init_number2 = 1
print(init_number1)
print(init_number2)
for i in range(0, 10):
    init_number1 = init_number1 + init_number2
    print(init_number1)
    init_number1, init_number2 = init_number2, init_number1

当函数遇到 return 那么这个函数就算是结束了,函数中做过的工作,保存的局部变量都会丢失。 再次调用的时候一切都是从头再来。

有没有什么办法让函数在退出之后还能保留状态呢?

  • 闭包(有些复杂)
  • 全局变量(会污染名命空间)
  • 生成器(推荐)

将函数中的 return 替换为 yield 即可

def counter():
    i = 0
    while i <= 5:
        yield i
        i += 1


c = counter()
print(c)  # <generator object counter at 0x000001F3A3D38C10>

生成器 和 迭代器、列表、元组等可迭代对象不一样。

生成器 我们可以看作一个制作机器,每次被调用将会返回一个数据,并且会记住当时的状态。

列表、元组这些可迭代对象则是容器,他们里面存放的是早已准备好的全部数据

生成器 可以看作是特殊的 迭代器

  • 都不支持下标
  • 不走回头路
  • 支持next()函数
def counter():
    i = 0
    while i <= 5:
        yield i
        i += 1


c = counter()

print(next(c))  # 0
print(next(c))  # 1
print(next(c))  # 2
print(next(c))  # 3
print(next(c))  # 4
print(next(c))  # StopIteration

生成器解决斐波那契数列

def fib():
    back1, back2 = 0, 1
    while True:
        yield back1
        back1, back2 = back2, back1 + back2

f = fib()
print(next(f))  # 0
print(next(f))  # 1
print(next(f))  # 1
print(next(f))  # 2
print(next(f))  # 3
print(next(f))  # 5
print(next(f))  # 8

生成器表达式

t = (i**2 for i in range(10))
for i in t:
    print(i)

生成器表达式和列表表达式最大的区别在于,列表推导式会将所有数据一下子生产出来并放到列表中。但生成器表达式,一次只会返回一个值。

递归

什么是递归? 自己调自己

def funcA():
    print("我被调用了")
    funcA()
funcA()

你会发现它一直在调用自己,我们可以加些代码让它停下来。

def funcA(count):
    if count > 0:
        print("我被调用了")
        count -= 1
        funcA(count)
funcA(10)

由上面代码可知,递归必须需要一个 结束条件 并且每次调用都会向着这个结束条件去推进

小练习 一

求一个数的阶乘

迭代实现

def factIter(n):
    result = n
    for i in range(1, n):
        result *= i
    return result


print(factIter(10))

递归实现

def factRecur(n):
    if n == 1:
        return 1
    else:
        return n * factRecur(n - 1)


print(factRecur(10))

小练习 二

斐波那契数列

def factRecur(n):
    if n == 1 or n == 2:
        return 1
    else:
        return factRecur(n - 1) + factRecur(n - 2)


print(factRecur(12))

如果你将斐波那契的12换成120 你将会发现递归的问题 效率慢

因为他需要将所有函数全部调用,最终一层一层返回,所以效率一定没有迭代快。

函数文档、类型注释

函数文档

我们使用help()方法可以展示出某个函数的函数文档

help(print)

会展示以下内容

Help on built-in function print in module builtins:

print(...)
    print(value, ..., sep=' ', end='\n', file=sys.stdout, flush=False)
    
    Prints the values to a stream, or to sys.stdout by default.
    Optional keyword arguments:
    file:  a file-like object (stream); defaults to the current sys.stdout.
    sep:   string inserted between values, default a space.
    end:   string appended after the last value, default a newline.
    flush: whether to forcibly flush the stream.

那么我们如何去给自己的函数编写文档呢?

def add(n1, n2):
    """
    功能:
        用来将n1和n2相加
    :param:
        - n1 一个数字
        - n2 另一个数字
    :return
        n1和n2的和
    """
    return n1 + n2

help(add)
"""
Help on function add in module __main__:

add(n1, n2)
    功能:
        用来将n1和n2相加
    :param:
        - n1 一个数字
        - n2 另一个数字
    :return
        n1和n2的和
"""

类型注释

我们可以通过以下方法告知其他代码开发者我们的函数需要什么类型的数据,以及返回什么类型的数据

def add(n1: int, n2: int) -> int:

    return n1 + n2


help(add) # add(n1: int, n2: int) -> int
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值