深浅拷贝
Python中变量储存的是变量的地址,该地址指向储存的数值,对于复杂数据结构来说,比如list其内部存储的是变量的地址, 当一个变量被赋值一个新的值时,其地址会发生变化
str1 = 'a'
str2 = str1
print(id(str1),id(str2))
#4495142000 4495142000
str1 = 'new'
print(id(str1),id(str2))
#4493843512 4495142000
使用copy的原因是说我们在编程的过程中,时常需要备份一个变量,并对其中一个备份进行修改操作,同时我们还想保证原变量的内容不受影响。python中的copy分为 deepcopy和copy
,两者的区别就是前者会完全复制原变量的所有内容,后者只会复制一层,举个例子:
from copy import copy, deepcopy
list1 = [1,2,3,['inner list']]
list1copy = copy(list1)
list1deepcopy = deepcopy(list1)
print(id(list1),id(list1copy))#
#112295998792 112228422600
#list1copy相当于在内存中新开辟出一块地址,只不过其内容与list1内容一样
list1copy[-1].append('new')
print(list1)
#[1, 2, 3, ['inner list', 'new']]
print(list1deepcopy)
#[1, 2, 3, ['inner list']]
#我这里改变了list1copy的内容但是list1的内容也被我改变了,但是list1deepcopy的内容没有被改变
以下情况默认使用浅拷贝
- 当我们使用切片操作的时候相当于使用了浅拷贝
- list数字 [[1]]3
a = [[] for _ in range(3)]
for i in a:
print(id(i))
print()
b = [[]]*3
for i in b:
print(id(i))
'''
112297100744
4518538120
112298066696
112235007304
112235007304
112235007304
'''
迭代器和生成器
python中很多类型的对象都可以被定义为可迭代的,比如基本的容器类型,list,set等等,字符串可以产生它的字符的迭代,字典可以生成它的键的迭代,迭代的机制基于:
- 一个对象obj是可迭代的,可以通过iter(obj)来产生一个迭代器
- 如果一个对象是可迭代的,那他一定包含iter方法,如果只包含iter方法的对象是一个可迭代对象
-一个迭代器必须实现iter和next方法
-python也为实现了len和getitem的类提供了一个自动的迭代器
2.迭代器每次调用next方法都会产生一个后续的元素,如果没有更多的元素,则会抛出StopIteration的异常
比如 list本身不是一个迭代器,但是可以通过iter(list)的方法生成一个迭代器,迭代器不会储存列表的元素,他会储存原始列表的当前索引,该索引指向下一个元素,因此如果一个列表在迭代器被创建之后,在其完成迭代之前,其内部元素被修改了,那么迭代器将会报告修改后的元素。
懒惰计算,python无需立刻构建数据结构去储存迭代列表的所有值,比如调用range(10000)时不是返回一个数字列表,而是返回一个可以迭代的range对象,这样的优势就是不需要在内存中储存10000个值,只当有需要的时候才会调用next方法产生下一个元素,类似的还有dict.keys()/values()/items()
Python中创建迭代器最方便的方式就是通过生成器,生成器语法类似函数,但是没有return,而是用yield来代替,函数在每次运行到yield的时候会暂停,只有当另一个值被请求的时候才会恢复:
def factor(n):
#用传统函数实现一个数的所有因子
rst = []
for i in range(1,n+1):
if n%i == 0:
rst.append(i)
return rst
def factorGen(n):
#用生成器实现一个数的所有因子
for i in range(1,n+1):
if n%i==0:
yield i
def Fib(n):
#用生成器实现斐波那契数列的前n个
start = 0
a = 0
b = 1
while start < n:
yield a
a,b=b,a+b
start+=1
python编码风格:
类:
应该以首字母大写的单数名词,多个单词组合在一起的时候应该每个单词首字母大写
函数:
小写,多个单词组合的时候应该用下划线组合
参数或者变量,
小写,若是一个常量值,应该用大写字母
_变量名:
变量前面带一个_表示,该变量是类的非公有变量,应该只在类内部被调用
变量名:
以双下划线开头的名字一般被认为是私有的或者是是特殊方法
常用测试方法:
输入序列:
1.空
2.只有一个元素
3.所有元素都一样
3.排序/反向排序
Python中的is和==
a is b: 判断a和b是不是同一个类的不同实例,或者说a和b是不是同一个对象的别名
a== b:判断a和b是不是指向同一个对象,如果a和b指向不同的对象,但是这些对象的值认为是等价的,那也是True
python运算符重载
比如a+b实际上是通过a.add(b)来实现重载的,一个比较特殊的用法就是,像 3 * ‘abc’, python根据左操作数的类进行判断,然后调用mul方法,然而如果左边的类没有实现mul 方法,python会用一种特殊的名为rmul的方法来检查右侧的操作数,类似的语法也包括,+,-,/,//,%,**等等
python自动化测试框架-unittest
pass
python中的name == 'main如何理解
举个抽象的例子理解一下,别人叫我的名字的时候就是name=='myname',如果是我自己称呼我自己的时候就是name=='main', 也就是说当我们的python文件直接运行的时候,if name=='main'下面的代码块会被运行,如果我们的python文件被当作是模块的形式导入时,模块中 if name=='main'下面的代码块就不会被运行
python中的slots的作用
slots是一个类变量,变量值可以是列表,元组,可迭代对象或者一个字符串,添加slots 的类的实例只能有固定的实例属性,不允许实例对象添加slots之外的实例属性,当我们知道一个类的属性是固定的时候或者一个类会有多个实例的时候,我们可以用slots 来节省内存,如果我们希望动态的为一个类添加属性,那么可以不适用
通常情况下,python用dict来储存实例,这样做的好处是允许在运行时对实例添加设置变量,但是对一些在编译器就确定属性的类来说这样做会很浪费内存,对此可以使用slots来告诉python不用dict,只分配固定的空间来存储一致的属性
Python基于数组的序列和基于链表的序列的优缺点:
基于数组的序列:访问一个元素的时间复杂度为O(1)
基于链表的序列:对一个元素的增加删除的时间复杂度为O(1)
Python定义一个抽象基类的目的
最近在学习python数据结构和算法的时候,发现书上的例子在写一个树的抽象数据类型和一个带有位置信息的列表抽象数据类型的时候,都先定义了两个抽象基类,之前一直不清楚这么做的目的是什么,查了一下,其实定义一个抽象基类的目的就是定义一些事物共同拥有的属性和方法,举个直接一点的例子就是,猫狗猪羊都属于动物,那么我们可以定一个动物的抽象基类,因为他们都有一些共同的动作,比如吃饭,发呆等等
网友评论