#!/usr/bin/env python # coding: utf-8 # # 编程入门11:Python面向对象 # 你已经知道在Python中“一切皆对象”,每个对象都有特定的类型,现在让我们来尝试创建自己的类型——这需要使用class关键字来定义新的“类”(Class),类是用来生成对象的“模板”,对象则是其所属类的“实例”——以下是在交互模式中自定义Thing类,并调用其默认构造器生成一个Thing类的实例对象(注意:自定义类的命名规范要求单词首字母大写): # ``` # In [1]: class Thing: # ...: """最简单的自定义类""" # ...: # # 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)。面向对象的编程方式使用类来模拟和组织现实世界的事物,可以令程序结构更灵活、条理更清晰。 # ![11_articleOOP.png](https://upload-images.jianshu.io/upload_images/10829283-dcd2df7804ef12fd.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) # # 上面定义的Thing类所生成的实例对象并不能做什么事情,让我们再来创建一个包含了具体子语句的“船”类并生成两个“船”对象: # ``` # In [5]: class Ship: # ...: """船类""" # ...: def __init__(self, name=None): # ...: """初始化船实例""" # ...: self.name = name # 船名 # ...: self.crew = 0 # 船员人数 # ...: def join(self, number): # ...: """船员加入""" # ...: self.crew += number # ...: return self.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) # Help on class Ship in module __main__: # # class Ship(builtins.object) # | 船类 # | # | Methods defined here: # | # | __init__(self, name=None) # | 初始化船实例 # | # | join(self, number) # | 船员加入 # | # | ---------------------------------------------------------------------- # | Data descriptors defined here: # | # | __dict__ # | dictionary for instance variables (if defined) # | # | __weakref__ # | list of weak references to the object (if defined) # # # 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 船的家族""" # # # class Ship: # """船类""" # def __init__(self, name=None): # """初始化船实例""" # self.name = name # 船名 # self.crew = 0 # 船员人数 # # def join(self, number): # """船员加入""" # self.crew += number # return self.crew # # # class Warship(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 简单的计算器 # """ # import tkinter as tk # # # class Calc(tk.Tk): # """计算器窗体类""" # def __init__(self): # """初始化实例""" # tk.Tk.__init__(self) # self.title("计算器") # self.memory = 0 # 暂存数值 # self.create() # # def create(self): # """创建界面""" # btn_list = ["C", "M->", "->M", "/", # "7", "8", "9", "*", # "4", "5", "6", "-", # "1", "2", "3", "+", # "+/-", "0", ".", "="] # r = 1 # c = 0 # for b in btn_list: # self.button = tk.Button(self, text=b, width=5, # command=(lambda x=b: self.click(x))) # self.button.grid(row=r, column=c, padx=3, pady=6) # c += 1 # if c > 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) # # def click(self, key): # """响应按钮""" # if key == "=": # 输出结果 # result = eval(self.entry.get()) # self.entry.insert(tk.END, " = " + str(result)) # elif key == "C": # 清空输入框 # self.entry.delete(0, tk.END) # elif key == "->M": # 存入数值 # self.memory = self.entry.get() # if "=" in self.memory: # ix = self.memory.find("=") # self.memory = self.memory[ix + 2:] # self.title("M=" + self.memory) # elif key == "M->": # 取出数值 # if self.memory: # self.entry.insert(tk.END, self.memory) # elif key == "+/-": # 正负翻转 # if "=" in self.entry.get(): # self.entry.delete(0, tk.END) # elif self.entry.get()[0] == "-": # self.entry.delete(0) # else: # self.entry.insert(0, "-") # else: # 其他键 # if "=" in self.entry.get(): # self.entry.delete(0, tk.END) # self.entry.insert(tk.END, key) # # # if __name__ == "__main__": # Calc().mainloop() # ``` # ![11_calc.png](https://upload-images.jianshu.io/upload_images/10829283-43263251cd6833e9.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) # # 程序窗体继承自tkinter.Tk类,要加部件就定义实例属性,要做事情就使用实例方法,这就是OOP的方式。 # # ——编程原来这样…… # # ## 编程小提示 # 这次给大家分享的是“Python资源大全中文版”,好用的东西都在其中了…… # https://github.com/jobbole/awesome-python-cn # # 下一篇:[编程入门12:Python异常处理](12_except.ipynb)