一、三大编程范式
二、编程进化论
三、面向对象设计与面向对象编程
1 # 面向对象设计 2 def dog(name,gender,type): # 相当于类的概念 3 # 动作 4 def jiao(dog): 5 print("一条 %s 正在叫" %dog["name"]) 6 def chi(dog): 7 print("一条 %s 正在吃骨头"%dog["name"]) 8 # 属性 特征 9 def init(name,gender,type): # 初始化函数 10 dog1 = { 11 "name": name, 12 "gender": gender, 13 "type": type, 14 "jiao": jiao , 15 "chi": chi 16 } 17 return dog1 18 return init(name,gender,type) 19 d1 = dog("xiaoming","mu","zangao") # 相当于一个对象 20 d2 = dog("xiaogang","mu","zangao") 21 print(d1) 22 d1["chi"](d1) 23 d2["jiao"](d2)
四、小结
五、类和对象
class Teacher: ‘这是一个教师类’ def __init__(self,name,age,job_in,teach_subjects): # 实例化 self.name = name # self 实例本身 self.age = age # 相当于teacher1.name = name self.job_in = job_in self.teach_subjects = teach_subjects def teach(self): print("%s 在 %s 教 %s" %(self.name,self.job_in,self.teach_subjects)) def talk(self,name,content): print("%s 在和 %s 讨论 %s"%(self.name,name,content)) teacher1 = Teacher("lhf",33,"oldboy","python") # 对象# 就相当于,理论上teacher1 = Teacher.__init__(teacher1,name,age,job_in,teach_subjects) teacher2 = Teacher("wusir",28,"oldboy","python") # 对象 teacher1.teach() # 调用教学方法 teacher2.talk("小明","如何学好python")
5.1 属性
类是用来描述一类事物,类的对象指的是这一类事物中的一个个体
是事物就要有属性,属性分为
1:数据属性:就是变量
2:函数属性:就是函数,在面向对象里通常称为方法
注意:类和对象均用点来访问自己的属性
类的数据属性
1 class ChinesePeople: 2 government='共产_党' 3 4 print(ChinesePeople.government)
类的函数属性
1 class Chinese: 2 '这是一个中国人的类' 3 goverment = "共产_党" # 数据属性 4 def __init__(self,name,age,gender): # 创建一个实例对象 5 print(" 我开始了") 6 self.mingzi = name 7 self.nianling = age 8 self.xingbei = gender 9 def gongfu(): # 函数属性 又称方法 10 print("会功夫") 11 def xihuanchi(self): 12 print("%s 讲究吃"%self.mingzi) 13 p1 = Chinese("小明",18,"female") # 对象 14 print(p1.__dict__) # 相当于init的返回值 执行__init__函数 {'mingzi': '小明', 'xingbei': 'female', 'nianling': 18} 15 print(p1.mingzi) # 小明 16 print(p1.goverment) #共产_党 17 print(Chinese.__dict__)# {'__init__': <function Chinese.__init__ at 0x0000016DDEDCF378>, '__dict__': <attribute '__dict__' of 'Chinese' objects>, 'gongfu': <function Chinese.gongfu at 0x0000016DDEDCF400>, 'goverment': '共产_党', 'xihuanchi': <function Chinese.xihuanchi at 0x0000016DDEDCF488>, '__module__': '__main__', '__doc__': '这是一个中国人的类', '__weakref__': <attribute '__weakref__' of 'Chinese' objects>} 18 Chinese.gongfu() #会功夫 19 Chinese.xihuanchi(p1) # 小明 讲究吃 20 p1.xihuanchi() #小明 讲究吃
查看类属性
我们定义的类的属性到底存到哪里了?有两种方式查看
dir(类名):查出的是一个名字列表
类名.__dict__:查出的是一个字典,key为属性名,value为属性值
特殊的类属性
1 # class dog : 2 # '这是定义一个狗的对象' 3 # name = "藏獒" 4 # def jiao(self): 5 # print("一条 %s 正在汪汪汪" %self) 6 # dog.jiao("藏獒") 7 # print(dir(dog)) # 显示结果是一个列表,包含类(包含内建属性在内的)所有的属性名 8 # print(dog.__dict__) # 显示结果是一个字典,包含类的所有属性:属性值 9 # 10 # print(dog.__name__)# 类C的名字(字符串) 11 # print(dog.__doc__)# 类C的文档字符串 12 # print(dog.__base__)# 类C的第一个父类(在讲继承时会讲) 13 # print(dog.__bases__)# 类C的所有父类构成的元组(在讲继承时会讲) 14 # print(dog.__dict__)# 类C的属性 15 # print(dog.__module__)# 类C定义所在的模块 16 # print(dog.__class__)# 实例C对应的类(仅新式类中)
5.2 类属性与对象(实例)属性
# 类属性的增删改查 # class Chinese: # country='China' # def __init__(self,name): # self.name=name # # def play_ball(self,ball): # print('%s 正在打 %s' %self.name,ball) # # def say_word(self,word): # # print('%s 说 %s' %(self.name,word)) # 数据属性的增删改查 # 查看类的属性 # print(Chinese.country) # 修改 # Chinese.country = "English" # print(Chinese.country) # 增加 # Chinese.location = "Asia" # print(Chinese.__dict__) # print(Chinese.location) # # 删除 # del Chinese.location # print(Chinese.__dict__) # 实例属性的增删改查 # 查看 # p1 = Chinese("xiaoming") # print(p1.__dict__) # # 查看 # print(p1.name) # # 删除 # del p1.name # print(p1.__dict__) # # 增加 # p1.gender = "man" # print(p1.__dict__) # # 修改 # p1.gender = "woman" # print(p1.__dict__) # 函数属性的增加修改 # class Chinese: # country='China' # def __init__(self,name): # self.name=name # # def play_ball(self,ball): # print('%s 正在打 %s' %(self.name,ball)) # # def say_word(self,word): # # print('%s 说 %s' %(self.name,word)) # # 增加 # def eat_food(self,food): # print("%s 正在吃 %s" %(self.name,food)) # Chinese.eat = eat_food # print(Chinese.__dict__) # p1 = Chinese("xiaoming") # # print(p1.__dict__) # p1.eat("fan")
# 注意事项
1 # 1 2 # class Chinese: 3 # country = "China" 4 # def __init__(self,name): 5 # self.name = name 6 # def play_ball(self,ball): 7 # print("%s 正在打 %s" %(self.name,ball)) 8 # p1 = Chinese('alex') 9 # print(p1.country) 10 # p1.country = "日本" # 相当于在实例里面创建了个新的 11 # print(p1.__dict__) # {'name': 'alex', 'country': '日本'} 12 # print("类的——",Chinese.country) 13 # print("实例的",p1.country) 14 15 # 2、不调用 . 直接找全局变量 16 # country = "中国" 17 # class Chinese: 18 # country = "China" 19 # def __init__(self,name): 20 # self.name = name 21 # print(country) # 中国 22 # def play_ball(self,ball): 23 # print("%s 正在打 %s" %(self.name,ball)) 24 # p1 = Chinese('alex') 25 # print(p1.country) # China
六 静态属性、类方法、静态方法
方法包括:普通方法、静态方法和类方法,三种方法在内存中都归属于类,区别在于调用方式不同。
-
普通方法:由对象调用;至少一个self参数;执行普通方法时,自动将调用该方法的对象赋值给self;
-
类方法:由类调用; 至少一个cls参数;执行类方法时,自动将调用该方法的类复制给cls;classmethod
-
静态方法:由类调用;无默认参数;staticmethod
1 class Room: 2 x = 1 3 def __init__(self,owner,name,length,width,height): 4 self.owner = owner 5 self.name = name 6 self.length = length 7 self.width = width 8 self.height = height 9 @property # 把函数属性封装为(静态属性)(数据属性) 10 def cal_volume(self): 11 return self.length * self.height * self.width 12 13 @classmethod # 类方法 : 专门给类使用,(数据属性)与实例无关,类方法只能访问类相关的属性,不能访问实例属性 14 def tell_info(cls,name): 15 print(cls) 16 print(cls.x,name) 17 @staticmethod # 静态方法只是名义上的归属类的管理,不能使用类变量和实例变量,是类的工具包 18 def text(a,b,c): 19 print(a,b,c) 20 Room.text(1,2,3) 21 Room.tell_info("xiaoming") 22 r1 = Room("小明","豪华别墅",10,20,30) 23 print(r1.cal_volume)
# 静态属性 class Room: tag = 1 def __init__(self, owner, name, width, length, high): self.owner = owner self.name = name self.width = width self.length = length self.high = high @property # 在调用函数的时候把括号去掉, 相当于调用属性, 像是(把函数属性,变成了数据属性)这样讲是不准确的 def cla_area(self): # print("%s 住的 %s 的总面积 %s" % (self.owner, self.name, self.width * self.length)) return "%s 住的 %s 的总面积 %s" % (self.owner, self.name, self.width * self.length) def test(self): print('from test', self.name) @classmethod # 只和类有关和实例没有关系 ,能调用数据属性, 类级别的调用 #定义备选构造方法 def test1(cls, x): print('from test1', cls.tag, x) @staticmethod # 类的工机具包, 不和实例绑定,不和类绑定, ,静 # 态方法就是普通的函数,只是碰巧在类的定义体中,而不是在模块层定义 def test2(): print("aaaaa") r1 = Room("主卧", 'sk', 20, 10, 100) r2 = Room("主卧", 'ck', 10, 10, 100) # print(r1.cla_area) # print(r2.cla_area) # 类方法 print(Room.tag) Room.test1("xiao") Room.test2()
七 组合
# 定义一个人的类,人有头,躯干,手,脚等数据属性,这几个属性又可以是通过一个类实例化的对象,这就是组合
# 用途:
# 1:做关联
# 2:小的组成大的
1 class Hand: 2 pass 3 class Foot: 4 pass 5 class Trunk: 6 pass 7 class Head: 8 pass 9 10 class Person: 11 def __init__(self,id_num,name,hand,foot,trunk,head): 12 self.id_num=id_num 13 self.name=name 14 self.hand=Hand() 15 self.foot=Foot() 16 self.trunk=Trunk() 17 self.head=Head()
1 class School: 2 '定义一个学校的类' 3 def __init__(self,name,addr): 4 self.name = name 5 self.addr = addr 6 def recruit_student(self): 7 print("%s 正在招生" %self.name) 8 class Course: 9 '定义一个课程的类' 10 def __init__(self,name,price,period,school): 11 self.name = name 12 self.price = price 13 self.period = period 14 self.school = school 15 s1 = School("oldboy","北京") # 为学校类创建一个对象 16 s2 = School("oldboy","南京") 17 s3 = School("oldboy","东京") 18 msg = ''' 19 1、老男孩 北京校区 20 2、老男孩 南京校区 21 3、老男孩 东京校区 22 ''' 23 while True: 24 print(msg) 25 menu = { 26 "1":s1, 27 "2":s2, 28 "3":s3 29 } # 对应学校的类 30 choice = input("选择学校》》》") 31 school_obj = menu[choice] # 就相当于是 school_obj = s1 32 name = input("课程名》》》") 33 price = input("课程费用》》》") 34 period = input("课程周期》》》") 35 new_course = Course(name,price,period,school_obj) # 最后传的是s1 36 print("课程 【%s】 属于【%s】 学校 %s 校区"%(new_course.name,new_course.school.name,new_course.school.addr))
八 面向对象编程三大特性
8.1 继承
一 什么是类的继承?
类的继承跟现实生活中的父、子、孙子、重孙子、继承关系一样,父类又称为基类。
python中类的继承分为:单继承和多继承
1 class Base: 2 pass 3 class Father: 4 pass 5 class Son(Father): #单继承 6 pass 7 class Son(Base,Father): #多继承 8 pass
二 子继承到底继承了父类的什么属性?
1 class Dad: 2 '这是父类' 3 money = 50000 4 def __init__(self,name): 5 self.name = name 6 def hit_son(self): 7 print("%s 正在打孩子"%self.name) 8 class Son(Dad): 9 '这是一个儿子的类' 10 money = 50000000 # 子类继承了父类的所有的属性,子类中定义了就不会再找父类中的方法 11 print(Dad.__dict__) 12 print(Son.__dict__) 13 14 s1 = Son("小明") 15 s1.hit_son() 16 print(s1.money) 17 print(Dad.money)
三 什么时候用继承?
1.当类之间有显著不同,并且较小的类是较大的类所需要的组件时,用组合比较好
例如:描述一个机器人类,机器人这个大类是由很多互不相关的小类组成,如机械胳膊类、腿类、身体类、电池类
2.当类之间有很多相同的功能,提取这些共同的功能做成基类,用继承比较好
例如
猫可以:喵喵叫、吃、喝、拉、撒
狗可以:汪汪叫、吃、喝、拉、撒
四 继承同时具有两种含义
1 import abc 2 class All_file(metaclass=abc.ABCMeta): 3 @abc.abstractmethod 4 def read(self): 5 pass 6 @abc.abstractmethod 7 def write(self): 8 pass 9 class Disk(All_file): 10 def read(self): 11 print("disk read") 12 def write(self): 13 print("disk write") 14 class Cdrom(All_file): 15 def read(self): 16 print("cdrom read") 17 def write(self): 18 print("cdrom write") 19 class Mem(All_file): 20 def read(self): 21 print("mem read") 22 def write(self): 23 print("mem write") 24 m1 = Mem() 25 m1.read()
实践中,继承的第一种含义意义并不很大,甚至常常是有害的。因为它使得子类与基类出现强耦合。
继承的第二种含义非常重要。它又叫“接口继承”。
接口继承实质上是要求“做出一个良好的抽象,这个抽象规定了一个兼容接口,使得外部调用者无需关心具体细节,可一视同仁的处理实现了特定接口的所有对象”——这在程序设计上,叫做归一化。
归一化使得高层的外部使用者可以不加区分的处理所有接口兼容的对象集合——就好象linux的泛文件概念一样,所有东西都可以当文件处理,不必关心它是内存、磁盘、网络还是屏幕(当然,对底层设计者,当然也可以区分出“字符设备”和“块设备”,然后做出针对性的设计:细致到什么程度,视需求而定)。
五 继承顺序
1 class A: 2 def test(self): 3 print("A") 4 class B(A): 5 def test(self): 6 print("A") 7 8 class C(A): 9 def test(self): 10 print("A") 11 12 class D(B): 13 def test(self): 14 print("A") 15 16 class E(C): 17 def test(self): 18 print("A") 19 20 class F(D,E): 21 def test(self): 22 print("A") 23 f1 = F() 24 f1.test() 25 print(F.__mro__) # 继承顺序就是mro列表定义的顺序
终极解密:python到底是如何实现继承的,对于你定义的每一个类,python会计算出一个方法解析顺序(MRO)列表,这个MRO列表就是一个简单的所有基类的线性顺序列表
为了实现继承,python会在MRO列表上从左到右开始查找基类,直到找到第一个匹配这个属性的类为止。
而这个MRO列表的构造是通过一个C3线性化算法来实现的。我们不去深究这个算法的数学原理,它实际上就是合并所有父类的MRO列表并遵循如下三条准则:
1.子类会先于父类被检查
2.多个父类会根据它们在列表中的顺序被检查
3.如果对下一个类存在两个合法的选择,选择第一个父类
六 子类中调用父类方法
子类继承了父类的方法,然后想进行修改,注意了是基于原有的基础上修改,那么就需要在子类中调用父类的方法
1 class Vehicle: 2 '定义交通工具类' 3 def __init__(self,name,speed,load,power): 4 country = "China" 5 self.name = name 6 self.speed = speed 7 self.load = load 8 self.power = power 9 def run(self): 10 print("开动啦。。。") 11 12 class Subway(Vehicle): 13 def __init__(self,name,speed,load,power,line): 14 # Vehicle.__init__(self,name,speed,load,power) # 调用父类的实例 15 # super().__init__(name,speed,load,power) 16 super(Subway,self).__init__(name,speed,load,power) 17 self.line = line 18 def run(self): 19 # Vehicle.run(self) # 调用父类的函数属性 20 super(Subway,self).run() 21 print("地铁%s号线欢迎您"%self.line) 22 line13 = Subway("中国地铁","180m/s","1000/车厢","电",13) 23 line13.run()
8.2 多态
什么是多态:
类的继承有两层意义:1.改变 2.扩展
多态就是类的这两层意义的一个具体的实现机制 即,调用不同的类实例化得对象下的相同的方法,实现的过程不一样
python中的标准类型就是多态概念的一个很好的示范如(str.__len__(),list.__len__(),tuple.__len__可以len(str),len(list),len(tuple)
1 class H20: 2 def __init__(self,name,tempterature): 3 self.name = name 4 self.tempterature = tempterature 5 def convert(self): 6 if self.tempterature <0: 7 print("温度太低变成冰") 8 elif self.tempterature >0 and self.tempterature<100: 9 print("温度在0—100之间是水") 10 elif self.tempterature>100: 11 print("温度太高变成蒸汽了蒸汽") 12 class Ice(H20): 13 pass 14 class Steam(H20): 15 pass 16 class Water(H20): 17 pass 18 19 i = Ice("冰",-10) 20 s = Steam("水蒸气",200) 21 w = Water("水",80) 22 23 def func(obj): 24 obj.convert() 25 func(i) 26 func(s) 27 func(w)
8.3 封装
第一个层面的封装:类就是麻袋,这本身就是一种封装
例:略
第二个层面的封装:类中定义私有的,只在类的内部使用,外部无法访问
python不依赖语言特性去实现第二层面的封装,而是通过遵循一定的数据属性和函数属性的命名约定来达到封的效果
约定一:任何以单下划线开头的名字都应该是内部的,私有的
1 ################### 单 _ 情况 2 class People: 3 _star='earth' 4 def __init__(self,id,name,age,salary): 5 self.id=id 6 self.name=name 7 self._age=age 8 self._salary=salary 9 10 def _get_id(self): 11 print('我是私有方法啊,我找到的id是[%s]' %self.id) 12 print(People._star) 13 p1=People('3706861900121221212','alex',28,10) 14 15 print(p1._age,p1._salary) 16 p1._get_id() 17 18 问题?为什么单杠情况还能被调用
python并不会真的阻止你访问私有的属性,模块也遵循这种约定,如果模块名以单下划线开头,那么from module import *时不能被导入,但是你from module import _private_module依然是可以导入的
其实很多时候你去调用一个模块的功能时会遇到单下划线开头的(socket._socket,sys._home,sys._clear_type_cache),这些都是私有的,原则上是供内部调用的
约定二:双下划线开头的名字
1 # ############ 双 __情况 ############## 2 class People: 3 __star='earth' # 双下划线开头 4 def __init__(self,id,name,age,salary): 5 self.id=id 6 self.name=name 7 self.__age=age 8 self._salary=salary 9 def _get_id(self): 10 print('我是私有方法啊,我找到的id是[%s]' %self.id) 11 print(People.__dict__)#__star存到类的属性字典中被重命名为_People__star 12 p1=People('333333','alex',18,10) 13 14 print(p1.__dict__)#__age存到类的属性字典中被重命名为_People__age 15 16 # print(People.star) # 这里不能被访问 17 # print(p1.age) # 这里不能被访问 18 19 print(p1._People__age) #可以通过这种方式访问 __age存到类的属性字典中被重命名为_People__age
双下滑线开头的属性在继承给子类时,子类是无法覆盖的(原理也是基于python自动做了双下滑线开头的名字的重命名工作)
第三层面的封装:明确区分内外,内部的实现逻辑,外部无法知晓,并且为封装到内部的逻辑提供一个访问接口给外部使用
九 面向对象的优点
从编程进化论我们得知,面向对象是一种更高等级的结构化编程方式,它的好处就两点
1:通过封装明确了内外,你作为类的缔造者,你是上帝,上帝造物的逻辑你无需知道(你知道了你tm也成上帝了),上帝想让你知道的你才能知道,这样就明确了划分了等级,物就是调用者,上帝就是物的创造者
2:通过继承+多态在语言层面支持了归一化设计
注意:不用面向对象语言(即不用class),一样可以做归一化(如老掉牙的泛文件概念、游戏行业的一切皆精灵),一样可以封装(通过定义模块和接口),只是用面向对象语言可以直接用语言元素显式声明这些而已;而用了面向对象语言,满篇都是class,并不等于就有了归一化的设计。甚至,因为被这些花哨的东西迷惑,反而更加不知道什么才是设计
十 python中关于OOP的常用术语
抽象/实现
抽象指对现实世界问题和实体的本质表现,行为和特征建模,建立一个相关的子集,可以用于 绘程序结构,从而实现这种模型。抽象不仅包括这种模型的数据属性,还定义了这些数据的接口。
对某种抽象的实现就是对此数据及与之相关接口的现实化(realization)。现实化这个过程对于客户 程序应当是透明而且无关的。
封装/接口
封装描述了对数据/信息进行隐藏的观念,它对数据属性提供接口和访问函数。通过任何客户端直接对数据的访问,无视接口,与封装性都是背道而驰的,除非程序员允许这些操作。作为实现的 一部分,客户端根本就不需要知道在封装之后,数据属性是如何组织的。在Python中,所有的类属性都是公开的,但名字可能被“混淆”了,以阻止未经授权的访问,但仅此而已,再没有其他预防措施了。这就需要在设计时,对数据提供相应的接口,以免客户程序通过不规范的操作来存取封装的数据属性。
注意:封装绝不是等于“把不想让别人看到、以后可能修改的东西用private隐藏起来”
真正的封装是,经过深入的思考,做出良好的抽象,给出“完整且最小”的接口,并使得内部细节可以对外透明
(注意:对外透明的意思是,外部调用者可以顺利的得到自己想要的任何功能,完全意识不到内部细节的存在)
合成
合成扩充了对类的 述,使得多个不同的类合成为一个大的类,来解决现实问题。合成 述了 一个异常复杂的系统,比如一个类由其它类组成,更小的组件也可能是其它的类,数据属性及行为, 所有这些合在一起,彼此是“有一个”的关系。
派生/继承/继承结构
派生描述了子类衍生出新的特性,新类保留已存类类型中所有需要的数据和行为,但允许修改或者其它的自定义操作,都不会修改原类的定义。
继承描述了子类属性从祖先类继承这样一种方式
继承结构表示多“代”派生,可以述成一个“族谱”,连续的子类,与祖先类都有关系。
泛化/特化
基于继承
泛化表示所有子类与其父类及祖先类有一样的特点。
特化描述所有子类的自定义,也就是,什么属性让它与其祖先类不同。
多态
多态的概念指出了对象如何通过他们共同的属性和动作来操作及访问,而不需考虑他们具体的类。
多态表明了动态(又名,运行时)绑定的存在,允计重载及运行时类型确定和验证。
举例:
水是一个类
不同温度,水被实例化成了不同的状态:冰,水蒸气,雾(然而很多人就理解到这一步就任务此乃多态,错,fuck!,多态是运行时绑定的存在)
(多态体现在由同一个类实例化出的多个对象,这些对象执行相同的方法时,执行的过程和结果是不一样的)
冰,水蒸气,雾,有一个共同的方法就是变成云,但是冰.变云(),与水蒸气.变云()是截然不同的两个过程,虽然调用的方法都一样
自省/反射
自省也称作反射,这个性质展示了某对象是如何在运行期取得自身信息的。如果传一个对象给你,你可以查出它有什么能力,这是一项强大的特性。如果Python不支持某种形式的自省功能,dir和type内建函数,将很难正常工作。还有那些特殊属性,像__dict__,__name__及__doc__