提供一种简介的方式来创建列表。通常用于对序列中共的每个元素应用一个操作,或者从一个序列中共筛选出符合特定条件的元素子集
列表推导式比传统的的循环语句更加简介,并且很多情况下也更便于阅读。
列表名 = [ expression for item in list if condition ]
# expression是对应每个元素应用的操作
# item是从列表获取的值
# condition是可选的筛选条件
1、将一个数值的列表的每个元素的都平方
numbers = [1, 2, 3, 4, 5]
squared = [n ** 2 for n in numbers]
2、筛选出一个列表中的偶数
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9]
evens = [n for n in numbers if n % 2 == 0]
3、将一个字符串列表中的所有字符转换成大写
words = ['hello', 'world', 'python', 'is', 'awesome']
upper_words = [word.upper() for word in words]
4、使用两个列表创建一个坐标列表
x_values = [1, 2, 3]
y_values = [4, 5, 6]
coordinates = [(x, y) for x in x_values for y in y_values]
5、使用列表推导式结合条件表达式创建包含条件结果的列表
numbers = [1, 2, 3, 4, 5]
odd_even = ['even' if n % 2 == 0 else 'odd' for n in numbers]
组包(packing)和拆包(unpacking)是指在没有明确指定变量数量的情况下,对多个值进行打包成一个元组(或者其它集合类型),或者将一个集合类型的对象解开成单独的变量。
组包(Packing)示例:
当我们将多个值赋给一个单独的变量时,Python 会自动将这些值组包成一个元组。
#组包示例
a = 1,2,3 #1,2,3被打包成元组赋给a
print(a) #输出:(1,2,3)
拆包(Unpacking)示例:
当我们有一个集合类型的对象(比如元组或列表)并且我们想要将其内部的元素赋给多个单独的变量时,我们可以使用拆包。
# 拆包例子
a, b, c = (1, 2, 3) # 元组拆包,元组中的值赋给了三个单独的变量
print(a) # 输出: 1
print(b) # 输出: 2
print(c) # 输出: 3
在上面的例子中,元组 (1, 2, 3)
被拆开,其内部的值分别赋给了变量 a
, b
, 和 c
。
拆包常用于函数返回多个值时,例如:
def get_point():
return 10, 20 # 返回一个元组(10, 20)
x, y = get_point() # 拆包
print(x) # 10
print(y) # 20
还有一种扩展的拆包语法,可以用于不确定元素数量的情况,它使用星号 *
来处理多余的元素:
a, *b, c = (1, 2, 3, 4, 5) # b会捕获中间的所有值
print(a) # 输出: 1
print(b) # 输出: [2, 3, 4]
print(c) # 输出: 5
在这个例子中, a
被赋值为第一个元素 1
,c
被赋值为最后一个元素 5
,而 b
是一个列表,包含了所有中间的元素 [2, 3, 4]。
在Python中,可以通过返回一个元组来实现函数返回多个值。当你从一个函数返回多个值时,Python会自动将这些值打包成一个元组。调用函数的地方可以直接拆包这个元组来获取多个返回值。
例如:
# 定义一个函数,它返回三个值
def get_user_info():
name = "Alice"
age = 25
city = "New York"
return name, age, city # 返回一个元组,包含三个值
# 调用函数并拆包返回的元组
user_name, user_age, user_city = get_user_info()
# 打印结果
print("Name:", user_name) # 输出: Name: Alice
print("Age:", user_age) # 输出: Age: 25
print("City:", user_city) # 输出: City: New York
在上面的代码中,get_user_info
函数返回三个值:name
, age
, 和 city
。当这个函数被调用时,返回的三个值被打包成一个元组。然后,我们通过拆包将这个元组中的值赋给了三个不同的变量:user_name
, user_age
, user_city
。
这种方法的好处是代码简洁明了,可以轻松地从函数中返回多个值,并在调用端简洁地获取这些值。
嵌套调用是指,在一个被调用函数体内又调用了另外一个函数
例如:
# 定义三个函数
def func_a():
print('Func A Start...')
func_b()
print('Func A Stop...')
def func_b():
print('Func B Start...')
func_c()
print('Func B Stop...')
def func_c():
print('Func C Start...')
print('Func C Stop...')
# 执行函数调用
func_a()
#输出:
#Func A Start...
#Func B Start...
#Func C Start...
#Func C Stop...
#Func B Stop...
#Func A Stop...
#代码执行顺序a-b-c-b-a
递归调用指的是函数在函数体内又调用自己的情况,递归函数需要满足两个情况:
1.基准情形:递归调用必须有一个或多个基准情形,即在不进行递归调用的情况下可以直接解决的简单情形(就是这个问题最终不再递归能解决)
2.递归步骤:递归调用必须逐步减少问题的规模,使问题主键基于基准情形(越调越简单,最终能解决)
例子,计算阶乘:
# 定义递归函数计算阶乘
def factorial(n):
# 基准情形:当n等于0时,阶乘结果是1
if n == 0:
return 1
# 递归步骤:n的阶乘是n乘以(n-1)的阶乘
else:
return n * factorial(n - 1)
# 调用递归函数
result = factorial(5) # 计算5的阶乘
print(result) # 输出: 120
在上面的代码中,factorial
函数递归地调用自己。我们知道,0的阶乘是1,所以这是我们的基准情形。否则,我们通过计算 n * factorial(n - 1)
来递归调用函数,并且每次调用都会减小 n
的值,直到它达到基准情形。
递归函数通常非常简洁,但需要注意避免无限递归和栈溢出的问题,在Python中,默认的最大递归深度是1000(这个值可以通过 sys.setrecursionlimit
来修改)。因此,对于大规模的问题,通常需要寻找非递归的解决方案或优化递归算法(例如使用尾调用优化或动态规划)。
在Python中,变量的作用域决定了在哪里可以访问和修改这个变量的值。根据作用域的不同,变量可以被分类为全局变量或局部变量。
全局变量:
global
?关键字声明。局部变量:
下面是一个展示全局变量和局部变量的例子:
# 定义全局变量
global_variable = "global variable"
# 定义一个函数,打印一个局部变量和全局变量
def my_function():
# 定义局部变量
local_variable = "local variable"
# 打印局部变量
print(local_variable)
# 打印全局变量
print(global_variable)
# 调用函数,观察变量打印结果
my_function()
# 尝试打印局部变量(会发生错误,因为它是局部的)
# print(local_variable) # 这行代码会引发错误,因为local_variable是函数内的局部变量
# 打印全局变量
print(global_variable) # 这是合法的,因为global_variable是全局变量
如果直接print(local_varible)
在这个例子中,global_variable
是一个全局变量,在函数 my_function
外部定义。local_variable
是在函数 my_function
内部定义的局部变量。在函数内部,我们可以访问并打印两个变量。但是,一旦我们尝试在函数外部访问 local_variable
,将发生错误,因为它已经超出了其作用域。
如果你想在函数内修改全局变量,你需要使用 global
关键字。下面是一个例子:
# 定义全局变量
counter = 0
# 定义一个函数,修改全局变量
def increment_counter():
global counter # 声明counter为全局变量
counter += 1 # 增加counter的值
# 调用函数
increment_counter()
# 打印全局变量,观察其是否被修改
print(counter) # 输出: 1
在这个例子中,我们在函数 increment_counter
里使用 global counter
告诉Python counter
是一个全局变量,这样我们就可以在函数内部递增它的值。如果没有 global
关键字,将会在函数内部创建一个名为 counter
的新局部变量,而不是修改全局变量 counter
。
NOTES:
函数变量查找顺序遵循LEGB规则
Local -> EnClosed -> Global -> Buildins 本地 -> 闭包 -> 全局 -> 内建
# 全局变量
num = 0
c_list = []
# 定义一个用来上传数据的函数
def upoad_data():
# 如果想修改全局变量,需要声明
global num
num = 100
print(num)
c_list.append(1)
# 定义一个用下载数据的函数
def download_data():
print(num,c_list)
# 测试
download_data()
upoad_data()
download_data()
#输出结果
# 0 []
# 100
# 100 [1]
在Python中,引用是对对象的间接访问。当你创建一个变量,Python实际上会在内存中创建一个对象,并让变量引用(指向)那个对象。当你将一个变量赋值给另一个变量时,新变量将引用同一个对象,而不是创建一个新对象(跟文件的快捷方式一个原理,文件只有一个,快捷方式很多)。
这里有一个简单的例子来说明引用的概念:
# 创建一个列表对象,并让变量a引用它
a = [1, 2, 3]
# 让变量b也引用同一个列表对象
b = a
# 修改列表中的一个元素
a[0] = 10
# 打印两个变量
print(a) # 输出: [10, 2, 3]
print(b) # 输出: [10, 2, 3],因为b和a引用的是同一个对象
# 通过 id() 函数可以得到数据在内存中的地址
print(id(a))
print(id(b))
# 输出结果如下:
# [10, 2, 3]
# [10, 2, 3]
# 2153874264192
# 2153874264192
在这个例子中,a
是一个列表对象的引用。当我们将 a
赋值给 b
时,b
也成为了同一个列表对象的引用。因此,当我们通过 a
修改列表中的元素时,通过 b
打印列表也会显示修改后的结果。
Python中的引用特别重要,因为它们影响了如何传递参数到函数中,以及函数如何影响这些参数。Python中的参数传递是通过“传对象引用”完成的,这意味着函数中的形参变成了实参对象的一个新的引用,所以如果你在函数内部修改了这个对象(如列表、字典等可变类型),原始对象也会受到影响。但如果你在函数里重新绑定了形参到一个新对象,原始对象不会改变。
下面是一个例子来说明这一点:
def append_to_list(lst):
# 这里修改的是lst引用的对象,所以原始对象也会改变
lst.append(4)
def reassign_list(lst):
# 这里将lst引用到了一个新的对象,原始对象不会改变
lst = [4, 5, 6]
# 创建一个列表对象
my_list = [1, 2, 3]
# 传递引用到函数,修改列表
append_to_list(my_list)
print(my_list) # 输出: [1, 2, 3, 4]
# 传递引用到函数,尝试重新赋值列表
reassign_list(my_list)
print(my_list) # 输出: [1, 2, 3, 4],因为重新赋值没有影响原始对象