浅拷贝(Shallow Copy)创建一个新的对象,该对象的内容是原始对象的引用。这意味着新对象与原始对象共享相同的内存地址,因此对于可变对象来说,如果修改了其中一个对象,另一个对象也会受到影响。浅拷贝通常使用copy
模块的copy()
函数或者对象的copy()
方法来完成。
下面是一个浅拷贝的示例:
import copy original_list = [1, 2, [3, 4]] copied_list = copy.copy(original_list) print(original_list) # [1, 2, [3, 4]] print(copied_list) # [1, 2, [3, 4]] # 修改原始列表中的元素 original_list[0] = 10 original_list[2][0] = 30 print(original_list) # [10, 2, [30, 4]] print(copied_list) # [1, 2, [30, 4]]
在上面的示例中,copy.copy()
函数创建了original_list
的浅拷贝copied_list
。当我们修改original_list
时,copied_list
中的嵌套列表也会受到影响。
深拷贝(Deep Copy)创建一个新的对象,该对象的内容是原始对象及其所有嵌套对象的副本。这意味着新对象在内存中是完全独立的,对其中一个对象的修改不会影响另一个对象。深拷贝通常使用copy
模块的deepcopy()
函数或者对象的deepcopy()
方法来完成。
下面是一个深拷贝的示例:
import copy original_list = [1, 2, [3, 4]] deep_copied_list = copy.deepcopy(original_list) print(original_list) # [1, 2, [3, 4]] print(deep_copied_list) # [1, 2, [3, 4]] # 修改原始列表中的元素 original_list[0] = 10 original_list[2][0] = 30 print(original_list) # [10, 2, [30, 4]] print(deep_copied_list) # [1, 2, [3, 4]]
在上面的示例中,copy.deepcopy()
函数创建了original_list
的深拷贝deep_copied_list
。即使我们修改original_list
,deep_copied_list
中的嵌套列表也不会受到影响。
需要注意的是,深拷贝可能会比浅拷贝更耗费时间和内存,因为它需要递归地复制所有嵌套对象。因此,在处理大型对象或嵌套层级很深的对象时,需要谨慎使用深拷贝。
总结起来,浅拷贝创建一个新对象,该对象与原始对象共享部分内存,而深拷贝创建一个完全独立的新对象,它复制了原始对象及其所有嵌套对象的内容。根据需求选择适当的拷贝方式可以帮助我们正确地处理对象并避免意外的副作用。
在Python中,is
和==
是用于比较对象的运算符,它们具有不同的功能和用途。
is
运算符用于比较两个对象的身份标识,即它们是否指向同一个内存地址。如果两个对象具有相同的身份标识,即它们是同一个对象,那么is
运算符返回True
;否则,返回False
。
==
运算符用于比较两个对象的值是否相等。它会比较两个对象的内容,而不关心它们是否指向同一个内存地址。如果两个对象的值相等,==
运算符返回True
;否则,返回False
。
下面是一个示例,展示了is
和==
运算符的区别:
list1 = [1, 2, 3] list2 = [1, 2, 3] list3 = list1 print(list1 is list2) # False,list1和list2不是同一个对象 print(list1 is list3) # True,list1和list3是同一个对象 print(list1 == list2) # True,list1和list2的值相等
在上面的示例中,list1
和list2
虽然具有相同的值,但它们是不同的对象,因此list1 is list2
返回False
。另一方面,list1
和list3
指向同一个对象,因此list1 is list3
返回True
。而list1 == list2
返回True
,因为它们的值相等。
需要注意的是,对于简单的值类型(如整数、字符串等),is
和==
的结果通常是一致的。但对于可变对象(如列表、字典等),is
和==
的结果可能不同,因为可变对象的身份标识可能相同,但值不同。
总结起来,is
运算符用于比较两个对象的身份标识,即它们是否指向同一个内存地址;而==
运算符用于比较两个对象的值是否相等。在编写代码时,需要根据具体的需求选择适当的运算符。如果要比较对象的值,应使用==
运算符;如果要比较对象的身份标识,应使用is
运算符。
**闭包(Closure)**是指在一个函数内部定义的函数,并且内部函数可以访问外部函数的变量。闭包可以捕获和保持外部函数的状态,即使外部函数已经执行完毕,内部函数仍然可以使用那些被捕获的变量。闭包在需要保持某些状态或者提供数据隐藏时非常有用。
下面是一个闭包的例子:
def outer_function(x): def inner_function(y): return x + y return inner_function closure = outer_function(10) result = closure(5) print(result) # 输出 15
在上面的例子中,outer_function
是外部函数,它接受一个参数x
并返回一个内部函数inner_function
。内部函数inner_function
可以访问外部函数outer_function
的参数x
,即使在outer_function
执行完毕后仍然有效。
**装饰器(Decorator)**是一种特殊的闭包,用于修改或增强函数的功能而不修改函数本身的定义。装饰器通常用于添加额外的代码,例如日志记录、性能分析、输入验证等。装饰器可以在不修改原函数代码的情况下,对函数进行包装和扩展。
下面是一个简单的装饰器的例子:
def logger(func): def wrapper(*args, **kwargs): print(f"Calling function: {func.__name__}") result = func(*args, **kwargs) print(f"Function {func.__name__} executed.") return result return wrapper @logger def add(a, b): return a + b result = add(2, 3) print(result) # 输出 5
在上面的例子中,logger
是一个装饰器函数,它接受一个函数作为参数,并返回一个新的函数wrapper
。wrapper
函数在调用被装饰的函数之前和之后打印日志信息。
通过使用@logger
语法,我们将装饰器应用到add
函数上。当调用add
函数时,实际上是调用了被装饰后的wrapper
函数,从而实现了日志记录的功能增强。
实际应用中,闭包和装饰器有许多用例。以下是一些示例:
def logger(func): def wrapper(*args, **kwargs): print(f"Calling function: {func.__name__}") print(f"Arguments: {args}, {kwargs}") result = func(*args, **kwargs) print(f"Function {func.__name__} executed.") print(f"Result: {result}") return result return wrapper @logger def add(a, b): return a + b result = add(2, 3)
**不可变对象(Immutable objects)**是指创建后不能被修改的对象。当对不可变对象进行操作时,会创建一个新的对象。以下是一些常见的不可变对象:
以下是不可变对象的一些特点:
**可变对象(Mutable objects)**是可以修改的对象,即可以改变对象的值或状态。以下是一些常见的可变对象:
以下是可变对象的一些特点:
**值传递(Pass-by-Value)**是指将实际参数的值复制一份,传递给函数或赋值给新变量。在这种情况下,函数或新变量操作的是复制后的值,对原始变量的修改不会影响到原始值。
**引用传递(Pass-by-Reference)**是指将实际参数的引用或地址传递给函数或赋值给新变量。在这种情况下,函数或新变量操作的是原始变量所在的内存地址,对变量的修改会直接影响到原始值。
在Python中,参数传递是通过引用传递的方式进行的。也就是说,函数或方法的参数传递的是对象的引用,而不是对象本身的副本。这意味着,如果在函数内部对引用的对象进行修改,会影响到原始对象。
下面是一个示例:
def modify_list(lst): lst.append(4) my_list = [1, 2, 3] modify_list(my_list) print(my_list) # 输出 [1, 2, 3, 4]
在上面的例子中,我们定义了一个modify_list
函数,它接受一个列表作为参数并向列表中添加一个元素。当我们调用modify_list(my_list)
时,实际上是将my_list
的引用传递给modify_list
函数。在函数内部,我们通过引用对列表进行修改,添加了一个新元素。这导致原始列表my_list
也被修改,输出结果为[1, 2, 3, 4]
。
在实际使用中,我们需要根据需求选择值传递还是引用传递。一般来说,如果希望在函数内部修改原始对象,可以使用引用传递。如果希望保持原始对象不变,可以使用值传递或传递对象的副本。
需要注意的是,在Python中,虽然参数传递是通过引用传递的方式,但对于不可变对象(如整数、字符串、元组),因为其值无法修改,所以表现类似于值传递。而对于可变对象(如列表、字典、集合),因为其值可以修改,所以表现类似于引用传递。
值传递例子:
def modify_value(value): value = value + 1 x = 10 modify_value(x) print(x) # 输出 10
在上面的例子中,我们定义了一个modify_value
函数,它接受一个整数参数value
。在函数内部,我们对value
进行加1操作。然而,即使在函数内部对value
进行了修改,原始变量x
的值并没有改变。这是因为整数是不可变对象,函数调用时发生的是值传递,传递的是x
的值的副本,而不是x
本身
最后感谢每一个认真阅读我文章的人,礼尚往来总是要有的,虽然不是什么很值钱的东西,如果你用得到的话可以直接拿走:
行动吧,在路上总比一直观望的要好,未来的你肯定会感谢现在拼搏的自己!如果想学习提升找不到资料,没人答疑解惑时,请及时加入群: 731789136,里面有各种测试开发资料和技术可以一起交流哦。
这些资料,对于【软件测试】的朋友来说应该是最全面最完整的备战仓库,这个仓库也陪伴上万个测试工程师们走过最艰难的路程,希望也能帮助到你!