乔克视界 乔克视界
首页
  • 运维
  • 开发
  • 监控
  • 安全
  • 随笔
  • Docker
  • Golang
  • Python
  • AIOps
  • DevOps
  • 心情杂货
  • 读书笔记
  • 面试
  • 实用技巧
  • 博客搭建
友链
关于
收藏
  • 分类
  • 标签
  • 归档

乔克

云原生爱好者
首页
  • 运维
  • 开发
  • 监控
  • 安全
  • 随笔
  • Docker
  • Golang
  • Python
  • AIOps
  • DevOps
  • 心情杂货
  • 读书笔记
  • 面试
  • 实用技巧
  • 博客搭建
友链
关于
收藏
  • 分类
  • 标签
  • 归档
  • Docker

  • Golang

  • AIOps

  • Python

    • 基础知识

      • Python之链表
      • Python 之类的初识
      • Python之函数式编程
      • Python之匿名函数
      • Python之自定义函数
        • 一、函数基础
        • 二、函数变量的作用域
        • 三、闭包
        • 四、总结
      • Python之异常处理
      • Python之条件与循环
      • Python之列表生成式
      • Python之生成器
      • Python之装饰器
      • Python之迭代器
      • Python之进程、线程、协程
      • Python之深浅拷贝
      • Python之反射
      • Python之并发编程
      • Python之垃圾回收机制
      • Python之断言assert
      • Python之上下文管理器和with语句
      • Python中JSON的对应关系
      • Python之单例模式
    • Django框架

    • 其他

  • DevOps

  • 专栏
  • Python
  • 基础知识
乔克
2025-07-19
目录

Python之自定义函数

函数是 python 编程中不可或缺的一部分。好的自定义函数可以使代码更加模块化、规范化。

# 一、函数基础

函数就是为了实现某一功能的代码块,只要函数写好了,就可以被重复利用,我们来看下面这个例子:

def my_func(message):
    print('Got a message: {}'.format(message))
    return 1

## 调用函数 my_func()
my_func('Hello World')
## 输出
Got a message: Hello World
1
2
3
4
5
6
7
8

其中:

  • def 是函数的声明
  • my_func 是函数的名称
  • message 是函数的参数
  • print 这一行是函数的实体,可以是相应的执行语句
  • 函数的最后可以返回调用结果(return,yield)

大概是下面这种形式:

def name(param1, param2, ..., paramN):
    statements
    return/yield value ## optional
1
2
3

python 函数可以设置默认值,如下:

def func(param = 0):
    ...
1
2

这样在调用 func()函数的时候,如果没有传递参数,则参数的默认值就为 0,如果传入了参数,则 param 就会被覆盖了为传入的值。

python 与其他语言相比的一大特点是:python 是 dynamically typed,可以接受任何数据类型。对于函数参数来说一样适用,如下:

def my_sum(a, b):
    return a + b

result = my_sum(3, 5)
print(result)

## 输出
8
1
2
3
4
5
6
7
8

上面的传递两个数值,其实传递两个列表也是可以的,表示两个列表相连接:

print(my_sum([1, 2], [3, 4]))

## 输出
[1, 2, 3, 4]
1
2
3
4

同理,传递字符串也是可以的,表示字符串的拼接:

print(my_sum('hello ', 'world'))

## 输出
hello world
1
2
3
4

但是,如果两个参数的数据类型不同,比如一个是列表,一个是字符串,这两者是无法相加的,那就会报错,如下:

print(my_sum([1, 2], 'hello'))
TypeError: can only concatenate list (not "str") to list
1
2

由上我们可以看到,python 不用考虑输入的数据类型,而是将其交给具体的代码和解释器去判断,同样的一个函数,可以应用在整型,列表,字符串等等的操作中。在 python 语言中,我们把这种行为称为多态。

python 的另一个特性是支持函数的嵌套,所谓的函数的嵌套,就是函数里面又定义了函数,如下:

def f1():
    print('hello')
    def f2():
        print('world')
    f2()
f1()

## 输出
hello
world
1
2
3
4
5
6
7
8
9
10

上面的例子中函数 f1()中又嵌套了一个函数 f2(),在调用 f1()的时候,会先打印字符串"hello",然后再在 f1()中调用 f2(),打印字符串"world"。

函数的嵌套的主要所用如下:

(1)、函数的嵌套可以保证内部函数的隐私,只能被外部函数所调用和访问,不会暴露在全局作用域,因此,有一些隐私数据不想暴露在外,这时候可以使用函数的嵌套。如下:

def connect_DB():
    def get_DB_configuration():
        ...
        return host, username, password
    conn = connector.connect(get_DB_configuration())
    return conn
1
2
3
4
5
6

这里的 get_DB_configuration 就是内部函数,它不会被除 connect_DB 以外的程序调用,如下直接调用是会报错的:

get_DB_configuration()

## 输出
NameError: name 'get_DB_configuration' is not defined
1
2
3
4

(2)、合理的使用嵌套,可以提高代码的运行效率。如下:

def factorial(input):
    ## validation check
    if not isinstance(input, int):
        raise Exception('input must be an integer.')
    if input < 0:
        raise Exception('input must be greater or equal to 0' )
    ...

    def inner_factorial(input):
        if input <= 1:
            return 1
        return input * inner_factorial(input-1)
    return inner_factorial(input)


print(factorial(5))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

上面的例子是递归计算一个数的阶乘,在计算之前需要检查输入是否合法,如果不使用函数嵌套,每一次递归的时候都要检查一次,大大降低了代码的运行效率。如果使用了递归,那么只会检查一次。

# 二、函数变量的作用域

python 中变量的作用域和其他语言类似。如果变量在函数内部定义,就称为局部变量,只有在函数内部有效,一旦函数执行完毕,局部变量会被回收,无法访问,如下:

def read_text_from_file(file_path):
    with open(file_path) as file:
        ...
1
2
3

我们内部定义的 fie 这个变量,只有在函数内部有效,在外部是无法访问的。

相应的,全局变量是定义在整个文件层次上面的,如下:

MIN_VALUE = 1
MAX_VALUE = 10
def validation_check(value):
    if value < MIN_VALUE or value > MAX_VALUE:
        raise Exception('validation check fails')
1
2
3
4
5

MIN_VALUE 和 MAX_VALUE 是全局变量,在任意地方都可以调用。但是我们不能在函数内部随意更改全局变量的值。比如下面的写法就会有问题:

MIN_VALUE = 1
MAX_VALUE = 10
def validation_check(value):
    MIN_VALUE += 1
validation_check(5)

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in validation_check
UnboundLocalError: local variable 'MIN_VALUE' referenced before assignment
1
2
3
4
5
6
7
8
9
10

这是因为 python 解释器会默认内部变量为局部变量,但是在局部又发现 MIN_VALUE 并没有声明,因此就无法操作,如果想要在函数内部操作全局变量的值,则需要 global 声明,如下:

MIN_VALUE = 1
MAX_VALUE = 10
def validation_check(value):
    global MIN_VALUE
    MIN_VALUE += 1
validation_check(5)
1
2
3
4
5
6

这里的 global 关键字并不是表示重新创建了一个变量,而是告诉 python 解释器,MIN_VALUE 就是全局变量,可以对其进行操作。

但是,如果局部变量和全局变量同名,那么在函数内部,局部变量会覆盖全局变量,如下:

MIN_VALUE = 1
MAX_VALUE = 10
def validation_check(value):
    MIN_VALUE = 3
1
2
3
4

类似的,对于嵌套函数来说,内部函数可以访问外部函数的变量,但是无法对其进行修改,如果要修改,则要加上 nonlocal 关键字,如下:

def outer():
    x = "local"
    def inner():
        nonlocal x ## nonlocal 关键字表示这里的 x 就是外部函数 outer 定义的变量 x
        x = 'nonlocal'
        print("inner:", x)
    inner()
    print("outer:", x)
outer()
## 输出
inner: nonlocal
outer: nonlocal
1
2
3
4
5
6
7
8
9
10
11
12

如果不加 nonlocal 关键字,内部函数变量又和外部函数变量同名,那么同样的,内部函数变量会覆盖外部函数的变量,如下:

def outer():
    x = "local"
    def inner():
        x = 'nonlocal' ## 这里的 x 是 inner 这个函数的局部变量
        print("inner:", x)
    inner()
    print("outer:", x)
outer()
## 输出
inner: nonlocal
outer: local
1
2
3
4
5
6
7
8
9
10
11

# 三、闭包

闭包其实和嵌套函数类似,不同的是嵌套函数返回的是一个值,而闭包返回的是一个函数,返回的函数通常赋予一个变量,这个变量可以被执行调用。

比如我们想计算一个数的 n 次幂,用闭包写成如下:

def nth_power(exponent):
    def exponent_of(base):
        return base ** exponent
    return exponent_of ## 返回值是 exponent_of 函数

square = nth_power(2) ## 计算一个数的平方
cube = nth_power(3) ## 计算一个数的立方
square
## 输出
<function __main__.nth_power.<locals>.exponent(base)>

cube
## 输出
<function __main__.nth_power.<locals>.exponent(base)>

print(square(2))  ## 计算 2 的平方
print(cube(2)) ## 计算 2 的立方
## 输出
4 ## 2^2
8 ## 2^3
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

这里的外部函数 nth_power()的返回值是 exponent_of()的函数名,而不是一个具体值,需要注意的是在执行 square = nth_power(2)和 cube = nth_power(3),外部函数 nth_power 的参数 exponent 仍然会被内部 exponent_of()函数记住,这样我们调用 square(2)和 cube(2),程序会顺利的输出结果。

闭包可以让程序变得更简洁易读,并且可以减少调用导致的不必要的开销,增强程序运行效率。

# 四、总结

(1)、python 中函数的参数可以接受任意数据类型,使用起来需要注意,必要是在函数开头加入数据类型检查;

(2)、python 中的函数参数可以设置默认值;

(3)、嵌套函数的使用,可以保证一定数据的隐私性,提高程序的运行效率;

(4)、合理的使用闭包,可以降低程序的复杂性,提高可读性;

作者:乔克

本文链接:https://jokerbai.com

版权声明:本博客所有文章除特别声明外,均采用 署名-非商业性-相同方式共享 4.0 国际 (CC-BY-NC-SA-4.0) 许可协议。转载请注明出处!

上次更新: 2025/07/19, 11:33:23
Python之匿名函数
Python之异常处理

← Python之匿名函数 Python之异常处理→

最近更新
01
使用 Generic Webhook Trigger 触发 Jenkins 多分支流水线自动化构建
07-19
02
使用Zadig从0到1实现持续交付平台
07-19
03
基于Jira的运维发布平台
07-19
更多文章>
Theme by Vdoing | Copyright © 2019-2025 乔克 | MIT License | 渝ICP备20002153号 |
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式