python基础

1深拷贝和浅拷贝

在浅拷贝时,拷贝出来的新对象的地址和原对象是不一样的但是新对象里面的可变元素(如列表)的地址和原对象里的可变元素的地址是相同的

在深拷贝时,所以地址都不一样

import copy
a=[1,2,3,4,5,['a','b']]
#赋值,传对象的引用
b=a
#对象拷贝,浅拷贝
c=copy.copy(a)
#对象拷贝,深拷贝
d=copy.deepcopy(a)
print("id(a)=",id(a))
print("id(b)=",id(b))
print("id(c)=",id(c))
print("id(d)=",id(d))

 可见a,b的地址是一样的,c,d的地址改变 

id(a)= 2380841993288
id(b)= 2380841993288
id(c)= 2380842008392
id(d)= 2380842007368

print("id(a)=",id(a[5]))
print("id(b)=",id(b[5]))
print("id(c)=",id(c[5]))
print("id(d)=",id(d[5]))

id(a)= 2201358550152
id(b)= 2201358550152
id(c)= 2201358550152
id(d)= 2201358578888

但是:a[5]的地址a,b,c是一样的只有d深拷贝不一样,所以要在a,b,c中修改a[5]或者b[5]或者d[5]都会跟着变

也可以这样理解:

深拷贝就是完全跟以前就没有任何关系了,原来的对象怎么改都不会影响当前对象

浅拷贝,原对象的list元素(list是可变对象)改变的话会改变当前对象,如果当前对象中list元素改变了,也同样会影响原对象。

浅拷贝就是藕断丝连

深拷贝就是离婚了

2.生成器

通过列表生成式,我们可以直接创建一个列表。但是,受到内存限制,列表容量肯定是有限的。而且,创建一个包含100万个元素的列表,

不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。

所以,如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的list,

从而节省大量的空间。在Python中,这种一边循环一边计算的机制,称为生成器:generator。

要创建一个generator,有很多种方法。第一种方法很简单,只要把一个列表生成式的[]改成(),就创建了一个generator:

>>> L = [x * x for x in range(10)]
>>> L
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
>>> g = (x * x for x in range(10))
>>> g
<generator object <genexpr> at 0x1022ef630>

创建Lg的区别仅在于最外层的[]()L是一个list,而g是一个generator。

我们可以直接打印出list的每一个元素,但我们怎么打印出generator的每一个元素呢?

如果要一个一个打印出来,可以通过next()函数获得generator的下一个返回值:

>>> next(g)
0
>>> next(g)
1
>>> next(g)
4
>>> next(g)
9
>>> next(g)
16
>>> next(g)
25
>>> next(g)
36
>>> next(g)
49
>>> next(g)
64
>>> next(g)
81
>>> next(g)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

generator保存的是算法,每次调用next(g),就计算出g的下一个元素的值,直到计算到最后一个元素,没有更多的元素时,抛出StopIteration的错误。

当然,上面这种不断调用next(g)实在是太变态了,正确的方法是使用for循环,因为generator也是可迭代对象:

>>> g = (x * x for x in range(10))
>>> for n in g:
...     print(n)
... 
0
1
4
9
16
25
36
49
64
81

 

所以,我们创建了一个generator后,基本上永远不会调用next(),而是通过for循环来迭代它,并且不需要关心StopIteration的错误。

generator非常强大。如果推算的算法比较复杂,用类似列表生成式的for循环无法实现的时候,还可以用函数来实现。

比如,著名的斐波拉契数列(Fibonacci),除第一个和第二个数外,任意一个数都可由前两个数相加得到:

1, 1, 2, 3, 5, 8, 13, 21, 34, ...

斐波拉契数列用列表生成式写不出来,但是,用函数把它打印出来却很容易:

def fib(max):
    n, a, b = 0, 0, 1
    while n < max:
        print(b)
        a, b = b, a + b
        n = n + 1
    return 'done'

 上面的函数可以输出斐波那契数列的前N个数:

>>> fib(6)
1
1
2
3
5
8
'done'

仔细观察,可以看出,fib函数实际上是定义了斐波拉契数列的推算规则,可以从第一个元素开始,推算出后续任意的元素,这种逻辑其实非常类似generator。

也就是说,上面的函数和generator仅一步之遥。要把fib函数变成generator,只需要把print(b)改为yield b就可以了:

def fib(max):
    n, a, b = 0, 0, 1
    while n < max:
        yield b
        a, b = b, a + b
        n = n + 1
    return 'done'

 这就是定义generator的另一种方法。如果一个函数定义中包含yield关键字,那么这个函数就不再是一个普通函数,而是一个generator: 

>>> f = fib(6)
>>> f
<generator object fib at 0x104feaaa0>

 

这里,最难理解的就是generator和函数的执行流程不一样。函数是顺序执行,遇到return语句或者最后一行函数语句就返回。而变成generator的函数,在每次调用next()的时候执行,遇到yield语句返回,再次执行时从上次返回的yield语句处继续执行。

举个简单的例子,定义一个generator,依次返回数字1,3,5:

def odd():
    print('step 1')
    yield 1
    print('step 2')
    yield(3)
    print('step 3')
    yield(5)

 调用该generator时,首先要生成一个generator对象,然后用next()函数不断获得下一个返回值: 

 

>>> o = odd()
>>> next(o)
step 1
1
>>> next(o)
step 2
3
>>> next(o)
step 3
5
>>> next(o)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

 同样的,把函数改成generator后,我们基本上从来不会用next()来获取下一个返回值,而是直接使用for循环来迭代: 

>>> for n in fib(6):
...     print(n)
...
1
1
2
3
5
8
但是用for循环调用generator时,发现拿不到generator的return语句的返回值。如果想要拿到返回值,必须捕获StopIteration错误,返回值包含在StopIterationvalue中:
>>> g = fib(6)
>>> while True:
...     try:
...         x = next(g)
...         print('g:', x)
...     except StopIteration as e:
...         print('Generator return value:', e.value)
...         break
...
g: 1
g: 1
g: 2
g: 3
g: 5
g: 8
Generator return value: done

 

3.列表操作:

(1).列表整合
list_coor= [['484', '518', '15', '12'], ['505', '514', '24', '23']]

d = [[int(r[col]) for r in list_coor] for col in range(len(list_coor[0]))]

print(d)
#[[484, 505], [518, 514], [15, 24], [12, 23]]
(2)对序列中的数据对象的引用的重复
myList = [1,2,3,4]
A = [myList]*3
print(A)
[[1, 2, 3, 4], [1, 2, 3, 4], [1, 2, 3, 4]]

 myList[2]=45
print(A)
[[1, 2, 45, 4], [1, 2, 45, 4], [1, 2, 45, 4]]

  (3)range函数

>>> range(10)
range(0, 10)
>>> list(range(10))
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

>>> range(5,10)
range(5, 10)
>>> list(range(5,10))
[5, 6, 7, 8, 9]

>>> list(range(5,10,2))
[5, 7, 9]

>>> list(range(10,1,-1))
[10, 9, 8, 7, 6, 5, 4, 3, 2]

  (4) 用ZIP处理列表

假设要合并相同长度的列表并打印结果。同样有一种更通用的方式,即用zip()函数获得想要的结果,代码如下:

countries= ['France', 'Germany', 'Canada']
capitals = ['Paris', 'Berlin', 'Ottawa']
for country, capital in zip(countries,capitals):
    print(country, capital)
France Paris
Germany Berlin
Canada Ottawa

  (5)使用itertools

Python的itertools模块是用于处理迭代器的工具集合。Itertools包含多种工具,用于生成输入数据的可迭代结果。
这里以itertools.combinations()为例。itertools.combinations()用于构建组合。这些是输入量的可能组合项。
import itertools
friends = ['Team 1', 'Team 2', 'Team 3', 'Team 4']
list(itertools.combinations(friends, r=2)) 
# [('Team 1', 'Team 2'),      ('Team 1', 'Team 3'),  ('Team 1', 'Team 4'),  ('Team 2', 'Team 3'),  ('Team 2', 'Team 4'),  ('Team 3', 'Team 4')]

  



正则化匹配中括号与数字
regex = re.findall('\[\d+\]',line)

posted @ 2019-12-06 15:48  奥布莱恩  阅读(150)  评论(0编辑  收藏  举报