深浅拷贝

----引自 https://www.cnblogs.com/caoj/p/7868844.html

1.深浅拷贝

  在Python中将一个变量的值传递给另外一个变量通常有三种:赋值、浅拷贝、深拷贝

  Python数据类型可氛围基本数据类型包括整型、字符串、布尔及None等,还有一种由基本数据类型作为最基本的元素所组成的像列表、元组、字典等。

  在Python中基本数据类型的赋值、深浅拷贝没有任何意义,都是指向同一块内存地址,也不存在层次问题。

  下面看基本数据类型的深浅拷贝

深浅拷贝
import copy
n1 = 'abc'
n2 = n1
n3 = copy.copy(n1)
n4 = copy.deepcopy(n1)
print(id(n1))    #输出140350336680040
print(id(n2))    #输出140350336680040
print(id(n3))    #输出140350336680040
print(id(n4))    #输出140350336680040
深浅拷贝

  以上代码说明Python的copy模块的copy和deepcopy函数实现了浅拷贝和深拷贝,可以看到,赋值、浅拷贝和深拷贝最后的id(Python内存地址的表达方式)都是一样的

  接下来讨论其他的列表、元组、字典等非基本数据类型对象的赋值、深浅拷贝的区别

  假设字典n1 = {"k1": "abc", "k2": 123, "k3": ["abc", 123]} 

  赋值是将变量的内存赋给另一个变量,让另一个变量指向那个内存地址

深浅拷贝

 

  浅拷贝

  浅拷贝就是在内存中将第一层额外开辟空间进行存放


深浅拷贝

深浅拷贝
n1 = {"k1": "abc", "k2": 123, "k3": ["abc", 123]}   
print(id(n1))    #140350328984328
n3 = copy.copy(n1)
print(id(n3))    #140350328986504可以看n3的内存地址已经和n1不同了


print(id(n1['k3']))    #140350328603976
print(id(n3['k3']))    #140350328603976 字典里的列表还是指向同一个列表
深浅拷贝

  深拷贝

  深拷贝就是在内存中将数据重新创建一份,不仅仅是第一层,第二层、第三层...都会重新创建


 深浅拷贝

 

深浅拷贝
n1 = {"k1": "abc", "k2": 123, "k3": ["abc", 123]}
print(id(n1))    #140350328984328
n3 = copy.deepcopy(n1)

print(id(n1['k3']))    #140350328603976
print(id(n3['k3']))    #140350328604296 #可以看到第二层的列表也拷贝了一份,内存地址已经完全不一样

#注意,这仅局限于非基本数据类型,基本数据类型还是同一个内存地址
深浅拷贝

 

 

2.函数

  函数的定义及调用

  定义一个函数要使用def关键字,依次写出函数名、括号、括号中的参数和冒号:,然后用缩进的代码块写函数体,函数体内可以调用return语句返回结果。

  函数名作为函数的名称,也可以像变量一样进行赋值操作、甚至作为参数进行传递。

  

  函数的参数

  1)位置参数

  这是最常见的定义方式,一个函数可以定义任意个参数,每个参数用逗号分隔,例如:

def Foo1(arg1, arg2):
    print(arg1, arg2)

  用这种方式定义的函数在调用的的时候也必须在函数名后的小括号里提供个数相等的值(实际参数),而且顺序必须相同,也就是说在这种调用中,形参和实参的个数必须一致,而且必须一一对应,也就是说第一个形参对应这第一个实参。例如:

Foo1('abc', 123)
#输出结果 abc 123

  也可以通过如下方式传递参数,而不必考虑顺序问题,但数量无论如何必须一致。

foo3(arg2 = 123, arg1 = 'abc')

 

  2)默认参数

  我们可以给某个参数指定一个默认值,当调用时,如果没有指定那个参数,那个参数就等于默认值

def Foo2(arg1, arg2 = 123):
     print(arg1, arg2)

  调用

深浅拷贝
Foo2('abc')
Foo2('abc', 345)

'''
执行结果 
    abc 123
    abc 345    
注意:定义的时候默认参数必须放到所有位置参数的后面进行定义,否则会报语法错误 
'''
深浅拷贝

  

  3)可变参数

  可变参数就是传入的参数个数是可变的,也可以是0个,例如

def Foo3(*args):
     print(args)

  调用

Foo3(1, 2, 'abc')

#执行结果  (1, 2, 'abc')  可以看到我们传递了三个参数都被Python转化为元祖,保存到args中了,这样我们就可以通过索引对参数记性调用,或者通过for in进行遍历

  

  4)关键字参数

  可变参数在调用过程中会组装成元组,元组只能通过索引进行调用,有时不是很方便,故Python可以通过关键字索引将传入的参数组装成字典

  

深浅拷贝
def Foo4(**kwargs):
    print(kwargs, type(kwargs))

Foo4(k1 = 'abc', k2 = 123)


#执行结果 {'k2': 123, 'k1': 'abc'} <class 'dict'>
#关键字参数允许传入0个或任意个参数名的参数,0个的话就是一个空字典
深浅拷贝

 

 

  参数组合

  在Python中定义函数,可以用必选参数(位置参数)、默认参数、可变参数、关键字参数这几种参数进行组合使用,但是顺序必须是,必选参数、默认参数、可变参数、关键字参数。

深浅拷贝
def Foo5(arg1, arg2='abc', *args, **kwargs):
    print('arg1:', arg1)
    print('arg2:', arg2)
    print('args', args)
    print('kwargs', kwargs)

Foo5(123, 'abc', 456, 'def', k1=123, k2='abc')


'''
执行结果
arg1: 123
arg2: abc
args (456, 'def')
kwargs {'k1': 123, 'k2': 'abc'}

'''
深浅拷贝

 

  lambda匿名函数

  匿名函数就是功能非常简单只需要一行代码就可以实现的,例如,求圆形面积

f = lambda r: 3.14 * r * r
print(f(4)) # 输出 50.24

#r相当于匿名函数的参数,当然也可以有多个参数,不用在写return,表达式就是返回的结果。

  使用匿名函数有个好处,因为函数没有名字,不用担心函数名冲突,此外匿名函数也是一个函数对象,也可以把匿名函数赋值给一个变量,再利用变量调用该函数。

  

  关于函数的return语句

  1)函数可以没有return语句,没有就默认返回None

  2)return语句有点类似循环的break,当函数执行到return语句时候,直接跳出函数的执行

  3)return可以返回多个值,多个值可以用两个变量接收,也可以额一个变量接收

深浅拷贝
def Foo6():
    return 123, 'abc'

res1, res2 =Foo6()
print('res1:', res1)    #res1: 123
print('res2:', res2)    #res2: abc

res = Foo6()
print('res:', res)  #res: (123, 'abc')

#返回多个值就是返回一个元组,使用两个变量接收的时候回将元组的元素与变量一一对应赋给多个变量
深浅拷贝

 

  关于可变参数和关键字参数的传递小技巧

  我们已经知道可变参数和关键字参数分别将传递的参数组装成元组和字典,那么我们同样可以直接将元组、列表和字典直接传递给函数作为参数,传递的时候列表和元组要在变量前面加一个*,字典要在前面加两个*,否则函数还是会把它们当成一个普通的参数传递进行处理

深浅拷贝
def Foo7(*args, **kwargs):
    print(args)
    print(kwargs)

li = [1, 2, 3]
dic = {'k':1, 'k2':2}
Foo7(li, dic)
Foo7(*li, **dic)

'''
执行结果
([1, 2, 3], {'k': 1, 'k2': 2})
{}    #可以看到两个参数都被可变参数接收了,关键字参数啥也没有
(1, 2, 3)
{'k': 1, 'k2': 2}

'''
深浅拷贝

 

 

  Python常用的内置函数


深浅拷贝

上一篇:TypeScript的基本使用(1)


下一篇:leetcode 165. 比较版本号