【Python】收集python编码过程中碰到的坑

 

【最新更新】

  • Linux环境下的路径一定要使用斜杠“/”而不要使用反斜杠“\”。否则识别不出来。

>>> os.path.exists(r"csv2\appl")
False
>>> os.path.exists(r"csv2\\appl")
False
>>> os.path.exists(r"csv2/appl")
True
  • 千万不要一边遍历文件夹,一边使用shutil.move。会移动错误的文件。

  • 给变量初始化时,赋值为可变对象


任何变量的初始化赋值,要么为空(None)、要么为不可变对象(数字、元祖、字符串)。千万不要初始化为可变对象(列表,字典)。

具体原因见《改变字典中一个key的value值,另一个不相关的key-value值也被改变了》:https://blog.csdn.net/ztf312/article/details/89577328

坑1:一边遍历,一边删除列表元素

可能出现问题:下标超出范围报错,或者返回结果不正确。

python 的列表遍历删除

a=[1,2,3,4,5,6,7]
b=[1,3,6]
for it in a:
    if it not in b:
        print("remove ", it)
        print(a)
        a.remove(it)
        print(a)
remove  2
[1, 2, 3, 4, 5, 6, 7]
[1, 3, 4, 5, 6, 7]
remove  4
[1, 3, 4, 5, 6, 7]
[1, 3, 5, 6, 7]
remove  7
[1, 3, 5, 6, 7]
[1, 3, 5, 6]

如果是字典一边遍历一边删除报错,像下面这样:

# result 是一个字典, 我想把里面属性值是None的属性删除
for key in result:
	if not result[key]:
		result.pop(key)
		continue

-----------------------------------------------------

RuntimeError: dictionary changed size during iteration   # 字典在迭代的时候改变了字典大小

一个失败的尝试是,定义keys=dic.keys(),将keys独立出dic。由于函数返回值是其内部值的索引,keys仍然会随dic变换。 
成功的尝试是,使用keys=list(dic.keys())另存一个变量,重新申请内存空间。这时遍历删除成功!

keys = list(result.keys())
for key in keys:
	if not result[key]:
		result.pop(key)
		continue

坑2:b=a,修改a,结果b也被改变了。

python的内存管理机制

坑3:input输入一个数字,使用时发现变成了字符串。

如果想使用input()函数输入一个数值,在得到结果后需要用int()将字符串类型转换为数值类型。

坑4:在for循环体内改变循环变量的值,结果下次循环依然不被影响。

for i in range(3):
	print "original:",i
	i=i+3
	print "new",i

original: 0
new 3
original: 1
new 4
original: 2
new 5

典型的用C语言思想,python并不买账。

5:remove是删除首个符合条件的元素,而不是根据特定索引。

>>> a=[1,2,3,1]
>>> a.remove(a[-1])
>>> a
[2, 3, 1]
>>> 

如上代码,本来是想删除最后一个元素,结果误删第一个。

而对于 del 来说,它是根据索引(元素所在位置)来删除的,如下例:

>>> a=[1,2,3,1]
>>> del a[0]
>>> a
[2, 3, 1]

pop是根据索引,返回的是索引指向那个数值。

>>> a=[1,2,3,1]
>>> a.pop(2)
3

坑6:python中字典的items()函数返回类型为list

我们知道两个列表可直接相加,两个字典直接相加会报错。

于是想到一种相加方式:a.items()+b.items()。但是错误地得到了一个列表。

>>> a={1:2}
>>> b={3:4}
>>> a+b
Traceback (most recent call last):
  File "<pyshell#123>", line 1, in <module>
    a+b
TypeError: unsupported operand type(s) for +: 'dict' and 'dict'

>>> c=a.items()+b.items()
>>> c
[(1, 2), (3, 4)]
>>> type(c)
<type 'list'>
正确使用方式:update()
>>> a
{1: 2}
>>> b
{3: 4}
>>> a.update(b)
>>> a
{1: 2, 3: 4}

7:元组或者列表加括号等于自身

>>> a=(1,2)
>>> (a)
(1, 2)
>>> b=[1,2]
>>> (b)
[1, 2]
>>> tuple(a)
(1, 2)
>>> tuple(b)
(1, 2)
>>> tuple([a])
((1, 2),)

这告诉我们如果需要使用元祖,最好用tuple()函数转换,而不是加括号强制转化。

8:迭代器无法改变列表内容

>>> a=["1\t4","a\tb"]
>>> for item in a:
	item = item.split("\t")
	
>>> a
['1\t4', 'a\tb']

>>> for i in range(len(a)):
	a[i]=a[i].split("\t")

>>> a
[['1', '4'], ['a', 'b']]

9:列表解析

列表解析总共有两种形式,很容易把二者混淆:

1. [i for i in range(k) if condition]:此时if起条件判断作用,满足条件的,将被返回成为最终生成的列表的一员。

2. [i if condition else exp for exp]:此时if...else被用来赋值,满足条件的i以及else被用来生成最终的列表。

以上情况对多个for仍然成立。

print([i for i in range(10) if i%2 == 0])  
print([i if i == 0 else 100 for i in range(10)])  
  
[0, 2, 4, 6, 8]  
[0, 100, 100, 100, 100, 100, 100, 100, 100, 100]  

坑10:矩阵转变为数组,再求和

>>> a=np.mat([[1,2,3],[4,5,6]])
>>> a
matrix([[1, 2, 3],
        [4, 5, 6]])
>>> sum(a)
matrix([[5, 7, 9]])
>>> b=[[1,2,3],[4,5,6]]
>>> sum(b)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for +: 'int' and 'list'
>>> np.array(a)
array([[1, 2, 3],
       [4, 5, 6]])
>>> np.array(sum(a))
array([[5, 7, 9]])
>>> np.array(sum(a))[0]
array([5, 7, 9])
>>> np.array(sum(a)[0])
array([[5, 7, 9]]) 

坑11:float精度损失、round假四舍五入

>>> b=max(np.linalg.eigvals(a))
>>> b
5.3722813232690143
>>> round(b,3)
5.3719999999999999
>>> round(b,2)
5.3700000000000001

可以看到两个问题:一室无限循环;二是精度损失;

建议不要用round(),因为它不是严格意义上的四舍五入,而是遇到5舍入时接近一个偶数;

建议用格式化表示:

>>> c = "%.4f" % b
>>> c
'5.3723'
>>> float(c)
5.3723

坑12:对象赋值,实际传递了引用

定义类:

import numpy as np
class ModelParams(object): 
    def __init__(self):            
        self.beta = 0.8*np.ones(NUMBER_OF_NODES)   

定义对象p,并赋值给p1:

p = NodeParams()      
print(">>>>test<<<<")
print(p.beta)
p1 = p
p1.beta = -1*np.ones(len(p.beta))
print(p.beta)
print(p1.beta)

输出结果(错误):

>>>>test<<<<
[ 0.8  0.8  0.8  0.8  0.8]
[-1. -1. -1. -1. -1.]
[-1. -1. -1. -1. -1.]

可以看到,对象的赋值只是传递了一个引用,改变被赋值的对象,原对象也会被改变。解决:采用深拷贝进行赋值

from copy import deepcopy


p = NodeParams()      
print(">>>>test<<<<")
print(p.beta)
p1 = p
p1.beta = -1*np.ones(len(p.beta))
print(p.beta)
print(p1.beta)

输出结果(正确):

>>>>test<<<<
[ 0.8  0.8  0.8  0.8  0.8]
[ 0.8  0.8  0.8  0.8  0.8]
[-1. -1. -1. -1. -1.]
123

坑12:列表相加与numpy数组相加

a=[1,2,3]

b=[4,5,6]

a+b
Out[447]: [1, 2, 3, 4, 5, 6]

a=np.array(a)

b=np.array(b)

a,b
Out[450]: (array([1, 2, 3]), array([4, 5, 6]))

a+b
Out[451]: array([5, 7, 9])

解决:用 numpy.concatenate 拼接多个数组

坑13:矩阵相乘与数组相乘

a=[[0,1],[2,3]]
c1=np.array(a)
c2=np.array([1,2])
c2*c1
Out[119]: 
array([[0, 2],
       [2, 6]])

np.mat(c2)*np.mat(c1)
Out[120]: matrix([[4, 7]])

坑14:格式化打印矩阵与打印数组

打印矩阵(失败):

def print_matriax(M):
    for i in range(len(M)):
        print(",".join([str(it) for it in M[i]]))
a = np.mat([[1,2],[3,4]])
print_matriax(a)
[[1 2]]
[[3 4]]

打印数组(成功):

def print_matriax(M):
    for i in range(len(M)):
        print(",".join([str(it) for it in M[i]]))
a = np.array([[1,2],[3,4]])
print_matriax(a)
1,2
3,4

打印矩阵最终的解决方案(成功):

def print_matriax(M):
    M = np.array(M)
    for i in range(len(M)):
        print(",".join([str(it) for it in M[i]]))
a = np.mat([[1,2],[3,4]])
print_matriax(a)
1,2
3,4

坑15:numpy数组减去常数

>>> a=np.ones(10)
>>> a
array([ 1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.])
>>> a-10
array([-9., -9., -9., -9., -9., -9., -9., -9., -9., -9.])

以上案例原本是numpy的简化操作,但是实际使用中,误引入一个bug:

原本想用np.array(a)-np.array(b),结果b的计算过程出错,得到一个常数,正常应该报错,但是numpy顺利进行了减法操作,导致这个bug被忽略。

坑16:np.concatenate(a,b)

如此操作两个numpy数组会报错,因为缺少一层括号。正确为:np.concatenate((a,b), axis=0)

坑17:c=a.append(b)

如此操作两个list数组会报错,因为返回append和extend返回None。正确为c=copy.deepcopy(a); c.append(b);

这里用copy.deepcopy是因为有时c不是一个单独数组,而是一个大数组中的子数组。为了避免随后改变a造成c的误变,用deepcopy保险一点。

坑18:numpy数组的子数组

numpy定义的数组,如果元素还是数组,会出现两种情况:(1) 子数组长度一样,为numpy.array类别。(2) 子数组长度不一样,为list类别。

a=np.array([[1,2],[3,4]])

type(a)
Out[99]: numpy.ndarray

type(a[0])
Out[100]: numpy.ndarray

b=np.array([[0, 1, 2, 3, 7, 13],[32, 33, 8, 30],[32, 33, 29, 23]])

type(b)
Out[102]: numpy.ndarray

type(b[0])
Out[103]: list

这会造成很多numpy特有函数失效,如flatten():

a.flatten()
Out[111]: array([1, 2, 3, 4])

b.flatten()
Out[112]: array([[0, 1, 2, 3, 7, 13], [32, 33, 8, 30], [32, 33, 29, 23]], dtype=object)

这里涉及到一个多维数组的概念,可能numpy认为多维数组的概念是列数相等。

回到上面的flatten()失效问题,正确的方式是采用np.hstack()函数,可成功将数组拉平。

np.hstack(b)
Out[114]: array([ 0,  1,  2, ..., 33, 29, 23])

19:numpy数组赋值中的bug

numpy数组,必须是像矩阵一样规整的格式,子数组长度相等。

定义一个二维numpy数组,尝试将其中一个子数组定义为变量,结果将所有元素都赋值为这个变量的值了:

a=np.array([[1,2],[3,4]])

a[0]
Out[4]: array([1, 2])

a[0]=999

a
Out[6]: 
array([[999, 999],
       [3, 4]])

错因:加上了索引之后的标签其实指代的就是具体的存储区。见下面案例,就是指向了整个数组存储区,成功赋值:

a=[[1,2],[3,4]]

a
Out[12]: [[1, 2], [3, 4]]

a=1

a
Out[14]: 1

尝试给子数组定义为字符数组,尝试失败,无法给int()对象赋值字符串:

a=np.array([[1,2],[3,4]])
a[0]=['a','b']
Traceback (most recent call last):

  File "<ipython-input-7-304e9d0d6e26>", line 1, in <module>
    a[0]=['a','b']

ValueError: invalid literal for int() with base 10: 'a'

尝试赋值为同格式子数组,成功:

a[0]=[-1,-2]

a
Out[9]: 
array([[-1, -2],
       [ 3,  4]])

20:【python3.6.3】 networkx中的G.nodes()返回类型不再是列表

for i in range(len(G.nodes)):

    print(G.nodes[i])

报错: keyerror: 0

原因:

G.nodes()返回类型是<class 'networkx.classes.reportviews.NodeView'>,这是一个迭代器。其中每个元素都是一个字典。完整代码见:

G = nx.random_graphs.random_regular_graph(2,20)

G.nodes()
Out[43]: NodeView((17, 18, 7, 14, 10, 11, 8, 13, 9, 15, 4, 5, 19, 3, 1, 0, 6, 16, 12, 2))

for i in range(len(G.nodes())):
    print(G.nodes[i])
    
{}
{}

21:定义函数时,如果有的参数有默认值,把这些参数放在没有默认值的参数前会报错

SyntaxError: non-default argument follows default argument

22: 注意区分networkx中的函数add_node与add_nodes_from

案例(python3.6.3):

G.add_node(12,13):TypeError: add_node() takes 2 positional arguments but 3 were given;

G.add_node([12, 13]):TypeError: unhashable type: 'list'

G.add_node((12,13)):成功加入一个编号为(12,13)的节点

G.add_nodes_from([12,13]):成功加入两个编号为12、13的节点

G.add_edge([(1,2),(3,4)]):TypeError: add_edge() missing 1 required positional argument: 'v'

G.add_edges_from([(1,2),(3,4)]):成功加入边

23:Error with matplotlib.show() : module 'matplotlib' has no attribute 'show' [duplicate]

不要用:import matplotlib as plt,

改用:import matplotlib.pyplot as plt

坑24:外部global定义的全局变量,在函数内赋值;

当你这样做的时候,实际上是在函数内重新定义了一个局部变量。

要想对这个全局变量成功赋值,需要重新在这个函数内部进行global的声明;

25:在子进程内修改变量,而没有返回结果。

python在函数内部修改对象(结构体、数组)时,是直接改变了这个对象,无返回值也可以。

但是,在多进程中,多进程中,同一个变量,各自有一份拷贝存在于每个进程中,这是多线程和多进程最大的不同。所以如果子进程无返回值,就失去了与主进程的交互,所做修改只是局部有效。

坑26:用lambda给字典赋值

lambda表达式中变量的作用域
>>> d = dict()
# 这里有个坑
>>> for i in range(5):
       d[i] = lambda :i**2
 
>>> d[2]()
16
>>> d[3]()
16
# 这样看的更清楚一些
# lambda表达式中i的值是调用时决定的
>>> i = 10
>>> d[0]()
100
# 写成下面这样子就没问题了
>>> d = dict()
>>> for i in range(5):
       d[i] = lambda x=i:x**2
 
>>> d[2]()
4
>>> d[3]()
9

27:函数也是对象,默认参数在首次调用的时候初始化,有坑。

坑在于,第二次调用时,如果默认参数没有被赋值,它指向第一次传入的值。

引用一个官方的经典示例地址 :

def bad_append(new_item, a_list=[]):
    a_list.append(new_item)
    return a_list

print(bad_append('1'))
print(bad_append('2'))

这个示例并没有按照预期打印:

['1']
['2']

而是打印了:

['1']
['1', '2']

其实这个错误问题不在默认参数上,而是我们对于及默认参数的初始化的理解有误。

Python哲学:

一切皆对象

函数也是一个对象,如下示例:

import types

def test():
    pass

print(type(test)) # <class 'function'>
print(isinstance(test, types.FunctionType)) # True

如此,函数就是类types.FunctionType或者其子类的实例对象。那么对象必然有其初始化的时候,一般来说,解释器在读到函数末尾时完成函数实例的初始化。初始化后,就有了函数名到函数对象这样一个映射关系,可以通过函数名访问到函数对象了,并且,函数的一切属性也确定下来,包括所需的参数,默认参数的值。因此每次调用函数时,默认参数值是相同的(如果有默认参数)。

最佳实践

不可变的默认参数的多次调用不会造成任何影响,可变默认参数的多次调用的结果不符合预期。那么在使用可变默认参数时,就不能只在函数定义时初始化一次,而应该在每次调用时初始化。

最佳实践是定义函数时指定可变默认参数的值为None,在函数体内部重新绑定默认参数的值。以下是对上面的两个可变默认参数示例最佳实践的应用:

def good_append(new_item, a_list = None):

    if a_list is None:
        a_list = []

    a_list.append(new_item)
    return a_list

print(good_append('1'))
print(good_append('2'))
print(good_append('c', ['a', 'b']))
  • 坑28:用zip生成dict时候的坑,当关键字是数字时得到错误组合:

dict(zip(['a','b'],[0,2]))
Out[13]: {'a': 0, 'b': 2}
dict(zip([1,1],[0,2]))
Out[14]: {1: 2}
  • 坑29:注意PIL.image与CV2通道RGB分割的区别

PIL

from PIL import Image

im1 = Image.open("photo.png")

(R, G, B) = im1.split()

openvc

import cv2

im2 = cv2.imread("photo.png")

(B, G, R) = cv2.split(im2)

注意: opencv中,RGB三个通道是反过来的.

# 福利:如何用Python PIL获取图片的RGB数值
from PIL import Image
im = Image.open("xxx.jpeg")
pix = im.load()
width = im.size[0]
height = im.size[1]
for x in range(width):
    for y in range(height):
        r, g, b = pix[x, y]
        print(r, g, b)
  • 坑30:Anaconda安装踩过的坑

python中安装yaml时,应该安装pyyaml:conda install pyyaml
创建新环境:conda create -n name python=3.6.3
安装失败,提示channel不符合。
解决:
输入命令: conda update conda ”。
然后输入以下命令:conda create -n py363 python=3.6.3 anaconda ”。

成功,随后可以activate py363。

关于错误:ImportError: No module named yaml,注意:

pip install pyyaml lets you import yaml. This package enables Python to parse YAML files.
pip install pyaml lets you import pyaml. This package allows pretty-printing of YAML files 

  • 坑31:用字典的key作为 if 语句的条件

如果你用字典get方法做判断,if dic.get("key"),此处有坑。

如果key不存在,正确;

如果key存在但是dic[key]=0,此时会跳过if语句,错误.

严格的写法: 

if dic.get('key', None) is not None:

发布了392 篇原创文章 · 获赞 487 · 访问量 239万+
展开阅读全文

function takes at least 2 arguments(1 given)

09-05

机器学习实战kNN篇手写数字识别: ``` def img2vector(filename): file=open(filename) imgvector=zeros((1,1024)) for i in range(32): line=file.readline() for j in range(32): imgvector[0,32*i+j]=int(line[j]) return imgvector def handwritingClassifier(): hwlabels=[] trainingfilelist=listdir('trainingDigits') m=len(trainingfilelist) trainingMat=zeros((m,1024)) for i in range(m): fn=trainingfilelist[i] filename1=fn.split('.')[0] filelabel=int(filename1.split('_')[0]) hwlabels.append(filelabel) trainingMat[i,:]=img2vector('trainingDigits/%s' % fn) testfilelist=listdir('testDigits') mT=len(testfilelist) errorCount=0.0 for i in range(mT): filenameT=testfilelist[i] filename1T=filenameT.split('.')[0] filelabelT=int(filename1T.split('_')[0]) testfilevector=img2vector('testDigits/%s' % filenameT) predictlabel=Classifier(testfilevector,trainingMat,hwlabels,5) if (predictlabel!=filelabelT): errorCount+=1.0 print "the classifier came back with %d ,the real answer is %d " % (predictlabel,filelabelT) print "the error rate is %f" % (errorCount/float(mT)) ``` 结果在命令行输入import kNN ...kNN.handwritingClassifier()的时候 TypeError Traceback (most recent call last) <ipython-input-1-b18521761a29> in <module>() 1 import kNN ----> 2 kNN.handwritingClassifier() C:\Users\Administrator\kNN.py in handwritingClassifier() 99 filelabel=int(filename1.split('_')[0]) 100 hwlabels.append(filelabel) --> 101 trainingMat[i,:]=img2vector('trainingDigits/%s' % fn) 102 testfilelist=listdir('testDigits') 103 mT=len(testfilelist) C:\Users\Administrator\kNN.py in img2vector(filename) 81 82 def img2vector(filename): ---> 83 file=open(filename) 84 imgvector=zeros((1,1024)) 85 for i in range(32): TypeError: function takes at least 2 arguments (1 given) 小白一枚,请问到底哪里有问题,open函数不是只要一个参数吗,用的是python2.7望指教!! 问答

请教:如何基于分笔数据利用matplotlib绘制时间-价格/成交量走势图?

06-16

背景:处理50etf和对应的认沽认购数据时,想将这三者的时间-价格走势在同一张图中体现。数据源是每日的分笔数据。整合后如下: ![图片说明](https://img-ask.csdn.net/upload/201906/16/1560661788_443267.png) 可以看到有很多时刻下对应的价格没有数据,并且有很多时间是重叠的。在这里我进行了两步处理: 1.对价格部分fillna ![图片说明](https://img-ask.csdn.net/upload/201906/16/1560662019_763660.png) 2.简单去重(保留重复时刻中首个的数据) ![图片说明](https://img-ask.csdn.net/upload/201906/16/1560662138_277838.png) 在得到这样一个dataframe之后,我利用matplotlib官方文档中提供的‘skip date where there is no date'方法对数据的x轴时间进行处理。 结果: ![图片说明](https://img-ask.csdn.net/upload/201906/16/1560662835_546153.png) 可以看到,三条线都有不同程度的断点,不连续,50ETF这条线问题尤其明显。 综合上述,我的疑问如下: 1 我的数据处理方式是正确的吗?(特别是fillna 和 duplicated部分的处理) 2 生成图像的断点/不连续原因是什么?如何解决?(求大佬赐代码)【已解决,实际是连续的,设置下线宽就看出来了。其他两个问题继续求解~】 3 如何对成交量的部分作一个区分?如内盘对应的成交量柱形显示为绿色,外盘对应的成交量柱形显示为红色。 第一次提问,不知道在哪里上传数据的源文件,请见谅。 谢谢各位大佬! 问答

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 编程工作室 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览