Python中的__call__()函数

__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
7
>>> from 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的用法

  1. 解压
    传入的data为batch个元组,即batch个(iamges,captions) 对儿
    images,captions=zip( data)
    表示zip(
    data)=(batch 个image,batch 个 caption)

  2. 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
2
m, n = 5, 3
matrix = [[1] * n] * m

其中m,n分别是行数和列数。乍一看没有什么问题,但是在赋值的时候出现了这样的一幕:

1
2
3
4
5
6
7
matrix
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
16
import 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
15
a
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
15
a
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 就会出现传引用的问题.

在网上大部分博客的结论是:

  1. 不可变对象作为函数参数,python通过值传递
  2. 可变对象作为函数参数,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
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
36
37
38
# 计算单个银行账户余额
def addinterest(balance, rate):
print("第二处", id(balance))
newBalance = balance * (1 + rate)
print()
print("第三处", id(balance))
print()
print("第四处", id(newBalance))
return newBalance


def main():
amount = 1000
print("第一处", id(amount))
print()
rate = 0.05
amount = addinterest(amount, rate)
print()
print("第五处", id(amount))
print()
print(amount)
print("第六处", id(amount))


main()
#结果
第一处 33533648

第二处 33533648

第三处 33533648

第四处 33563344

第五处 33563344

1050.0
第六处 33563344

可以看到第一种情况,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
3
import sys
for line in sys.stdin:
N = line.strip()

Python牛客注意事项

提高通过率技巧

  1. 时间复杂度问题:增加条件break或continue出循环,能break尽量break;减少不必要的判断条件(比如在不在字典中);做一些排序来减少后期的工作量
  2. 空间复杂度问题:
  3. 边界条件:某些数据是不是始终存在

    测试用例通过但0%的原因:

    1.没有理解题意

输入

而可以考虑用input

1
2
3
Input=input().split(' ')
a=int(Input[0])
b=int(Input[1])

注意输入进去的都是string类型

输出

一般逐行打印

1
2
3
for 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

一般的sorted

1
2
sorted([5, 2, 3, 1, 4])
#[1, 2, 3, 4, 5]

其实list也自带sort()方法

1
2
3
4
a = [5, 2, 3, 1, 4]
a.sort()
a
#[1, 2, 3, 4, 5]

Key参数/函数
从python2.4开始,list.sort()和sorted()函数增加了key参数来指定一个函数,此函数将在每个元素比较前被调用。 例如通过key指定的函数来忽略字符串的大小写:

1
2
sorted("This is a test string from Andrew".split(), key=str.lower)
['a', 'Andrew', 'from', 'is', 'string', 'test', 'This']

key参数的值为一个函数,此函数只有一个参数且返回一个值用来进行比较。这个技术是快速的因为key指定的函数将准确地对每个元素调用。

更广泛的使用情况是用复杂对象的某些值来对复杂对象的序列排序,例如:

1
2
3
4
5
6
7
>>> student_tuples = [
('john', 'A', 15),
('jane', 'B', 12),
('dave', 'B', 10),
]
>>> sorted(student_tuples, key=lambda student: student[2]) # sort by age
[('dave', 'B', 10), ('jane', 'B', 12), ('john', 'A', 15)]

同样的技术对拥有命名属性的复杂对象也适用,例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
>>> class 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),
]
>>> sorted(student_objects, key=lambda student: student.age) # sort by age
[('dave', 'B', 10), ('jane', 'B', 12), ('john', 'A', 15)]

升序和降序
list.sort()和sorted()都接受一个参数reverse(True or False)来表示升序或降序排序。例如对上面的student降序排序如下:

1
2
3
4
>>> sorted(student_tuples, key=itemgetter(2), reverse=True)
[('john', 'A', 15), ('jane', 'B', 12), ('dave', 'B', 10)]
>>> sorted(student_objects, key=attrgetter('age'), reverse=True)
[('john', 'A', 15), ('jane', 'B', 12), ('dave', 'B', 10)]

其他语言普遍使用的排序方法-cmp函数

1
2
3
4
>>> def numeric_compare(x, y):
return x - y
>>> sorted([5, 2, 4, 1, 3], cmp=numeric_compare)
[1, 2, 3, 4, 5]

或者你可以反序排序:

1
2
3
4
>>> def reverse_numeric(x, y):
return y - x
>>> sorted([5, 2, 4, 1, 3], cmp=reverse_numeric)
[5, 4, 3, 2, 1]

-------------本文结束感谢您的阅读-------------