使用模块
Python本身就内置了很多非常有用的模块,只要安装安装完毕,这些模块就可以立刻使用。
我们以内建的 sys 模块为例,编写一个 hello 的模块:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
'a test module'
# 一个字符串表示文档注释,任何模块代码的第一个字符串都被视为模块的文档注释
__author__
=
'xiaoming'
#作者名 可以删除 上面的也可以不要
import
sys
#导入 sys 模块
def
test():
args
=
sys.argv
if
len
(args)
=
=
1
:
print
(
'Hello,world!'
)
elif
len
(args)
=
=
2
:
print
(
'Hello,%s'
%
args[
1
])
else
:
print
(
"Too many argumments!"
)
if
__name__
=
=
'__main__'
:
test()
|
sys模块有一个 argv 变量,用list 存储了命令的所有参数。argv至少有一个元素,以你为第一个参数永远是该 .py 文件的名称,例如:
运行
1
|
E:\Python>python lianxi.py 获得的 sys.argv 就是 [
'lianxi.py'
]
|
运行
1
|
E:\Python>python lianxi.py Mingtian 获得的sys。argv 就是[
'lianxi.py '
,
'Mingtian'
]
|
最后,注意到这两行代码:
1
2
|
if
__name__
=
=
'__main__'
:
test()
|
当我们在命令行运行 lianxi 模块文件时,python 解释器把一个特殊变量 __name__置为__main__,而如果在其他地方导入该lianxi 模块时,if 前段将失败,因此,这种if 测试可以让一个模块通过命令行运行一个额外的代码,最常见的就是运行测试。
用命令号运行 lianxi.py :
1
2
|
E:\Python>python lianxi.py
Hello,world!
|
1
2
|
E:\Python>python lianxi.py Mingtian
Hello,Mingtian
|
启动python交互环境,再导入练习模块:
1
2
3
|
>>>
import
lianxi
>>> lianxi.test()
Hello,world!
|
导入时没有打印 hello,world,因为没事执行test()函数。调用test()函数才会打印 hello,world
作用域
在一个模块中,我们可能会定义很多函数和变量,担忧的函数和变量我们希望给别人使用,有的函数和变量我们希望仅仅在模块内部使用。在Python中,是通过 _ 前缀来实现的。
正常的函数和变量名是公开的(public),可以被直接饮用,比如:abc,x123,PI等:
类似__xxx__这应的函数或变量就是非公开的(private),不应该被直接饮用, 比如_abc,__abc等:
之所以我们说,private函数和变量“不应该”被直接饮用,而不是“不能”被直接饮用,是因为Python并没有一种方法可以完全限制访问private函数或变量,但是,从变成习惯上不应该应用private函数或变量。
private函数或变量不应该被别人引用,那它们有什么用呢?:
1
2
3
4
5
6
7
8
9
10
|
def
_private_1(name):
return
'Hello, %s'
%
name
def
_private_2(name):
return
'Hi, %s'
%
name
def
greeting(name):
if
len
(name) >
3
:
return
_private_1(name)
else
:
return
_private_2(name)
|
我们在模块里公开greeting()
函数,而把内部逻辑用private函数隐藏起来了,这样,调用greeting()
函数不用关心内部的private函数细节,这也是一种非常有用的代码封装和抽象的方法,即:
外部不需要引用的函数全部定义成private,只有外部需要引用的函数才定义为public。
面向对象编程
面向对象编程 ------ Object Oriented Programming,简称OOP,是一种程序设计思想。OOP把对象作为程序的基本单元,一个对象包含了数据和操作数据的函数。
面向过程的程序设计把计算机程序视为一系列的命令集合,及一组函数的顺序执行。为了简化程序设计,面向过程把函数继续切为分子函数,即把大块函数通过切割成小块函数来降低体统的复杂度。
而面向对象的程序设计把计算机程序视为一组对象的集合,而每个对象都可以接受其他对象发过来的消息,并处理这些消息,计算机程序的执行 就是一系列消息在各个对象之间传递。
在python中,所有数据类型都可以视为对象,当然也可以自定义对象。自定义的的数据类型就是面向对象中的类class的概念。
Eg:显示一个学生的成绩
(1)面向过程:
1
2
3
4
|
>>> std1
=
{
'name'
:
'Micheal'
,
'score'
:
98
}
>>> std2
=
{
'name'
:
'Bob'
,
'score'
:
81
}
>>>
def
print_score(std):
print
(
'%s:%s'
%
(std[
'name'
],std[
'score'
]))
|
1
2
3
4
|
>>> print_score(std1)
Micheal:
98
>>> print_score(std2)
Bob:
81
|
(2)采用面向对象的程序设计思想,我们首选思考的不是程序的执行流程,而是student这种数据类型应该被视为一个对象,这个对象拥有 name 和 score 这两个属性(Property)。如果要打印一个学生的成句,首相必须创建出这个学生对应的对象,然后,给对象发一个 print_score 消息,让自己把自己的数据打印出来。
1
2
3
4
5
6
|
class
Student(
object
):
def
__init__(
self
,name,score):
self
.name
=
name
self
.score
=
score
def
print_score(
self
):
print
(
'%s:%s'
%
(
self
.name,
self
.score))
|
给对象发消息实际上就是调用对象对应的关联函数,我们称之为对象的方法(Method)。面向对象的程序写出来就想这样:
1
2
3
4
5
6
|
>>> bart
=
Student(
'Bart'
,
59
)
>>> lisa
=
Student(
'Lisa'
,
98
)
>>> bart.print_score()
Bart:
59
>>> lisa.print_score()
Lisa:
98
|
类和实例
面向对象最重要的概念就是类(Class)和实例(Instance),必须牢记类是抽象的模板,比如Student类,而实例是根据类创建出来的一个个具体的“对象”,每个对象都拥有相同的方法,但各自的数据可能不同。
在Python中,定义类是通过class
关键字:
1
2
|
>>>
class
Student(
object
):
pass
|
class
后面紧接着是类名,即Student
,类名通常是大写开头的单词,紧接着是(object)
,表示该类是从哪个类继承下来的,
通常,如果没有合适的继承类,就使用object
类,这是所有类最终都会继承的类。
定义好了Student
类,就可以根据Student
类创建出Student
的实例,创建实例是通过类名+()实现的:
1
2
3
4
5
|
>>> bart
=
Student(
'xiaoming'
,
66
)
>>> bart
<__main__.Student
object
at
0x02CEAA70
>>>> Student
<
class
'__main__.Student'
>
|
可以看到,变量bart
指向的就是一个Student
的实例,后面的0x10a67a590
是内存地址,每个object的地址都不一样,而Student
本身则是一个类。
可以自由地给一个实例变量绑定属性,比如,给实例bart
绑定一个name
属性:
1
2
3
|
>>> bart.name
=
'xaioming'
>>> bart.name
'xaioming'
|
由于类可以起到模板的作用,因此,可以在创建实例的时候,把一些我们认为必须绑定的属性强制填写进去。通过定义一个特殊的__init__
方法,在创建实例的时候,就把name
,score
等属性绑上去:
1
2
3
4
|
class
Student(
object
):
def
__init__(
self
,name,score):
self
.name
=
name
self
.score
=
score
|
注意__init__方法的第一个参数永远是self,表示创建的实例的本身,因此,在__init__方法内部,就可以把各种属性绑定带self,因为self就指向创建的实例本身。有了 __inlt__方法在创建实例的时候,就不能传入空的参数了,必须传入与__init__方法匹配的参数,但self不需要传,Python解释器自己会把实例变量穿进去:
1
2
3
4
|
>>> bart
=
Student(
'xiaoming'
,
66
)
>>> bart.name
'xiaoming'
>>> bart.score
66
|
和普通的函数相比,在类中定义的函数只有一点不同,就是第一个参数永远是实例变量self,并且,调用时,不用传递该参数。除此之外,类的方法和普通函数没有什么区别,所以,你任然可以用默认参数、可变参数、关键字参数和命名关键字参数
数据封装
面向对象编程的一个重要特点就是数据封装。在上面的student类中,每个实例就拥有各自的name 和 score 这些数据。
我们可以通过函数来访问这些数据,比如打印一个学生的成绩:
1
2
|
def
print_score(
self
):
print
(
'%s:%s'
%
(
self
.name,
self
.score))
|
1
2
|
>>>
def
print_score(std):
print
(
'%s: %s'
%
(std.name, std.score))
|
1
2
|
>>> print_score(bart)
Bart Simpson:
59
|
既然Student实例本身就拥有这些数据,要访问这些数据,就没有必要从外面的函数去访问,可以直接在Student类的内部定义访问数据的函数,这样,就把''数据"给封装起来了。这些封装数据的函数是和Student类本身关联起来的,我们称之为类的方法:
1
2
3
4
5
6
7
8
9
|
class
Student(
object
):
def
__init__(
self
,name,score):
self
.name
=
name
self
.score
=
score
def
print_score(
self
):
print
(
'%s:%s'
%
(
self
.name,
self
.score))
>>> bart
=
Student(
'xiaoming'
,
99
)
>>> bart.print_score()
xiaoming:
99
|
访问限制
在class内部,可以有属性和方法,而外部代码可以通过直接调用实例变量的方法来操作数据,这样,就隐藏了内部的复杂 逻辑。
但是,从前面Student类的定义来看,外部代码还是可以自由地修改一个实例的name,score属性:
1
2
3
4
5
|
>>> bart
=
Student(
'Bart'
,
98
)
>>> bart.score98
>>> bart.score
=
59
>>> bart.score
59
|
如果要让内部属性,可以把属性的名称前加上两个下划线__,在python中,实例的变量名如果以__开头,就变成了一个私有变量(private),只有内部可以访问,外部不能访问,所以,我们把Student类改一改:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
class
Student(
object
):
def
__init__(
self
,name,score):
self
.__name
=
name
self
.__score
=
score
def
print_score(
self
):
print
(
'%s:%s'
%
(
self
.__name,
self
.__score))
def
get_grade(
self
):
if
self
.score>
=
90
:
return
'A'
elif
self
.score>
=
60
:
return
'B'
else
:
return
'C'
|
改完后,对于外部代码来说,没什么变动,但是已经无法从外部访问实例变量.__name
和实例变量.__score
了:
1
2
3
4
5
6
|
>>> bart
=
Student(
'Bart'
,
98
)
>>> bart.__name
Traceback (most recent call last)
:
File
"<pyshell#11>"
, line
1
,
in
<module>
bart.__name
AttributeError:
'Student'
object
has no attribute
'__name'
|
这样就确保了外部代码不能随意修改对象内部的状态,这样通过访问限制的保护,代码更加健壮。
但是如果外部代码要获取name和score怎么办?可以给Student 类增加 get_name 和 get_score 这样的方法:
1
2
3
4
5
6
|
class
Student(
object
):
...
def
get_name(
self
):
return
self
.__name
def
get_score(
self
):
return
self
.__score
|
如果又要允许外部代码修改score怎么办?可以再给Student类增加set_score
方法:
1
2
3
4
|
class
Student(
object
):
...
def
set_score(
self
, score):
self
.__score
=
score
|
在方法中,可以对参数做检查,避免传入无效的参数:
1
2
3
4
5
6
7
|
class
Student(
object
):
...
def
set_score(
self
, score):
if
0
<
=
score <
=
100
:
self
.__score
=
score
else
:
raise
ValueError(
'bad score'
)
|
需要注意的是,在Python中,变量名类似__xxx__
的,也就是以双下划线开头,并且以双下划线结尾的,是特殊变量,特殊变量是可以直接访问的,不是private变量,所以,不能用__name__
、__score__
这样的变量名。
有些时候,我们会看到以一个下划线开头的实例变量名,比如_name
,这样的实例变量外部是可以访问的,但是,按照约定俗成的规定,当你看到这样的变量时,意思就是,“虽然我可以被访问,但是,请把我视为私有变量,不要随意访问”。
双下划线开头的实例变量是不是一定不能从外部访问呢?其实也不是。不能直接访问__name
是因为Python解释器对外把__name
变量改成了_Student__name
,所以,仍然可以通过_Student__name
来访问__name
变量:
1
2
|
>>> bart._Student__name
'Bart Simpson'
|
因为不同版本的Python解释器可能会把__name
改成不同的变量名。
总的来说就是,Python本身没有任何机制阻止你干坏事,一切全靠自觉。