Python小白学习笔记第7课:Python函数(2024版)

50次阅读
没有评论

共计 5843 个字符,预计需要花费 15 分钟才能阅读完成。

Python 小白学习笔记第 6 课:Python 表达式和语句(2024 版)

函数其实就是一段重用的代码,给这段代码起一个名字,这就是函数定义。使用函数可以将功能上相对独立且会被重复使用的代码封装起来,当需要这些代码的时候,不用把重复的代码再写一遍,而是通过调用函数实现对既有代码的复用。

Python 自带的函数叫做“内置函数”,也就是 Python 帮我们提前写好的,我们直接拿来用就行。

内置函数有很多,具体可查看官方文档:https://docs.python.org/zh-cn/3/library/functions.html

输入输出

其实,我们之前一直在使用的print(),就是内置函数,用来将内容输出到屏幕上。在 Python 中,与之对应的还有一个函数input(),用来获取键盘输入。

name = input("请输入您的姓名:")  # 提示用户输入名字
age = input("请输入您的年龄:")  # 提示用户输入年龄
print("欢迎光临,", name)  # 打印欢迎信息
print("您今年", age, "岁了!")  # 打印年龄信息

"""
请输入您的姓名:张三
请输入您的年龄:18
欢迎光临, 张三
您今年 18 岁了!"""

自定义函数

内置函数虽然不少,但毕竟数量有限,只靠内置函数是不可能实现所有功能的。因此,编程中往往需要将频繁使用的代码封装为自定义函数。语法格式如下:

def 函数名(参数):
    函数体
    return 返回值

定义完函数,就可以在程序的任何地方使用 函数名 () 运行这段代码,即函数调用。

def my_print():
    print("自定义输出函数")

my_print()  # 自定义输出函数

函数参数

我们把函数定义时的参数称为形式参数(形参),函数调用时的参数称为实际参数(实参)。

位置参数

def my_print(n):
    print("*" * n)

my_print(5)  # *****
my_print()  # 报错

函数定义时有几个参数,函数调用时就必须传入几个参数,否则会报错,除非参数有默认值。

关键字参数

使用关键字参数允许函数调用时参数的顺序与声明时不一致,因为 Python 解释器能够用参数名匹配参数值。

def my_print(name, age):
    print("欢迎光临,", name)
    print("您今年", age, "岁了!")

my_print(age=18, name="张三")

默认参数

函数调用时,默认参数的值如果没有传入,则使用默认值。

def my_print(n=5):
    print("*" * n)

my_print()  # *****
my_print(10)  # **********

Python 在创建函数(未执行)时,如果函数的参数有默认值,则会在函数内部创建一块区域维护这个默认值。

在特定情况【默认参数的值是可变类型 list/dict/set】和【函数内部会修改这个值】下,参数的默认值有坑:

def my_print(a1, a2=[1, 2]):
    a2.append(666)
    print(a1, a2)

my_print(100)  # 100 [1, 2, 666]
my_print(200)  # 200 [1, 2, 666, 666]
my_print(300, [888, 777])  # 300 [888, 777, 666]
my_print(300)  # 300 [1, 2, 666, 666, 666]

动态参数

即定义函数时在形参位置用 * 或 ** 可以接任意个参数。

def my_print(*args, **kwargs):
    print(args, kwargs)

my_print("姓名", "年龄", name="女", age=18)  # ('姓名', '年龄') {'name': '女', 'age': 18}

在执行函数时,实参也可以用* 或 **

def my_print(a1, a2):
    print(a1, a2)

my_print(*[11, 22])  # 结果为:11 22
my_print(**{"a1": 11, "a2": 22})  # 结果为:11 22

形参和实参都用* 和 **

def my_print(*args, **kwargs):
    print(args, kwargs)

my_print(*[11, 22], **{"k1": 1, "k2": 2})  # 结果为:(11, 22) {'k1': 1, 'k2': 2}

参数组合

在 Python 中定义函数,可以多种参数组合使用。但是,参数定义顺序必须是:位置参数、默认参数、动态参数。

函数做参数

函数就相当于是一个变量,同时也可被哈希,所以也可将函数作为参数,传入到函数之中。

匿名函数

基于 lambda 表达式实现定义一个没有名字的函数,格式为:lambda 参数: 函数体

匿名函数支持任意参数,但只能支持单行的代码。所以,匿名函数只能处理非常简单的功能。

func = lambda x: x + 100
value = func(10)
print(value)  # 结果为:110

内置函数

callable()

是否可在后面加括号执行,函数、类、具有 call 方法的对象都是可执行的。

def func():
    pass

print(callable(func))  # 结果为:True

exec()

函数 exec 将字符串作为代码执行。

s = "print('hello, world')"
exec(s)  # hello, world

在大多数情况下,还应添加第二个参数,用作代码字符串的命名空间;否则代码将污染你的命名空间,即修改你的变量。

exec("print = 1")
print('666')  # 报错

通过 exec 执行赋值语句创建的变量位于 scope 中:

scope = {}
exec("print = 1", scope)
print("666")

eval()

计算用字符串表示的 Python 表达式的值,并返回结果:

a = eval(input("请输入:"))  # 假设输入:1 + 2 * 3
print(a)  # 7

与 exec 一样,也可向 eval 提供一个命名空间。

scope = {}
scope["x"] = 2
scope["y"] = 4
print(eval("x * y", scope))  # 8

type()

获取一个对象的类型。

s = "李小龙"
print(type(s))  # <class 'str'>

filter()

用于过滤序列,过滤掉不符合条件的元素,返回一个迭代器对象,如果要转换为列表,可以使用 list() 来转换。

该函数接收两个参数,第一个为函数,第二个为序列,序列的每个元素作为参数传递给函数进行判断,然后返回 True 或 False,最后将返回 True 的元素放到新列表中。

a = [1, 2, 3, 4, 5]
b = filter(lambda x: x > 2, a)
print(list(b))  # 结果为:[3, 4, 5]

map()

根据提供的函数对指定序列做映射。

a = [1, 2, 3]
b = map(lambda x: x > 2, a)
print(list(b))  # 结果为:[False, False, True]

reduce()

对参数序列中元素进行累积。

from functools import reduce

a = reduce(lambda x, y: x + y, [1, 2, 3], 10)
print(a)  # 结果为:16

zip()

p 将两个序列“缝合”起来,并返回一个由元组组成的序列。

names = ['李小龙', '李中龙', '李大龙']
ages = [10, 15, 18]
s = zip(names, ages)
for name, age in s:
    print(name, 'is', age, 'years old')

当序列长度不同时,函数 zip 将在最短的序列用完后停止“缝合”。

enumerate()

函数 enumerate 能够迭代索引 - 值对:

lst = ['Python', 'Java', 'C']
for index, string in enumerate(lst):
    if 'Python' in string:
        lst[index] = '666'
print(lst)  # ['666', 'Java', 'C']

函数特殊用法

  • 函数名 + ():执行函数
  • 函数名.__name__:获取函数的名称,其实是个字符串
  • 函数名.__doc__:获取函数的注释
    def func():
      """我是 func 函数"""
      print(666)
    
    func()  # 666
    print(func.__name__)  # func
    print(func.__doc__)  # 我是 func 函数

闭包

在一个外函数中定义了一个内函数,内函数里用到了外函数的临时变量,并且外函数的返回值是内函数的引用,这样就构成了一个闭包。

一般情况下,如果一个函数结束,函数内部的所有东西都会释放掉,还给内存,局部变量都会消失。但是闭包是一种特殊情况,如果外函数在结束的时候发现自己的临时变量将来会在内部函数中用到,就把这个临时变量绑定给了内部函数,然后自己再结束。

def outer(arg):
    def inner():
        print(arg)  # 在内函数中用到了外函数的临时变量
    return inner  # 外函数的返回值是内函数的引用

func = outer(666)  # 内函数的引用返回给了 func
func()  # 执行 inner 函数,结果为:666

计数器

def counter(first=0):
    def add_one():
        nonlocal first
        first += 1
        print(first)
    return add_one

num = counter()
num()  # 1
num()  # 2
num()  # 3

需要使用 nonlocal 关键字修饰外部函数的变量,才可在内部函数中修改它。

柯里化

指将原来接受两个参数的函数变成新的接受一个参数的函数的过程。新的函数返回一个以原有第二个参数为参数的函数,如 z =f(x,y)转换成 z =f(x)(y)的形式。

def add(x, y):
    return x + y

print((add(1, 2)))  # 3

def plus(x):
    def fn(y):
        return x + y

    return fn

print(plus(2)(3))  # 5

原函数调用为 add(1, 2),柯里化目标为 plus(2)(3)。

装饰器

装饰器其实是一种闭包,在不修改原函数内容的前提下,通过 @函数(装饰器的语法糖写法)可以实现在函数前后自定义执行一些功能(批量操作会更有意义)。

装饰器实际上是将原函数更改为其他函数,然后在此函数中再去调用原函数。

def outer(origin):
    def inner(*args, **kwargs):
        print("before")
        res = origin(*args, **kwargs)  # 调用原来的 func 函数
        print("after")
        return res
    return inner

@outer  # func = outer(func)
def func(a1, a2):
    print("我是 func 函数")
    value = (11, 22, 33)
    return value

value = func(11, a2=22)
print(value)

输出结果如下:

before
我是 func 函数
after
(11, 22, 33)

多个装饰器的装饰过程:离函数最近的装饰器先装饰,然后外面的装饰器再进行装饰,由内到外的装饰过程。

计时器

import time

def timmer(func):
    def wrapper():
        start_time = time.time()
        func()
        stop_time = time.time()
        print('运行时间是 %s 秒' % (stop_time - start_time))
    return wrapper

@timmer
def i_can_sleep():
    time.sleep(3)

i_can_sleep()

带有参数的装饰器

def new_tips(argv):
    def tips(fn):
        def inner(a, b):
            print("start %s" % argv)
            fn(a, b)
            print("stop\n")
        return inner
    return tips

@new_tips("add")
def add(a, b):
    print(a, "+", b, "=", a + b)

@new_tips("sub")
def sub(a, b):
    print(a, "-", b, "=", a - b)

add(1, 2)
sub(1, 2)

类装饰器

想要让类的实例对象能够像函数一样进行调用,需要在类里面使用 call 方法,把类的实例变成可调用对象,类装饰器装饰函数功能在 cal 方法里面进行添加。

class Check:
    def __init__(self, fn):
        self.__fn = fn

    def __call__(self, *args, **kwargs):
        print("登陆")
        self.__fn(*args, **kwargs)

@Check
def comment():
    print("发表评论")

comment()

迭代器

lst = [1, 2, 3]
it = iter(lst)
print(next(it))  # 1
print(next(it))  # 2
print(next(it))  # 3

生成器

生成器是由函数 +yield 关键字创造出来的写法,在特定情况下,用他可以节省内存。当函数中有 yield 存在时,这个函数就叫生成器函数。

def func():
    print(666)
    yield 123

f = func()
print(f)  # 结果为:<generator object func at 0x00000>

执行生成器函数时,函数体默认不会被执行,返回一个生成器对象。

def func():
    print(666)
    yield 123  # 有点像 return,执行到这个位置就不再执行
    print(777)
    yield 456
    print(888)

v = func()
n1 = next(v)  # next 里面放生成器对象,进入生成器函数并执行
print(n1)

输出结果如下:

666
123
n2 = next(v)  # 从上次 yield 返回位置继续向下执行
print(n2)

输出结果如下:

777
456
n3 = next(v)  # 从上次 yield 返回位置继续向下执行
print(n3)

这时没有 yield 了,默认return None,程序会报错:生成器中的代码执行完毕了。

自定义 range 函数

def frange(start, stop, step):
    x = start
    while x < stop:
        yield x
        x += step

for i in frange(1, 10, 0.5):
    print(i)

项目开发

def func():
    print(666)
    yield 123
    print(777)
    yield 456

data = func()
for item in data:
    print(item)  # 本质是 next(data)

正文完
 0
三毛笔记
版权声明:本站原创文章,由 三毛笔记 于2024-01-23发表,共计5843字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。
评论(没有评论)