【Python】 零碎知识积累 I

  大概也是出于初高中时学化学,积累各种反应和物质的习惯,还有大学学各种外语时一看见不认识的词就马上记下来的习惯,形成了一种能记一点是一点的零碎知识记录的癖好。这篇文章就是专门拿来记录这些零碎知识的,没事的时候看看回忆回忆,说不定也能学到点什么。

  ■  关于输出不同进制数

  在格式化输出字符串中可以写%d,%x,%o来分别输出十、十六、八进制的值

  但是并没有二进制的转换输出,想要二进制时可以用bin函数,bin(num)会输出'0bxxxx',xxxx就是num的二进制表达了

  ■  其实习惯了写for A in ... 的形式,但很多时候可以把A替换为A,B这种元组形式,这样可以让表达更加清晰。比如

for item in dict.items():
#换成
for key,value in dict.items():

  

  ■  要善于看文档。碰到不认识的函数,模块等可以看其__doc__,或者help(...),以及。。度娘

  ■  import keyword;print keyword.kwlist 可以查看python中所有的关键字,感觉没有想象的那么多..+

  

  ■  id函数可以用来查看某个对象在内存中的地址值,借助id可以来看两个对象是不是引用了同一个内存地址还是只是值上的相等。

  

  ■  给函数传递参数时,如果混合了虚名实名参数,那么虚名参数一定要写在实名参数之前,否则python无法识别参数顺序,并报错:

func(value1,value2,param3=value3)    #正确

func(param3=value3,value1,value2)    #错误

  ■  类中的方法可以创建本类的实例。当然不包括构造方法,否则就无限递归了。。

  ■  关于赋值和复制

  赋值就是用 = 号的语句,而复制在Python中又可以分为浅复制和深复制。

  赋值语句只是单纯的多关联一个引用。常见的说明这一点的例子就是我让a和b两个变量都等于某个列表,通过a引用列表修改其内容后,通过b访问到的就是修改过后的内容了。换句话说,赋值没有复制任何引用关系,是复制了零层引用关系。

  浅复制的操作有比如copy模块的copy方法,序列的切片,list/dict等可变对象的类型方法等。通过这些操作得到的内容赋值给某个变量之后,这个变量相对于原值就是一个通过浅复制得到的东西。浅复制的特点是只复制最表面的一层引用关系。比如有a=[1,2,3,[4]],令b = copy.copy(a)。之后查看id(a)和id(b),可以看到两者是不同的。但是id(a[3])和id(b[3])却是相同的。原因就是浅复制只复制第一层关系。

  深复制相对的,就是复制所有相关的引用关系了。上面例子中的id(a[3])和id(b[3])也会变得不同了。

  ■  eval 和 repr 和 exec

  这三个函数看起来都很黑科技

  1.  eval是evaluate的简称,可以将符合格式的str转化成list,tuple,dict等数据格式。除了转换格式之外,eval也可以解析字符串,调用python函数,并且返回此语句的返回值。,比如eval('open("file","r").read()')可以返回某文件的内容(但是不能有语句 比如eval('import os')或者eval('print 1')都是会报错的)

  2.  repr是represent的简称,将各种数据类型转化成str,是eval的一种逆操作。但是repr和str()不同。

    简单来说,repr是把传递进来的参数原原本本的转化成一个字符串,甚至包括引号,举例子如下:

>>>print repr('a\nb')
'a\nb' #注意是带引号的
>>>print r'a\nb'
a\nb #不带引号但是因为是r开头的字符串,不对\n做转义解释
>>>print 'a\nb'
a
b

    而str则是致力于生成一个给人看的字符串,虽然在内存中他有可能还是带有未转义字符的,但是print出来之后是人可以一眼看明白的

    简而言之,print repr(...)所得到的东西就是...,连引号都算在里面。当然,这个用repr转化字符串并没有啥实际意义,因为转化出来的东西肯定有引号。总之要清楚这两者不一样就好了

  3.  exec用来将字符串转化为语句。

    虽然看起来和eval好像很像,但区别在于eval是返回值的,而exec不返回值,只做操作

    如:

>>> eval('1+2')
3
>>> exec('1+2') #返回值是None,但是内存中进行了1+2的计算
>>> eval('print \'1+2\' ')
SyntaxError: invalid syntax
>>> exec('print \'1+2\' ')
1+2

    另外,eval和exec除了第一个参数是一个字符串之外,还可以加上一个第二个参数,是个字典。这个字典的意义就是为前面字符串里的特定变量赋值,相当于一个小命名空间:

>>>exec('print a+1',{'a':2})
3

    再另外,eval和exec是有一定安全风险的,比如一个web应用接受用户的一些数据来做操作,可能你期盼用户发送一个规则的字符串过来然后懒一点直接用eval把它给python化,但是如果他传过来一个'open("file","w").close()',如果直接eval掉,那就相当于把之前存在的file给清了。。这样这个用户就达到了攻击目的。

  ■  上节中说到了str和repr的区别,那就顺便提一下__str__和__repr__的区别。这两个魔法方法的共同点是要返回一个字符串,但是这个字符串被用在不同的地方,举例如下:

class Test(object):
def __init__(self):
pass def __repr__(self):
return "Expression of repr" def __str__(self):
return "Expression of str" ######在python shell中######
>>>sample = Test()
>>>sample
Expression of repr
>>>print sample
Expression of str

  也就是说,通常我之前所说的对象在内存中的值,其实是指这个类在定义__repr__时返回的字符串,这个字符串也是函数repr(sample)的返回值。而print语句,则是提取出__str__方法的返回值并且把它输出到stdout上。
  以str类型为例,str的__str__方法返回的就是这个字符串本身的值,而__repr__方法会在两端自动加上两个单引号。在python shell中键入变量名直接回车是显示内存中的值的嘛,但是我要用什么方法来表示它是个字符串呢,自然就是要往显示出来的值里面加上两个引号了。但是呢字符串的值本身是没有引号的,所以repr方法为其加上引号,就是为了人在键入变量名查看内存中的值时可以看懂其是个字符串。

  

  ■  在遍历字典时,用for key,value in dict.items()更加直观。

  ■  lambda语句和lambda函数(匿名函数)

    和def一个函数相对来看,lambda语句的作用是建立一个匿名函数,并返回这个函数对象。函数处理过程较为简单的函数可以用lambda来简化代码。另外,可以把返回出来的函数对象实名化:

add = lambda a,b : a+b
#冒号右边紧跟的就是返回值,不能是None。也可以是个返回值不为None的函数嘛
#冒号左边写这个匿名函数的参数,可以写零到若干个。

    lambda的实际应用有很多,我暂时可以想到的就有序列在进行sorted时用的key参数可以是个匿名函数对象来实现根据某个指标排序。另外wxpython中的事件绑定,Bind方法,若用匿名函数可以实现向事件处理函数传递参数的功能。

  ■  不要忘了max(seq)和min(seq)两个显而易见的函数。挺有用的

  ■  经常用到的if语句的条件判断,其实不止能判断True和False,对于其他类型的数据,如[],(),{},"",None都是会被判成False的。而里面有数据的话就又是True.所以以后写条件可以不用写if ls == []:怎么样怎么样,而直接if ls:就好了,简洁明了!

■  global关键字用于把跟着的变量声明为全局变量。在看似不属于其的命名空间里也可以引用它。

■  讲一个一维列表二维化的方式,用简单的一行语句来表达:

[[array[rows*y+cols] for cols in range(y)] for rows in range(x)]
#array是一个一维列表(或者数组),x表示二维化后的行数,y表示列数

■  想用C,java里面的switch语句结构,但是python里没有的时候,可以

  1.  搞很多个if判断,不过这样很繁琐

  2.  建立一个字典,key是switch的各个case的值,而value写函数对象进行相应的操作

■  关于在函数的参数前加*,分成两个情况

  1.在定义函数的时候:需要知道的是,很多场合下写的*args,**kwargs中的args和kwargs只是一种习惯,你可以把它换成其他变量名称,在函数中换那个名称引用就可以了。
    一个*表示函数可以接受任意多个函数,它会把所有不是实名参数的参数整合成一个元组,并把其作为参数名代表的对象。比如:

def test(paraA,paraB,*args):
print paraA
print paraB
print type(args),args if __name__ == "__main__":
test(1,2,3,4,5) #输出:
1
2
<type 'tuple'> (3,4,5)

    两个**表示可以接受任意多个实名参数,即传参数的时候超过限额的部分也可以加上参数的名称了。而函数会把kwargs整合成一个字典供函数引用。比如:

def test(paramA,paramB='B',**kwargs):
print paramA
print paramB
print type(kwargs),kwargs if __name__ == "__main__":
test(1,2,C=3,D=4,F=5) #输出
1
2
<type 'dict'> {'C':3,'D':4,'F':5} test(1,2,3,4,5) #报错,参数个数不对

   2. 调用函数时加上*

    当在调用函数时参数前加上*,(这个参数通常是个sequence之类的iterable对象)意思是把参数值拆分成一个个小单元,并将小单元一一传递给函数作为参数。单元的个数和函数定义时要求的参数值一致。比如:

    

def test(a,b,c):
print "a:",a
print "b:",b
print "c:",c if __name__ == "__main__":
test(*[1,2,3]) #输出:
a: 1
b: 2
c: 3

■  说到函数的实名虚名参数问题,还有一个顺序问题需要注意。就是说在定义和使用时虚名参数一定要放在实名参数之前

  def func(a,b,c=1)  正确

  def func(a,b=1,c)  错误

■  zip函数

  zip函数接受包括0个在内任意多个sequence作为参数。将每个sequence的[0]组合成元组1,[1]组合成元组2........

  最终返回的是[元组1,元组2,元组3....]

  如果参数的长度参差不齐,那就以短的序列为标准,即长的序列多于最短的那个长度的那部分全部都舍去

■  if和else可以写在一行里

  比较容易想到的是[x for x in range(10) if x%2 == 0]这样的列表生成器

  这种列表表达式还可以接上else:[x if x%2 == 0 else x*3 for x in range(10)] 相当于偶数原封不动输出,奇数乘以三

  上面这种表达式的根源是类似后面这个表达式的用法 a if a>0 else -a (计算绝对值)

  在函数的返回语句中大概可以通过这样的表达式来做一个最后的判断。return a if a > 0 else -a

■  类里面可以出现一些常量,写在所有成员方法的外面,前面不用写self.,但是调用的时候要用self.常量名调用。

  比如:

class test():
class_dict = {"ip":"127.0.0.1"}
def __init__(self):
self.port = 8080 def __str__(self):
return self.class_dict.get("ip"),self.port print test()
>>>127.0.0.1 8080

  但是一般来说和java里面不一样,这些常量起的都是一些辅助作用

■  Python中自带了round函数来进行小数的进位工作。比如round(1.234,2)就可以得到1.23。而round(1.235,2)可以得到1.24。

■  利用好__future__的unicode_literals

  众所周知,Python3版本中的字符串都是默认是unicode类型而不是str类型了,这大大方便了文本处理之类的工作。那么在Python2里面如何使用这个特性,就在代码开始的地方from __future__ imort unicode_literals即可。

  需要注意的是,即使是默认所有字符串变量都是unicode了也不意味着可以省去文件头上面的coding=xxx的声明。IDE都是无法直接识别中文等非ascii字符的。为了不报syntax error就需要coding=xxx这声明

上一篇:python多线程详解


下一篇:[转] Python Traceback详解