您的位置:首页 > 编程语言 > Python开发

Python Class System

2014-03-18 15:51 337 查看

1.序言

本文旨在说明:在Python里自定义class时,方法的第一个参数必须是该class的instance自身的引用(一般用self命名)。

在其他语言里,定义方法的时候,第一个参数不必是类实例的引用,一般约定俗成用this关键字来表示当前实例的引用,可是Python自成一派。由于网络上绝大部分文章都说成这是硬性规定,因此笔者觉得很有必要去研究一下Python里的classSystem是如何构筑起来的,并在此基础上说明self一词的作用。

2.面向对象编程

对象是数据和对数据的相关操作的封装。属于对象的数据与操作也可以称之为对象的属性(attributes)。对象具有层次构造,最下层的称之为instance,在其之上的称为class。class也具有层次构造,下层class会继承上层class的属性。有必要的时候可以再定义上层的属性。在Python里,一个class可以继承多个class(多重继承)。

更多内容详见Wikipedia(日本語)和Wikipedia(英語)。

3.Python里class的一般写法

首先,我们使用Python的classsystem来写一段程序。

IT公司“LiveGate”雇佣了大量的IT技术人员,并用Python写了一个管理技术人员信息的程序。理应用数据库来储存这些信息,但这里出于演示方便就不使用了。接着我们看一下表示IT技术人员的class的代码:

[code1](workers.py)



代码

01:#!/usr/bin/envpython
02:
03:"""
04:WorkersinaITcompanynamedLiveGate
05:"""
06:
07:classWorkers:
08:"""Thisisaclassofworkersworkinginthecompany."""
09:
10:def__init__(self,name,position,email,age,salary):
11:self.name=name
12:self.position=position
13:self.email=email
14:self.age=age
15:self.salary=salary
16:
17:
18:classITWorkers(Workers):
19:"""ThisisaclassofITengineers."""
20:
21:OS='WinNT'
22:
23:def__init__(self,language,*av):
24:Workers.__init__(self,*av)
25:self.language=language
26:
27:defwork(self,n):
28:"""ITengineersshouldwork."""
29:
30:ifself.position=='webcreator':
31:w='makeswebsite'
32:elifself.position=='serveradministrator':
33:w='checksthetrafic'
34:elifself.position=='programmer':
35:w='writesprograms'
36:
37:print'%s%sfor%d,hoursusing%son%s'%(self.name,w,n,self.language,self.OS)
38:
39:##------------------------------------------------------------------------------------------------
40:henley=ITWorkers('PHP','Henley','webcreator','henley@livegate.com',32,700)
41:thomas=ITWorkers('Python','Thomas','serveradministrator','thomas@livegate.com',37,900)
42:gates=ITWorkers('C','Gates','programmer','gates@livegate.com',42,1200)
43:
44:henley.OS='Mac'
45:thomas.OS='Linux'
46:
47:if__name__=='__main__':
48:
49:henley.work(8)
50:thomas.work(7)
51:gates.work(10)

D:\doc\05-07\py_test>pythonworkers.py
Henleymakeswebsitefor8hours,usingPHPonMac
Thomaschecksthetraficfor7hours,usingPythononLinux
Gateswritesprogramsfor10hours,usingConWinNT

4.假如Python没有classsystem?

这里我们思考一下,假如Python没有classsystem,我们应该如何处理这种情况呢。当然,可以不使用OOP来写程序,但在这里,我们想创建属于自己的classsystem。

实际上,使用把函数当成数据一样来对待的编程语言(广义上指函数式语言)来创建OOP语言是非常简单的。可以使用hash表(Python里称字典)来表示各个对象的名字空间,对象的层次构造也可以根据hash表的层次结构来表示。由于Python也是把函数当成数据来对待,所以很容易实现OOP。

我们尝试用自己的classsystem来重新把workers.py写一遍。参考重新编写的代码,那您应该明白方法的第一个参数为什么是self了。

[code2](workers2.py)

代码

01:#!/usr/bin/envpython
02:
03:"""
04:Thiscodedemostrateshoweasytoimprementanobjectorientatedsystemonafunctionalprogramminglanguage.
05:Itonlyrequiresanestedhashtable.
06:"""
07:
08:
09:defCls(cls=None,**key):
10:"""makinganewclass"""
11:key['class']=cls
12:returnkey
13:
14:defnew(cls,**key):
15:"""makinganinstance"""
16:key['class']=cls
17:returnkey
18:
19:
20:defgeta(obj,attr):
21:"""gettingtheattributeofobject"""
22:ifattrinobj:
23:returnobj[attr]
24:elif(obj['class']):
25:returngeta(obj['class'],attr)
26:else:
27:returnNone
28:
29:deftell(obj,method,*av):
30:"""tellobjectdosomething"""
31:fun=geta(obj,method)
32:ifcallable(fun):
33:returnfun(obj,*av)
34:
35:if__name__=='__main__':
36:
37:defit_work(self,n):
38:"""ThisfuncitondemonstrateshowITengineerswork.
39:Noticethatargumentsofthiefunctionisidenticaltothemethod'work'inworkers.py"""
40:
41:ifgeta(self,'position')=='webcreator':
42:w='makeswebsite'
43:elifgeta(self,'position')=='serveradministrator':
44:w='checksthetrafic'
45:elifgeta(self,'position')=='programmer':
46:w='writesprograms'
47:
48:print'%s%sfor%d,hoursusing%son%s'%
(geta(self,'name'),w,n,geta(self,'language'),geta(self,'OS'))
49:
50:workers=Cls()#dummyclass
51:it_workers=Cls(workers,OS='winNT',work=it_work)#classofITworkers
52:
53:henley=new(it_workers,language='PHP',name='henley',
54:position='webcreator',email='henley@livegate.com',age=32,salary=700)
55:thomas=new(it_workers,language='Python',name='Thomas',
56:position='serveradministrator',email='thomas@livegate.com',age=37,salary=900)
57:gates=new(it_workers,language='C',name='Gates',
58:position='programmer',email='gates@livegate.com',age=42,salary=1200)
59:henley['OS']='Mac'
60:thomas['OS']='Linux'
61:
62:tell(henley,'work',8)
63:tell(thomas,'work',7)
64:tell(gates,'work',10)

代码

D:\doc\05-07\py_test>pythonworkers2.py
Henleymakeswebsitefor8hours,usingPHPonMac
Thomaschecksthetraficfor7hours,usingPythononLinux
Gateswritesprogramsfor10hours,usingConwinNT

对比[code1]与[code2],可以看出他们相似的地方:

[code1][code2]
obj.attributegeta(obj,'attribute')
obj.method(*av)tell(obj,'method',*av)
defwork(self,n)defit_work(self,n)
这并不是偶然,Python里的class从原理上来说是这样实现的(请参考:Pythonreferencemanual3.Datamodel)。实际上,Python已经为我们准备了跟函数geta一样功能的getattr函数。而且在特殊变量__dict__里定义了用于定义对象名字空间的hash表。我们可以试试在命令行里输入如下代码。粗体字是返回结果。

D:\doc\05-07\py_test>python
Python2.4.1(#65,Mar302005,09:13:57)[MSCv.131032bit(Intel)]onwin32
Type"help","copyright","credits"or"license"formoreinformation.
[x]>>>importsys
[x]>>>fromworkersimport*
[1]>>>gates.__dict__
{'salary':1200,'name':'Gates','language':'C','age':42,'position':'programmer',
'email':'gates@livegate.com'}

[2]>>>henley.__dict__
{'salary':700,'name':'Henley','language':'PHP','age':32,'position':'webcreator',
'OS':'Mac','email':'henley@livegate.com'}

[3]>>>ITWorkers.__dict__
{'__module__':'workers','work':<functionworkat0x00A34630>,'OS':'WinNT',
'__doc__':'ThisisaclassofITengineers.','__init__':<function__init__
at0x00A345F0>}

[4]>>>ITWorkers.work(gates,10)
Gateswritesprogramsfor10,hoursusingConWinNT
[5]>>>gates.__class__.work(gates,10)
Gateswritesprogramsfor10,hoursusingConWinNT
[6]>>>getattr(henley,'OS')
'Mac'
[7]>>>getattr(henley,'work')
<boundmethodITWorkers.workof<workers.ITWorkersinstanceat0x00A33760>>
[8]>>>getattr(henley,'work')(8)
henleymakeswebsitefor8hours,usingPHPonMac

导入sys和workers模块,然后试试敲进上面的8行命令。Gates的名字空间里([1])有各类项目(entry),但是没有'OS'这一项。Henley的名字空间里([2])有定义'OS'。ITWorkers的名字空间里([3])除了预置的__module__,__doc__,还有我们定义的'OS',work,__init__。特别的,方法(<functionworkat0x00A34630>)作为函数被保存在内存里。正如前面提及的一样,Henley使用自身定义的'OS',而Gates则向上搜寻,使用类ITWorkers里的'OS'('OS'不存在Gates这个名字空间里)。

因为在类ITWorkers里定义了函数work,我们可以试着像[4]那样直接调用它。其输出跟调用gates.work(10)一样。由于每个实例有一个内置属性,__class__,它指向该实例所属的类,所以我们可以像[5]那样调用方法且得到相同的结果。

最后我们试试getattr函数。像[6]那样,getattr(henley,'OS')得到的结果跟henley.OS一样。我们把它应用到方法上看看([7])。返回如下结果:

<boundmethodITWorkers.workof<workers.ITWorkersinstanceat0x00A33760>>

<workers.ITWorkersinstanceat0x00A33760>这是Henley在内存中的地址。这个函数不用'function'而用'boundmethod'表示。其实'boundmethod'可以像[8]那样调用。这就说明了为什么从外部调用类方法的时候,第一个参数不必是实例自身的引用。不过,真正的理由应该是那样的做法不够酷:p。'boundmethod'可以看做是[code2]里tell函数的语法糖(構文糖衣)。

从上面可以看出,在函数式语言里引入classsystem时,作为方法而定义的函数,很自然地,需要指向实例的参数。通过def关键字,在类里面定义过程与定义普通的函数一样,只是其作用域被限定在class里面。所以定义方法的第一个参数必须是self,否则不能引用实例里的变量。

5.结语

Python基本上是函数式语言(广义的),面向对象是其使用hash表后的附属物而已。这一点与原本作为面向对象编程语言而设计的C++,Java,Ruby等相异。

Python把过程的定义合并成函数的定义(没有将函数的定义与方法的定义区分开来),但在定义方法时,第一个参数必须是实例的引用。这是约定俗成的。

函数式语言要比面向对象语言更加抽象。Python深受函数式语言Haskell的影响。实际上,像[code2]所示的那样,函数式语言可以简单地实现面向对象编程。

本文出处:http://www.shido.info/py/python7.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: