note one
字符串和编码
字符编码最早是美国发明的ASCII编码,只占一个字节共127个字符
这对于要处理其他文字是显然不够的,所以国际上制定了统一的编码方式Unicode,占两个字节。那么问题又随之而来,如果我使用的全是英文,采用Unicode进行编码,这样就浪费了一半的空间,为了解决这个问题,又发展到了UTF-8这种编码方式。
在计算机内存中,统一使用Unicode编码,当需要保存到硬盘或者需要传输的时候,就转换为UTF-8编码
编码间的转换
把u'xxx'转换为UTF-8编码的"xxx"用encode("utf-8")
u"xxx".encode("utf-8")
u"中午".encode("utf-8")
反过来,把UTF-8编码的字符串"xxx"转换为Unicode字符串u"xxx"用decode("utf-8")
"abc".decode("utf-8")
格式化
如何输出格式化话的字符串,在python中,采用的格式化方式和C语言一致,用%实现。
在python3.x中,把"xxx"和u"xxx"统一成Unicode编码
note two
迭代
如何判断一个对象是可迭代对象?方法是通过collections模块的Iterable类型判断:
from collections import Iterable
In [41]: isinstance("abc",Iterable)
Out[41]: True
In [42]: isinstance([1,2,3],Iterable)
Out[42]: True
In [43]: isinstance(123,Iterable)
Out[43]: False
Python内置的enumerate函数可以把一个list变成索引-元素对,这样就可以在for循环中同时迭代索引和元素本身:
In [44]: for i,value in enumerate(["A","B","C"]):
....: print i,value
....:
0 A
1 B
2 C
note three
列表生成式
是python内置的非常简单却强大的可以用来创建list的生成式
In [45]: [x*x for x in range(0,11)]
Out[45]: [0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
In [46]: [x*x for x in range(0,11)if x%2==0]
Out[46]: [0, 4, 16, 36, 64, 100]
使用内建的isinstance函数可以判断一个变量是不是字符串:
In [47]: x = "abc"
In [48]: y = 123
In [49]: isinstance(x,str)
Out[49]: True
In [50]: isinstance(y,str)
Out[50]: False
note four
生成器
在循环过程中不断推算出后续的元素,这样就不必创建完整的list,从而节省大量的空间。
创建generator的一种简单方法,只要把列表生成式的[]改成(),就创建了一个generator:
In [51]: g = (x*x for x in range(10))
In [52]: g
Out[52]: <generator object <genexpr> at 0x000000000408CF78>
generator保存的是算法,每次调用next()方法,就计算出下一个元素的值,直到没有更多的元素时,抛出stopIteration的错误。
如果推算算法比较复杂时,用类似列表生成式的for循环无法实现的时候,还可以用函数来实现。
另一种定义generator的方法:如果一个函数定义中包含yield关键字,那么这个函数就不再是一个普通函数,而是一个generator:
这里,最难理解的就是generator和函数的执行流程不一样。函数是顺序执行,遇到return语句或者最后一行函数语句就返回。而变成generator的函数,在每次调用next()的时候执行,遇到yield语句返回,再次执行时从上次返回的yield语句处继续执行。
note five
函数式编程
函数式编程的一个特点就是允许把函数本身作为参数传入另一个函数,还允许返回一个函数。
函数名也是变量
In [1]: L = [1,2,3]
In [2]: sum(L)
Out[2]: 6
In [3]: S = sum
In [4]: S(L)
Out[4]: 6
既然变量可以指向函数,函数的参数能接收变量,那么一个函数就可以接收另一个函数作为参数,这种函数就称之为高阶函数。
一个最简单的高阶函数:
In [7]: def add (x,y,f):
...: return f(x) + f(y)
...:
In [9]: add(-2,8,f)
Out[9]: 10
python内建了map()和reduce()函数
map()函数接收两个参数,一个是函数,一个是序列,map将传入的函数依次作用到序列的每个元素,并把结果作为新的list返回。
In [10]: def f(x):
....: return x*x
....:
In [11]: map(f,[1,2,3,4,5])
Out[11]: [1, 4, 9, 16, 25]
reduce把一个函数作用在一个序列[x1,x2,x3...]上,这个函数必须接收两个参数,reduce把结果继续和序列的下一个元素做累积计算,其效果就是:
def add(x,y):
....: return x + y
....:
In [14]: reduce(add,[1,3,5,7,9])
Out[14]: 25
filter()函数用于过滤序列,和map类似,filter()也接受一个函数和序列。和map不同的是,filter 把传入的函数依次作用于每个元素,然后根据返回值是True还是False决定保留还是丢弃元素。
In [18]: def is_odd(n):
....: return n % 2 == 1
....:
In [19]: filter(is_odd,[1,2,3,4,5,6,7])
Out[19]: [1, 3, 5, 7]
返回函数
In [20]: def lazy_sum(*args):
....: def sum():
....: ax = 0
....: for n in args:
....: ax = ax + n
....: return ax
....: return sum
....:
In [22]: f = lazy_sum(1,3,5,7,9)
In [23]: f
Out[23]: <function __main__.sum>
In [24]: f()
Out[24]: 25
我们在函数lazy_sum中又定义了函数sum,并且,内部函数sum可以引用外部函数lazy_sum的参数和局部变量,当lazy_sum返回函数sum时,相关参数和变量都保存在返回的函数中,这种称为“闭包(Closure)”的程序结构拥有极大的威力。
note six
装饰器(decorator)
本质上,decorator就是一个返回函数的高阶函数。
模块搜索路径
当我们试图加载一个模块时,python会在指定的路径下搜索对应的.py文件,如果找不到就会报错。
默认情况下,python解释器会搜索当前目录、所有已安装的内置模块和第三方模块,搜索路径放在sys模块的path变量中:
In [4]: import sys
In [5]: sys.path
Out[5]:
['',
'C:\Anaconda2\Scripts',
'D:\pythonCode\Code',
'C:\Anaconda2\python27.zip',
'C:\Anaconda2\DLLs',
'C:\Anaconda2\lib',
'C:\Anaconda2\lib\plat-win',
'C:\Anaconda2\lib\lib-tk',
'C:\Anaconda2',
'C:\Anaconda2\lib\site-packages',
'C:\Anaconda2\lib\site-packages\Sphinx-1.4.1-py2.7.egg',
'C:\Anaconda2\lib\site-packages\win32',
'C:\Anaconda2\lib\site-packages\win32\lib',
'C:\Anaconda2\lib\site-packages\Pythonwin',
'C:\Anaconda2\lib\site-packages\setuptools-23.0.0-py2.7.egg',
'C:\Anaconda2\lib\site-packages\IPython\extensions',
'C:\Users\MyHome\.ipython']
如果我们要添加自己的搜索目录,有两种方法:
1. 直接修改sys.path,添加要搜索的目录:
import sys
sys.path.append(newfilepath)
这种方法是在运行时修改,运行结束后失效。
2.设置环境变量PYTHONPATH,该环境变量的内容会被自动添加到模块搜索路径中。
使用__future__
python 提供了__future__模块,把下一个新版本的特性导入到当前版本,在当前版本中测试一些新版本特性。
example:
在python2.x中,如果整数相除,结果认为整数,余数会被扔掉;而在python3.x中,所有的除法都是精确除法。
In [6]: 10/3
Out[6]: 3
In [7]: from __future__ import division
In [8]: 10/3
Out[8]: 3.3333333333333335
In [9]: 10//3
Out[9]: 3
类和实例
类是创建实例的模板,而实例则是一个个具体的对象,各个实例拥有的数据都是相互独立,互不影响。
方法就是与实例绑定的函数,和普通函数不同,方法可以直接访问实例的数据
继承与多态
继承好处:
1.子类获得了父类的全部功能
2.当子类和父类都存在相同的run()方法时,子类的run()覆盖了父类的run(),在代码运行的时候,总会调用子类的run()。这样我们就获得了继承的另外一个好处:多态。
当我们定义一个class的时候,我们实际上就定义了一种数据类型。我们定义的数据类型和python自带的数据类型,比如str、list没有什么两样
继承可以把父类的所有功能都直接拿过来,这样就不必从零做起,子类只需要新增自己特有的方法,也可以把父类不适合的方法覆盖重写;有了继承才能有多态。在调用类实例方法的时候,尽量把变量视作父类类型,这样,所有子类类型都可以正常被接受;
note seven
使用__slots__
当我们定义了一个class,创建一个class的实例后,我们可以给该实例绑定任何属性和方法,这就是动态语言的灵活性。
给实例绑定方法,对另一个实例是不起作用的;为了给所有实例都绑定方法,可以给class绑定方法;
1.给实例绑定方法
class Student(object):
pass
s = Student()
s.name = "Michael"
print s.name
def set_age(self,age):
self.age = age
from types import MethodType
s.set_age = MethodType(set_age,s,Student)
s.set_age(24)
print s.age
2.给类绑定方法:
Student.set_score = MethodType(set_score,None,Student)
如果我们要现在class的属性,只允许对Student实例添加name和age属性,为了达到限制的目的,python允许在定义class的时候,定义一个特殊的__slots__变量,来限制该class能添加的属性:
In [19]: class Student(object):
....: __slots__ = ("name","age")
....:
In [20]: s = Student()
In [21]: s.name = "Michael"
In [22]: s.age = 23
In [23]: s.score = 96
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-23-6ec895cb7710> in <module>()
----> 1 s.score = 96
AttributeError: 'Student' object has no attribute 'score'
在使用__slots__要注意,__slots__定义的属性仅对当前类起作用,对继承的子类是不起作用的