04Python 深拷贝浅拷贝 函数 递归 集合

Posted 虫虫研究室

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了04Python 深拷贝浅拷贝 函数 递归 集合相关的知识,希望对你有一定的参考价值。

1 深拷贝浅拷贝

1.1 a==b与a is b的区别

  • a == b    比较两个对象的内容是否相等(可以是不同内存空间)
  • a is b  比较a与b是否指向同一个内存地址,也就是a与b的id是否相同
 1 >>> a = 1
 2 >>> b = 1
 3 >>> a == b
 4 True
 5 >>> a is b
 6 True
 7 >>> a = 257
 8 >>> b = 257
 9 >>> a is b
10 False
出于对性能的考虑,Python内部做了很多的优化工作,对于整数对象,Python把一些频繁使用的整数对象缓存起来,保存到一个叫small_ints的链表中,在Python的整个生命周期内,任何需要引用这些整数对象的地方,都不再重新创建新的对象,而是直接引用缓存中的对象。Python把这些可能频繁使用的整数对象规定在范围[-5, 256]之间的小对象放在small_ints中,但凡是需要用些小整数时,就从这里面取,不再去临时创建新的对象。因为257不再小整数范围内,因此尽管a和b的值是一样,但是他们在Python内部却是以两个独立的对象存在的,各自为政,互不干扰。
 1 >>> c = 257
 2 >>> def foo():
 3 ...     a = 257
 4 ...     b = 257
 5 ...     print a is b
 6 ...     print a is c
 7 ...
 8 >>> foo()
 9 True
10 False
Python程序由代码块构成,代码块作为程序的一个最小基本单位来执行。一个模块文件、一个函数体、一个类、交互式命令中的单行代码都叫做一个代码块。在上面这段代码中,由两个代码块构成,c = 257作为一个代码块,函数foo作为另外一个代码块。Python内部为了将性能进一步的提高,凡是在一个代码块中创建的整数对象,如果存在一个值与其相同的对象于该代码块中了,那么就直接引用,否则创建一个新的对象出来。Python出于对性能的考虑,但凡是不可变对象,在同一个代码块中的对象,只有是值相同的对象,就不会重复创建,而是直接引用已经存在的对象。因此,不仅是整数对象,还有字符串对象也遵循同样的原则。所以 a is b就理所当然的返回True了,而c和a不在同一个代码块中,因此在Python内部创建了两个值都是257的对象。

  

  • l2 = l1,相当于l2也指向与l1的内存地址,修改l1的值,l2也会跟着改变
1 l1 = [1,1,1,1,2,3,4,5]
2 l2 = l1 #浅拷贝, l1和l2指向同一个内存地址
3 print(id(l1)) #查看内存地址
4 print(id(l2))
5 for i in l2:
6     if i%2!=0:
7         l1.remove(i) #删除奇数
8 print(l1) #循环删list的时候,会导致下标错位,结果是不对的 

运行结果如下:

42001160
42001160
[1, 1, 2, 4]

1.2 浅拷贝

import copy
l1 = [1,1,1,1,2,3,4,5]
l2 = l1 #浅拷贝, l和l2实际指向同一个内存地址
l3 = l1.copy() #浅拷贝
print(id(l1), id(l2), id(l3))
ll1 = [[1,2,3],[4,5,6]]
ll2 = ll1 #浅拷贝, l和l2实际指向同一个内存地址
ll3 = ll1.copy() #浅拷贝
print(id(ll1), id(ll2), id(ll3))

运行结果如下:

36164360 36164360 36164552
36165704 36165704 36165640

  • 解析

1、b = a: 赋值引用,a 和 b 都指向同一个对象

 

2、b = a.copy(): 浅拷贝, a 和 b 是一个独立的对象,但他们的子对象还是指向统一对象(是引用)。

 

1 import copy
2 a = [1, 2, 3]
3 b = [4, 5, 6]
4 c = [a, b]
5 d = copy.copy(c)
6 print(id(c))
7 print(id(d))
42500296
42500232
1 import copy
2 a = [1, 2, 3]
3 b = [4, 5, 6]
4 c = (a, b)
5 d = copy.copy(c)
6 print(id(c))
7 print(id(d))
35510088
35510088
  • 浅拷贝可变类型(比如列表),第一层的列表也会拷贝
  • 浅拷贝不可变类型(比如元祖),d指向c的内存地址

  

1.3 深拷贝

 1 import copy 2 l = [1,1,1,2,3,4,5] 3 l2 = copy.deepcopy(l)# 深拷贝 

  • 解析

b = copy.deepcopy(a): 深度拷贝, a 和 b 完全拷贝了父对象及其子对象,两者是完全独立的。

 

2 函数

2.1 函数定义 

 1 def db_connect(ip,port=3306): #1.位置参数,必填;2.默认值参数 2 print(ip,port) #函数体 

 1 def my3():
 2     a = 1
 3     b = 2
 4     c = 3
 5     return a,b,c
 6 
 7 b,c,d = my3()
 8 s = my3()
 9 print(b,c,d)
10 print(s)

运行结果:

1 2 3
(1, 2, 3)

2.2 return作用

1、结束函数,只要函数里面遇到return,函数立即结束运行
2、返回函数处理的结果

  • 例:判断一个字符串是否是小数(含正小数和负小数)
 1 def check_float(s):
 2     \'\'\'
 3     这个函数的作用就是判断传入的字符串是否是合法的小数
 4     :param s: 传入一个字符串
 5     :return: True/false
 6     \'\'\'
 7     s = str(s)
 8     if s.count(\'.\')==1:
 9         s_split = s.split(\'.\')
10         left,right = s_split
11         if left.isdigit() and right.isdigit():
12             return True
13         elif left.startswith(\'-\') and left[1:].isdigit() \\
14             and right.isdigit():
15             return True
16     return False #上面的几个条件未满足,则会走到这一步
17 
18 print(check_float(1.3))
19 print(check_float(-1.3))
20 print(check_float(\'01.3\'))
21 print(check_float(\'-1.3\'))
22 print(check_float(\'-a.3\'))
23 print(check_float(\'a.3\'))
24 print(check_float(\'1.3a3\'))
25 print(check_float(\'---.3a3\'))

2.3 可变参数

 1 def send_sms(*args): #可变参数,参数组
 2     #1、不是必传的
 3     #2、它把传入的元素全部都放到了一个元组里面
 4     #3、不限制参数个数
 5     #4、它用在参数比较多的情况下
 6     for p in args:
 7         print(p)
 8 
 9 send_sms()
10 print(\'-----------------------------\')
11 send_sms(1861231231)
12 print(\'-----------------------------\')
13 send_sms(1861231231,1232342,42342342)

运行结果:

-----------------------------
1861231231
-----------------------------
1861231231
1232342
42342342

 

  • 有时候我们传入一个列表,本意是希望将列表中的所有元素都当做参数传递进去,这里直接将[\'a\', \'b\', \'c\']看做一个整体了,怎么办?
  • 其实只需要在调用时前面加一个*号,就能实现将列表中的每个元素传递进去了。
  • 其实不光列表,任何序列类型数据对象,比如字符串、元组都可以通过这种方式将内部元素逐一作为参数,传递给函数。
  • 而字典,则会将所有的key逐一传递进去。

 

2.4 关键字参数

1 def send_sms2(**kwargs):
2     #1、不是必传的
3     #2、不限制参数个数
4     print(kwargs)
5 
6 send_sms2()
7 send_sms2(name=\'xiaohei\',sex=\'nan\')
8 send_sms2(addr=\'北京\',country=\'中国\',c=\'abc\',f=\'kkk\')

运行结果:

{}
{\'name\': \'xiaohei\', \'sex\': \'nan\'}
{\'addr\': \'北京\', \'country\': \'中国\', \'c\': \'abc\', \'f\': \'kkk\'}

2.5 参数顺序

1 def my(name,country=\'China\',*args,**kwargs):
2     #1、位置参数 2、默认值参数 3、可变参数 4、关键字
3     print(name)
4     print(country)
5     print(args)
6     print(kwargs)
7 my(\'xiaojun\',\'Japan\',\'beijing\',\'天通苑\',color=\'红色\',
8    age=32)

运行结果:

xiaojun
Japan
(\'beijing\', \'天通苑\')
{\'color\': \'红色\', \'age\': 32}

2.6 全局变量

 1 name = \'wangcan\'#全局变量
 2 names = []
 3 def get_name():
 4     names.append(\'hahaha\')
 5     name = \'hailong\'
 6     print(\'1、函数里面的name\',name) 
 7 
 8 def get_name2():
 9     global name #声明name是全局变量
10     print(\'2、get_name2\',name)
11 
12 get_name2() 
13 get_name()
14 print(names)
15 print(\'3、函数外面的name\',name) 
#运行结果:
2、get_name2 wangcan
1、函数里面的name hailong
[\'hahaha\']
3、函数外面的name wangcan

  

1 name = \'wangcan\'#全局变量
2 def get_name3():
3     name = \'我是谁\' #不会对全局变量产生作用
4     print(name)
5 get_name3()
6 print(name)
#运行结果:
我是谁
wangcan

2.7 函数调用

1 def db_connect(ip, user, password, db, port):
2     print(ip)
3     print(user)
4     print(password)
5     print(db)
6     print(port)
7 
8 db_connect(user = \'abc\', port= 3306, db = 1, ip = \'234234\', password = \'123456\') #记不住顺序可以这样调用
9 db_connect(\'192\',\'root\', db = 2, password = \'234\', port = 45)#这样也行, 混搭, 但\'192\',\'root\'必须按顺序写在前面

 

3 递归

函数自己调用自己
递归最多999次
效率没有循环高
1 #阶乘 1 × 2 × 3 × ... × n
2 def fact(n):
3     if n==1:
4         return 1
5     return n * fact(n - 1)

 

4 集合

1、天生可以去重
2、集合是无序

  • 集合定义

 1 jihe = set() 

  • 集合例子

 1 l=[1,1,2,2,3,3] 2 res = set(l) 3 print(res) 

#运行结果:集合
{1, 2, 3}

 

4.1 交集、并集、差集、对称差集

 1 xingneng =[\'tanailing\',\'杨帆\',\'liurongxin\',\'小黑\']
 2 zdh = [\'tanailing\',\'杨帆\',\'liurongxin\',\'小军\',\'海龙\']
 3 xingneng = set(xingneng)
 4 zdh = set(zdh)
 5 #取交集
 6 res1 = xingneng.intersection(zdh)#取交集
 7 res2 = xingneng &  zdh   #取交集
 8 #取并集
 9 res3 = xingneng.union(zdh) #取并集,把2个集合合并到一起,然后去重
10 res4 = xingneng | zdh
11 #取差集
12 res5 = xingneng.difference(zdh) #取差集,在a里面有,在b里面没有的
13 res6 = xingneng - zdh #取差集
14 #取对称差集
15 res7 =xingneng.symmetric_difference(zdh)#两个里不重复的值
16 res8 = xingneng ^ zdh
17 
18 print(res1)
19 print(res2)
20 print(res3)
21 print(res4)
22 print(res5)
23 print(res6)
24 print(res7)
25 print(res8)
#运行结果
{\'liurongxin\', \'杨帆\', \'tanailing\'}
{\'liurongxin\', \'杨帆\', \'tanailing\'}
{\'tanailing\', \'liurongxin\', \'小军\', \'海龙\', \'杨帆\', \'小黑\'}
{\'tanailing\', \'liurongxin\', \'小军\', \'海龙\', \'杨帆\', \'小黑\'}
{\'小黑\'}
{\'小黑\'}
{\'海龙\', \'小军\', \'小黑\'}
{\'海龙\', \'小军\', \'小黑\'}

  

4.2 其他方法

1 import string
2 l1 = set(string.ascii_lowercase)
3 l2 = {\'a\',\'b\',\'c\'}
4 print(l2.issubset(l1)) #子集
5 print(l1.issuperset(l2)) #父集
6 print(l1.isdisjoint(l2)) #有交集,返回False, 没有交集,返回True
#运行结果
True
True
False
1 l2 = {\'a\',\'b\',\'c\'}
2 l2.add(\'s\') #
3 print(l2)
4 
5 l2.remove(\'a\') #
6 print(l2)
7 
8 l2.pop()#随机删
9 print(l2)
#运行结果:
{\'c\', \'a\', \'b\', \'s\'}
{\'c\', \'b\', \'s\'}
{\'b\', \'s\'}

 

 

以上是关于04Python 深拷贝浅拷贝 函数 递归 集合的主要内容,如果未能解决你的问题,请参考以下文章

Python中的赋值,浅拷贝和深拷贝的区别

python深拷贝和浅拷贝的区别

Python入门-6面向对象编程:11对象的浅拷贝和深拷贝

python for循环中的浅拷贝

2.深拷贝与浅拷贝的区别

Python基础入门深浅拷贝函数内置函数文件处理三元运算递归