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

编程 · 01-23 · 156 人浏览

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关键字修饰外部函数的变量,才可在内部函数中修改它。

装饰器

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

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

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(func):
        def nei(a, b):
            print('start %s' % argv)
            func(a, b)
            print('stop')
        return nei
    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)

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

输出结果如下:

start add
1 + 2 = 3
stop
None
start sub
1 - 2 = -1
stop
None

迭代器

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)
Python
Theme Jasmine by Kent Liao