学习如果只输入没有输出便很难内化成为自己的知识,因此我希望能将自己学习python的经验及学到的知识积累下来,以便日后翻阅,也希望为后来者提供借鉴。
以下是正文部分:
Python解释器
当我们编写Python代码时,我们得到的是一个包含Python代码的以.py为扩展名的文本文件。要运行代码,就需要Python解释器去执行.py文件。
主要有:
CPython(C语言开发)
,
IPython(基于CPython之上的一个交互式解释器)
注:CPython用>>>作为提示符,而IPython用In [序号]:作为提示符。
PyPy(python编写)
Jython(java编写)
IronPython(运行于.Net)
Python的解释器很多,但使用最广泛的还是CPython。如果要和Java或.Net平台交互,最好的办法不是用Jython或IronPython,而是通过网络调用来交互,确保各程序之间的独立性。
直接命令行执行.py文件与在Python交互式命令行下执行python语句的区别:
1.命令行下执行.py文件不会自动打印每一行语句的结果,但是交互式会。
2.直接输入python进入交互模式,相当于启动了Python解释器,但是等待你一行一行地输入源代码,每输入一行就执行一行。
直接运行.py文件相当于启动了Python解释器,然后一次性把.py文件的源代码给执行了,你是没有机会以交互的方式输入源代码的。如:
>>> 1+1
2
但是如果将1+2
保存为.py文件后执行,不会有任何结果。
普通的字符串’ABC’在Python内部都是ASCII编码的。Python提供了ord()
和chr()
函数,可以把字母和对应的数字相互转换:
>>> ord('A')
65
>>> chr(65)
'A'
Python输入输出
输出
用print加上字符串,就可以向屏幕上输出指定的文字。比如输出’hello, world’,用代码实现如下:
>>> print 'hello, world'
print语句也可以跟上多个字符串,用逗号“,”隔开,就可以连成一串输出:
>>> print 'The quick brown fox', 'jumps over', 'the lazy dog'
The quick brown fox jumps over the lazy dog
注:python3.0以上版本和python2.7不同,需要加()
输入
python提供了raw_input输入。示例:
name = raw_input('please input your name:')
print 'hello',name
注意:raw_input返回类型为字符串,如果需要其他类型则需强制转换,如int(raw_input('birth: '))
Python字符串
1.如果字符串里面有很多字符都需要转义,就需要加很多\,为了简化,Python还允许用r”表示”内部的字符串默认不转义
2.如果字符串内部有很多换行,用\n写在一行里不好阅读,为了简化,Python允许用”’…”’的格式表示多行内容,如:
>>> print '''line1
... line2
... line3'''
line1
line2
line3
上面是在交互式命令行内输入,如果写成程序,就是:
print '''line1
line2
line3'''
多行字符串”’…”’还可以在前面加上r使用。
%运算符就是用来格式化字符串的。在字符串内部,%s表示用字符串替换,%d表示用整数替换,有几个%?占位符,后面就跟几个变量或者值,顺序要对应好。如果只有一个%?,括号可以省略。
常见的占位符有:
%d 整数
%f 浮点数
%s 字符串
%x 十六进制整数
Python变量
变量的概念基本上和初中代数的方程变量是一致的,只是在计算机程序中,变量不仅可以是数字,还可以是任意数据类型。
变量在程序中就是用一个变量名表示了,变量名必须是大小写英文、数字和_的组合,且不能用数字开头
1.在Python中,等号=是赋值语句,可以把任意数据类型赋值给变量,同一个变量可以反复赋值,而且可以是不同类型的变量,如:
a = 123 # a是整数
print a
a = 'ABC' # a变为字符串
print a
这种变量本身类型不固定的语言称之为动态语言,与之对应的是静态语言。静态语言在定义变量时必须指定变量类型,如果赋值的时候类型不匹配,就会报错。例如Java是静态语言,赋值语句如下:
int a = 123; // a是整数类型变量
a = "ABC"; // 错误:不能把字符串赋给整型变量
和静态语言相比,动态语言更灵活,就是这个原因。
2.变量在计算机内存中的表示:
a = 'hello world'
Python解释器干了两件事情:
在内存中创建了一个’hello world’的字符串;
在内存中创建了一个名为a的变量,并把它指向’hello world’。
也可以把一个变量a赋值给另一个变量b,这个操作实际上是把变量b指向变量a所指向的数据
python约定常量用全大写字母表示,但是没有任何机制保证常量值不变,因此没有绝对意义上的常量。但是python是一门功能强大的语言,可以自己定义一个常量类来实现常量的功能。如:
# -*- coding: utf-8 -*-
class _const:
class ConstError(TypeError) : pass
def __setattr__(self, key, value):
# self.__dict__
if self.__dict__.has_key(key):
raise self.ConstError,"constant reassignment error!"
self.__dict__[key] = value
import sys
sys.modules[__name__] = _const()
可通过以上方式写const类实现,使用时import即可。
- 关于编码的小问题:
Python通过加上# -*- coding: utf-8 -*-
可以设置utf-8编码,但是我输出sys.getdefaultcoding()时出现ascii,说明编码并未更改,于是尝试了另一种方法:
import sys
reload(sys)
sys.setdefaultencoding('utf-8')
列表与元组
- list
list是一种有序的集合,可以进行修改,增删等操作。
相关操作有:
1.len()
:获得list元素的个数
>>> a = [1,2,3]
>>> len(a)
3
2.用索引
来访问list中每一个位置的元素,索引从0开始
>>> a[0]
1
>>> a[1]
2
>>> a[3]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
IndexError: list index out of range
当索引超出了范围时,Python会报一个IndexError错误,所以,要确保索引不要越界,记得最后一个元素的索引是len(classmates) - 1
。
如果要取最后一个元素,除了计算索引位置外,还可以用-1做索引,直接获取最后一个元素:
>>> a[-1]
3
>>> a[-2]
2
>>> a[-3]
3
>>> a[-4]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
IndexError: list index out of range
3.append()
,追加:
>>> a.append(4)
>>> a[-1]
4
4.insert()
,添加至指定索引处:
>>> a.insert(1,'hacker')
>>> a
[1,'hacker',2,3,4]
5.pop()
,删除最后一个元素:
>>> a.pop()
4
>>> a[-1]
3
6.要把某个元素替换成别的元素,可以直接赋值给对应的索引位置:
>>> a[0]= 'hello'
>>> a
['hello','hacker',2,3]
!注意:list各元素数据类型可以不同,且可以为另一个list,取出该list元素中的子元素可以通过类似于二元数组的形式表示如b=[p,q] a=[1,b]
中,要取出p可通过b[0]或a[1][0]
- tuple
tuple和list非常类似,但是tuple一旦初始化就不能修改,因此除了查找元素之外的追加和插入等功能均没有·。
特别注意:
tuple元素不能赋值成另外的元素。
不可变的tuple有什么意义?因为tuple不可变,所以代码更安全。如果可能,能用tuple代替list就尽量用tuple。
tuple的陷阱:当你定义一个tuple时,在定义的时候,tuple的元素就必须被确定下来,比如:
>>> t = (1, 2)
>>> t
(1, 2)
如果要定义一个空的tuple,可以写成():
>>> t = ()
>>> t
()
但是,要定义一个只有1个元素的tuple,如果你这么定义:
>>> t = (1)
>>> t
1
定义的不是tuple,是1这个数!这是因为括号()既可以表示tuple,又可以表示数学公式中的小括号,这就产生了歧义,因此,Python规定,这种情况下,按小括号进行计算,计算结果自然是1。
所以,只有1个元素的tuple定义时必须加一个逗号,,来消除歧义:
>>> t = (1,)
>>> t
(1,)
Python在显示只有1个元素的tuple时,也会加一个逗号,,以免你误解成数学计算意义上的括号。
另:tuple也可以将列表作为元素,此时对于元组来说它的三个元素永远指向对应的三个对象,但是列表中的元素却可以变化,看起来就像元组的元素产生了变化,如:
>>> t = ('a', 'b', ['A', 'B'])
>>> t[2][0] = 'X'
>>> t[2][1] = 'Y'
>>> t
('a', 'b', ['X', 'Y'])
条件判断语句
if <条件判断1>:
<执行1>
elif <条件判断2>:
<执行2>
elif <条件判断3>:
<执行3>
else:
<执行4>
if判断条件还可以简写,比如写:
if x:
print 'True'
只要x是非零数值、非空字符串、非空list等,就判断为True,否则为False。
循环语句
Python的循环有两种,一种是for…in循环,依次把list或tuple中的每个元素迭代出来,第二种循环是while循环
如:
sum = 0
for x in range(101):
sum = sum + x
print sum
range()函数
,可以生成一个整数序列,比如range(5)生成的序列是从0开始小于5的整数:
>>> range(5)
[0, 1, 2, 3, 4]
while循环示例:
sum = 0
n = 99
while n > 0:
sum = sum + n
n = n - 2
print sum
字典和set集合
- dict
采用key-value方式存储数据,一个key对应一个value,in
方法可判断键是否存在于dict,get
可返回键对应的值,获取键值也可以通过dict[‘name’]方式。
- set
set和dict类似,也是一组key的集合,但不存储value。由于key不能重复,所以,在set中,没有重复的key。
set和dict的唯一区别仅在于没有存储对应的value,但是,set的原理和dict一样,所以,同样不可以放入可变对象,因为无法判断两个可变对象是否相等,也就无法保证set内部“不会有重复元素”。List等可变对象不可放入,含有List的tuple也不能放入。
函数
函数名其实就是指向一个函数对象的引用,完全可以把函数名赋给一个变量,相当于给这个函数起了一个“别名”:
>>> a = abs # 变量a指向abs函数
>>> a(-1) # 所以也可以通过a调用abs函数
1
在Python中,定义一个函数要使用def语句,依次写出函数名、括号、括号中的参数和冒号:,然后,在缩进块中编写函数体,函数的返回值用return语句返回。如:
def my_abs(x):
if x >= 0:
return x
else:
return -x
参数说明:
在Python中定义函数,可以用必选参数、默认参数、可变参数和关键字参数,这4种参数都可以一起使用,或者只用其中某些,但是请注意,参数定义的顺序必须是:必选参数、默认参数、可变参数和关键字参数。
默认参数一定要用不可变对象,如果是可变对象,运行会有逻辑错误!
要注意定义可变参数和关键字参数的语法:
*args是可变参数,args接收的是一个tuple;
**kw是关键字参数,kw接收的是一个dict。
以及调用函数时如何传入可变参数和关键字参数的语法:
可变参数既可以直接传入:func(1, 2, 3),又可以先组装list或tuple,再通过args传入:func((1, 2, 3));
关键字参数既可以直接传入:func(a=1, b=2),又可以先组装dict,再通过kw传入:func({‘a’: 1, ‘b’: 2})。
使用*args和**kw是Python的习惯写法,当然也可以用其他参数名,但最好使用习惯用法。
切片
- L[0:3]表示,从索引0开始取,直到索引3为止,但不包括索引3。即索引0,1,2,正好是3个元素。
- 如果第一个索引是0,还可以省略,如:
L[:3]
- Python支持L[-1]取倒数第一个元素,那么它同样支持倒数切片,但是倒数第一个元素的索引是-1。(L[-2:0]出现的是空列表,亲测)
L[::5]
表示全体每五个取一个,类似的:100:2
则是隔两个取前一百个数中的一个。- 除列表外,元组和字符串也可以切片,但是切片之后结果仍为元组和字符串。
迭代
任何可迭代对象都可以作用于for循环,包括我们自定义的数据类型,只要符合迭代条件,就可以使用for循环。
- dict类型可以用于迭代,但是由于dict存储方式与list不同,因此可能不是按照正常顺序输出。默认情况下,dict迭代的是key。如果要迭代value,可以用for value in d.itervalues()
,如果要同时迭代key和value,可以用for k, v in d.iteritems()
。
>>> d = {'a': 1, 'b': 2, 'c': 3}
>>> for key in d:
... print key
...
a
c
b
- 字符串也是可迭代对象,因此,也可以作用于for循环:
>>> for ch in 'ABC':
... print ch
...
A
B
C
- 当我们使用for循环时,只要作用于一个可迭代对象,for循环就可以正常运行,而我们不太关心该对象究竟是list还是其他数据类型。
那么,如何判断一个对象是可迭代对象呢?方法是通过collections模块的Iterable类型判断:
>>> from collections import Iterable
>>> isinstance('abc', Iterable) # str是否可迭代
True
>>> isinstance([1,2,3], Iterable) # list是否可迭代
True
>>> isinstance(123, Iterable) # 整数是否可迭代
False
- Python内置的
enumerate
函数可以把一个list变成索引-元素对,这样就可以在for循环中同时迭代索引和元素本身:
>>> for i, value in enumerate(['A', 'B', 'C']):
... print i, value
...
0 A
1 B
2 C
列表生成式
- 通过
[x for x in L[]]
可生成列表,如·:
>>> [x * x for x in range(1, 11)]
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
- for循环后面还可以加上if判断,这样我们就可以筛选出仅偶数的平方:
>>> [x * x for x in range(1, 11) if x % 2 == 0]
[4, 16, 36, 64, 100]
- 还可以使用两层循环,可以生成全排列:
>>> [m + n for m in 'ABC' for n in 'XYZ']
['AX', 'AY', 'AZ', 'BX', 'BY', 'BZ', 'CX', 'CY', 'CZ']
- 列表生成式也可以使用两个变量来生成list:
>>> d = {'x': 'A', 'y': 'B', 'z': 'C' }
>>> [k + '=' + v for k, v in d.iteritems()]
['y=B', 'x=A', 'z=C']
- 列出当前目录下的所有文件和目录名,可以通过一行代码实现:
>>> import os # 导入os模块,模块的概念后面讲到
>>> [d for d in os.listdir('.')] # os.listdir可以列出文件和目录
['.android', '.appletviewer', '.astropy', '.bash_history', '.deliverycenter.installs', '.deliverycenter.system', '.eclipse'] #由于篇幅限制,此处省略XX字,明白意思就好
- 把一个list中所有的字符串变成小写:
>>> L = ['Hello', 'World', 'IBM', 'Apple']
>>> [s.lower() for s in L]
['hello', 'world', 'ibm', 'apple']
生成器
通过列表生成式,我们可以直接创建一个列表。但是,受到内存限制,列表容量肯定是有限的。而且,创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。
所以,如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的list,从而节省大量的空间。在Python中,这种一边循环一边计算的机制,称为生成器(Generator)。
要创建一个generator,有很多种方法。第一种方法很简单,只要把一个列表生成式的[]改成(),就创建了一个generator。通过generator的next()
方法可以打印出generator的每一个元素
,如:
>>> g = (x * x for x in range(10))
>>> g.next()
0
>>> g.next()
1
>>> g.next()
4
>>> g.next()
9
>>> g.next()
16
>>> g.next()
25
>>> g.next()
36
>>> g.next()
49
>>> g.next()
64
>>> g.next()
81
>>> g.next()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
正确的方法是使用for循环,因为generator也是可迭代对象:
>>> g = (x * x for x in range(10))
>>> for n in g:
... print n
...
0
1
4
9
16
25
36
49
64
81
所以,我们创建了一个generator后,基本上永远不会调用next()方法,而是通过for循环来迭代它。
- 在函数中添加
yield
可以将函数变成生成器,如:
>>> def odd():
... print 'step 1'
... yield 1
... print 'step 2'
... yield 3
... print 'step 3'
... yield 5
...
>>> o = odd()
>>> o.next()
step 1
1
>>> o.next()
step 2
3
>>> o.next()
step 3
5
>>> o.next()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
注:odd不是普通函数,而是generator,在执行过程中,遇到yield就中断,下次又继续执行。执行3次yield后,已经没有yield可以执行了,所以,第4次调用next()就报错。
函数是顺序执行,遇到return语句或者最后一行函数语句就返回。而变成generator的函数,在每次调用next()的时候执行,遇到yield语句返回,再次执行时从上次返回的yield语句处继续执行。
函数式编程
- 函数名其实就是指向函数的变量!对于abs()这个函数,完全可以把函数名abs看成变量,它指向一个可以计算绝对值的函数!
如果把abs指向其他对象,会有什么情况发生?
>>> abs = 10
>>> abs(-10)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'int' object is not callable
把abs指向10后,就无法通过abs(-10)调用该函数了!因为abs这个变量已经不指向求绝对值函数了!
当然实际代码绝对不能这么写,这里是为了说明函数名也是变量。要恢复abs函数,请重启Python交互环境。
注:由于abs函数实际上是定义在__builtin__
模块中的,所以要让修改abs变量的指向在其它模块也生效,要用__builtin__.abs = 10
。
把函数作为参数传入,这样的函数称为高阶函数,函数式编程就是指这种高度抽象的编程范式。如:
def add(x, y, f):
return f(x) + f(y)
map()函数
map()传入的第一个参数是f
,即函数对象本身;第二个参数是待处理的列表,示例:
>>> def f(x):
... return x * x
...
>>> map(f, [1, 2, 3, 4, 5, 6, 7, 8, 9])#将f(x)作用于列表
[1, 4, 9, 16, 25, 36, 49, 64, 81]
#将数字转换成字符串
>>> map(str, [1, 2, 3, 4, 5, 6, 7, 8, 9])
['1', '2', '3', '4', '5', '6', '7', '8', '9']
reduce()函数
reduce把一个函数作用在一个序列[x1, x2, x3…]上,这个函数必须接收两个参数,reduce把结果继续和序列的下一个元素做累积计算,其效果就是:
reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)
filter()函数
filter()函数用于过滤序列。和map()类似,filter()也接收一个函数和一个序列。和map()不同的时,filter()把传入的函数依次作用于每个元素,然后根据返回值是True还是False决定保留还是丢弃该元素。
例如,在一个list中,删掉偶数,只保留奇数,可以这么写:
def is_odd(n):
return n % 2 == 1 #此处返回布尔值
filter(is_odd, [1, 2, 4, 5, 6, 9, 10, 15])
# 结果: [1, 5, 9, 15]
sorted函数
- sorted()函数可以对list进行排序:
>>> sorted([36, 5, 12, 9, 21])
[5, 9, 12, 21, 36]
- sorted()函数也是一个高阶函数,它还可以接收一个比较函数来实现自定义的排序。比如,如果要倒序排序,我们就可以自定义一个reversed_cmp函数:
def reversed_cmp(x, y):
if x > y:
return -1
if x < y:
return 1
return 0
传入自定义的比较函数reversed_cmp,就可以实现倒序排序:
>>> sorted([36, 5, 12, 9, 21], reversed_cmp)
[36, 21, 12, 9, 5]
- 字符串排序:
>>> sorted(['bob', 'about', 'Zoo', 'Credit'])
['Credit', 'Zoo', 'about', 'bob']
默认情况下,对字符串排序,是按照ASCII的大小比较的,由于’Z’ < ‘a’,结果,大写字母Z会排在小写字母a的前面。
要进行忽略大小写排序,就可以定义一个忽略大小写函数,然后传入sorted即可:
def cmp_ignore_case(s1, s2):
u1 = s1.upper()
u2 = s2.upper()
if u1 < u2:
return -1
if u1 > u2:
return 1
return 0
>>> sorted(['bob', 'about', 'Zoo', 'Credit'], cmp_ignore_case)
['about', 'bob', 'Credit', 'Zoo']
注意:sorted传入的是一个列表,不是单纯的字符或数字或元组。
Python闭包
一个函数和它的环境变量合在一起,就构成了一个闭包(closure)。
包(closure)是函数式编程的重要的语法结构。函数式编程是一种编程范式 (而面向过程编程和面向对象编程也都是编程范式)。在面向过程编程中,我们见到过函数(function);在面向对象编程中,我们见过对象(object)。函数和对象的根本目的是以某种逻辑方式组织代码,并提高代码的可重复使用性(reusability)。闭包也是一种组织代码的结构,它同样提高了代码的可重复使用性。
下面看一个闭包的实际例子:
def line_conf(a, b):
def line(x):
return ax + b
return line
line1 = line_conf(1, 1)
line2 = line_conf(4, 5)
print(line1(5), line2(5))
这个例子中,函数line与环境变量a,b构成闭包。在创建闭包的时候,我们通过line_conf的参数a,b说明了这两个环境变量的取值,这样,我们就确定了函数的最终形式(y = x + 1和y = 4x + 5)。我们只需要变换参数a,b,就可以获得不同的直线表达函数。由此,我们可以看到,闭包也具有提高代码可复用性的作用。
如果没有闭包,我们需要每次创建直线函数的时候同时说明a,b,x。这样,我们就需要更多的参数传递,也减少了代码的可移植性。利用闭包,我们实际上创建了泛函。line函数定义一种广泛意义的函数。这个函数的一些方面已经确定(必须是直线),但另一些方面(比如a和b参数待定)。随后,我们根据line_conf传递来的参数,通过闭包的形式,将最终函数确定下来。
注意: 返回的函数并不会立即执行,只是具有处理数据的某种功能,但是只有调用时才会代入参数进行计算。如:
def count():
fs = []
for i in range(1, 4):
def f():
return i*i
fs.append(f)
return fs
f1, f2, f3 = count()
在上面的例子中,每次循环,都创建了一个新的函数,然后,把创建的3个函数都返回了。
你可能认为调用f1(),f2()和f3()结果应该是1,4,9,但实际结果是:
>>> f1()
9
>>> f2()
9
>>> f3()
9
全部都是9!原因就在于返回的函数引用了变量i,但它并非立刻执行。等到3个函数都返回时,它们所引用的变量i已经变成了3,因此最终结果为9。
返回闭包时牢记的一点就是:返回函数不要引用任何循环变量,或者后续会发生变化的变量。
如果一定要引用循环变量怎么办?方法是再创建一个函数,用该函数的参数绑定循环变量当前的值,无论该循环变量后续如何更改,已绑定到函数参数的值不变.
匿名函数
关键字lambda
表示匿名函数,例如:lambda x: x * x
等价于:
def f(x):
return x * x
冒号前面的x表示函数参数
装饰器
在代码运行期间动态增加功能的方式,称之为“装饰器”(Decorator)。
例如:
def log(func):
def wrapper(*args, **kw):
print 'call %s():' % func.__name__
return func(*args, **kw)
return wrapper
上面的log,因为它是一个decorator,所以接受一个函数作为参数,并返回一个函数。我们要借助Python的@
语法,把decorator置于函数的定义处:
@log
def now():
print '2013-12-25'
调用now()函数,不仅会运行now()函数本身,还会在运行now()函数前打印一行日志:`
>>> now()
call now():
2013-12-25
把@log放到now()函数的定义处,相当于执行了语句:
now = log(now)
由于log()是一个decorator,返回一个函数,所以,原来的now()函数仍然存在,只是现在同名的now变量指向了新的函数,于是调用now()将执行新函数,即在log()函数中返回的wrapper()函数。
wrapper()函数的参数定义是(*args, **kw)
,因此,wrapper()函数可以接受任意参数的调用。在wrapper()函数内,首先打印日志,再紧接着调用原始函数。(关于可变和关键字参数前面已介绍,任何函数都可以通过(*args, **kw)
进行调用)
带参数的decorator:
import functools
def log(text):
def decorator(func):
@functools.wraps(func)#使用functools.wraps()把原始函数的__name__等属性复制到wrapper()函数中
def wrapper(*args, **kw):
print '%s %s():' % (text, func.__name__)
return func(*args, **kw)
return wrapper
return decorator
提醒:不能改为:
def log(func):
print 'call %s():' % func.__name__
return func
此处未利用闭包,函数的__name__
属性没有变,无法实现装饰器功能。
偏函数
Python的functools模块提供了很多有用的功能,其中一个就是偏函数(Partial function)。
- 利用偏函数可以固定参数过多的函数的部分参数,从而使函数具有特定功能或者简化函数功能。
functools.partial
的作用就是,把一个函数的某些参数给固定住(也就是设置默认值),返回一个新的函数,调用这个新函数会更简单。例如:
>>> import functools
>>> int2 = functools.partial(int, base=2)
>>> int2('1000000')
64
Python模块
在Python中,一个.py文件就称之为一个模块(Module)。
- 为了避免模块名冲突,Python又引入了按目录来组织模块的方法,称为包(Package)。
- 每一个包目录下面都会有一个
__init__.py
的文件,这个文件是必须存在的,否则,Python就把这个目录当成普通目录,而不是一个包。__init__.py
可以是空文件,也可以有Python代码,因为__init__.py
本身就是一个模块,而它的模块名就是包名。
编写模块标准格式:
1 #!/usr/bin/env python #让这个文件可以直接在Unix/Linux/Mac上运行
2 # -*- coding: utf-8 -*- #表示.py文件本身使用标准UTF-8编码
3
4 ' a test module ' #表示模块的文档注释,任何模块代码的第一个字符串都被视为模块的文档注释
5
6 __author__ = 'your_name' #作者名
7
8 import sys #导入模块
9 #以下是测试函数
10 def test():
11 args = sys.argv
12 if len(args)==1:
13 print 'Hello, world!'
14 elif len(args)==2:
15 print 'Hello, %s!' % args[1]
16 else:
17 print 'Too many arguments!'
18 #当直接运行.py文件时__name__变成了__main__,可以进行测试;当import该模块时if语句不通过,因此测试函数不执行,需要调用test()才可执行
19 if __name__=='__main__':
20 test()
注:sys模块有一个argv变量,用list存储了命令行的所有参数。argv至少有一个元素,因为第一个参数永远是该.py文件的名称,例如:
运行python hello.py
获得的sys.argv就是['hello.py']
- 导入模块时,还可以使用别名,这样,可以在运行时根据当前环境选择最合适的模块。如:
try:
import cStringIO as StringIO
except ImportError: # 导入失败会捕获到ImportError
import StringIO
Python标准库一般会提供StringIO和cStringIO两个库,这两个库的接口和功能是一样的,但是cStringIO是C写的,速度更快.以上代码优先导入cStringIO。如果有些平台不提供cStringIO,还可以降级使用StringIO。导入cStringIO时,用import … as …指定了别名StringIO,因此,后续代码引用StringIO即可正常工作。
模块作用域
在一个模块中,我们可能会定义很多函数和变量,但有的函数和变量我们希望给别人使用,有的函数和变量我们希望仅仅在模块内部使用。在Python中,是通过_
前缀来实现的。
正常的函数和变量名是公开的(public),可以被直接引用,比如:abc,x123,PI等;
类似xxx这样的变量是特殊变量,可以被直接引用,但是有特殊用途,比如上面的__author__
,__name__
就是特殊变量,hello模块定义的文档注释也可以用特殊变量__doc__
访问,我们自己的变量一般不要用这种变量名;
类似_xx
和__xxx
这样的函数或变量就是非公开的(private),不应该被直接引用,比如_abc,__abc
等;
之所以我们说,private函数和变量“不应该”被直接引用,而不是“不能”被直接引用,是因为Python并没有一种方法可以完全限制访问private函数或变量,但是,从编程习惯上不应该引用private函数或变量。此处与java中的public 及 private修饰符意义相同,但是功能上不同。java有相关机制,如果调用private函数会报错,但是python更类似于君子协定,你调用语法上可以通过,但是违背了模块编写人的意图,不符合习惯。
安装第三方模块
在Python中,安装第三方模块,是通过setuptools这个工具完成的。Python有两个封装了setuptools的包管理工具:easy_install
和pip
。目前官方推荐使用pip。
模块搜索路径
默认情况下,Python解释器会搜索当前目录、所有已安装的内置模块和第三方模块,搜索路径存放在sys模块的path变量中:
>>> import sys
>>> sys.path
['', '/Library/Python/2.7/site-packages/pycrypto-2.6.1-py2.7-macosx-10.9-intel.egg', '/Library/Python/2.7/site-packages/PIL-1.1.7-py2.7-macosx-10.9-intel.egg', ...]
如果我们要添加自己的搜索目录,有两种方法:
一是直接修改sys.path,添加要搜索的目录:
>>> import sys
>>> sys.path.append('/Users/michael/my_py_scripts')
这种方法是在运行时修改,运行结束后失效。
第二种方法是设置环境变量PYTHONPATH,该环境变量的内容会被自动添加到模块搜索路径中。设置方式与设置Path环境变量类似。注意只需要添加你自己的搜索路径,Python自己本身的搜索路径不受影响。
- 安装PIL提示could not find a version that satisfies the requirement PIL.(form versions:)No matching distribution found for PIL问题:
原因:PIL官方只有32位的安装文件,安装时会提示找不到python的安装路径。
64位Win7下无法安装PIL库的原因是:PIL官方http://www.pythonware.com/products/pil/
提供的PIL二进制安装库包都是32位的。
64位程序和32位程序检测注册表的位置是不一样的:64位程序检测HKEY_LOCAL_MACHINESOFTWAREPython,而32位程序检测HKEY_LOCAL_MACHINESOFTWAREWow6432NodePython。
如果安装的python是64位的,其相关信息都在HKEY_LOCAL_MACHINESOFTWAREPython下面,而32位程序则在HKEY_LOCAL_MACHINESOFTWAREWow6432NodePython下面找Python安装信息,结果找不到了,所以会抱错。
网上有非官方的64位库(官方源码编译版),叫做pillow。Pillow是PIL的替代版本。其网址为:http://www.lfd.uci.edu/~gohlke/pythonlibs/
Pillow 是 PIL 的替代版本,PIL 软件包提供了基本的图像处理功能,如:改变图像大小,旋转图像,图像格式转换,色场空间转换,图像增强,直方图处理,插值和滤波等等。
Pillow 为了解决 PIL 的两个问题:
不兼容 setuptools
报告的问题太多,而更新太慢
安装了pillow就要用from PIL import Image替换import Image
大家还可以参考https://pillow.readthedocs.io/en/4.0.x/installation.html
解决:用pillow替代PIL,原来的import image也由from PIL import Image代替(亲测有效),即改用pip install Pillow
安装,用’from PIL import Image ‘引入。
__future__
模块:Python提供了future模块,把下一个新版本的特性导入到当前版本,于是我们就可以在当前版本中测试一些新版本的特性。
以上是python基础部分学习,后续面向对象编程,异常处理,IO,多线程,网络编程等留在下一篇继续探讨。
实话说,廖雪峰的python教程对于学习Python的同学来说是很好的资源,本篇文章也是该学习教程的笔记,在此感谢廖大大。后续部分在学习了之后会继续推出,敬请期待。