APP下载 微博 微信

Hao4K影音

[KODI] 教你如何开发XBMC(KODI)插件,怎么入门及个人能的Python基础和实用技巧

[复制链接]

发表于 2018年04月26日 23:30 102387 0 来源:多媒体软件> KODI 回帖奖励 |倒序浏览 |

小落叶 帖主

2018-4-26 23:30

教你如何开发XBMC(KODI)插件,怎么入门及个人能的Python基础和实用技巧。

Python是目前xbmc插件开发使用的唯一编程语言,要是想自己做个插件玩玩,Python这玩意儿多少要会点。python老鸟可以直接无视本文。
本文不作为完整的python学习教程,但可以帮助你更快入门和掌握要点。如果能在您日后的开发过程中起到一丁点作用,那将是我的荣幸!

话说磨刀不误砍柴工。下面几楼分块跟大家分享一下我的python学习使用心得


(一)环境搭建
    为了更好的学习Python,建议搭建一些软件环境来提高学习开发效率。
    Python是一门开源的程序设计语言,是一种可交互执行的解释性脚本语言,非常适合简单应用和插件开发。如果只是编写xbmc插件,并且在xbmc里面进行调试的话,可以不用下载安装python软件包。在官方下载地址上有多个版本下载,因为XBMC内置的解释器是基于python2的,所以请大家下载2.7.3版本进行安装。特别注意,python 3在很多地方与python 2是不兼容的。
    DreamPie是一个很好的Python Shell,我经常拿它来进行交互开发,或者作为高级计算器。Python有个优势,不像其他编译性语言一样,一定要等到程序写完,编译通过以后才能运行得到结果。而Python却可以在Python Shell中单独允许一条语句。比如说,你不知道字符串去空格的函数是不是strip了,那么很简单,直接到pytho shell中跑一句 ' abc '.strip() 看得到的结果是不是'a'就知道了,不对再去查文档也不迟。很多时候,一个很大的py文件里面某地方出错了,你完全可以把其中某一些语句挑出来单独在python shell里面去跑,省去反复不停的执行整个文件,而且减少依赖。
    我是用的最多的Python开发环境是Ulipad,因为本身是python写的,跨平台,windows/linux/mac os都可以用。另外一款PyScripter是针对windows平台开发的优秀IDE。两者都具有语法高亮、自动缩进、类型浏览等多种特性,可以提高代码编制效率。当然不嫌麻烦的话,也可以使用记事本来写代码。


(二)变量
    Python的变量是没有类型的,可以赋任意类型的值。变量不需单独定义,一旦赋值即可使用。print函数可以输出变量内容。

  1. >>> url = 'http://www.baidu.com'
  2. >>> page = 4
  3. >>> print url
  4. http://www.baidu.com
  5. >>> print page
  6. 4
  7. >>> page += 5
  8. >>> url += '?page=' + str(page)
  9. >>> print url
  10. http://www.baidu.com?page=9
复制代码

上面的示例都是在python shell中交互运行的结果,以>>>开头的是输入的语句,其他的部分是显示结果。可以看到不需要像有些语言用var声明变量,也不需要int/string/char []*之类的类型定义符来进行定义。很简单,你想用的时候,直接赋值就行了,然后就可以对变量进行任意操作,比如用+=进行自加/连接,作为print函数的参数来输出变量的值。

  1. >>> a = 123
  2. >>> print a
  3. 123
  4. >>> a = 1.24
  5. >>> print a
  6. 1.24
  7. >>> a = "I'm a fine"
  8. >>> print a
  9. I'm a fine
  10. >>> a = (1, 2.4, "hello", {'a': 0, 'b': 1})
  11. >>> print a
  12. (1, 2.4, 'hello', {'a': 0, 'b': 1})
复制代码

在上面的示例中,可以看到同一个变量可以赋不同的值。最后一个看起来稍微复杂点,将在后面的数据类型中一一讲到。

(三)数字
    Python中的数字分整数和浮点数。python的整数有int和long,但是我们使用的时候不用管他。python中整数长度是没有限制的,这和别的很多语言不同,也就是说可以轻松的在python中完成大整数的运算

  1. >>> 2**30
  2. 186: 1073741824
  3. >>> 9**99
  4. 187: 29512665430652752148753480226197736314359272517043832886063884637676943433478020332709411004889L
  5. >>> 2358321783728157823*23594389258432 + 29512665430652752148753480226197736314359272517043832886063884637676943433478020332709411004889
  6. 210: 29512665430652752148753480226197736314359272517043832886063884693320105595399861474399840518425L<blockquote>>>> (1+2)*(39-13)
复制代码
在Python中,long类型的数值会在最后加上一个大写的L,但是你在输入的时候完全可以不用写。这是内部的类型转换,是无需关注和进行显示转换的。整数和浮点数进行运算时,会自动将整数转化为浮点后进行运算,得到浮点数结果。用int函数将浮点数转换为整数时,不进行四舍五入,而是简单的抛弃小数部分,这点需要注意。我们可以用int(x+0.5)的方式来进行四舍五入,当然也可以直接使用round()函数。
    还有一点需要注意的是,/在python 2.x中,对于整数而言跟//的用法是相同的,都是取整数部分,这点很容易被忽略。%符号是用于取余数的运算符。divmod可以同时获得商和余数。

  1. >>> 5/3
  2. 221: 1
  3. >>> 5%3
  4. 222: 2
  5. >>> 5/3.0
  6. 223: 1.6666666666666667
  7. >>> 5//3
  8. 224: 1
  9. >>> 5//3.0
  10. 225: 1.0
  11. >>> divmod(5, 3)
  12. 226: (1, 2)
复制代码
对于变量加1,没有像c语言里面那样的x++,一般用x=x+1,或者简化为x+=1。

(四)字符串
    字符串是我们在程序中使用最多的类型。Python中没有字符类型,只有字符串。字符串可以用单引号或者双引号包围起来。可以用\符号在行的末尾进行换行,这是一种语法形式的换行,在很多地方使用,比如这行代码太长了。\换行的字符串实际并不包括换行符。
  1. 236: 'bbb'
  2. >>> print 'hello'
  3. hello
  4. >>> print "hello"
  5. hello
  6. >>> 'hello' == "hello"
  7. 237: True
  8. >>> print 'hello \
  9. ... world'
  10. hello world
复制代码

单引号和双引号可以嵌套使用。这点在使用中很方便,比如一个html代码片段,里面有双引号,在字符串里面就必须进行转义。但是使用单引号就可以省却这个麻烦。
  1. >>> "<a href="#">test</a>"
  2. 238: '<a href="#">test</a>'
  3. >>> '<a href="#">test</a>'
  4. 239: '<a href="#">test</a>'
复制代码

把几个字符串放到一起,Python会自动进行拼接,也可以用+号进行显示拼接。
  1. >>> print 'hello' "world"
  2. helloworld
  3. >>> print 'hello' +"world"
  4. helloworld
复制代码

字符串中包含换行符的,需要用\n进行表示。但是Python有一种很方便的方法,就是使用连续三个’或者"将字符串包围起来,则其中所有字符都原封不动的保留,包括空格和回车。这在构造一段html代码的时候很有用。

  1. >>> print 'Dear J:\n Hi.\n yours.'
复制代码

正则表达式是我们在编写插件过程中最常见的技巧,但是正则表达式本身有\符号进行转义。比如\\表示\,\*表示*。但是\本身在python字符串中也是起转移作用的,那么要么你多转义几次,要么使用raw string,就是在字符串加上一个r,表示字符串内不需要进行转义。下面的示例在正则表达式中表示三个字符:^\*

  1. >>> print '\\^\\\\\\*'
  2. \^\\\*
  3. >>> print r'\^\\\*'
  4. \^\\\*
复制代码

字符串是只读的,不能修改其中的某些字符。如果需要修改,必须重新构造一个字符串。

  1. >>> s='abb'
  2. >>> s[0]='b'
  3. Traceback (most recent call last):
  4. File "<pyshell#374>", line 1, in <module>
  5. s[0]='b'
  6. TypeError: 'str' object does not support item assignment
  7. >>> s='b' + s[1:]
  8. >>> s
  9. 236: 'bbb'
复制代码

上例中的s[1:]用法在python中叫做切片(slice)。不仅对于字符串,对于后面讲到的tuple和列表都是同样的用法。基本的用法是s[start:end:step],start是切片开始的位置,python中从0开始,end表示结束的位置,注意s[end]这个字符本身不包含在内。这样end-start就是最后切片的长度,当然如果end小于start,得到的是空字符串''。step表示切片的步长,默认为1。简单来说,就是从start开始取,依次是start+step、start+step*2、...一致到小于end的所有字符。start、end、step都可以为负数,start和end负数表示从字符串后面开始数起,-1表示最后一个字符,step为负数就从后往前切片,这要求start大于end。len()函数用来获取字符串长度。几个参数都可以省略,start省略表示从0开始,end省略表示取到最后,step默认就是1了。说起来有点犯迷糊,看下面的例子就容易理解了。

  1. >>> a='hello world'
  2. >>> len(a)
  3. 240: 11
  4. >>> a[:] #copy of string
  5. 241: 'hello world'
  6. >>> a[2:] #from the third
  7. 242: 'llo world'
  8. >>> a[2:-1] #except the last one
  9. 243: 'llo worl'
  10. >>> a[::2] # the even chars
  11. 244: 'hlowrd'
  12. >>> a[::-1] #reverse of string
  13. 245: 'dlrow olleh'
  14. >>> 'magnet:?xt=urn:btih:8fcffdf6062379a6a1a0505bb809919870d240eb&dn=%5B%E8%A5%BF%E6%B8%B8%E9%99%8D%E9%AD%94%E7%AF%87%5D.2013.HDTV.720p.x264.AAC-iSCG%5B%E5%9B%BD%E8%AF%AD%E4%B8%AD%E8%8B%B1%E5%AD%97%E5%B9%951.7G%5D'[20:60]
  15. 246: '8fcffdf6062379a6a1a0505bb809919870d240eb'
复制代码

下面的例子里面有一些字符串常见操作:

  1. >>> 'hello world'.upper() #大写
  2. 247: 'HELLO WORLD'
  3. >>> 'HELLO WORLD'.lower() #小写
  4. 248: 'hello world'
  5. >>> 'hello world'.capitalize() #首字母大写
  6. 249: 'Hello world'
  7. >>> ' ab '.strip() #去空格
  8. 250: 'ab'
  9. >>> ' ab '.lstrip() #去除左侧空格
  10. 251: 'ab '
  11. >>> ' ab '.rstrip() #取出右侧空格
  12. 252: ' ab'
  13. >>> 'bc' in 'abcd' #判断是否包含某字符串
  14. 253: True
  15. >>> '中文test123'.encode('base64') #base64编码
  16. 262: '5Lit5paHdGVzdDEyMw==\n'
  17. >>> print '5Lit5paHdGVzdDEyMw==\n'.decode('base64') #base64解码
  18. 中文test123
  19. >>> '中文test123'.encode('hex').upper() #十六进制编码
  20. 264: 'E4B8ADE6968774657374313233'
  21. >>> print 'E4B8ADE6968774657374313233'.decode('hex')
  22. 中文test123
  23. >>> 'abcdabc'.replace('ab', '**') #替换
  24. 265: '**cd**c'
  25. >>> 'ab|cd|ef'.split('|') #按指定符号分割字符串
  26. 267: ['ab', 'cd', 'ef']
  27. >>> 'ab cd ef'.split() #按空格分割字符串
  28. 268: ['ab', 'cd', 'ef']
  29. >>> 'http://www.baidu.com'.partition('://') #按制定字符串分割成两部分,比split效率更高
  30. 271: ('http', '://', 'www.baidu.com')
  31. >>> ' , '.join(['hello', 'world', '!']) #连接字符串
  32. 270: 'hello , world , !'
复制代码

上面提到用join来将一个列表拼接成字符串的方法经常要用到,而且是效率最高的方法。一些需要动态拼接的字符串,都先append到一个列表,最后用join来形成最终的字符串。
    检查字符串是否以xx开头或者结尾,分别用'abc'.startswith('ab')和'abc'.endswith('bc')的函数来校验。更复杂的需要用到正则表达式,在Python中有re模块对正则表达式进行支持。由于正则是一个非常庞大的话题,在此不做详解。
    转换和格式化

  1. >>> str(10)
  2. 272: '10'
  3. >>> str(1.5)
  4. 273: '1.5'
  5. >>> int('335')
  6. 274: 335
  7. >>> 'htt://%s/test/?page=%d' % ('www.baidu.com', 11)
  8. 275: 'htt://www.baidu.com/test/?page=11'
复制代码

(五)列表和tuple
    列表(list)在Python中的地位也非常重要,在插件开发过程中更是经常用到。list有点像C语言的数组,可以按索引遍历访问其中的每一个元素,可以对其进行修改。但是list与c的数组有天壤之别,便利之处也是数组遥不可及的。
    首先,list长度不固定,可以任意追加、插入、删除元素,也可以一个元素都没有,即经常用到的空列表[]。从某种意义上说,list更像数据结构里面的链表,在内存中并不占有连续的空间。list元素也不限定数据类型,可以是任意Python类型,数字、字符串、字典...,甚至是另一个list,或者一个函数。
    list可以用索引进行访问,比如x[2];可以切片,比如x[:-3];还可以迭代for item in aList: print item。下面来看看list长什么样子吧。
  1. >>> urls = []
  2. >>> urls.append('www.baidu.com')
  3. >>> urls.append('www.google.com')
  4. >>> urls.insert(0, 'xbmc.org') #注意顺序,在最前方插入
  5. >>> urls.extend(['a', 'b', 'c'])
  6. >>> urls
  7. 278: ['xbmc.org', 'www.baidu.com', 'www.google.com', 'a', 'b', 'c']
  8. >>> urls.pop() #pop the last one and return
  9. 279: 'c'
  10. >>> urls
  11. 280: ['xbmc.org', 'www.baidu.com', 'www.google.com', 'a', 'b']
  12. >>> urls.pop(0) # pop the first
  13. 281: 'xbmc.org'
  14. >>> urls
  15. 282: ['www.baidu.com', 'www.google.com', 'a', 'b']
  16. >>> urls[2:]
  17. 283: ['a', 'b']
  18. >>> urls[2] = [1,2,3]
  19. >>> urls
  20. 284: ['www.baidu.com', 'www.google.com', [1, 2, 3], 'b']
  21. >>> urls[2:2] = [4, 5, 6]
  22. >>> urls
  23. 285: ['www.baidu.com', 'www.google.com', 4, 5, 6, [1, 2, 3], 'b']
  24. >>> len(urls)
  25. 286: 7
复制代码

从上面的示例可以看到,可以在列表中任意追加、插入、替换元素。请大家不要误会,list不是一定要从空列表[]开始。你完全可以一开始就 urls = ['a', 'b','c']。切片的操作和字符串类似,只不过字符串不能修改,而列表是可以修改的。urls[2:2]=[4,5,6]就利用这个技巧,在2的位置上加了三个元素,注意和urls[2]=进行区别。extend函数直接将另一个list直接追加到最后,省的一个一个append。
    列表可以查找和删除指定元素,不仅仅是通过索引位置,还可以根据元素的值进行定位和删除,分别是index和remove函数。需要注意的是,如果多个元素值相同的话,只针对第一个出现的元素。sort和reverse函数分别对list进行排序和反转,这两个函数都不返回值,这点需要注意一下。如果需要排序结果,但不影响原list的话,就使用sorted函数。

  1. >>> urls.append(4)
  2. >>> urls.count(4)
  3. 287: 2
  4. >>> urls.index(4)
  5. 288: 2
  6. >>> urls.remove(4)
  7. >>> urls
  8. 289: ['www.baidu.com', 'www.google.com', 5, 6, [1, 2, 3], 'b', 4]
  9. >>> urls.sort()
  10. >>> urls
  11. 290: [4, 5, 6, [1, 2, 3], 'b', 'www.baidu.com', 'www.google.com']
  12. >>> urls.reverse()
  13. >>> urls
  14. 291: ['www.google.com', 'www.baidu.com', 'b', [1, 2, 3], 6, 5, 4]
  15. >>> sorted(urls)
  16. 292: [4, 5, 6, [1, 2, 3], 'b', 'www.baidu.com', 'www.google.com']
复制代码

range函数返回一个数字列表。参数和切片有些类似,可以指定起至值和step。看下面的例子就明白了:

  1. >>> range(10)
  2. 293: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
  3. >>> range(5, 10)
  4. 294: [5, 6, 7, 8, 9]
  5. >>> range(10, -1, -2)
  6. 295: [10, 8, 6, 4, 2, 0]
复制代码

因为返回的结果是一个list,是需要在内存中真实存在的。所以如果你需要一个从1到一百万的序列,请不要使用range函数,而是用xrange,得到的是一个迭代器(iterator),不会一次性在内存中生成。
    下面要讲一下关于迭代的问题。对于任意可以迭代的数据,我们用 for 变量 in 数据 的语法形式来进行访问。我们已经知道,list可以根据索引访问。那么类似c语言数组的做法,很容易想到的是下面的用法:

  1. >>> aList = ['a', 'b', 3, 4, 'e', 'f', 3.9]
  2. >>> for i in range(len(aList)):
  3. ... print aList[i]
  4. a
  5. b
  6. 3
  7. 4
  8. e
  9. f
  10. 3.9
复制代码

但是,这种访问方式很明显多此一举了。按照Python的思想,要用最简单的语句最多的事情。

  1. >>> for item in aList:
  2. ... print item
  3. a
  4. b
  5. 3
  6. 4
  7. e
  8. f
  9. 3.9
复制代码

即简单,又简洁。如果同时确实元素的索引值,可以用enumerate函数

  1. >>> for i, item in enumerate(aList):
  2. ... print i, item
  3. 0 a
  4. 1 b
  5. 2 3
  6. 3 4
  7. 4 e
  8. 5 f
  9. 6 3.9
复制代码

enumerate返回的一个元素为tuple的list,可能类似[(0, 'a'), (1, 'b'), ...]这样的形式。我们在for里面用到了两个变量,这叫做unpack(好像是这么称呼),就是将一个序列解开到多个变量。顺便先提一下, a,b这样的形式就叫做tuple,它和(a,b)是一样的,类似list,区别在于它不能修改。我们来看看upack怎么用。

  1. >>> a,b = [1,2]
  2. >>> a, b
  3. 296: (1, 2)
  4. >>> print a, b
  5. 1 2
  6. >>> a, b = ('hello', 'world')
  7. >>> print a,b
  8. hello world
  9. >>> (a, b, c) = [3, 4, 5]
  10. >>> print a, b, c
  11. 3 4 5
  12. >>> name, url = ('百度', 'www.baidu.com')
  13. >>> print name
  14. 百度
  15. >>> print url
  16. www.baidu.com
  17. >>> name, url = url, name
  18. >>> print name
  19. www.baidu.com
  20. >>> print url
  21. 百度
复制代码

可以看到,unpack在变量赋值的过程中是非常有用的。name, url = aList 相当于name=aList[0] url=aList[1]。显然前面的方式更加简洁明了。甚至我们用name, url = url, name这样的语句,简单的交换了两个变量的值,这在c语言里面不用第三个变量中转是无法做到的。
    list的迭代使用的确很方便,很强大。但是很多情况下还不需要这么复杂,因为有list comprehension,就是用一种表达式将原来的list进行运算变形,得到新的表达式。比如我需要得到1-10这10个数的平方,保存为一个列表。采用传统的方法:

  1. >>> aList = []
  2. ... for i in range(11):
  3. ... aList.append(i*i)
  4. >>> aList
  5. 298: [0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
复制代码

你再看看下面更简洁的方法:

  1. 298: [0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
  2. >>> aList = [i*i for i in range(11)]
  3. >>> aList
  4. 299: [0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
复制代码

别看这个例子很简单,其实在我们写程序的过程中经常会用到,大大简化我们的程序。有兴趣也可以翻翻我写的插件源代码,到处充斥这这样的应用。
    我们还可以用if来对产生的结果进行过滤。比如上例中,我要挑出3次方被3除余2的数字的平方(有点绕口)。

  1. <blockquote>>>> [i*i for i in range(11) if i**3 % 3 == 2]
复制代码

再来说说tuple,前面已经大致提到过。形如(1,2,3)用括号和逗号构造的序列就叫做tuple,有的翻译为元组,我觉得不习惯。它和list很相似,可以用索引进行访问,可以进行迭代,可以切片。tuple和list最大的区别就是它和字符串一样,不能对它进行任何修改。字符串从某种意义来讲,可以认为是单个字符组成的tuple。
    tuple用于某些不希望别人修改它的场合,比如作为函数参数传入,作为字典的key等等。构造tuple很简单,需要注意的是,如果一个tuple只有一个元素,不是(1)这样的形式,这样会当作括号符进行运算,得到1这个整数。正确的语法是(1,),就是后面一定要有一个逗号,虽然有点怪异,但是习惯就好了。讲到这里,顺便提一下,tuple、list和我们后面要讲到的dict(字典),最后一个元素后面都可以带一个逗号,不会出现语法错误。我们经常会这么干,便于追加记录,比如

  1. a = [
  2. 'a',
  3. 'b',
  4. ]
复制代码

tuple也可以用+连接,构造新的tuple。下面的例子介绍一些常见的tuple形式和运算

  1. >>> (1, 2, 'a', 'b')
  2. 300: (1, 2, 'a', 'b')
  3. >>> (1, 2, 'a', 'b') + (4, 5)
  4. 301: (1, 2, 'a', 'b', 4, 5)
  5. >>> (1, 2, 'a', 'b') + (6, )
  6. 302: (1, 2, 'a', 'b', 6)
  7. >>> ()
  8. 303: ()
  9. >>> (1,)
  10. 304: (1,)
复制代码

tuple和list可用tuple和list内置函数进行相互转换

  1. >>> aList = range(10)
  2. >>> aList
  3. 307: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
  4. >>> t = tuple(aList)
  5. >>> t
  6. 308: (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
  7. >>> list(aList)
  8. 309: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
复制代码

迭代同样适用于tuple

  1. >>> ''.join(str(i) for i in t)
  2. 311: '0123456789'
复制代码

上例中,t是一个10个数字的tuple。通过list comprehension表达式,将t里面的每一个数字用str函数转换成字符串。最后的得到一个generator用''.join连接在一起,形成最终的字符串。中间的表达式的结果实际上是一个generator,不用了解太深,知道可以这么用就可以了。

  1. >>> (str(i) for i in t)
  2. 312: <generator object <genexpr> at 0x3144140>
复制代码

我们可以用in来判断某个指定的元素在list/tuple中是否存在。比如 'a' in ('a', 'b'),'b' not in ['a', 'b']

最后,来看一个稍微复杂点的例子吧,将文件中以LOG: 开头的行显示出来,并且去掉LOG:。

  1. print ''.join([line[4:] for line in open('a.txt') if line.startswith('LOG:')])
复制代码
其中open函数打开一个文件,返回一个每行数据的迭代,基本上你可以认为返回一个list,每个元素表示一行。

(六)字典
字典也是Python中非常重要的数据类型。它是一种key-value对,以hash表方式存储的数据结构。dict只能用key进行访问,它是无序的。网页API经常用到的JSON数据格式,经常见到dict的身影。比如说豆瓣电影上 西游降魔篇的介绍 http://api.douban.com/v2/movie/subject/5308265

{
"rating": {
"max": 10,
"average": 7.2,
"stars": "40",
"min": 0
},
"reviews_count": 3673,
"wish_count": 25322,
"collect_count": 236509,
"douban_site": "",
"year": "2013",
"images": {
"small": "http:\/\/img3.douban.com\/spic\/s24423024.jpg",
"large": "http:\/\/img3.douban.com\/lpic\/s24423024.jpg",
"medium": "http:\/\/img3.douban.com\/mpic\/s24423024.jpg"
},
"alt": "http:\/\/movie.douban.com\/subject\/5308265\/",
"id": "5308265",
"mobile_url": "http:\/\/movie.douban.com\/subject\/5308265\/mobile",
"title": "西游降魔篇",
"do_count": null,
"seasons_count": null,
"schedule_url": "http:\/\/movie.douban.com\/subject\/5308265\/cinema\/",
"episodes_count": null,
"genres": ["喜剧", "奇幻", "冒险"],
"countries": ["中国大陆", "香港"],
"casts": [{
"avatars": {
"small": "http:\/\/img3.douban.com\/img\/celebrity\/small\/1218.jpg",
"large": "http:\/\/img3.douban.com\/img\/celebrity\/large\/1218.jpg",
"medium": "http:\/\/img3.douban.com\/img\/celebrity\/medium\/1218.jpg"
},
"alt": "http:\/\/movie.douban.com\/celebrity\/1011513\/",
"id": "1011513",
"name": "文章"
}, {
"avatars": {
"small": "http:\/\/img3.douban.com\/img\/celebrity\/small\/1268.jpg",
"large": "http:\/\/img3.douban.com\/img\/celebrity\/large\/1268.jpg",
"medium": "http:\/\/img3.douban.com\/img\/celebrity\/medium\/1268.jpg"
},
"alt": "http:\/\/movie.douban.com\/celebrity\/1138320\/",
"id": "1138320",
"name": "舒淇"
}, {
"avatars": {
"small": "http:\/\/img3.douban.com\/img\/celebrity\/small\/1656.jpg",
"large": "http:\/\/img3.douban.com\/img\/celebrity\/large\/1656.jpg",
"medium": "http:\/\/img3.douban.com\/img\/celebrity\/medium\/1656.jpg"
},
"alt": "http:\/\/movie.douban.com\/celebrity\/1274242\/",
"id": "1274242",
"name": "黄渤 "
}, {
"avatars": {
"small": "http:\/\/img3.douban.com\/img\/celebrity\/small\/3083.jpg",
"large": "http:\/\/img3.douban.com\/img\/celebrity\/large\/3083.jpg",
"medium": "http:\/\/img3.douban.com\/img\/celebrity\/medium\/3083.jpg"
},
"alt": "http:\/\/movie.douban.com\/celebrity\/1274317\/",
"id": "1274317",
"name": "罗志祥"
}
],
"current_season": null,
"original_title": "西游降魔篇",
"summary": "大唐年间妖魔横行,一小渔村因为饱受鱼妖之害请来道士(冯勉恒 饰)除妖,年轻驱魔人陈玄奘(文章 饰)前来帮忙却被误认为骗子,幸亏职业赏金驱魔人段小姐(舒淇 饰)帮助玄奘制服了鱼妖真身(李尚正 饰)。二人又在高家庄为制服猪妖猪刚鬣(陈炳强 饰) 而再次相遇,这次除妖没有成功 ,但是段小姐却对玄奘二见钟情。玄奘求助师父,得知除妖的办法是去找被压在五指山下的孙悟空(黄渤 饰)帮忙,于是他准备前往五指山,途中又遇到段小姐和手下五煞,段小姐连蒙带哄想与玄奘在一起却屡次遭拒,在四妹(周秀娜 饰)调教下想变得更有女人味却适得其反。二人决裂后玄奘独自上路,与此同时降魔师(释延能 饰)、天残脚(张超理 饰)、空虚公子(罗志祥 饰)也一同前往除妖。经过千辛万苦玄奘终于找到孙悟空,段小姐又再次出现并交给玄奘一件重要的东西,猪妖终于被降服,但是更大的危机又出现在了玄奘面前,原来孙悟空与传闻中不一样,玄奘的除魔之路能否继续?\n本片是周星驰多年之后的力作,演员阵容延续了以往,除了主演外,还请了诸如卢正雨、杨迪等网络红人加盟。&#169;豆瓣",
"subtype": "movie",
"directors": [{
"avatars": {
"small": "http:\/\/img3.douban.com\/img\/celebrity\/small\/281.jpg",
"large": "http:\/\/img3.douban.com\/img\/celebrity\/large\/281.jpg",
"medium": "http:\/\/img3.douban.com\/img\/celebrity\/medium\/281.jpg"
},
"alt": "http:\/\/movie.douban.com\/celebrity\/1048026\/",
"id": "1048026",
"name": "周星驰"
}, {
"avatars": {
"small": "http:\/\/img3.douban.com\/img\/celebrity\/small\/13093.jpg",
"large": "http:\/\/img3.douban.com\/img\/celebrity\/large\/13093.jpg",
"medium": "http:\/\/img3.douban.com\/img\/celebrity\/medium\/13093.jpg"
},
"alt": "http:\/\/movie.douban.com\/celebrity\/1274244\/",
"id": "1274244",
"name": "郭子健"
}
],
"comments_count": 104398,
"ratings_count": 204027,
"aka": ["除魔传奇", "西游", "大话西游之除魔传奇", "三藏伏魔", "大话西游3", "西游·降魔篇", "Journey to the West: Conquering the Demons", "Odyssey"]
}

看看这些数据,基本上就是dict和list的组合。dict的表达能力很强,它的key可以是整数、字符串、tuple,以字符串居多。value可以是任意类型。上面的示例中可以看到很多dict和list相互嵌套使用。
看看字典一些常见操作:

  1. >>> d = {'a': 1, 'b': 2, 'c': 3}
  2. >>> d['b']
  3. 314: 2
  4. >>> d['v']
  5. Traceback (most recent call last):
  6. File "<pyshell#502>", line 1, in <module>
  7. d['v']
  8. KeyError: 'v'
  9. >>> d.get('v')
  10. >>> d.get('v', 'default value')
  11. 315: 'default value'
  12. >>> d.get('b', 'default value')
  13. 316: 2
  14. >>> len(d)
  15. 317: 3
  16. >>> 'a' in d
  17. 318: True
  18. >>> 'v' in d
  19. 319: False
  20. >>> 'v' not in d
  21. 320: True
  22. >>> d.keys()
  23. 321: ['a', 'c', 'b']
  24. >>> d.values()
  25. 322: [1, 3, 2]
  26. >>> d.items()
  27. 323: [('a', 1), ('c', 3), ('b', 2)]
  28. >>> d['b'] = 100
  29. >>> d
  30. 324: {'a': 1, 'b': 100, 'c': 3}
  31. >>> del d['b']
复制代码

我们注意到,在尝试访问不存在的key的时候,会抛出KeyErro异常,但是用get函数却返回None。后者效率更高,而且可以指定缺省值。比如我们从参数列表中取页码,就可以params.get('page', '1'),这样如果参数page存在则直接返回,不存在就给默认值1。keys、values、items函数分别返回key、value和tuple的列表,可用于遍历。del将数据从字典中删除。
get函数,有些类似于下面的代码,但是更简洁、高效。

  1. d.get(key, default)

  2. if key in d:
  3. return d['key']
  4. else:
  5. return default
复制代码

再演示一个具体的应用,比如我要统计一个字符串中间,出现的每个字符的频次。

  1. <blockquote>>>> result = {}
复制代码

非常的简洁。另外一个类似的函数setdefault,也是在key存在时返回相应的value,不存在则返回默认值。不同的是,如果key不存在,它会将default值写到dict里面去。上面的例子,如果变一下需求,只是统计一下出现的字符,而不需要频次,可以这么做

  1. <span style="line-height: 1.5;">>>> result = {}</span>
复制代码

setdefault可以随便给个缺省值,因为我们要的只是key。当然你可一个把 result.setdefault(char, None) 替换成result[char] = None 来达到同样的目的。但是这样每次都需要去写dict,效率上应该会低一些,也没有前面的语句意思更加明确。

另外一个pop函数,跟get和setdefault有异曲同工之妙,它的作用是通过key删除一个元素,并返回它的value,如果不存在则返回默认值,但是不指定default参数则会抛出异常。这个函数用到的几率也很高。比如你从一些准备发送post的数据中间,把page参数挑出来,放到url后面的params参数中间,就可以使用pop函数。

  1. >>> data = {'a': 1, 'b':2, 'page': 15}
  2. >>> page = data.pop('page', 1)
  3. >>> page
  4. 325: 15
  5. >>> data
  6. 326: {'a': 1, 'b': 2}
复制代码

注意不要将pop函数和popitem函数混淆了。popitem是从dict中随机删除一条记录,返回(key, value)。

如果你希望建立一个学生成绩表,并且将成绩初始化为0,不需要一个一个对dict赋值,你可以这么做

  1. >>> students = ['Jack', 'Tom', 'Jerry']
  2. >>> dict.fromkeys(students, 0)
  3. 327: {'Jack': 0, 'Jerry': 0, 'Tom': 0}
复制代码

清空dict只需要调用clear()函数。copy()函数可以将dict复制一份,不过需要注意的是浅拷贝(shadow copy),就是只复制value引用。看下面的示例就明白了。

  1. >>> d = {'a': 1, 'b': 2, 'c': (1,2), 'd': [1,2], 'e': {'hi': 'boy'}}
  2. >>> d2 = d.copy()
  3. >>> d2
  4. 332: {'a': 1, 'b': 2, 'c': (1, 2), 'd': [1, 2], 'e': {'hi': 'boy'}}
  5. >>> d['a'] = 10
  6. ... d['c'] = (3,4)
  7. ... d['d'][0] = 100
  8. ... d['e']['evil'] = True
  9. >>> d2
  10. 333: {'a': 1, 'b': 2, 'c': (1, 2), 'd': [100, 2], 'e': {'evil': True, 'hi': 'boy'}}
  11. >>> d
  12. 334: {'a': 10, 'b': 2, 'c': (3, 4), 'd': [100, 2], 'e': {'evil': True, 'hi': 'boy'}}
复制代码

d2是d的一份copy,但是我们修改d['d']里面的元素,却导致了d2被改变。可以看到,d['d']是被copy到d2['d']来了,但是只是引用,它们都指向同一个list。而整数、tuple等其他不可修改的数据则不存在类似的问题。一定要引起注意,这是很容易犯的错误,而且很不容易被发现。

最后就是dict的遍历,比较容易懂,注意是in d.items()而不是in d就行了

  1. >>> for key, val in {'a': 1, 'b': 2, 'c': 100}.items():
  2. ... print '%s=%s' % (key, val)
  3. a=1
  4. c=100
  5. b=2
复制代码

(七)函数
函数在Python以def关键词定义,python函数可以指定缺省值。python函数调用的时候,不仅可以通过位置传递参数,也可以通过参数名称来进行传递,命名参数传递时,不需要指定参数顺序。

  1. >>> def add(a, b=1):
  2. ... return a+b
  3. >>> add(1,2)
  4. 335: 3
  5. >>> add(1)
  6. 336: 2
  7. >>> add(b=2, a=3)
  8. 339: 5
复制代码

函数本身是一种数据类型,可以赋值给变量,然后通过变量来调用。

  1. >>> def add(a, b):
  2. ... return a+b
  3. ... def sub(a, b):
  4. ... return a-b
  5. >>> data = [(1,2), (7,3)]
  6. >>> [[f(a,b) for f in (add, sub)] for a,b in data]
  7. 341: [[3, -1], [10, 4]]
复制代码

不知道大家看明白了没有。首先定义了add和sub两个函数,分别用于加、减传入的两个参数。后面是一个list comprehension,data是一个元素为两个元素tuple的list,for a,b 将tuple进行unpack,对于list的第一个元素(1,2),得到的是a=1, b=2。返回另外一个list:[f(a,b) for f in (add, sub)]。这里又是一个list comprehension,对(add, sub)这个tuple进行遍历,我们说过,函数也是一种数据,所以for f in (add, sub)实际上是先后给f这个变量赋上add和sub两个函数,然后f就可以当作相应的函数来调用了。当f 取 add时,f(a,b)就是add(a,b)。所以最后得到的就是data中每个pair的和与差。没那么复杂,想想就明白了。

还一种常用的形式,就是把函数当作dict的value。根据key 进行选择。比如说,你需要根据一个url的协议来选择相应的函数来运行,你可以这么做

  1. if protocol == 'http':
  2. handle_http(url)
  3. elif protocol == 'ftp':
  4. handle_ftp(url)
  5. elif ...
  6. ...
  7. else
  8. handle_default(url)
复制代码

有点类似其他语言中的switch,但是Python中没有switch,因为根本就不需要这种语法,直接用dict就可以实现,而且更清晰。

  1. handlers = {
  2. 'http': handle_http,
  3. 'ftp': handle_ftp,
  4. ...
  5. }
  6. handler = handlers.get(protocol, handle_default)
  7. handler(url)
复制代码

带来的另外一个好处,就是把协议和对应的处理函数放在一个字典里面,整个handlers可以作为数据管理,你甚至可以从配置文件里面加载这样的隐射关系!好处不言而喻了。

最后简单介绍一下lambda表达式。它是Python里面的匿名函数。比如 lambda x:x+1 就是一个接受一个参数,并且返回x+1值的函数。注意冒号后面的只能是一个表达式,而不能是多个语句,你可以使用条件表达式,比如 abs = lambda x: x if x>=0 else -x 。这样就简单定义了一个取绝对值的函数。配合dict,你可以这么做:

  1. operator = 'add'
  2. data = (1, 3)
  3. {'add': lambda x,y: x+y,
  4. 'sub': lambda x,y: x-y
  5. }[operator](*data)
复制代码


你能猜到它的结果吗?对了,就是4!需要说明一点的是,在传递参数过程中,使用*号,是将后面的数据展开。f(*(1,2,3))就等同于f(1,2,3)。类似的用法,**可以解开dict,用于命名参数。比如f(a,b),你可以这么调用 f(**{'a':1, 'b':2})。是不是觉得Python非常强大呢?!


(八)模块、package
模块是什么?简单来说,模块就是一个.py文件,虽然这种说法不一定很确切。package差不多就是一个包含.py模块的目录。Python内置了大量的模块,除了Python语法本身的强大以外,其他的功能都依赖Python丰富的模块和第三方模块。比如你要处理图像,就有堪比PhotoShop的PIL包,要访问网页就有httplib、urllib、urllib2、requests等等一系列好的模块等你来挑。

假如你有一个utils.py文件,里面包含了大量的通用函数。而同目录的插件执行文件addon.py需要调用这些函数,怎么办呢,很简单,只要在文件前面使用import utils语句,你就可以用utils.myfunc来调用utils模块的myfunc函数了。当然如果你不想写那么长的名称,你也可以使用from utils import myfunc的语法来引入myfunc函数,调用就更简单了,直接myfunc()。需要注意的是,如果另外一个模块也有myfunc函数,而且都要在addons里面使用,那么from .. import myfunc就会造成名字空间冲突了,但是import 模块名的形式不会出现这样的问题。一般来说from .. import 的运行效率更好一些。

Python的文档非常完善,虽然都是english。在你不清楚一个模块或者一个函数怎么使用的时候,最直接的办法就是查Python的docs。我想很少有人能够丢开python docs来编写一个大型程序吧。

你的utils.py不一定每次都在跟addon.py相同的目录下,很可能他放到了common目录下面了,那么import utils肯定是找不到模块的。怎么办呢?两种办法,一是构造一个common的包,然后用 import common.utils引入;另一种是增加Python的搜索路径,然后import utils。

我们先说第一种方法,更通用的方法。前面说过,package就是包含py模块的目录,Python怎么知道这就是一个package呢?成为package的先决条件是,这个目录下必须有一个叫做__init__.py的文件,哪怕是个空文件都行。事实上,很多package的__init__.py都是空文件,什么都没做,这个文件是在你访问package的时候自动执行的,如果你不是非常了解它是什么样的运作机制,那么就让它永远空着吧。package是可以嵌套的,其实就是目录层级关系嘛。Python自带的有一个模块就是xml.sax.saxutils,你必须import xml.sax.saxutils,或者from xml.sax.saxutils import *来使用它。就这么简单。

Python是怎么寻找这些module和package的呢?很明显,当前目录是排在最前面的,如果在当前目录下搜索不到的话,它会在sys.path指定的目录里面去查找。

  1. >>> import sys
  2. ... print sys.path
  3. ['', '/usr/local/lib/python2.7/dist-packages', '/usr/local/lib/python2.7/dist-packages/dreampielib/data', '/usr/lib/python2.7', '/usr/lib/python2.7/plat-linux2', '/usr/lib/python2.7/lib-tk', '/usr/lib/python2.7/lib-old', '/usr/lib/python2.7/lib-dynload', '/usr/local/lib/python2.7/dist-packages', '/usr/lib/python2.7/dist-packages', '/usr/lib/python2.7/dist-packages/PIL', '/usr/lib/python2.7/dist-packages/gst-0.10', '/usr/lib/python2.7/dist-packages/gtk-2.0', '/usr/lib/pymodules/python2.7', '/usr/lib/python2.7/dist-packages/wx-2.8-gtk2-unicode']
复制代码

你的显示结果跟我可能不同。Python会按照list里面目录的顺序,挨个挨个去搜索我们要import的module或package,找不到就抛出ImportError异常。你可以自己修改sys.path,不过就是一个列表嘛!

  1. sys.path.append('common')
复制代码

这样就将common目录加到了搜索路径当中,你再使用import utils肯定就能找到你的utils模块了。

Python有一个package库 https://pypi.python.org/pypi,第三方的package绝大多数都在这里提交,你可以在这里找到绝大多数你所需要的功能。在Python的安装环境中,如果你需要安装使用第三方package,一般使用easy_install或者pip来安装。在这里不多说。

在插件开发过程中,我们也往往会用到很多第三方模块,一般放到resources/lib目录下面,然后增加sys.path里面的搜索路径,或者新建__init__.py,以resources.lib.xxx的形式来import。具体放置的位置不做强制限定。还有一种情况,就是已经有人将Python package做成了xbmc的插件供大家使用,我们只需要在addon.xml的requires节中增加相应的依赖关系即可。

  1. <requires>
  2. <import addon="xbmc.python" version="2.0"/>
  3. <import addon="script.module.xbmcswift2" version="2.4.0" optional="false"/>
  4. <import addon="script.module.elementtree" version="1.2.7" optional="false"/>
  5. <import addon="script.module.beautifulsoup" version="3.2.0" optional="false"/>
  6. </requires>
复制代码


(九)中文编码
如果你不用中文,这节你就甭看了 ^_^

首先要介绍一下,Python里面有str和unicode两种数据类型。前者说白了就是一些有序的字节串,每个字节可以从0~255没有任何限制,Python本身也不明白你这些字节序列是什么含义,到底是英文字符,还是中文字符,是gbk编码的中文,还是utf-8的编码。所以我们谈到的编码只有在I/O的时候才有意义。

unicode是一种特殊的字符串,它采用两个字节来代表一个字符,无一例外。随着计算机系统软件的国际化,很多程序内部都使用unicode编码,它可以涵盖世界上任何民族语言和方言的字符,不会产生任何奇异。像windows在2000以后吧,API内部都是使用unicode来表示字符串的。对于unicode而言,肯定是没有字符集而言的。

那么什么是字符集?字符集其实就是一种读取str内部自己序列的规范。拿GBK来说,ascii值在某个阀值以下的当作普通ascii处理,超过的需要跟后面一个字节连在一起来识别字符,中文都是占用两个字节。而UTF-8中的中文是以三个字节来表示的,英文也只占用一个字节。GBK能表示的字符数量有限,甚至有些生僻汉字都可能不被包含在内。但是UTF-8是以Unicode为基础的,它可以涵盖所有Unicode字符。因为计算机世界的标准大都还是说english的国家制定的,可能考虑到编码效率等诸多原因,一般在网络传输或者以文件形式存储时,采用UTF-8编码,而不是Unicode的双字节表示模式(UTF-16好像就是这个)。

XBMC采用的是UTF-8字符集,可以测试,传utf-8编码的中文串给xbmc显示正常。当然你传unicode过去也没问题,它内部应该做了转换的。

我们一般需要注意字符集的地方主要有HTTP数据、数据库数据和文件内容这几类。基本上都是在I/O部分,读取网络数据、数据库里面的记录、读取文件内容,还有GET/POST网页、写数据库和写文件的时候。还有一种I/O,就是用input函数要求用户输入和使用print在屏幕打印数据的时候,也涉及到字符集的问题。

回到编写XBMC插件的话题上来说,需要把握的原则是尽可能的在程序中使用unicode,这样最大可能的避免中文字符集造成的问题。.py文件是文本文件,那么它本身也是有字符集编码的,你用windows的记事本,很可能就是GBK,有些编辑软件设置的默认字符集是UTF-8。在这里我建议大家都是用utf-8编码保存.py文件,避免出现不必要的麻烦,和可能丢失数据的风险(GBK不能表示的字符有很多)。

大家可能注意到了,在.py文件的第一行或者第二行可能都有这么一条语句 #coding=utf-8。看起来是注释,但是python解释器会根据这个来识别字符集,有点类似html里面的编码指定。事实上,你如果没有指定coding,而包含了中文字符,在执行的时候肯定会报错的。像前面讲到的一样,建议大家都是用utf-8编码保存python源代码,然后在最前面加上#coding=utf-8指明编码。

我们来看看,在.py代码中使用中文字符串的时候,会是一个什么样的情况。比如 a = '中文',python解释器注意到这里是一个str类型的字符串,因为文件编码是utf-8,那么在内存里面,变量a就是一个编码为utf-8的字符串,说白了就是6个字节(一个汉字占3个字节)。但是如果你的编码是GBK,并且用#coding=gbk表明,那么得到的变量a就是一个4个字节的GBK编码的字符串。

试想一下,如果我的文件保存的时候使用utf-8编码,但是前面的申明却写成了gbk,会出现什么样的结果?我们来做下实验,新建一个a.py文件,内容如下:
  1. #coding=gbk
  2. print repr('中文')
复制代码


文件以utf-8编码保存,python a.py执行看看会出现什么状况?

  1. $ python a.py
  2. File "a.py", line 2
  3. SyntaxError: 'gbk' codec can't decode bytes in position 14-15: illegal multibyte sequence
复制代码

嗯,发现什么没有?python解释器首先根据你指定的编码来识别文件内容,你看你让它用gbk编码来读取文件内容,前面的英文字符部分没问题,一旦碰到“中文”就出问题了,明明文件是6个字节保存的utf-8编码,你强制让它用GBK解码,能成功吗?好比你用密码abc压缩的rar文件,你一定要用密码123来解密,可能吗?这说明文件保存的编码一定要与声明的编码一致!


既然同一个python文件,在使用不用编码保存的时候,中文字符串会出现完全不同的结果,我们怎么保证程序运行的正确性呢?答案是设置非ascii字符的一律使用unicode字符串。
a = u'中文'
这样,不管你的源代码是gbk编码也好,是utf-8编码也好,还是什么别的乱七八糟的编码也好,python解释器都会忠实的给你转换成unicode。哇哦,这下可以松口气了!所以这里提到另一个重要的原则,就是.py源码中出现的的中文字符串一律使用unicode字符串!我们可以在需要的时候再进行编码。

下面要提到的是系统默认编码

  1. >>> import sys
  2. >>> sys.getdefaultencoding()
  3. 'ascii'
复制代码

python解释器使用的默认编码是ascii,这个默认编码什么时候使用呢?在你进行编码转换,但是没有明确指定需要转换的编码时就使用这个默认编码(有时候好像也参考了操作系统指定的编码)。

  1. >>> a = '中文'
  2. >>> print a
  3. 中文
  4. >>> print repr(a)
  5. '\xe4\xb8\xad\xe6\x96\x87'
  6. >>> unicode(a)
  7. Traceback (most recent call last):
  8. File "<stdin>", line 1, in <module>
  9. UnicodeDecodeError: 'ascii' codec can't decode byte 0xe4 in position 0: ordinal not in range(128)
  10. >>> b = a.decode()
  11. Traceback (most recent call last):
  12. File "<stdin>", line 1, in <module>
  13. UnicodeDecodeError: 'ascii' codec can't decode byte 0xe4 in position 0: ordinal not in range(128)
  14. >>> b = a.decode('utf-8')
  15. >>> print b
  16. 中文
复制代码

我的linux系统采用了utf-8编码,就是说我在交互环境下输入的字符串是utf-8编码的,print输出utf-8字符串,显示正常。当我使用decode要求转换成unicode的时候,你明显看到python在尝试使用ascii编码进行解码,结果当然会失败。后面我指定字符集utf-8转换到unicode就正确了。然后print b的时候,python可能使用了系统的默认字符集转换,而不是ascii,所以显示正常。如果以后碰到print打印的内容是乱码的话,尝试转换成unicode再看。

在前面也看到了,str和unicode之间转换可以用str(u'unicode string', encoding)和unicode('str string', encoding),也可以使用encode和decode进行转换。

下面具体说一说,在处理网页的的时候,怎么处理编码问题。常见的网页一般采用GBK或者UTF-8这两种字符集。最简单的办法,就是根据html代码里面指定的编码或者http header指定的编码对获取的数据进行decode。有些模块,比如requests甚至自动给你进行了这样的转换。python内置的urllib2.urlopen(url).read()获取的是原始数据,服务器传过来的字节流,你只要根据对应的字符集进行decode,就可以得到unicode字符串,然后你针对这个unicode字符串进行处理,然后传给xbmc,一定不会出现编码问题。当然,如果网页本身是utf-8编码,你也可以偷懒,不用先decode成unicode字符串,反正xbmc也认识。

其他的编码问题其实还有很多,但是要具体问题具体分析,比如获取的json或者xml里面也存在字符集的问题。但是,编码问题万变不离其宗,在python interpreter中多实验,多想想,然后多google,问题总能得到完美解决。了解了编码的本质和python内部编码处理的机制,处理起中文编码问题起来都会得心应手。祝你好运! ^_^

回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

返回列表 本版积分规则



绑定微信 + 关注公众号,第一时间获得消息通知!