[Python]语法细节

189 阅读21分钟

这篇文章主要对以下Python基础语法的一些细节进行介绍.

  • 语句和变量
  • 对象和类型
  • 数字和运算
  • 条件和循环
  • 函数和可调用对象

1. 语句和变量

1.1 语句折行

  • 如果一行代码太长, 可以折成多行来写, 看起来会更清楚
  • Python中, 如果一个语句被小括号, 中括号, 大括号包起来, 是可以跨行书写的
server = {
    'ip' : '172.16.17.32',
    'port' : 8080
}
  • 如果没有用括号包起来, 可以使用 \ 来续行
x = 1
y = 2
if x == 1 and \
    y == 2:
    print("x and y are equal")
  • 双引号"和单引号'字符串, 都不能折行, 但是三引号'''/"""可以折行
print("""hello 
      world""")
  • 使用 ; 可以把多个语句写到同一行. 但是强烈不建议这么做
x = 1; y = 3
print(x);print(y)

1.2 缩进规范

  • Python中使用缩进表示语句块.
  • 同一个语句块中的代码必须保证严格的左对齐(左边有同样数目的空格和制表符).
x = 0 
if x == 1:
    print(x)
     x += 1 # 多缩进一个空格 
#执行结果
# /main.py
#   File "z:\gitee-study\Python\study\main.py", line 4
#     x += 1
# IndentationError: unexpected indent

1.3 多重赋值(链式赋值)

  • 可以同时给多个变量赋相同的值
x = y = 1
print(x)
print(y)

1.4 多元赋值

  • 可以同时给多个变量赋不同的值
x, y = 1, 2
print(x)
print(y)
  • 多元赋值很强大,比如交换两个变量
x, y = 1, 2
x,y = y,x
print(x)
print(y)

1.5 作用域和生命周期

  • Python中, def, class, lamda(会改变变量的作用域
  • if, else, elif, while, for, try/except不会改变变量的作用域
for i in range(0,10):
    print(i)
print(i) # 出了for循环  仍然可以访问到i


def Func():
    x = 1 
    print(x)
print(x)  #出了def的函数语句块, 就不再能访问到x变量了
  • 内建函数globals()返回了全局作用域下都有哪些变量, 内建函数locals()返回了局部作用域下都有哪些变量
a = 1
def Func():
    x = 0
    print(globals())
    print(locals())
Func()

print("--------------------------")
print(globals())
print(locals())

# 输出结果
# main.py
# {'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x000001F9C6DD6CD0>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'z:\\gitee-study\\Python\\study\\main.py', '__cached__': None, 'a': 1, 'Func': <function Func at 0x000001F9C717F280>}
# {'x': 0}
# --------------------------
# {'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x000001F9C6DD6CD0>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'z:\\gitee-study\\Python\\study\\main.py', '__cached__': None, 'a': 1,: 1, 'Func': <function Func at 0x000001F9C717F280>}
# {'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x000001F9C6DD6CD0>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'z:\\gitee-study\\Python\\study\\main.py', '__cached__': None, 'a': 1, 'Func': <function Func at 0x000001F9C717F280>}

1.6 特殊标识符

  • Python使用下划线_作为变量的前缀和后缀, 来表示特殊的标识符
  • _xxx表示一个 "私有变量", 使用 from module import * 无法导入
# add.py
def _Add(x,y):
    return x + y

# main.py
from add import *
print(_Add(1,2))
# 输出

#   File "z:\gitee-study\Python\study\main.py", line 3, in <module>
#     print(_Add(1,2))
# NameError: name '_Add' is not defined
  • _xxx_ (前后一个下划线)和__xxx__ (前后两个下划线) 一般是系统定义的名字. 我们自己在给变量命名时要 避开这种风格. 防止和系统变量冲突

1.7 文档字符串

  • 多行注释,使用三引号'''/"""在函数/类开始位置表示. 这个东西也被称为 文档字符串
  • 使用对象的 doc 属性就能看到这个帮助文档了
def Add(x, y):
    '''
    define function for add two number
    '''
    return x + y
print(Add.__doc__) # 输出: define function for add two number
  • 或者使用内建函数 help 也可以做到同样的效果
def Add(x, y):
    '''
    define function for add two number
    '''
    return x + y
print(help(Add))  
# 输出:
# Help on function Add in module __main__:

# Add(x, y)
#     define function for add two number

# None

1.8 模块文档

  • 不光一个函数/类可以有一个文档字符串. 一个模块也同样可以
# add.py
'''
define some function for add
'''
def Add(x,y):
    return x + y

# main.py
import add

print(add.__doc__) # define some function for add

1.9 Uinx起始行

  • 对于Linux或者Mac这类的Unix类系统, 可以给Python代码文件增加一个起始行, 用来指定程序的执行方式.
#!/usr/bin/python
print('haha')
  • 和写bash脚本一样的

2. 对象和类型

2.1 一切都是对象

一个Python的对象, 包含三部分信息:

  • 身份: 每一个对象都有一个唯一的身份标识自己. 实用内建函数id可以看到这个标识的具体的值.
  • 类型: 对象的类型决定了对象保存什么样的值, 可以进行什么样的操作, 以及遵守什么样的规则. 实用内建函 数type可以查看对象的类型. 本质上就是把对象分分类.
  • : 表示对象中具体保存的内容. 有些对象的值可以修改, 有些对象的值不能修改, 这个称为对象的 "可变性"
  • Python中任何类型的值其实都是一个对象(判定一个东西是不是对象, 就尝试用id取一下看能否取到). 100 这样的字面值常量, 也是对象; 一个字符串, 也是对象; 一个函数, 也是对象; 一个类型(type函数的返回值), 其实也 是对象; 一个模块, 还是对象;

2.2对象和引用

以下面的代码为例:

a = 2
b = 3
b = 2
  • 按照C语言中的理解, a = 2 , 相当于先创建一个变量名a, 给a分配了内存空间, 然后把数字2存到这块地址空间中.
  • 但Python中完全不是这样的. 我们在解释器中敲入 a = 2 , 首先有一个 integer class 被唤醒(找到了整数对象的图纸)
  • 根据 integer class 这个图纸, 在内存中开辟一段空间(得到了对象id), 并填充入对象的类型和值.

image.png

  • 在创建变量b = 3,同样的方式进行创建

image.png

  • 在执行b = 2 ,并不会把b的值修改为2.而是直接访问已经存在的type和value匹配的id.会直接访问对象a,

image.png

a = 2
print(id(a)) # 1998012574032
b = 3
print(id(b)) # 1998012574064
b = 2
print(id(b)) # 1998012574032
  • Python中的变量名, 只是一个访问对象的 "钥匙" . 多个不同的变量, 完全可以访问相同的对象, 因此这种变量名也叫对象的 "引用"

  • Python和C/C++的区别:

    • 在 Python 中,变量并不直接存储数据。相反,变量名实际上是指向对象的引用(或指针)。当你写下 a = 2 时,Python 会创建一个整数对象 2,然后让 a 指向这个对象
      • 对象模型:所有的值(如 2、字符串、列表等)在 Python 中都是对象。每个对象在内存中都有一个固定的内存地址。
      • 整数池(Interning) :对于一些小的、常用的对象(比如整数 -5 到 256、字符串等),Python 会使用 对象池interning 技术来优化内存利用。例如,数字 2 在 Python 中可能只有一个实例,而其他变量指向这个实例。
    • 在 C/C++ 中,变量通常是一个内存地址的标识符,并且直接存储数据。例如,当你写 int a = 2; 时,a 直接存储 2 的值在内存中。
      • 值传递:对于基础数据类型(如 intfloat 等),变量 a 存储的是实际的值。每次赋值时,都是复制值到新的内存位置(如果是基本数据类型的话)。
      • 内存模型:C/C++ 更加接近硬件层面,变量通常直接表示内存中的位置,存储的是数据本身。
    • 在 Python 中,a = 2 只会有一个 2 对象存在,多个变量(如 ab)可以指向同一个对象。
    • 在 C/C++ 中,变量 ab 会各自独立存储 2,它们占用不同的内存空间,即使它们的值相同。

这种差异是由于 Python 是一个 动态类型语言,变量更像是对象的引用,而 C/C++ 是静态类型语言,变量直接存储数据。

2.3 对象和类型

  • 有些对象是 "相似" 的, 于是我们把对象可以进行归类. 比如把所有的整数归为一类, 把所有的浮点数对象归为一类, 把所有的字符串归为一类.
  • 相同的类型的对象, 须有相同的操作. 比如, 所有的整数对象, 都可以进行加减乘除操作. 所有的字符串都可 以使用切片操作.
  • Python是一种动态强类型编程语言.
  • 动态是指运行时进行变量类型检查; 强类型指类型检查严格, 并且"偏向于"不允许隐式转换

2.3.1 标准类型

  • 整型
  • 浮点型
  • 复数型
  • 布尔型
  • 字符串
  • 列表
  • 元组
  • 字典

2.3.2 其他内建类型

  • 类型
    • 类型也是对象
print(type(type(100)))  # <class 'type'>
print(id(type(100)))  # 140703443525584
  • Null对象(None)
    • Python有一个特殊的类型, 称为NoneType. 这个类型只有一个对象, 叫做None
    print(type(None)) # <class 'NoneType'>
    
    • NoneType类似于C语言中的void
    • None类似于C语言的空指针NULL
    • None这个对象没啥有用的属性, 它的布尔值为False
  • 文件
  • 函数
  • 模块

2.3.3 内建类型的布尔值

  • 所有的标准对象, 均可以用于布尔测试(放到if条件中).
  • 下列对象的布尔值是False
    • None
    • False
    • 所有值为0的数字(0, 0.0, 0.0 + 0.0j)
    • "" (空字符串)
    • [] (空列表)
    • () (空元组)
    • {} (空字典)
    • 其他情况下, 这些内建对象的布尔值就是True

2.3.4 对象值的比较

  • 所有内建类型对象都可以进行比较操作来进行值比较.(比较的双方必须是相同类型).
  • 比较运算的结果是True或者False.
  • 比较运算符包括 ==, !=, <, >, <=, >=

2.3.5 对象身份的比较

  • 变量名只是对象的一个引用. 那么两个变量名是否指向同一个对象呢?
  • 可以使用 id 这个内建函数来比较,如果id的值相同, 说明是指向同一个对象
a = 100
b = a
print(id(a) == id(b)) # True
  • Python还提供了 isis not关键字, 直接进行判定
a = 100
b = a
print(a is b ) # True
print(a is not b) # False

2.3.6 对象类型比较

  • 两个对象比较类型是否相同, 可以使用内建函数type
a = 100
print(type(a) == type(100)) # True
  • Python还提供了一个内建函数 isinstance用来判断两个类型是否相同
a = []
print(isinstance(a,list)) # True
a = 100
print(isinstance(a,type(100))) # True

2.3.7 类型工厂函数

  • 常见的类型工厂函数
函数描述示例
int()将对象转换为整数类型(如果可能的话)。int("123")123
float()将对象转换为浮点数类型(如果可能的话)。float("3.14")3.14
str()将对象转换为字符串类型。str(100)"100"
list()将可迭代对象转换为列表类型。list((1, 2, 3))[1, 2, 3]
tuple()将可迭代对象转换为元组类型。tuple([1, 2, 3])(1, 2, 3)
set()将可迭代对象转换为集合类型。set([1, 2, 3, 3]){1, 2, 3}
dict()创建一个字典,通常通过键值对的可迭代对象创建。dict([("a", 1), ("b", 2)]){'a': 1, 'b': 2}
bool()将对象转换为布尔类型。bool("")False
complex()将对象转换为复数类型,接受两个参数,实部和虚部。complex(3, 4)(3+4j)
  • int(): 将字符串、浮点数等转换为整数,通常会舍弃小数部分。
  • float(): 将字符串、整数等转换为浮点数。
  • str(): 将其他类型的对象转换为字符串形式。
  • list(): 将可迭代对象(如元组、字符串)转换为列表。
  • tuple(): 将可迭代对象(如列表、字符串)转换为元组。
  • set(): 将可迭代对象转换为集合,集合会去除重复的元素。
  • dict(): 从键值对创建字典,通常接受键值对的可迭代对象(如元组)。
  • bool(): 转换为布尔类型,空值、0None 等为 False,其他为 True
  • complex(): 将两个数值转换为复数类型,第一个是实部,第二个是虚部。

2.3.8 Python不支持的类型

  • char, byte: 可以使用长度为1的字符串, 或者整数代替;
  • 指针: Python替你管理内存, 虽然id()返回的值接近于指针的地址, 但是并不能主动去修改;
  • int/short/long: 以前版本的Python中也是区分int和long的, 但是后来统一在一起了; 记住Python中的整数 表示的范围, 只取决于你机器内存的大小.
  • float/double: Python的float其实就是C语言的double. Python设计者认为没必要支持两种浮点数

3. 数字和运算

3.1 整数的位运算

  • Python 整数的位运算

在 Python 中,整数支持位运算(bitwise operations),这些运算直接操作整数的二进制位。常见的位运算符包括按位与、按位或、按位异或、按位取反、左移和右移。

运算符描述示例代码示例输出
&按位与:只有两位都是 1 时,结果为 1a = 5; b = 3; result = a & b1 (二进制: 001)
^按位异或:两位不同时,结果为 1a = 5; b = 3; result = a ^ b6 (二进制: 110)
~按位取反:将 1001a = 5; result = ~a-6 (二进制: ...11111010)
<<左移:将二进制位向左移动,等同于乘以 2^na = 5; result = a << 110 (二进制: 1010)
>>右移:将二进制位向右移动,等同于除以 2^na = 5; result = a >> 12 (二进制: 10)
  • 说明

  • 按位与 (&):只有两个数字的相同位都是 1 时,结果才为 1,否则为 0

  • 按位或 (|):只要两个数字的相同位中有一个是 1,结果就为 1

  • 按位异或 (^):只有两个数字的相同位不同(一个是 1,另一个是 0)时,结果才为 1

  • 按位取反 (~):将每个二进制位反转(1001),对于有符号整数,结果为 -(x + 1),其中 x 是原始值。

  • 左移 (<<):将数字的二进制位向左移动,等同于乘以 2^n,其中 n 是移动的位数。

  • 右移 (>>):将数字的二进制位向右移动,等同于除以 2^n,其中 n 是移动的位数。

3.2 常用内置函数/模块

  • abs: 求一个数的绝对值.
  • divmod: 返回一个元组, 同时计算商和余数
a,b = divmod(10,3)
print(a) # 3
print(b) # 1
  • str: 将数字转换成字符串.
  • round: 对浮点数进行四舍五入. round有两个参数, 第一个是要进行运算的值, 第二个是保留小数点后多少位

import math
for i in range(0,10):
    print(round(math.pi,i))
# 输出:
# 3.0
# 3.1
# 3.14
# 3.142
# 3.1416
# 3.14159
# 3.141593
# 3.1415927
# 3.14159265
# 3.141592654
  • 整数进制转换: oct(), hex(), 参数是一个整数, 返回值是对应字面值的字符串.
函数描述示例代码输出
bin()将整数转换为二进制字符串bin(10)'0b1010'
oct()将整数转换为八进制字符串oct(10)'0o12'
hex()将整数转换为十六进制字符串hex(255)'0xff'
number = 42

# 转换为二进制、八进制、十六进制
binary = bin(number)   # 二进制
octal = oct(number)    # 八进制
hexa = hex(number)     # 十六进制

print(f"Binary: {binary}, Octal: {octal}, Hexadecimal: {hexa}")  # Binary: 0b101010, Octal: 0o52, Hexadecimal: 0x2a
  • math/cmath模块: 提供一些方便的数学运算的函数. math是常规数学运算; cmath是复数运算;
函数功能语法参数说明返回值说明示例
sqrt(x)返回 x 的平方根math.sqrt(x)x: 非负实数返回 x 的平方根math.sqrt(16) 输出: 4.0
factorial(x)返回 x 的阶乘math.factorial(x)x: 非负整数返回 x 的阶乘math.factorial(5) 输出: 120
sin(x), cos(x), tan(x)三角函数math.sin(x)x: 弧度返回对应的三角函数值math.sin(math.pi / 2) 输出: 1.0
函数功能语法参数说明返回值说明示例
sqrt(x)返回复数 x 的平方根cmath.sqrt(x)x: 复数返回复数 x 的平方根cmath.sqrt(1 + 1j) 输出: (1.09868411346781+0.4550898605622279j)
phase(x)返回复数 x 的相位角(弧度)cmath.phase(x)x: 复数返回复数的相位角cmath.phase(1 + 1j) 输出: 0.7853981633974483
polar(x)返回复数 x 的极坐标表示cmath.polar(x)x: 复数返回复数的模和相位(极坐标)cmath.polar(1 + 1j) 输出: (1.4142135623730951, 0.7853981633974483)
  • 随机数random模块
函数功能语法参数说明返回值说明示例
random()生成一个 [0.0, 1.0) 范围的随机浮点数random.random()无参数返回一个浮动值,范围 [0.0, 1.0)random.random() 输出: 0.5377851432190762
randint(a, b)生成一个 [a, b] 范围的随机整数random.randint(a, b)a, b: 整数返回一个整数,范围为 [a, b]random.randint(1, 10) 输出: 7
choice(sequence)从序列中随机选择一个元素random.choice(sequence)sequence: 可迭代对象从序列中返回一个随机元素random.choice(['apple', 'banana', 'cherry']) 输出: 'banana'
shuffle(sequence)打乱序列中的元素random.shuffle(sequence)sequence: 可迭代对象将序列中的元素打乱random.shuffle(['apple', 'banana', 'cherry']) 输出: ['cherry', 'apple', 'banana']

4. 条件和循环

4.1 缩进和悬挂else

  • 在C语言中,下面这段代码
if (x > 0)
	if (y > 0)
		printf("x > 0 and y > 0\n");
else
            printf("x < = 0");
  • 在C语言中, 如果不使用{ }来标明代码块, else会和最近的if匹配. 就意味着上面的else, 执行的逻辑其实是 y <= 0 . 在Python中, 就需要使用不同级别的缩进来标明, 这个else和哪个if是配对的.
x = 0
y = 0
if x > 0:
    if y > 0:
        print('x and y > 0')
else: # 和 x > 0 匹配
    print('x <= 0')
x = 0
y = 0
if x > 0:
    if y > 0:
        print('x and y > 0')
    else: # 和 y > 0 匹配
        print('x > 0 and y <= 0')
    

4.2 条件表达式

  • Python中并没有 ? : 这样的三目运算符, 理由是Python设计者觉得这个玩意太丑 T_T
  • 取而代之的是, 一种叫做条件表达式的东西.
x, y, smaller = 3, 4, 0  
if x < y :
    smaller = x
else:
    smaller = y

# 用条件表达式:
smaller = x if x < y else y

4.3 和循环搭配的else

  • else不光可以和if搭伙, 还能和while, for搭伙
# for item in iterable:
#     # 循环体
#     if condition:
#         break
# else:
#     # 循环正常结束后执行


nums = [1,2,3,4,5]
target = 6
for i in nums:
    if i == target:
        print(f"index = {i}")
        break
else:
    print(f"none  index = {i}")
# while condition:
#     # 循环体
#     if condition_to_break:
#         break
# else:
#     # 循环正常结束后执行


# 示例: 使用 while 循环查找一个数字
counter = 0
target = 5

while counter < 10:
    counter += 1
    if counter == target:
        print(f"找到了目标 {target}")
        break
else:
    print(f"没有找到目标 {target}")
  • 注意, 和循环搭配的else子句, 只会在循环条件不满足的时候才会执行(对于for来说就是整个序列遍历完成). 如果循环中途break了, 仍然会跳过else

5. 函数和可调用对象

5.1 函数定义和调用

  • 使用def关键字定义一个函数
  • 函数的定义只是创建了一个函数, 并没有执行函数体中的代码. 要在真正进行函数调用时, 才执行函数体中的代码.
# 函数定义
def Func():
    print("Func()")
    
# 函数调用
Func()
  • 函数的定义也可以放在其他函数内部~. 但是这样函数的作用域也就只是在函数内部才有效了
def Func1():
    def Func2():
        print('Hello')
    Func2()

Func2()
# Traceback (most recent call last):
#   File "z:\gitee-study\Python\study\main.py", line 6, in <module>
#     Func2()
# NameError: name 'Func2' is not defined

5.2 函数的参数

  • 函数定义时, 可以在 () 中填入这个函数都需要接受哪些参数. 注意, 此处不需要指定参数类型
  • Python是动态类型语言, 所以在你写下代码的时候, 解释器并不知道参数的类型是什么. 而是在运行时(调用 函数的时候)函数才知道了类型
def Func(x):
    print(x)

Func(1) # 1
Func("Hello") # Hello
Func([1,2,3]) # [1, 2, 3]
  • 只要传入的参数, 能够支持函数体中的各种操作就可以了. 否则就会运行时报错.
def Add(x,y):
    return x + y
print(Add(1,2)) # 3
print(Add('Hello',"world")) # Helloworld
print(Add([1,2,3],[4,5,6])) # [1,2,3,4,5,6]
print(Add(1,"Hello")) # error
  • 定义函数时, 支持给函数指定默认参数. 这样如果调用函数时不显式的指定参数, 就会使用默认参数作为参数值.
  • 默认参数是非常有用的, 尤其是一些库的接口, 可能需要传入很多参数, 那么默认参数就可以帮我们减轻使用负担.
  • 对于多个默认参数的函数, 可以按照顺序给函数的某几个参数进行传参
def Func(x = 10,y = 20, z = 30):
    print(x,y,z)

Func() # 10  20 30
Func(100) # 100 20 30 

5.3 关键字参数

  • 当我们有多个默认参数, 同时又只想传其中的某几个的时候, 还可以使用关键字参数的方式进行传参.
  • 例如内建函数 sorted (用来给序列进行排序), 函数原型为:
sorted(iterable[, cmp[, key[, reverse]]])
  • 函数有四个参数. 第一个参数表示传入一个可迭代的对象(比如列表, 字符串, 字典等); 剩余三个参数都具备默认参数, 可以不传.
a = [1,4,3,6]
print(sorted(a)) # [1, 3, 4, 6]
  • 对于这几个默认参数, 可以通过现实的指定关键字, 来说明接下来这个参数是传给哪个参数.
  • sorted可以支持自定制排序规则
# 1. 逆序排序 
a = [1,4,3,6]
print(sorted(a,reverse = True)) # [6, 4, 3, 1]

# 2. 按元素的绝对值排序    
a = [1,3,4,2]
print(sorted(a,key = abs))
# 3. 按字符串长度
a = ['aa','bbbb','c']
print(sorted(a,key = len))
  • 和其他编程语言不同, Python的函数形参, 变量名不是随便写的. 尤其是一个默认参数的时候, 形参名可能会随时被拎出来, 作为关键字参数协助函数调用时传入实参.

5.4 参数组

  • 还可以将一个元组或者字典, 作为参数组, 来传给函数. 这样就可以帮助我们实现 "可变长参数"
  • 通过将参数名前加一个*号, * 之后的内容表示是一个元组.
def Log(prefix,*data):

    print(prefix)
    # 提取参数 (遍历数组即可)
    for i in range(0,len(data)):
        print(data[i])
    # 提取参数
    print(prefix + '\t'.join(data))
Log('[INFO]',"hello","world")
  • 通过在参数名前加两个星号 **, 星号后面的部分表示传入的参数是一个字典. 这时候调用函数就可以按照关键字参数的方式传参了.
def Log(prefix, **data):
    print(prefix +'\t'.join(data.values())) #[INFO]127.0.0.1 80      1234

Log('[INFO]', ip = '127.0.0.1', port = '80', userid = '1234')

5.5 函数重载

  • 如果Python中有两个相同名字的函数,但是参数不同,这种在C++中构成函数重载的情况.在python中并不会构成重载
def Func():
    print("1")
def Func(x = 1):
    print("2")

Func() # 2
  • 因为在Python中,函数也是对象. 代码中定义两个相同名字的函数, 这样的操作类似于
a = 1 
a = 2 
print(a) # 2
  • 相当于是对Func这个变量重新进行了绑定
  • Python不支持重载归根结底是为了同一个函数, 能够适应几种不同类型的输入数据.
  • 重载的关键词有两个: 不同参数类型; 不同参数数目.
  • Python的变量类型是在运行时检查的, 同一个函数, 就已经可以处理不同类型的参数了.
  • 通过参数组和默认参数, 解决处理不同数目的参数的能力.
  • 通过关键字参数, 也极大的加强了传参的灵活性, 而不比像C++那样只能按顺序传参.
  • 所以Python完全没有任何必要再去支持 "重载" 这样的语法了. 包括后续出现的一系列动态类型语言, 都不再支持重载了.

5.6 函数返回值

  • 通过return关键字返回一个对象.
  • 如果没有return 语句 ,则返回None对象
  • 可以同时返回N个对象, 通过分割(本质上是在返回一个元组)
def Func():
    print("Hello World")

print(Func())
# 输出:
# Hello World
# None

5.7 函数也可以做为函数的参数

  • 函数也是一个对象, 因此可以作为其他函数的参数
  • 以sorted函数为例, 给一个序列进行排序. 这个函数可以支持自定制比较规则.
a = [1,3,4,2]
print(sorted(a)) # [1,2,3,4]

from functools import cmp_to_key
a = [1,-3,4,2]
# 自定义比较规则
def Cmp(x,y):
    if abs(x) < abs(y):
        return -1
    elif abs(x) > abs(y):
        return 1
    else:
        return 0
    
print(sorted(a,key=cmp_to_key(Cmp))) # [1, 2, -3, 4]