Python小白必学的面向对象
我们已经知道,在Python中,“一切都是对象”,每个对象都有特定的类型。现在让我们试着创建自己的类型——这需要使用class关键字来定义新的“类”(Class),类是用来生成对象的“模板”,对象是其所属类的“实例”——以下是在交互模式下定制Thing类,并调用其默认构造器生成Thing类的实例对象(注:自定义类的命名规范要求单词首字母大写):
In[1]:classThing: ...:"""最简单的自定义类""" ...: In[2]:type(Thing) Out[2]:type In[3]:t=Thing() In[4]:type(t) Out[4]:__main__.Thing
可以看出,thing对象属于type类型,是type类型的例子;t对象属于thing类型,是thing类型的例子——当你在程序中定义自己的类来生成例子对象时,即使是“面向对象编程”(Object-Oriented Programming,OOP)。面向对象的编程方法使用类别来模拟和组织现实世界中的事物,可以使程序结构更加灵活和清晰。
上面定义的Thing类生成的实例对象什么都做不了。让我们创建一个包含特定子句的“船”类,生成两个“船”对象:
In[5]:classShip: ...:"""船类""" ...:def__init__(self,name=None): ...:"""初始化船的例子""" ...:self.name=name#船名 ...:self.crew=0#船员人数 ...:defjoin(self,number): ...:"""船员加入""" ...:self.crew+=number ...:returnself.crew ...: In[6]:s1=Ship("郑和") In[7]:s1.crew=200 In[8]:s2=Ship("戚继光") In[9]:s2.join(100) Out[9]:100 In[10]:s2.crew Out[10]:100
Ship类定义了一种特殊的“初始化”方法__init__,这样,在调用构造器生成实例时,可以添加新的实例“属性”(Property),所谓实例属性是实例对象的“成员变量”。例如,Ship类的实例增加了name和crew属性——从现实概念来看,任何船都有两个数据:船名和船员人数,但每艘船都有自己的具体数据值。实例属性和实例方法是最常见的两种成员。Python规定,特殊成员名称从两个下划线开始和结束。其他类别的成员名称遵循标准的变量命名规范。请注意,这里有一个详细的概念:作为类别成员的__init__属于函数,作为实例成员__init_______________________self“,它将指向生成的实例对象来操作其成员,相应的实例方法没有此参数,因此在调用Ship结构器时,只需要输入一个参数(或者不能输入任何参数,因为name指定了默认值)。除了实例属性,还可以定义新的实例方法,让实例做更多的事情——比如“船”,还有一种“船员加入”的方法。
In[11]:help(Ship) HelponclassShipinmodule__main__: classShip(builtins.object) |船类 | |Methodsdefinedher: | |__init__(self,name=None) |初始化船实例 | |join(self,number) |船员加入 | |---------------------------------------------------------------------- |Datadescriptorsdefinerer: | |__dict__ |dictionaryforinstancevariabless(ifdefined) | |__weakref__ |listofweakreferencestobject(ifdefined) In[12]:type(Ship.__init__) Out[12]:function In[13]:type(s2.__init__) Out[13]:method In[14]:s1.__dict__ Out[14]:{'crew':200,'name':'郑和'}
实例对象之所以有默认存在的特殊成员,是因为面向对象编程的一个重要特征是“继承”(Inheritance)——使用继承机制可以有机地组织复杂的系统。所有类别都是同一个庞大家庭的成员——定义类别时,可以在类别名称后加括号指定“基本类别”,新类别将成为其“子类别”;如果不指定基类,默认为最基本的“object“子类。子类将继承基类的现有成员。如果子类在定义属性和方法时与基类成员同名,它将“覆盖”基类成员。例如,以下程序定义了“船”及其子类“战舰”:
"""ship.py船的家族""" classShip: """船类""" def__init__(self,name=None): """初始化船的例子""" self.name=name#船名 self.crew=0#船员人数 defjoin(self,number): """船员加入""" self.crew+=number returnself.crew classWarship(Ship): """战舰类""" def__init__(self,name=None,level=None): super().__init__(name)#首先调用基本的初始方法 self.level=level#舰级 if__name__=="__main__": ws1=Warship("蓝色空间","恒星级") ws1.join(500) print("{}战舰{}号,现有舰员{}人。".format(ws1.level,ws1.name,ws1.crew))
Warship类可以重新定义__init__,这将覆盖Ship类__init__,所以先调用基类___init__可以继承基类定义的实例属性name和crew。
下一个例子是一个简单的计算器:
"""tkcalc.简单的pyw计算器 """ importtkinterastk classCalc(tk.Tk): """计算器窗体类""" def__init__(self): """初始化实例""" tk.Tk.__init__(self) self.title("计算器") self.memory=0#暂存数值 self.create() defcreate(self): """创建界面""" btn_list=["C","M->","->M","/", "7","8","9","*", "4","5","6","-", "1","2","3","+", "+/-","0",".","="] r=1 c=0 forbinbtn_list: self.button=tk.Button(self,text=b,width=5, command=(lambdax=b:self.click(x))) self.button.grid(row=r,column=c,padx=3,pady=6) c+=1 ifc>3: c=0 r+=1 self.entry=tk.Entry(self,width=24,borderwidth=2, bg="yellow",font=("Consolas",12)) self.entry.grid(row=0,column=0,columnspan=4,padx=8,pady=6) defclick(self,key): """响应按钮""" ifkey=="=":#输出结果 result=eval(self.entry.get()) self.entry.insert(tk.END,"="+str(result)) elifkey=="C":#清空输入框 self.entry.delete(0,tk.END) elifkey=="->M":#存入数值 self.memory=self.entry.get() if"="inself.memory: ix=self.memory.find("=") self.memory=self.memory[ix+2:] self.title("M="+self.memory) elifkey=="M->":#取出数值 ifself.memory: self.entry.insert(tk.END,self.memory) elifkey=="+/-":#正负翻转 if"="inself.entry.get(): self.entry.delete(0,tk.END) elifself.entry.get()[0]=="-": self.entry.delete(0) else: self.entry.insert(0,"-") else:#其他键 if"="inself.entry.get(): self.entry.delete(0,tk.END) self.entry.insert(tk.END,key) if__name__=="__main__": Calc().mainloop()