第8章 模块
简介
使用模块, 在其他程序中可以重用多个函数; 模块基本上就是一个包含了所定义的函数和变量的文件;
Note 模块的文件名必须以 .py 为扩展名;
编写模块的另一种方式是使用编写python解释器本身的机器语言; e.g. C语言: http://docs.python.org/py3k/extending/index.html, 使用标准的python解释器时, 可以在py代码中使用这些模块;
sys模块
1
2
3
4
5
6
|
import
sys
print
(
'The command line arguments are:'
)
for
i
in
sys.argv:
print
(i)
print
(
'\nThe PYTHONPATH is'
, sys.path,
'\n'
)
|
>import语句输入sys模块, 告诉python开始使用这个模块; sys包含了与python解释器和他的环境相关的函数;
python执行import sys的时候, 它在sys.path变量中所列的目录中寻找sys.py(内建模块); 如果找到了文件, 这个模块的主块中的语句将被运行, 然后模块能够被使用;
Note 初始化过程仅在第一次输入模块的时候进行; sys是system的缩写;
>sys模块中的argv变量通过使用点号指明--sys.argv--, 这样名称不会和程序中使用的argv变量冲突, 也表明了是sys模块的部分; sys.argv变量是一个字符串的列表; sys.argv包含了命令行参数的列表(list), 即用命令行传递给你的程序参数;
在命令行执行: python using_sys.py we are arguments 使用了python命令执行模块, 后面的内容作为参数传递给程序, python将参数存储在sys.argv变量中;
输出:
1
2
3
4
5
6
7
8
9
10
|
The command line arguments are:
using_sys.py
we
are
arguments
The PYTHONPATH
is
[
'/home/swaroop/byte/code'
,
'/usr/lib/python23.zip'
,
'/usr/lib/python2.3'
,
'/usr/lib/python2.3/plat-linux2'
,
'/usr/lib/python2.3/lib-tk'
,
'/usr/lib/python2.3/lib-dynload'
,
'/usr/lib/python2.3/site-packages'
,
'/usr/lib/python2.3/site-packages/gtk-2.0'
]
|
Note 脚本的名称总是sys.argv列表的第一个参数; 这里'using_sys.py'是sys.argv[0], 'we'是sys.argv[1]... python从0开始计数, 不是1开始;
sys.path包含输入模块的目录名称列表; sys.path的第一个字符串可能是空的--空字符串表示当前目录也是sys.path的一部分, 和PYTHONPATH环境变量一致; 这意味着你可以直接输入位于当前目录的模块, 否则你要把模块放在sys.path所列的目录之中;
[windows下当前目录会被输出, 不需要python命令直命令行输入文件名file.py就可以执行python模块]
当前目录:
1
|
import
os;
print
(os.getcwd())
|
字节编译的.pyc文件
输入模块相对是比较费时的操作, python有一些技巧可以使得输入模块更快: 创建 字节编译的文件, 以.pyc为扩展名; 字节编译的文件与python变换程序的中间状态有关; 在下次从别的程序输入模块的时候, .pyc文件会快很多, 因为一部分输入模块所需的处理已经完成; 而且字节编译的文件与平台无关; [二进制文件?]
Note .pyc文件通常在相应的.py文件的同一个目录中创建, 如果python在那个目录没有写入权限, pyc无法创建;
from..import语句
如果想要直接输入argv变量到程序中(避免每次打sys.), 可以使用 from sys import argv 语句; 如果想输入所有sys模块使用的名字, 可以使用 from sys import *语句; 对所有模块适用; 一般来说, 应该避免使用 from..import而使用import, 这样可以使程序更易读, 还可以避免名称冲突;
[和Java差不多]
模块的__name__
每个模块都有名称, 在模块中可以通过语句来找出模块的名称; 在模块中可以通过语句找出模块的名称; 在某些情况特别有用: 当一个模块第一次输入的时候, 模块的主块会被运行; 假如指向在程序本身被使用的时候运行主块, 而在它被别的模块输入的时候不运行主块; 这可以通过模块的__name__属性完成;
1
2
3
4
|
if
__name__
=
=
'__main__'
:
print
(
'This program is being run by itself'
)
else
:
print
(
'I am being imported from another module'
)
|
>每个模块都有__name__, 如果它是'__main__'说明模块被用户单独运行; 可以根据这个进行相应的操作;
1
2
3
4
5
|
$ python using_name.py
This program
is
being run by itself
$ python
>>>
import
using_name
I am being imported
from
another module
|
制造自己的模块
每个python程序是一个模块, 确保.py扩展名;
1
2
3
4
5
6
7
8
9
|
#!/usr/bin/python
# Filename: mymodule.py
def
sayhi():
print
(
'Hi, this is mymodule speaking.'
)
version
=
'0.1'
# End of mymodule.py
|
>模块, 和普通的python程序比没有什么特别之处;
Note 这个模块应该被放置在要输入它的程序的同一个目录, 或者在sys.path的目录中;
1
2
3
4
|
import
mymodule
mymodule.sayhi()
print
'Version'
, mymodule.version
|
>使用点号来使用模块成员;
from...import
from..import语法:
1
2
3
4
5
|
from
mymodule
import
sayhi, version
# Alternative:# from mymodule import *
sayhi()
print
(
'Version'
, version)
|
>输入所有 * , 或者输入可选的函数或属性;
1
|
from
mymodule
import
*
|
Note 如果在导入模块中已经有一个version名字的声明, 会产生名字冲突; 对于每个模块都有版本号version, 因此推荐使用import;
Note 禅Zen python的一个指导原则是"显式优于隐式"; import this; 或者 http://stackoverflow.com/questions/228181/zen-of-python
dir()函数
内建的dir函数可列出模块定义的标识符; 标识符有函数, 类和变量;
当你为dir()提供一个模块名的时候, 它返回模块定义的名称列表; 如果不提供参数, 它返回当前模块中定义的名称列表;
1
2
3
4
5
6
7
8
|
>>> a
=
5
# create a new variable 'a'
>>>
dir
()
[
'__builtins__'
,
'__doc__'
,
'__name__'
,
'a'
,
'sys'
]
>>>
>>>
del
a
# delete/remove a name
>>>
>>>
dir
()
[
'__builtins__'
,
'__doc__'
,
'__name__'
,
'sys'
]
|
>如果输入dir(sys), 会输出一个庞大的属性列表; 默认dir()会返回当前模块的属性列表;
Note 输入的模块会是当前列表的一部分;
>定义变量a, dir()会把a增加到列表中;
Note del语句删除当前模块中的变量/属性/名称, del之后变量就无法使用, 对程序来说不存在了;
dir()函数对任何对象都有用, e.g. dir(print), dir(str)...
打包/封装
包只是模块的文件夹, 使用一个特殊的__init__.py文件, 指示python, 这个文件夹是特殊的, 包含python模块;
e.g. 名为'世界'的程序包, 分装'亚洲', '非洲'...分包一次包含'印度', '马达加斯加'等等;
组织文件夹: ~ - <在sys.path现有的一些文件夹>/ - world/ -init.py -asia/ -init.py -india/-init.py -foo.py -africa/ -init.py -madagascarr/ -init.py -bar.py~
包只是为了分层次组织模块的方便, 标准库中会有很多包的实例;
概括
模块能为你在别的程序中重用它提供的服务和功能; python附带的标准库就是一组模块的例子;
第9章 数据结构
数据结构用来处理数据或者存储一组相关数据;
python有三种内建的数据结构: 列表, 元组, 字典;
列表
list是处理一组有序项目的数据结构, 你可以在一个列表中存储一个序列的项目; 就像你有一个购物列表, 上面记载了你要买的东西; 不过在你的购物表上可能每样东西都独占一行, 在python中, 每个项目之间是用逗号分割的;
列表中的项目应该包括在方括号中; 一旦创建了列表, 可以添加, 删除或搜索列表中的项目; 因此列表是可变的数据类型;
对象与类的快速入门
列表是对象和类的一个例子; 当你使用变量i并且给它赋值5的时候, 可以认为是创建了一个int类型的对象(实例); 参见: help(int);
类有方法, 是仅仅为类定义的函数; 当你有该类的对象的时候才能使用这些方法; 使用点号来使用对象的方法;
类有域, 是仅仅为类定义的变量; 当你有该类的实例的时候才能使用这些变量/名称; 通过点号使用;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
# This is my shopping list
shoplist
=
[
'apple'
,
'mango'
,
'carrot'
,
'banana'
]
print
(
'I have'
,
len
(shoplist),
'items to purchase.'
)
print
(
'These items are:'
,)
# Notice the comma at end of the line
for
item
in
shoplist:
print
(item, end
=
' '
)
print
(
'\nI also have to buy rice.'
)
shoplist.append(
'rice'
)
print
(
'My shopping list is now'
, shoplist)
print
(
'I will sort my list now'
)
shoplist.sort()
print
(
'Sorted shopping list is'
, shoplist)
print
(
'The first item I will buy is'
, shoplist[
0
])
olditem
=
shoplist[
0
]
del
shoplist[
0
]
print
(
'I bought the'
, olditem)
print
(
'My shopping list is now'
, shoplist)
|
>shoplist是购物列表; 只存储了购买物品的名字字符串; 但是你可以在列表中添加任何类型的对象包括数字甚至其他列表;
>for..in循环在列表项目间递归; 列表也是一个序列;
Note print语句的结尾使用逗号来消除print语句自动打印的换行符; [这是py2的, py3中输入参数 end='' 可以达到效果, 因为end的默认值是'\n']
>append方法在列表中添加一个项目 [默认加在列表最后]; 将列表传递给print, 可以得到简洁的输出;
>sort方法对列表排序, 这个方法会修改列表本身, 而不是返回一个修改后的列表拷贝--和字符串的工作方法不同; 这里的列表是可变的[variable], 而字符串是不可变的[const];
>del语句删除列表中的项目, del shoplist[0]表示删除第一个元素; python计数从0开始;
元组
和列表很类似, 不过元组和字符串一样是不可变的, 你不能修改元组; 元组通过圆括号中用逗号分隔的项目定义; 元组通常用在使语句或用户定义的函数能够安全地采用一组值的时候, 被使用的值不会改变;
1
2
3
4
5
6
7
8
|
zoo
=
(
'wolf'
,
'elephant'
,
'penguin'
)
print
(
'Number of animals in the zoo is'
,
len
(zoo))
new_zoo
=
(
'monkey'
,
'dolphin'
, zoo)
print
(
'Number of animals in the new zoo is'
,
len
(new_zoo))
print
(
'All animals in new zoo are'
, new_zoo)
print
(
'Animals brought from old zoo are'
, new_zoo[
2
])
print
(
'Last animal brought from old zoo is'
, new_zoo[
2
][
2
])
|
>变量zoo是一个元组, len函数可以获得元组长度; 表明元组也是一个序列;
Note 元组之内的元组不会失去身份[保留元组的本来属性, 次序, 元素]
>使用方括号来指明某个项目的位置来访问元组中的项目: 索引运算符; new_zoo[2]访问new_zoo中的第3个项目; new_zoo[2][2]来访问new_zoo中第3个项目的第3个项目;[递归索引]
圆括号 尽管括号是可选的, 但我们总是使用它明确表示元组, 避免歧义; e.g. print(1,2,3) 和 print((1,2,3))不同, 前者打印3个数字, 后者输出一个元组;
Note 含有0和或1个项目的元组; 一个空的元组由一对空的圆括号组成, e.g. theempty = (); 但是含有单个元素的元组必须在第一个(唯一的)项目后跟一个逗号, 这样才能区分元组和表达式--带圆括号的对象, e.g. singleton = (2, );
Note To Perl: 列表中的列表不会失去特性(变成平的一层), 列表不会像Perl中那样被打散; 同样地元组中的元组, 列表中的元组, 元组中的列表都是这样; python中它们是使用另一个对象存储的对象;
元组与打印语句
元组最普遍的用法是在打印语句中: [或者使用format(var1, var2,...)]
1
2
3
4
5
|
age
=
22
name
=
'Swaroop'
print
(
'%s is %d years old'
%
(name, age))
print
(
'Why is %s playing with that python?'
%
name)
|
>print语句使用跟着%符号的项目元组的字符串, 可以实现定制功能; 让输出满足某种特定格式, %s字符串, %d整数; 元组必须按照相应的顺序; 这个方法使得输出操作更简单, 也省去了逗号;
python把元组中的项目转换成字符串/整数并且用字符串/整数的值替换定制的位置;
>对于单个的定制, 可以在定制后面跟单个项目, 不需要圆括号; [有括号也不会错]
字典
类似于通过联系人名字查找地址和联系人详细情况的地址簿; 把键key(名字)和值value(地址)联系在一起;
Note 键必须是唯一的; 只能使用不可变的对象(例如字符串)来作为字典的键, 但是可以用可变/不可变的对象作为字典的值; 应该使用简单的对象作为键;
e.g. d = {key1 : value1, key2 : value2}; 键/值对用冒号分隔, 对之间用逗号分隔, 都包含在花括号中; [看起来和Map功能类似]
Note 字典中的键/值对是没有顺序的, 如果想要特定顺序, 应该在使用之前对他们手动排序;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
# 'ab' is short for 'a'ddress'b'ook
}
print
(
"Swaroop's address is %s"
%
ab['Swaroop'])
# Adding a key/value pair
# Deleting a key/value pair
del
ab[
'Spammer'
]
print
(
'\nThere are %d contacts in the address-book\n'
%
len
(ab))
for
name, address
in
ab.items():
print
(
'Contact %s at %s'
%
(name, address))
if
'Guido'
in
ab:
# OR ab.has_key('Guido')
print
(
"\nGuido's address is %s"
%
ab['Guido'])
|
[使用format代替%: print('联系人 {0} 的地址是 {1}'.format(name, address)) ]
>使用索引操作符来寻址一个键并且赋值, 这样就增加了一个新的键/值对; e.g. Guido
>del删除键值对, 只需要指明字典和哟哦那个索引操作符指明要删除的键; 执行del的时候无需知道那个键对应的值;
>items方法返回元组的列表, 每个元组包含一对项目--键和对应的值; for..in循环中name和adress变量得到每个项目的键和值然后打印;
>使用in操作检验一个键/值对是否存在, 也可以使用dict类的has_key方法;
关键字参数与字典; 如果在使用字典的函数中使用关键字参数, 这个键值对是在函数定义的参数列表中指定的, 在函数中访问变量时, 它只是访问了字典的键(编译器中叫 符号表);
序列
列表, 元组和字符串都是序列; [类似数组]
序列的特点 1) 索引操作符 2) 切片操作; 索引操作符可以从序列中抓取特定项目; 切片操作能够获取序列的一个切片, 即一部分程序;
成员测试: in/not in
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
shoplist
=
[
'apple'
,
'mango'
,
'carrot'
,
'banana'
]
# Indexing or 'Subscription' operation
print
(
'Item 0 is'
, shoplist[
0
])
print
(
'Item 1 is'
, shoplist[
1
])
print
(
'Item 2 is'
, shoplist[
2
])
print
(
'Item 3 is'
, shoplist[
3
])
print
(
'Item -1 is'
, shoplist[
-
1
])
print
(
'Item -2 is'
, shoplist[
-
2
])
# Slicing on a list
print
(
'Item 1 to 3 is'
, shoplist[
1
:
3
])
print
(
'Item 2 to end is'
, shoplist[
2
:])
print
(
'Item 1 to -1 is'
, shoplist[
1
:
-
1
])
print
(
'Item start to end is'
, shoplist[:])
# Slicing on a string
name
=
'swaroop'
print
(
'characters 1 to 3 is'
, name[
1
:
3
])
print
(
'characters 2 to end is'
, name[
2
:])
print
(
'characters 1 to -1 is'
, name[
1
:
-
1
])
print
(
'characters start to end is'
, name[:])
|
下标操作: 使用方括号内的数字来指定一个序列时, python会抓取序列中对应位置的项目; python从0开始计算; e.g. shoplist[0]是第一个项目, shoplist[3]代表第四个元素;
索引可以是负数: 位置从序列尾开始计算; e.g. shoplist[-1]代表最后一个元素; -2是倒数第二个项目;
切片操作: 序列后的方框内有一对可选数字, 用冒号分割; 和操作索引符相似, 数字是可选的, 冒号是必须的; 第一个数表示切片开始位置, 第二个数表示切片结束; 如果不指定第一个数, python就从序列首开始; 没有指定第二个数, 则python会停止在序列尾; e.g. shoplist[1:3]返回位置1, 2, 在位置3停止, 因此切片有两个项目;
Note 序列从开始位置开始, 在结束位置之前结束; 即开始位置包含在切片中, 结束位置会被排除在切片外;
shoplist[:]则返回整个序列的拷贝; shoplist[:-1]返回除了最后一个项目外包含所有项目的序列切片;
切片的步长 默认为1, 可以指定切片第三个参数作为步长: e.g. shoplist[::2]将获得0, 2, ...的项目;
使用python解释器交互地尝试不同切片组合, 在提示符下马上能看到结果; 序列可以让你使用相同的方法访问元组, 列表和字符串;
集合
集合是简单对象的无序集合;
使用集合可以测试成员, 确定是否是集合子集; 可以找到两个集合的交集;
1
2
3
4
5
6
7
8
|
bri
=
set
([
'巴西'
,
'俄罗斯'
,
'印度'
])
print
(
'印度'
in
bri)
print
(
'usa'
in
bri)
bric
=
bri.copy()
bric.add(
'中国'
)
print
( bric.issuperset(bri))
bri.remove(
'俄罗斯'
)
print
( bri & bric)
# 或者 bri.intersection(bric)
|
参考/关联
名称到对象的绑定: 当创建一个对象并给它赋一个变量的时候, 这个变量仅仅 参考reference[引用] 那个对象, 而不是表示对象本身; 变量名指向计算机中存储的那个对象的内存; 是所谓的名字绑定;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
print
'Simple Assignment'
shoplist
=
[
'apple'
,
'mango'
,
'carrot'
,
'banana'
]
mylist
=
shoplist
# mylist is just another name pointing to the same object!
del
shoplist[
0
]
print
'shoplist is'
, shoplist
print
'mylist is'
, mylist
# notice that both shoplist and mylist both print the same list without
# the 'apple' confirming that they point to the same object
print
'Copy by making a full slice'
mylist
=
shoplist[:]
# make a copy by doing a full slice
del
mylist[
0
]
# remove first item
print
'shoplist is'
, shoplist
print
'mylist is'
, mylist
# notice that now the two lists are different
|
Note 如果想要复制一个列表或者类似的序列或者其他复杂的对象(不是像整数这样的简单对象 [基本类型]), 那么必须使用切片操作来取得拷贝; 如果想要使用另一个变量名, 那么两个名称都参考/关联[引用]同一个对象;
Note To Perl: 列表的赋值语句不创建拷贝/副本, 要使用切片操作来建立序列的拷贝;
更多字符串的内容
字符串也是对象, 具有方法; 程序中使用的字符串都是str类对象, 参见help(str);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
name
=
'Swaroop'
# This is a string object
if
name.startswith(
'Swa'
):
print
(
'Yes, the string starts with "Swa"'
)
if
'a'
in
name:
print
(
'Yes, it contains the string "a"'
)
if
name.find(
'war'
) !
=
-
1
:
print
(
'Yes, it contains the string "war"'
)
delimiter
=
'_*_'
mylist
=
[
'Brazil'
,
'Russia'
,
'India'
,
'China'
]
print
( delimiter.join(mylist))
|
>startwith 测试字符串是否以给定字符串开始; in操作符用来检验给定字符串是否为另一个字符串的一部分;
>find 用来找出给定字符串在另一个字符串中的位置, 返回-1表示找不到;
>join 以一个作为分隔符的字符串, 用充当分隔符的字符串连接序列条目, 会返回一个生成的大字符串;
---TBC---YC