__call__()函数
python中类的实例(对象)可以被当做函数对待.也就是说,我们可以将它们作为输入传递到其他的函数/方法中并调用它们,正如我们调用一个正常的函数那样。而类中的call()函数的意义正在于此.为了将一个类实例当作函数调用,我们需要再类中事先call()方法.假设x是X类的一个实例.调用x.__call__(1,2)等同于调用x(1,2).这个实例本身在这里相当于一个函数.
Python pickle模块学习
Python提供了一个简单的持久化功能.可以将对象以文件的形式存放在磁盘上.
Pickle模块只能在Python中使用,python中几乎所有的数据类型(列表,字典,集合,类等),都可以用pickle来序列化.
pickle序列化以后的数据,可读性差,人一般无法识别
pickle.dump(obj,file[,protocol])
序列化对象,并将结果数据流写入到文件对象中.参数protocol是序列化模式,默认值为0,表示文本的形式的序列化。protocol也可以是1或2,表示二进制的形式序列化
pickle.load(file)
反序列化对象.将文件中的数据解析为一个Python对象.其中要注意的是,在load(file)的时候,要从python中能够找到类的定义,否则会报错.
pickle.clear_memo()
清空pickler的“备忘”,使用pickler实例在序列化对象的时候,它会记住已经被序列化的对象引用,所以对同一对象多次调用dump(obj),pickler不会”傻傻”的去多次序列化
Python collections
collections是Python内建的一个集合模块,提供了许多有用的集合类.
namedtuple
我们知道tuple可以表示一个元组,是不可变的集合,例如一个点的二维坐标就可以这样表示成1
p=(1,2)
但是,看到(1,2),很难看出这个tuple是用来表示一个坐标的
定义一个class又小题大做了,这时,namedtuple就派上了用场:1
2
3
4
5
6
7>>>from collections import namedtuple
>>>Point=namedtuple('Point',['x','y'])
>>>p=Point(1,2)
>>>p.x
1
>>>p.y
2
namedtuple是一个函数,它用来创建一个自定义的tuple对象,并且规定了tuple元素的个数,并可以用属性而不是索引来引用tuple的某个元素.
这样一来,我们的namedtuple可以很方便地定义一种数据类型,它具备tuple的不变性,又可以根据属性来引用,十分方便.
deque
使用list存储数据时,按索引访问元素很快,但是插入和删除元素就很慢了,因为list是线性存储,数据量大的时候,插入和删除效率很低.
deque是为了高效地实现插入和删除操作的上香列表,适合用于队列和栈1
2
3
4
5
6>>>from collections import deque
>>>q=deque(['a','b','c'])
>>>q.append('x')
>>>q.appendleft('y')
>>>q
deque(['y','a','b','c','x'])
deque除了实现list的append和pop外,还支持appendleft()和popleft()
Counter
Counter是一个简单的计数器,例如,统计字符出现的个数1
2
3
4
5
6
7from collections import Counter
c=Counter()
for ch in 'programming':
c[ch]=c[ch]+1
...
c
Counter({'g':2,'m'2,'r':2,'a':1,'i':1,'o':1,'n':1,'p':1})
Counter实际上也是dict的一个子类,上面的结果可以看出,字符’g’,’m’,’r’各出现2次,其他字符出现1次
zip
感觉本文成了python专项贴.
zip的用法
- 解压
传入的data为batch个元组,即batch个(iamges,captions) 对儿
images,captions=zip( data)
表示zip( data)=(batch 个image,batch 个 caption) - 压
data=zip(images,captions)
例子1
2
3
4
5
6
7
8
9>>>a=[1,2,3]
>>>b=[4,5,6]
>>>c=[4,5,6,7,8]
>>>zipped=zip(a,b)
[(1,4),(2,5),(3,6)]
>>>zip(a,c)
[(1,4),(2,5),(3,6)]
>>>zip(*zipped)
[(1,2,3),(4,5,6)]
老生常谈的深拷贝和浅拷贝
https://blog.csdn.net/edogawachia/article/details/79762310
平时写Python用惯了numpy的矩阵类型,只用python自带的list做有关矩阵的(二维数组的)处理的时候碰到各种bug。这里是今日份的bug和解决方案。
问题缘起
在一个程序中,我们希望用list实现一个二维数组,然后对其中的元素挨个根据下标的指引来进行赋值。我们对这个二维数组也就是矩阵的初始化是这样的:1
2m, n = 5, 3
matrix = [[1] * n] * m
其中m,n分别是行数和列数。乍一看没有什么问题,但是在赋值的时候出现了这样的一幕:1
2
3
4
5
6
7matrix
Out[199]: [[1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1]]
matrix[1][0] = 233
matrix
Out[201]: [[233, 1, 1], [233, 1, 1], [233, 1, 1], [233, 1, 1], [233, 1, 1]]
深拷贝与浅拷贝
首先要知道拷贝与引用的区别,拷贝只是单单对值得复制,引用是像指针一样的存在
在python 中,对一个list后面用 乘号 再加上 数字 的方法来初始化一个list,实际上是对这个list进行了浅拷贝(shallow copy),在python中,有深拷贝(deep copy) 和 浅拷贝 的区别。简单来讲就是:深拷贝就是把要拷贝的对象整体复制一份,存在新开辟的空间里;而浅拷贝指的是,对于要拷贝的对象要复制一份,但是对于其内部的子对象就不复制了,而是直接引用,也就是类似于添加了一个链接而已,如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16import copy
a
Out[210]: [888, 2, 3, [4, 5]]
b = copy.copy(a)
b
Out[212]: [888, 2, 3, [4, 5]]
b[0] = 233
b
Out[214]: [233, 2, 3, [4, 5]]
a
Out[215]: [888, 2, 3, [4, 5]]
b[3][1] = 666
b
Out[217]: [233, 2, 3, [4, 666]]
a
Out[218]: [888, 2, 3, [4, 666]]
这里就很明显了,我们对a做一个浅拷贝,目的是b,然后我们对b进行操作,如果对list中的整数赋值,也就是对象中的元素赋值,那么就只改变b中的这个位置的元素值,而a的不变;但是如果我们对list中的list,也就是子对象进行赋值,那么我们法线,这个操作同样也影响了a中的结果。
(很明显浅拷贝就是只是对浅层元素进行拷贝,深层元素采用引用)
copy 这个模块里的copy()是浅拷贝的函数,我们在试一下深拷贝deepcopy()函数:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15a
Out[219]: [888, 2, 3, [4, 666]]
b = copy.deepcopy(a)
b
Out[221]: [888, 2, 3, [4, 666]]
b[0] = 1
b
Out[223]: [1, 2, 3, [4, 666]]
a
Out[224]: [888, 2, 3, [4, 666]]
b[3][0] = 233
b
Out[226]: [1, 2, 3, [233, 666]]
a
Out[227]: [888, 2, 3, [4, 666]]
这就很科学了!两个对象 a 和 b 互不打扰,很和谐。
另外,我们通常的赋值操作(list的直接赋值)又是怎样的结果呢?1
2
3
4
5
6
7
8
9
10
11
12
13
14
15a
Out[229]: [888, 2, 3, [4, 666]]
b = a
b
Out[231]: [888, 2, 3, [4, 666]]
b[0] = 1
b
Out[233]: [1, 2, 3, [4, 666]]
a
Out[234]: [1, 2, 3, [4, 666]]
b[3][0] = 1
b
Out[236]: [1, 2, 3, [1, 666]]
a
Out[237]: [1, 2, 3, [1, 666]]
可以看出来,直接赋值的结果是比浅拷贝还浅拷贝的。。。因为a和b根本上就是指向的同一个对象!也就是说,a和b是这个对象的两个引用(reference),修改一个就会改变另一个。
对上文中问题的解释
了解了这两个概念的区别,就可以解释上述的问题。首先,我们对1
2
3[1] * 3 # 创建了 1 的三个浅拷贝,得到了[1,1,1],此时我们修改某个1,不会影响其他的,因为这是int的元素,不是子对象
[[1] * 3] * 4 创建了list列表[1,1,1]的四个浅拷贝,这里得到的list是以list为元素类型的,因此改变一个就会影响其他
# 这就是我们开始会得到所有行都改变的原因,因为他们实际上指向的是同一个东西!
改用如下方式初始化数组,就可以得到一个可以通过二维下标访问的矩阵了。
1 | matrix = [[ 1 for i in range(n)] for i in range(m)] |
这就是之前整理过的list comprehension的方法生成list。
Python函数的的传值和传引用
https://www.jianshu.com/p/2c83db2f5ef3
Python函数有一个很变态的地方,就是他调用函数时,将传递参数的方式是传引用.这种传参对于int,double,str,tuple型没有什么问题,但对于list,dict,set 就会出现传引用的问题.
在网上大部分博客的结论是:
- 不可变对象作为函数参数,python通过值传递
- 可变对象作为函数参数,python通过引用传递
注:Python中,数值类型(int和float)、字符串str、元组tuple都是不可变类型。而列表list、字典dict、集合set是可变类型。
这种时候我就会比较想念C++的形参。
那么怎么样处理才能使得我们的可变对象在经过函数后不改变呢
网上看到的一个方法是在函数中新建对象,然后将传递的参数赋给他。
对比:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35# 处理多个银行账户的余额信息
def addInterest(balances, rates):
print()
print("第二处", id(balances))
for i in range(len(balances)):
balances[i]= balances[i]*(1+rates)
print()
print("第三处",id(balances))
def test():
amounts = [1000,105,3500,739]
print()
print("第一处",id(amounts))
rate = 0.05
addInterest(amounts, rate)
print()
print(amounts)
print()
print("第四处",id(amounts))
test()
#结果
第一处 41203656
第二处 41203656
第三处 41203656
第三处 41203656
第三处 41203656
第三处 41203656
[1050.0, 110.25, 3675.0, 775.95]
第四处 41203656
1 | # 计算单个银行账户余额 |
可以看到第一种情况,id都是一样的,而第二种情况新建了对象,id是变化的了
得出结论:对于可变对象作为函数参数,且参数不指向其他对象时,相当于引用传递;否则,若参数指向其他对象,则对参数变量的操作并不影响原变量的对象值
大大的总结
对于不可变对象作为函数参数,相当于C系语言的值传递;
对于可变对象作为函数参数,且参数不指向其他对象时,相当于C系语言的引用传递。
对于可变对象作为函数参数,参数指向其他对象,对参数变量的操作不影响原变量的值。
python牛客网做题总结
现在许多公司都是在牛客网上进行笔试的,因此有必要了解一下牛客网上的编程方式
坑很多:
一、题目提示有多个case 或者 输出‘空,检查是否输入多个case’1
2
3
4
5
6
7
8
while True:
try:
# your code
except:
break
二、题目提示多组同时输入或加入while循环超时1
2
3import sys
for line in sys.stdin:
N = line.strip()
Python牛客注意事项
提高通过率技巧
- 时间复杂度问题:增加条件break或continue出循环,能break尽量break;减少不必要的判断条件(比如在不在字典中);做一些排序来减少后期的工作量
- 空间复杂度问题:
- 边界条件:某些数据是不是始终存在
测试用例通过但0%的原因:
1.没有理解题意
输入
而可以考虑用input1
2
3Input=input().split(' ')
a=int(Input[0])
b=int(Input[1])
注意输入进去的都是string类型
输出
一般逐行打印1
2
3for i in result:
print(i)
常用数据结构
list和dict比较好用,不能导入numpy库
list:做整体的排序
dict:做局部的排序,要在单个条件下xxx
常用函数
list
min() max() sum() .index()
.remove(具体内容) .pop(索引号) del a[索引号]
.sort(key=lambda x: x[0])
.sort(key=lambda x:(-x[1],x[0],x[2]))#默认是升序,加个符号变降序,可做多级排序。
sorted()#不在本地做排序
元素 in list名称
变量
float(‘inf’)
字典
keys()
.values()
.items()#返回的是元组
字符串
.strip() 去除首位空格
.strip().strip(‘-a’)去除首位空格和字符
S[:3]+S[5:] 拼接字符串,去除某个字符
.replace(‘a’,’b’) 替换字符
re.sub(‘a’,’b’,s) 替换字符
ord(a)是用来将单个字符转化成它的ASCII值,因为python里不像C++那样两个字符相减可以求出ASCII数值差
chr(100)是ord的逆过程,将ASCII值转化成字符
Python sorted
一般的sorted1
2sorted([5, 2, 3, 1, 4])
#[1, 2, 3, 4, 5]
其实list也自带sort()方法1
2
3
4a = [5, 2, 3, 1, 4]
a.sort()
a
#[1, 2, 3, 4, 5]
Key参数/函数
从python2.4开始,list.sort()和sorted()函数增加了key参数来指定一个函数,此函数将在每个元素比较前被调用。 例如通过key指定的函数来忽略字符串的大小写:1
2sorted("This is a test string from Andrew".split(), key=str.lower)
['a', 'Andrew', 'from', 'is', 'string', 'test', 'This']
key参数的值为一个函数,此函数只有一个参数且返回一个值用来进行比较。这个技术是快速的因为key指定的函数将准确地对每个元素调用。
更广泛的使用情况是用复杂对象的某些值来对复杂对象的序列排序,例如:
1 | student_tuples = [ |
同样的技术对拥有命名属性的复杂对象也适用,例如:1
2
3
4
5
6
7
8
9
10
11
12
13
14class Student:
def __init__(self, name, grade, age):
self.name = name
self.grade = grade
self.age = age
def __repr__(self):
return repr((self.name, self.grade, self.age))
student_objects = [
Student('john', 'A', 15),
Student('jane', 'B', 12),
Student('dave', 'B', 10),
]
lambda student: student.age) # sort by age sorted(student_objects, key=
[('dave', 'B', 10), ('jane', 'B', 12), ('john', 'A', 15)]
升序和降序
list.sort()和sorted()都接受一个参数reverse(True or False)来表示升序或降序排序。例如对上面的student降序排序如下:1
2
3
42), reverse=True) sorted(student_tuples, key=itemgetter(
[('john', 'A', 15), ('jane', 'B', 12), ('dave', 'B', 10)]
'age'), reverse=True) sorted(student_objects, key=attrgetter(
[('john', 'A', 15), ('jane', 'B', 12), ('dave', 'B', 10)]
其他语言普遍使用的排序方法-cmp函数1
2
3
4def numeric_compare(x, y):
return x - y
5, 2, 4, 1, 3], cmp=numeric_compare) sorted([
[1, 2, 3, 4, 5]
或者你可以反序排序:1
2
3
4def reverse_numeric(x, y):
return y - x
5, 2, 4, 1, 3], cmp=reverse_numeric) sorted([
[5, 4, 3, 2, 1]