一、三大编程范式
编程范式即编程的方法论,标识一种编程风格
三大编程范式:
1.面向过程编程
2.函数式编程
3.面向对象编程
二、编程进化论
1.编程最开始就是无组织无结构,从简单控制流中按步写指令
2.从上述的指令中提取重复的代码块或逻辑,组织到一起(比方说,你定义了一个函数),便实现了代码重用,且代码由无结构走向了结构化,创建程序的过程变得更具逻辑性
3.我们定义函数都是独立于函数外定义变量,然后作为参数传递给函数,这意味着:数据与动作是分离的
4.如果我们把数据和动作内嵌到一个结构(函数或类)里面,那么我们就有了一个‘对象系统’(对象就是数据与函数整合到一起的产物)。
1 假设你是一条狗,我们用结构化的数据来描述你 2 1:数据与动作分离 3 2:数据与动作结合 4 5 导向: 6 1.面向对象设计 7 2.什么是类什么是对象
三、面向对象设计与面向对象编程
def定义函数是面向对象
class定义类是面向对象
1、面向对象设计
面向对象设计(Object oriented design):将一类具体事物的数据和动作整合到一起,即面向对象设计
示例1:
1 #相当于:类 2 def dog(name,gender,type): 3 #狗的动作 4 def jiao(dog): 5 print(‘一条狗[%s],汪汪汪‘ % dog[‘name‘]) 6 def chi_shi(dog): 7 print(‘一条[%s] 正在吃肉‘ % dog[‘type‘]) 8 #狗的属性 9 def init(name,gender,type): 10 dog1 = { 11 ‘name‘:name, 12 ‘gender‘:gender, 13 ‘type‘:type, 14 ‘jiao‘:jiao, #动作 15 ‘chi_shi‘:chi_shi, #动作 16 } 17 return dog1 18 return init(name,gender,type) 19 20 d1=dog(‘元吴‘,‘母‘,‘中华犬‘) 21 d2=dog(‘alex‘,‘母‘,‘藏敖‘) 22 print(d1) 23 print(d2) 24 d1[‘jiao‘](d1) 25 d2[‘chi_shi‘](d2)
什么是类?
类:把一类事物的相同的特征和动作整合到一起就是类,类是一个抽象的概念。
什么是对象?
对象:就是基于类而创建的一个具体的事物(指具体存在的),也是特征和动作整合到一起。
示例2:
学校类:
特征:name,addr,type
动作:考试,招生,开除学生
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 #Author: nulige 4 5 def school(name,addr,type): 6 def init(name, addr, type): 7 sch = { 8 ‘name‘: name, 9 ‘addr‘: addr, 10 ‘type‘: type, 11 ‘kao_shi‘: kao_shi, 12 ‘zhao_sheng‘: zhao_sheng, 13 } 14 return sch 15 def kao_shi(school): 16 print(‘%s 学校正在考试‘ %school[‘name‘]) 17 def zhao_sheng(school): 18 print(‘%s %s 正在招生‘ %(school[‘type‘],school[‘name‘])) 19 return init(name,addr,type) 20 21 s1=school(‘oldboy‘,‘沙河‘,‘私立学校‘) 22 print(s1) 23 print(s1[‘name‘]) 24 25 s1[‘zhao_sheng‘](s1) 26 27 s2=school(‘清华‘,‘北京‘,‘公立学校‘) 28 29 print(s2) 30 print(s2[‘name‘],s2[‘addr‘],s2[‘type‘]) 31 s2[‘zhao_sheng‘](s2)
2、面向对象编程
面向对象编程(object-oriented programming):用定义类+实例/对象的方式去实现面向对象的设计
1 #用面向对象编程独有的语法class去实现面向对象设计 2 class Dog: 3 def __init__(self,name,gender,type): #实例化的过程,就是运行__init__ 4 self.name=name 5 self.gender=gender 6 self.type=type 7 8 def bark(self): 9 print(‘一条名字为[%s]的[%s],狂吠不止‘ %(self.name,self.type)) 10 11 def yao_ren(self): 12 print(‘[%s]正在咬人‘ %(self.name)) 13 14 def chi_shi(self): 15 print(‘[%s]正在吃屎‘ %(self.type)) 16 17 dog1=Dog(‘alex‘,‘female‘,‘京巴‘) 18 dog2=Dog(‘wupeiqi‘,‘female‘,‘腊肠‘) 19 dog3=Dog(‘yuanhao‘,‘female‘,‘藏獒‘) 20 21 dog1.bark() 22 dog2.yao_ren() 23 dog3.chi_shi()
小结:
在Python中,类和 OOP 都不是日常编程所必需的。尽管它从一开始设计就是面向对象的,并且结构上支持 OOP,但 Python 没有限定或要求你在你的应用中写 OOP 的代码。
用面向对象语言写程序和一个程序的设计是面向对象的,两者就是两码事。
四、类和对象
1.什么叫类:类是一种数据结构,就好比一个模型,该模型用来表述一类事物(事物即数据和动作的结合体),用它来生产真实的物体(实例)。
2.什么叫对象:睁开眼,你看到的一切的事物都是一个个的对象,你可以把对象理解为一个具体的事物(事物即数据和动作的结合体)
(铅笔是对象,人是对象,房子是对象,狗是对象,alex是对象,配齐是对象,元昊是对象)
3.类与对象的关系:对象都是由类产生的,上帝造人,上帝首先有一个造人的模板,这个模板即人的类,然后上帝根据类的定义来生产一个个的人
4.什么叫实例化:由类生产对象的过程叫实例化,类实例化的结果就是一个对象,或者叫做一个实例(实例=对象)
五、类的相关知识
1、初识类
在python中声明函数与声明类很相似。
声明函数
1 2 3 |
|
声明类
1 ‘‘‘ 2 class 类名: 3 ‘类的文档字符串‘ 4 类体 5 ‘‘‘ 6 7 #我们创建一个类 8 class Data: 9 pass 10 11 #用类Data实例化出一个对象d1 12 d1=Data()
经典类与新式类
1 大前提: 2 1.只有在python2中才分新式类和经典类,python3中统一都是新式类 3 2.新式类和经典类声明的最大不同在于,所有新式类必须继承至少一个父类 4 3.所有类甭管是否显式声明父类,都有一个默认继承object父类(讲继承时会讲,先记住) 5 在python2中的区分 6 经典类: 7 class 类名: 8 pass 9 10 经典类: 11 class 类名(父类): 12 pass 13 14 在python3中,上述两种定义方式全都是新式类
示例:
1 #经典类(python2.7) 2 class Chinese: 3 ‘这是一个中国人的类‘ 4 pass 5 6 print(Chinese) 7 8 #新式类(python3.0) 9 10 #实例化到底干了什么? 11 p1=Chinese() #实例化 12 print(p1)
2、属性
类是用来描述一类事物,类的对象指的是这一类事物中的一个个体
是事物就要有属性,属性分为
1:数据属性:就是变量
2:函数属性:就是函数,在面向对象里通常称为方法
注意:类和对象均用点来访问自己的属性
类的属性
理论:数据属性即变量,类的定义与函数又极其类似,其实可以用函数的作用域来理解类的属性调用。
类的数据属性
1 #定义一个中文人的类,然后在类中定义一个类的属性,政府是共产堂,这样,只要是中文人他们的党永远都是共产堂 2 #类属性又称为静态变量,或者是静态数据。这些数据是与它们所属的类对象绑定的,不依赖于任何类实例。 3 #如果你是一位Java或C++程序员,这种类型的数据相当于在一个变量声明前加上static关键字。 4 5 class Chinese: 6 government=‘共产堂‘ 7 8 print(Chinese.government)
类的函数属性(又称为方法)
1 class Chinese: 2 government=‘共产_党‘ 3 def sui_di_tu_tan(): 4 print(‘90%的中国人都喜欢随地吐痰‘) 5 6 def cha_dui(self): 7 print(‘一个中国人-->%s<--插到了前面‘ %self) 8 9 Chinese.sui_di_tu_tan() 10 11 person1_obj=‘alex‘ 12 Chinese.cha_dui(person1_obj) #带参函数,所以调用时需要传入参数,将‘alex‘传给self
查看类属性
我们定义的类的属性到底存到哪里了?有两种方式查看
dir(类名):查出的是一个名字列表
类名.__dict__:查出的是一个字典,key为属性名,value为属性值
1 #共有属性,动作(方法) 2 class Chinese: 3 ‘这是一个中国人的类‘ 4 dang=‘共产堂‘ #数据属性 5 def sui_di_tu_tan(): #函数属性 6 print(‘朝着墙上就是一口痰‘) 7 def cha_dui(self): #加了self的就必须传一个参数,否则会报错 8 print(‘插到了前面‘) 9 10 print(Chinese.dang) 11 Chinese.sui_di_tu_tan() 12 Chinese.cha_dui(‘元昊‘) 13 14 # print(dir(Chinese)) #查看系统内置属性 15 # print(Chinese.__dict__) #查看属性字典 16 print(Chinese.__dict__[‘dang‘]) #调用数据属性,本身就是在数据属性中找东西 17 Chinese.__dict__[‘sui_di_tu_tan‘]() #调用函数属性,有了函数的内存地址,加(),就可以运行 18 Chinese.__dict__[‘cha_dui‘](1) #调用函数属性
执行结果:
1 共产堂 2 在地上就是一口痰 3 插到了前面 4 共产堂 5 在地上就是一口痰 6 插到了前面
特殊的类属性
1 class Chinese: 2 ‘我们都是中国人,我们骄傲的活着,我们不服任何事和物‘ 3 government=‘共产堂‘ 4 def sui_di_tu_tan(): 5 print(‘90%的中国人都喜欢随地吐痰‘) 6 7 def cha_dui(self): 8 print(‘一个中国人-->%s<--插到了前面‘ %self) 9 10 print(Chinese.__name__) #类C的名字(字符串) 11 print(Chinese.__doc__) #类C的文档字符串 12 print(Chinese.__base__) #类C的第一个父类(在讲继承时会讲) 13 print(Chinese.__bases__) #类C的所有父类构成的元组(在讲继承时会讲) 14 print(Chinese.__dict__) #类C的属性 15 print(Chinese.__module__)#类C定义所在的模块 16 print(Chinese.__class__) #实例C对应的类(仅新式类中)
六、对象相关知识
对象是由类实例化而来,类实例化的结果称为一个实例或者称作一个对象
1.1、实例化
什么是实例化?
由类生产对象的过程就叫实例化
1 class Chinese: 2 ‘我们都是中国人,我们骄傲的活着,我们不服任何事和物‘ 3 government=‘共产_党‘ 4 def sui_di_tu_tan(): 5 print(‘90%的中国人都喜欢随地吐痰‘) 6 7 def cha_dui(self): 8 print(‘一个中国人-->%s<--插到了前面‘ %self) 9 10 person1=Chinese() #类名加上括号就是实例化(可以理解为函数的运行,返回值就是一个实例) 11 12 #这就是实例化,只不过你得到的person1实例,没有做任何事情
1.2、构造函数
类是数据属性和函数属性的结合,描述的是一类事物
这类事物的一个具体表现就是一个实例/对象,比方说中国人是一个类,而你就是这个类的一个实例
你除了有中国人这个数据属性外,还应该有名字,年龄,性别等数据属性
如何为实例定制数据属性,可以使用类的一个内置方法__init__()该方法,在类()实例化是会自动执行
1 class Chinese: 2 ‘我们都是中国人,我们骄傲的活着,我们不服任何事和物‘ 3 government=‘共产_党‘ 4 def __init__(self,name,age,gender): #实例化的过程可以简单理解为执行该函数的过程,实例本身会当作参数传递给self(这是默认的步骤) 5 self.name=name 6 self.age=age 7 self.gender=gender 8 9 def sui_di_tu_tan(): 10 print(‘90%的中国人都喜欢随地吐痰‘) 11 12 def cha_dui(self): 13 print(‘一个中国人-->%s<--插到了前面‘ %self) 14 15 # person1=Chinese() #会报错 16 #自动执行__init__方法,而这个方法需要参数, 17 # 这些参数应该写在类名后面的括号里,然后由类传 18 #给__init__函数,也就说,传给类的参数就是传给__init__的参数 19 20 person1=Chinese(‘alex‘,1000,‘female‘) 21 person2=Chinese(‘wupeiqi‘,10000,‘female‘) 22 person3=Chinese(‘yuanhao‘,9000,‘female‘) 23 24 print(person1.__dict__)
注意:在说实例化的时候说过,执行类()会自动返回一值,这个值就是实例,而类()会自动执行__init__,所以一定不要在该函数内定义返回值,会冲突。
1.3、实例属性
理论:
1.实例只有数据属性(实例的函数属性严格来说是类的函数属性)
2.del 实例/对象,只是回收了实例的数据属性,函数属性是属于类的,是不会回收。
实例化的过程实际就是执行__init__的过程,这个函数内部只是为实例本身即self设定了一堆数据(变量),所以实例只有数据属性。
1 class Chinese: 2 ‘我们都是中国人,我们骄傲的活着,我们不服任何事和物‘ 3 government=‘共产_党‘ 4 def __init__(self,name,age,gender): 5 self.name=name 6 self.age=age 7 self.gender=gender 8 9 def sui_di_tu_tan(): 10 print(‘90%的中国人都喜欢随地吐痰‘) 11 12 def cha_dui(self): 13 print(‘一个中国人-->%s<--插到了前面‘ %self) 14 15 16 person1=Chinese(‘alex‘,1000,‘female‘) 17 print(person1.__dict__) #查看实例的属性,发现里面确实只有数据属性 18 print(person1.name,person1.age,person1.gender) #访问实例的数据属性
那么我们想访问实例的函数属性(其实是类的函数属性),如何实现
1 class Chinese: 2 ‘我们都是中国人,我们骄傲的活着,我们不服任何事和物‘ 3 government=‘共产_党‘ 4 def __init__(self,name,age,gender): 5 self.name=name 6 self.age=age 7 self.gender=gender 8 9 def sui_di_tu_tan(): 10 print(‘90%的中国人都喜欢随地吐痰‘) 11 12 def cha_dui(self): 13 print(‘一个中国人-->%s<--插到了前面‘ %self) 14 15 16 person1=Chinese(‘alex‘,1000,‘female‘) 17 print(person1.__dict__) #查看实例的属性,发现里面确实只有数据属性 18 print(Chinese.__dict__)#函数属性只存在于类中 19 Chinese.cha_dui(person1)#我们只能通过类去调用类的函数属性,然后把实例当做变量传递给self
改进版本
1 class Chinese: 2 ‘我们都是中国人,我们骄傲的活着,我们不服任何事和物‘ 3 government=‘共产_党‘ 4 def __init__(self,name,age,gender): 5 self.name=name 6 self.age=age 7 self.gender=gender 8 9 def sui_di_tu_tan(): 10 print(‘90%的中国人都喜欢随地吐痰‘) 11 12 # def cha_dui(self): 13 # print(‘一个中国人-->%s<--插到了前面‘ %self) 14 15 def cha_dui(self): 16 print(‘一个中国人-->姓名:%s 年龄:%s 性别:%s<--插到了前面‘ %(self.name,self.age,self.gender)) 17 18 def eat_food(self,food): 19 print(‘%s 正在吃 %s‘ %(self.name,food)) 20 21 person1=Chinese(‘alex‘,1000,‘female‘) 22 print(person1.__dict__) #查看实例的属性,发现里面确实只有数据属性 23 print(Chinese.__dict__)#函数属性只存在于类中 24 Chinese.cha_dui(person1)#我们只能通过类去调用类的函数属性,然后把实例当做变量传递给self 25 Chinese.eat_food(person1,‘韭菜馅饼‘) 26 27 28 person1.cha_dui()#其实就是Chinese.cha_dui(person1) 29 person1.eat_food(‘飞禽走兽‘) #本质就是Chinese.eat_food(person1,‘飞禽走兽‘)
3、实例属性和查看实例属性的方法如下:
同样是dir和内置__dict__两种方式
原理图
示例:
1 class Chinese: 2 ‘这是一个中国人的类‘ 3 4 dang=‘共产堂‘ 5 6 def __init__(self,name,age,gender): 7 print(‘我是初始化函数,我开始运行了‘) 8 self.mingz=name #name,age,gender都封装在self里面 9 self.nianji=age #p1.nianji=age 10 self.xingbin=gender #p1.xingbin=gender 11 print(‘我结束啦‘) 12 13 def sui_di_tu_tan(): 14 print(‘在地上就是一口痰‘) 15 def cha_dui(self): 16 print(self) 17 print(‘%s 插到了前面‘ %self) 18 #return None (系统默认就是return None,这里不要加return) 19 20 #数据属性 21 p1=Chinese(‘元昊‘,‘18‘,‘female‘) #实例化的过程,就是调用__init__ 22 print(p1.__dict__) #查看字典属性 #==>{‘nianji‘: ‘18‘, ‘mingz‘: ‘元昊‘, ‘xingbin‘: ‘female‘} 23 print(p1.mingz) #p1.nianji=age, 直接调用__init__里面的属性, 24 print(p1.dang) #类的作用域概念,如果__init__里面没有,就去外面那层找,外层找到Class Chinese类字典 25 26 #实例属性没有函数属性,函数属性属于类 27 print(Chinese.__dict__) 28 Chinese.sui_di_tu_tan() 29 Chinese.cha_dui(p1) 30 31 #p1.sui_di_tu_tan() #class把p1作为参数自动加到sui_di_tu_tan(p1)括号里面,所以会报错。因为是python底层自动加上的。 32 p1.cha_dui() #p1里面没有,但是类里面有,所以能找到
执行结果:
1 我是初始化函数,我开始运行了 2 我结束啦 3 {‘mingz‘: ‘元昊‘, ‘xingbin‘: ‘female‘, ‘nianji‘: ‘18‘} 4 元昊 5 共产堂 6 {‘__init__‘: <function Chinese.__init__ at 0x0121F0C0>, ‘__doc__‘: ‘这是一个中国人的类‘, ‘__module__‘: ‘__main__‘, ‘__weakref__‘: <attribute ‘__weakref__‘ of ‘Chinese‘ objects>, ‘sui_di_tu_tan‘: <function Chinese.sui_di_tu_tan at 0x0121F270>, ‘dang‘: ‘共产堂‘, ‘cha_dui‘: <function Chinese.cha_dui at 0x0121F228>, ‘__dict__‘: <attribute ‘__dict__‘ of ‘Chinese‘ objects>} 7 在地上就是一口痰 8 <__main__.Chinese object at 0x01681210> 9 <__main__.Chinese object at 0x01681210> 插到了前面 10 <__main__.Chinese object at 0x01681210> 11 <__main__.Chinese object at 0x01681210> 插到了前面 12 China 13 Japan 14 {‘name‘: ‘alex‘} 15 {‘__init__‘: <function Chinese.__init__ at 0x0121F108>, ‘dang‘: ‘共产堂‘, ‘__module__‘: ‘__main__‘, ‘play_ball‘: <function Chinese.play_ball at 0x0121F198>, ‘__weakref__‘: <attribute ‘__weakref__‘ of ‘Chinese‘ objects>, ‘country‘: ‘Japan‘, ‘__doc__‘: None, ‘__dict__‘: <attribute ‘__dict__‘ of ‘Chinese‘ objects>} 16 共产堂 17 {‘__init__‘: <function Chinese.__init__ at 0x0121F108>, ‘__module__‘: ‘__main__‘, ‘play_ball‘: <function Chinese.play_ball at 0x0121F198>, ‘__weakref__‘: <attribute ‘__weakref__‘ of ‘Chinese‘ objects>, ‘__doc__‘: None, ‘__dict__‘: <attribute ‘__dict__‘ of ‘Chinese‘ objects>} 18 {‘__init__‘: <function Chinese.__init__ at 0x0121F108>, ‘__module__‘: ‘__main__‘, ‘play_ball‘: <function Chinese.play_ball at 0x0121F198>, ‘__weakref__‘: <attribute ‘__weakref__‘ of ‘Chinese‘ objects>, ‘__doc__‘: None, ‘eat‘: <function eat_food at 0x011F2F60>, ‘__dict__‘: <attribute ‘__dict__‘ of ‘Chinese‘ objects>} 19 alex 正在吃虾 20 test
1.4、特殊实例属性
__class__
__dict__
1 class Chinese: 2 ‘我们都是中国人,我们骄傲的活着,我们不服任何事和物‘ 3 government=‘共产_党‘ 4 def __init__(self,name,age,gender): 5 self.name=name 6 self.age=age 7 self.gender=gender 8 9 def sui_di_tu_tan(): 10 print(‘90%的中国人都喜欢随地吐痰‘) 11 12 # def cha_dui(self): 13 # print(‘一个中国人-->%s<--插到了前面‘ %self) 14 15 def cha_dui(self): 16 print(‘一个中国人-->姓名:%s 年龄:%s 性别:%s<--插到了前面‘ %(self.name,self.age,self.gender)) 17 18 def eat_food(self,food): 19 print(‘%s 正在吃 %s‘ %(self.name,food)) 20 21 person1=Chinese(‘alex‘,1000,‘female‘) 22 23 print(person1.__class__) 24 print(Chinese) 25 26 person2=person1.__class__(‘xiaobai‘,900,‘male‘) 27 print(person2.name,person2.age,person2.gender)
警告:类和对象虽然调用__dict__返回的是一个字典结构,但是千万不要直接修改该字典,会导致你的oop不稳定。
1.5、类属性与对象(实例)属性
1、类属性的增、删、改、查
1 class Chinese: 2 country=‘China‘ 3 def __init__(self,name): 4 self.name=name 5 6 def play_ball(self,ball): 7 print(‘%s 正在打 %s‘ %(self.name)) 8 9 #查看 10 print(Chinese.country) 11 12 #修改 13 Chinese.country=‘Japan‘ 14 print(Chinese.country) 15 16 p1=Chinese(‘alex‘) 17 print(p1.__dict__) #从类的字典里面去找,上面已经修改过了,所以是Japan 18 19 #增加 20 Chinese.dang=‘共产堂‘ 21 22 #printChinese.dang) 23 #print(p1.dang) 24 25 #删除 26 del Chinese.dang 27 del Chinese.country 28 29 print(Chinese.__dict__) 30 #print(Chinese.country) 31 32 33 #给类增加一个函数属性 34 def eat_food(self,food): #定义了一个函数, 35 print(‘%s 正在吃%s‘ %(self.name,food)) 36 37 Chinese.eat=eat_food 38 39 print(Chinese.__dict__) 40 p1.eat(‘虾‘) #给函数属性传参数 41 42 43 #增加 44 def test(self): 45 print(‘test‘) 46 47 Chinese.play_ball=test 48 p1.play_ball() #Chinese.play_ball(p1)
执行结果:
1 China 2 3 Japan 4 5 {‘name‘: ‘alex‘} 6 7 {‘play_ball‘: <function Chinese.play_ball at 0x01558108>, ‘__init__‘: <function Chinese.__init__ at 0x01085660>, ‘__doc__‘: None, ‘__weakref__‘: <attribute ‘__weakref__‘ of ‘Chinese‘ objects>, ‘__module__‘: ‘__main__‘, ‘__dict__‘: <attribute ‘__dict__‘ of ‘Chinese‘ objects>} 8 9 {‘play_ball‘: <function Chinese.play_ball at 0x01558108>, ‘__init__‘: <function Chinese.__init__ at 0x01085660>, ‘__doc__‘: None, ‘eat‘: <function eat_food at 0x01532F60>, ‘__weakref__‘: <attribute ‘__weakref__‘ of ‘Chinese‘ objects>, ‘__module__‘: ‘__main__‘, ‘__dict__‘: <attribute ‘__dict__‘ of ‘Chinese‘ objects>} 10 11 alex 正在吃虾 12 13 test
2、实例属性的增、删、改、查
1 class Chinese: 2 country=‘China‘ 3 def __init__(self,name): 4 self.name=name 5 6 def play_ball(self,ball): 7 print(‘%s 正在打 %s‘ %(self.name,ball)) 8 p1=Chinese(‘alex‘) 9 print(p1.__dict__) 10 11 #查看 12 # print(p1.name) 13 # print(p1.play_ball) 14 15 #增加 16 p1.age=18 17 print(p1.__dict__) 18 print(p1.age) 19 20 #不要修改底层的属性字典 21 # p1.__dict__[‘sex‘]=‘male‘ 22 # print(p1.__dict__) 23 # print(p1.sex) 24 25 #修改 26 p1.age=19 27 print(p1.__dict__) 28 print(p1.age) 29 30 #删除 31 del p1.age 32 print(p1.__dict__)
执行结果:
1 {‘name‘: ‘alex‘} 2 3 {‘age‘: 18, ‘name‘: ‘alex‘} 4 18 5 6 {‘age‘: 19, ‘name‘: ‘alex‘} 7 19 8 9 {‘name‘: ‘alex‘}
1.其实你会发现,实例化就是 类名(),然后返回的结果是一个对象,加上括号是不是跟函数运行很像,函数运行完了有返回值,是不是很像,没错,就是一样的。
2.函数又作用域的概念,其实类也有作用域的概念,二者一样
3.你可以把class当做最外层的函数,是一个作用域
1 #定义一个类,只当一个作用域去用,类似于c语言中的结构体 2 class MyData: 3 pass 4 5 x=10 6 y=20 7 MyData.x=1 8 MyData.y=2 9 10 print(x,y) 11 print(MyData.x,MyData.y) 12 print(MyData.x+MyData.y)
4.实例化会自动触发init函数的运行,最后返回一个值即实例,我们要找的实例属性就存放在init函数的局部作用域里
5.类有类的属性字典,就是类的作用域,实例有实例的属性字典,即实例的作用域
6.综上,一个点代表一层作用域,obj.x先从自己的作用域找,自己找不到去外层的类的字典中找,都找不到,就会报错
7.在类中没有使用点的调用,代表调用全局变量。
上面知识应用:
示例1:
1 class Chinese: 2 country=‘China‘ 3 def __init__(self,name): 4 self.name=name 5 6 def play_ball(self,ball): 7 print(‘%s 正在打 %s‘ %(self.name,ball)) 8 9 p1=Chinese(‘alex‘) 10 print(p1.country) #访问的是类的 11 p1.country=‘日本‘ #把country改成日本 12 print(‘类的--->‘,Chinese.country) #类的---> China 13 print(‘实例的‘,p1.country) #在类的字典里,新增了一个p1.country=‘日本‘。所以发果是:实例的 日本
示例2:
1 country=‘中国‘ 2 class Chinese: 3 def __init__(self,name): 4 self.name=name 5 6 def play_ball(self,ball): 7 print(‘%s 正在打 %s‘ %(self.name,ball)) 8 9 p1=Chinese(‘alex‘) 10 # print(p1.country) #p1.country就是在类里面找,找不到就报错。
示例3:
1 country=‘中国‘ 2 class Chinese: 3 def __init__(self,name): 4 self.name=name 5 6 def play_ball(self,ball): 7 print(‘%s 正在打 %s‘ %(self.name,ball)) 8 9 #另外定义一个函数,接收输入 10 def shi_li_hua(): 11 name=input(‘>>: ‘) 12 p1=Chinese(name) 13 # print(p1.country) 14 print(p1.name) 15 shi_li_hua()
示例4:
1 country=‘中华人民共和国‘ 2 class Chinese: 3 country=‘中国‘ 4 5 def __init__(self,name): 6 self.name=name 7 print(‘--->‘,country) #即不是类的属性,也不是实例的属性。所以他就是变量的直接赋值 8 9 def play_ball(self,ball): 10 print(‘%s 正在打 %s‘ %(self.name,ball)) 11 12 p1=Chinese(‘alex‘) #打印出来的值是变量, ---> 中华人民共和国
示例5:
1 country=‘中国‘ #从外面去找 2 class Chinese: 3 country=‘中国‘ 4 def __init__(self,name): 5 self.name=name 6 print(‘--->‘,country) #即不是类的属性,也不是实例的属性。所以他就是变量 7 8 def play_ball(self,ball): 9 print(‘%s 正在打 %s‘ %(self.name,ball)) 10 11 #调用实例数据属性的两种方法如下: 12 13 #用.的方式调用: 14 print(Chinese.__dict__) 15 print(Chinese.country) 16 17 结果: 18 ---> 中国 19 20 #通过实例调用 21 p1=Chinese(‘alex‘) 22 print(‘实例--------》‘,p1.country) 23 24 结果: 25 实例--------》 中国
总结:
.的方式调用的是属性,要么跟类有关,要么跟实例有关
不加.就跟类没关,跟实例没关。
示例1:
1 class Chinese: 2 country=‘China‘ 3 def __init__(self,name): 4 self.name=name 5 6 def play_ball(self,ball): 7 print(‘%s 正在打 %s‘ %(self.name,ball)) 8 p1=Chinese(‘alex‘) 9 10 print(p1.country) #打印的就是country本身 11 p1.country=‘Japan‘ #增加了一个实例,类本身不会更改 12 print(Chinese.country)
示例2:
1 class Chinese: 2 country=‘China‘ 3 l=[‘a‘,‘b‘] 4 def __init__(self,name): 5 self.name=name 6 7 def play_ball(self,ball): 8 print(‘%s 正在打 %s‘ %(self.name,ball)) 9 10 p1=Chinese(‘alex‘) 11 print(p1.l) #p1本身自己没有,找的就是类,所以打印出来的值就是:[‘a‘, ‘b‘] 12 13 p1.l=[1,2,3] 14 print(Chinese.l) #l 的值不会变,因为他是在1里面加入了一个新的字典。结果:[‘a‘, ‘b‘] 15 print(p1.__dict__) #因为他是在1里面加入了一个新的字典。结果:{‘l‘: [1, 2, 3], ‘name‘: ‘alex‘}
示例3:
1 class Chinese: 2 country=‘China‘ 3 l=[‘a‘,‘b‘] 4 def __init__(self,name): 5 self.name=name 6 7 def play_ball(self,ball): 8 print(‘%s 正在打 %s‘ %(self.name,ball)) 9 10 p1=Chinese(‘alex‘) 11 print(p1.l) #p1本身自己没有,找的就是类,所以打印出来的值就是:[‘a‘, ‘b‘] 12 13 p1.l.append(‘c‘) #这不是在赋值和给实例新增属性。执行结果:[‘a‘, ‘b‘] 14 print(p1.__dict__) #执行结果:{‘name‘: ‘alex‘} 15 print(Chinese.l) #改的就是类里面的东西,所以直接增加进去了。执行结果:[‘a‘, ‘b‘, ‘c‘]
理论 :类的作用域即函数作用域
七、静态属性、类方法、静态方法
1、静态属性
作用就是:封装逻辑,把函数属性封装成数据属性
1 class Room: 2 def __init__(self,name,owner,width,length,heigh): 3 self.name=name 4 self.owner=owner 5 self.width=width 6 self.length=length 7 self.heigh=heigh 8 9 @property #静态属性 10 def cal_area(self): #计算面积 11 return self.width * self.length 12 13 @property 14 def cal_volume(self): #计算体积 15 return self.width * self.length * self.heigh 16 17 r1=Room(‘厕所‘,‘alex‘,1000,100,100000) 18 r2=Room(‘公共厕所‘,‘yuanhao‘,1,1,1) 19 r3=Room(‘主人房‘,‘nulige‘,10,10,10) 20 21 print(r1.cal_area) 22 print(r2.cal_area) 23 print(r3.cal_volume) 24 print(r1.name) 25 print(r2.name) 26 print(r3.name)
执行结果:
1 100000 2 1 3 1000 4 厕所 5 公共厕所 6 主人房
2、类方法
类方法作用:专门给类使用与实例无关,类方法只能访问类相关属性,不能访问实例属性(跟实例没有任何关系)
1 class Room: 2 tag=1 3 def __init__(self, name, owner, width, length, heigh): 4 self.name = name 5 self.owner = owner 6 self.width = width 7 self.length = length 8 self.heigh = heigh 9 10 @property 11 def cal_area(self): 12 return self.width * self.length 13 14 @classmethod #classmethod类方法只是给类使用(不管是否存在实例),只能访问实例变量
15 def tell_info(cls,x): 16 print(cls) 17 print(‘---->‘,cls.tag) 18 19 Room.tell_info(1)
执行结果:
1 <class ‘__main__.Room‘> 2 ----> 1
3、静态方法
1 ‘‘‘ 2 staticmethod静态方法只是名义上的归属类管理,不能使用类变量和实例变量,是类的工具包 3 ‘‘‘ 4 5 class Room: 6 def __init__(self,name,owner,width,length): 7 self.name=name 8 self.owner=owner 9 self.width=width 10 self.length=length 11 12 @property 13 def cal_area(self): 14 return self.width * self.length 15 16 @staticmethod #类的工具包,不跟类绑定,也不跟实例绑定(无法调用实例的东西)
17 def wash_body(): 18 print(‘洗刷刷,洗刷刷‘) 19 20 def test(): 21 print(‘这可不是静态方法,用类调用没问题,你用一个实例调用看看‘) 22 23 Room.wash_body() 24 25 r1=Room(‘厕所‘,‘alex‘,10,10) 26 r1.wash_body() 27 28 29 30 #------ 31 Room.test() 32 r1.test() #会报错,因为如果test不是静态方法,那么r1会吧自己传给test的第一个参数self,test无参所以报错 33 34 静态方法
八、组合
一、组合的组成部分
定义一个人的类,人有头,驱赶,手,脚等数据属性,这几个属性又可以是通过一个类实例化的对象,这就是组合
简单理解:就是大类包含小类,就是组合。
用途:
1:做关联
2:小的组成大的
1 class Hand: 2 pass 3 4 class Foot: 5 pass 6 7 class Trunk: 8 pass 9 10 class Head: 11 pass 12 13 class Person: 14 def __init__(self,id_num,name): 15 self.id_num=id_num #身份证号码 16 self.name=name 17 self.hand=Hand() 18 self.foot=Foot() 19 self.trunk=Trunk() 20 self.head=Head() 21 22 p1=Person(‘111111‘,‘alex‘) #给实例传值 23 print(p1.__dict__) #查看实例的属性字典
执行结果:
1 {‘foot‘: <__main__.Foot object at 0x011B4130>, ‘trunk‘: <__main__.Trunk object at 0x011B4170>, ‘id_num‘: ‘111111‘, ‘hand‘: <__main__.Hand object at 0x011B40D0>, ‘name‘: ‘alex‘, ‘head‘: <__main__.Head object at 0x011B4190>}
二、选课系统
示例1:
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 #Author: nulige 4 5 #选课系统 6 7 #一种很LOW的关联方法: 8 class School: 9 def __init__(self,name,addr): 10 self.name=name 11 self.addr=addr 12 self.course_list=[] 13 14 def zhao_sheng(self): 15 print(‘%s 正在招生‘ %self.name) 16 17 class Course: 18 def __init__(self,name,price,period): 19 self.name=name 20 self.price=price 21 self.period=period 22 23 s1=School(‘oldboy‘,‘北京‘) 24 s2=School(‘oldboy‘,‘上海‘) 25 s3=School(‘oldboy‘,‘天津‘) 26 27 c1=Course(‘linux‘,5800,‘1h‘) 28 c2=Course(‘python‘,9800,‘1h‘) 29 30 s1.course_list.append(c1) 31 s1.course_list.append(c2) 32 print(s1.__dict__) #打印字典属性 33 34 for course_obj in s1.course_list: 35 print(course_obj.name,course_obj.price)
执行结果:
1 {‘course_list‘: [<__main__.Course object at 0x01984190>, <__main__.Course object at 0x019841B0>], ‘name‘: ‘oldboy‘, ‘addr‘: ‘北京‘} 2 3 linux 5800 4 python 9800
示例2:
用组合的方式实现
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 #Author: nulige 4 5 class School: 6 def __init__(self,name,addr): 7 self.name=name 8 self.addr=addr 9 10 #招生 11 def zhao_sheng(self): 12 print(‘%s 正在招生‘ %self.name) 13 14 class Course: 15 def __init__(self,name,price,period,school): 16 self.name=name 17 self.price=price 18 self.period=period 19 self.school=school 20 21 s1=School(‘oldboy‘,‘北京‘) 22 s2=School(‘oldboy‘,‘上海‘) 23 s3=School(‘oldboy‘,‘天津‘) 24 25 # c1=Course(‘linux‘,5800,‘1h‘,‘oldboy北京‘) 26 # c1=Course(‘python‘,9800,‘1h‘,s1 ) 27 28 msg=‘‘‘ 29 1 老男孩 北京校区 30 2 老男孩 上海校区 31 3 老男孩 天津校区 32 ‘‘‘ 33 while True: 34 print(msg) 35 menu={ 36 ‘1‘:s1, 37 ‘2‘:s2, 38 ‘3‘:s3, 39 } 40 41 choice=input(‘请选择学校:‘) 42 school_obj=menu[choice] 43 name=input(‘课程名: ‘) 44 price=input(‘课程费用: ‘) 45 period=input(‘课程周期: ‘) 46 new_course=Course(name,price,period,school_obj) 47 print(‘课程[%s] 属于[%s] 学校‘ %(new_course.name,new_course.school.name))
执行结果:
九、面向对象三大特性
什么是类的继承?
类的继承跟现实生活中的父、子、孙子、重孙子、继承关系一样,父类又称为基类。
python中类的继承分为:单继承和多继承
1、继承
1 class ParentClass1: 2 pass 3 4 class ParentClass2: 5 pass 6 7 class SubClass(ParentClass1): #单继承 8 pass 9 10 class SubClass(ParentClass1,ParentClass2): #多继承 11 pass
2、子继承到底继承了父类的什么属性?
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 #Author: nulige 4 5 #继承 6 class Dad: 7 ‘这个是爸爸类‘ 8 money=10 9 def __init__(self,name): 10 print(‘爸爸‘) 11 self.name=name 12 13 def hit_son(self): 14 print(‘%s 正在打儿子‘ %self.name) 15 16 ‘这个是儿子类‘ 17 class Son(Dad): 18 money = 10000000000000 19 20 s1=Son(‘alex‘) #继承父类的属性
21 print(s1.money) 22 print(Dad.money)23 # print(Dad.__dict__) 24 # print(Son.__dict__)
执行结果:
1 爸爸 2 10000000000000 3 10
3、什么时候用继承?
1.当类之间有显著不同,并且较小的类是较大的类所需要的组件时,用组合比较好
例如:描述一个机器人类,机器人这个大类是由很多互不相关的小类组成,如机械胳膊类、腿类、身体类、电池类
2.当类之间有很多相同的功能,提取这些共同的功能做成基类,用继承比较好
例如
猫可以:喵喵叫、吃、喝、拉、撒
狗可以:汪汪叫、吃、喝、拉、撒
如果我们要分别为猫和狗创建一个类,那么就需要为 猫 和 狗 实现他们所有的功能,如下所示:
1 class 猫: 2 3 def 喵喵叫(self): 4 print ‘喵喵叫‘ 5 6 def 吃(self): 7 # do something 8 9 def 喝(self): 10 # do something 11 12 def 拉(self): 13 # do something 14 15 def 撒(self): 16 # do something 17 18 class 狗: 19 20 def 汪汪叫(self): 21 print ‘汪汪叫‘ 22 23 def 吃(self): 24 # do something 25 26 def 喝(self): 27 # do something 28 29 def 拉(self): 30 # do something 31 32 def 撒(self): 33 # do something
上述代码不难看出,吃、喝、拉、撒是猫和狗都具有的功能,而我们却分别的猫和狗的类中编写了两次。如果使用 继承 的思想,如下实现:
动物:吃、喝、拉、撒
猫:喵喵叫(猫继承动物的功能)
狗:汪汪叫(狗继承动物的功能)
伪代码方式实现示例:
1 class 动物: 2 3 def 吃(self): 4 # do something 5 6 def 喝(self): 7 # do something 8 9 def 拉(self): 10 # do something 11 12 def 撒(self): 13 # do something 14 15 # 在类后面括号中写入另外一个类名,表示当前类继承另外一个类 16 class 猫(动物): 17 18 def 喵喵叫(self): 19 print ‘喵喵叫‘ 20 21 # 在类后面括号中写入另外一个类名,表示当前类继承另外一个类 22 class 狗(动物): 23 24 def 汪汪叫(self): 25 print ‘汪汪叫‘
用继承的方式实现示例:
1 class Animal: 2 3 def eat(self): 4 print("%s 吃 " %self.name) 5 6 def drink(self): 7 print ("%s 喝 " %self.name) 8 9 def shit(self): 10 print ("%s 拉 " %self.name) 11 12 def pee(self): 13 print ("%s 撒 " %self.name) 14 15 16 class Cat(Animal): 17 18 def __init__(self, name): 19 self.name = name 20 self.breed = ‘猫‘ 21 22 def cry(self): 23 print(‘喵喵叫‘) 24 25 class Dog(Animal): 26 27 def __init__(self, name): 28 self.name = name 29 self.breed=‘狗‘ 30 31 def cry(self): 32 print(‘汪汪叫‘) 33 34 35 # ######### 执行 ######### 36 37 c1 = Cat(‘小白家的小黑猫‘) 38 c1.eat() 39 40 c2 = Cat(‘小黑的小白猫‘) 41 c2.drink() 42 43 d1 = Dog(‘胖子家的小瘦狗‘) 44 d1.eat()
3、继承同时具有两种含义
含义一.继承基类的方法,并且做出自己的改变或者扩展(代码重用)
含义二.声明某个子类兼容于某基类,定义一个接口类,子类继承接口类,并且实现接口中定义的方法
归一化式继承方式实现示例:
1 #接口继承 2 3 import abc 4 class All_file(metaclass=abc.ABCMeta): 5 @abc.abstractmethod #就是一种规范 6 def read(self): 7 pass 8 9 @abc.abstractmethod #装饰器 10 def write(self): 11 pass 12 13 class Disk(All_file): 14 def read(self): 15 print(‘disk read‘) 16 17 def write(self): 18 print(‘disk write‘) 19 20 class Cdrom(All_file): 21 def read(self): 22 print(‘cdrom read‘) 23 24 def write(self): 25 print(‘cdrom write‘) 26 27 class Mem(All_file): 28 def read(self): 29 print(‘mem read‘) 30 31 def write(self): 32 print(‘mem write‘) 33 34 m1=Mem() 35 m1.read() 36 m1.write()
执行结果:
1 mem read 2 mem write
1、实践中,继承的第一种含义意义并不很大,甚至常常是有害的。因为它使得子类与基类出现强耦合。
2、继承的第二种含义非常重要。它又叫“接口继承”。
接口继承实质上是要求“做出一个良好的抽象,这个抽象规定了一个兼容接口,使得外部调用者无需关心具体细节,可一视同仁的处理实现了特定接口的所有对象”——这在程序设计上,叫做归一化。
3、归一化使得高层的外部使用者可以不加区分的处理所有接口兼容的对象集合,就好象linux的泛文件概念一样,所有东西都可以当文件处理,不必关心它是内存、磁盘、网络还是屏幕(当然,对底层设计者,当然也可以区分出“字符设备”和“块设备”,然后做出针对性的设计:细致到什么程度,视需求而定)。
4、继承顺序
继承在Python不同版本中是有区别的。
python3: 新式类
pyhon2: 新式类与经典类
新式类示例:
1 #继承顺序就是mro列表定义的顺序 2 3 class A: 4 def test(self): 5 print(‘ A‘) 6 7 class B(A): 8 def test(self): 9 print(‘ B‘) 10 11 class C(A): 12 def test(self): 13 print(‘ C‘) 14 15 class D(B): 16 def test(self): 17 print(‘ D‘) 18 19 class E(C): 20 def test(self): 21 print(‘ E‘) 22 23 class F(D,E): 24 # def test(self): 25 # print(‘ F‘) 26 pass 27 f1=F() 28 f1.test() 29 print(F.__mro__) #python2中没有这个属性 30 31 #新式类继承顺序:F->D->B->E->C->A 32 #经典类继承顺序:F->D->B->A->E->C
经典类示例
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 #Author: nulige 4 class A: 5 def test(self): 6 print(‘A‘) 7 pass 8 9 class B(A): 10 def test(self): 11 print(‘B‘) 12 pass 13 14 class C(A): 15 def test(self): 16 print(‘C‘) 17 pass 18 19 class D(B): 20 def test(self): 21 print(‘D‘) 22 pass 23 24 class E(C): 25 def test(self): 26 print(‘E‘) 27 pass 28 29 class F(D,E): 30 def test(self): 31 print(‘F‘) 32 pass 33 f1=F() 34 f1.test() #经典类:F->D->B->A-->E-->C
执行结果:
1 F->D->B->A-->E-->C
总结:
1.子类会先于父类被检查
2.多个父类会根据它们在列表中的顺序被检查
3.如果对下一个类存在两个合法的选择,选择第一个父类
5、子类中调用父类方法
子类继承了父类的方法,然后想进行修改,注意了是基于原有的基础上修改,那么就需要在子类中调用父类的方法
方法一:父类名.父类方法() ===>相当于在子类中调用父类的方法
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 #Author: nulige 4 #子类中调用父类的方法 5 class Vehicle: 6 Country=‘China‘ 7 8 def __init__(self,name,speed,load,power): 9 self.name=name 10 self.speed=speed 11 self.load=load 12 self.power=power 13 14 def run(self): 15 print(‘开动啦‘) 16 print(‘开动啦‘) 17 18 class Subway(Vehicle): 19 def __init__(self,name,speed,load,power,line): 20 Vehicle.__init__(self,name,speed,load,power) 21 self.line=line 22 23 def show_info(self): 24 print(self.name,self.speed,self.load,self.power,self.line) 25 26 def run(self): 27 Vehicle.run(self) 28 print(‘%s %s 线,开动啦‘ %(self.name,self.line)) 29 line13=Subway(‘北京地铁‘,‘100m/s‘,1000000,‘电‘,13) 30 31 line13.show_info() 32 33 line13.run()
执行结果:
1 北京地铁 100m/s 1000000 电 13 2 开动啦 3 开动啦 4 北京地铁 13 线,开动啦
方法二:用super方法
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 #Author: nulige 4 5 class Vehicle: 6 Country = ‘China‘ 7 8 def __init__(self, name, speed, load, power): 9 self.name = name 10 self.speed = speed 11 self.load = load 12 self.power = power 13 14 def run(self): 15 print(‘开动啦‘) 16 print(‘开动啦‘) 17 18 19 class Subway(Vehicle): 20 def __init__(self, name, speed, load, power, line): 21 #super用法,传参数和不传参数是一样的 22 super().__init__(name, speed, load, power) #这两个作用是一样的,一个不传参数 23 #super(Subway,self).__init__(name, speed, load, power) #这两个作用是一样的,一个传参数, 24 self.line = line 25 26 def show_info(self): 27 print(self.name, self.speed, self.load, self.power, self.line) 28 29 def run(self): 30 super().run() #用super的方法 31 print(‘%s %s 线,开动啦‘ % (self.name, self.line)) 32 33 34 line13 = Subway(‘北京地铁‘, ‘100m/s‘, 1000000, ‘电‘, 13) 35 line13.show_info() 36 line13.run()
十、多态
多态:表明了动态绑定的存在,调用了不同的方法。才能展示出来。反映的是运行时候的状态。
什么是多态?
由不同的类实例化得到的对象,调用同一个方法,执行的逻辑不同。
示例1:
1 s1=‘abc‘ 2 l=[1,2] 3 s1.__len__() 4 l.__len__()
示例2:
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 #Author: nulige 4 5 class H2O: 6 def __init__(self,name,temperature): #名字,温度 7 self.name=name 8 self.temperature=temperature 9 def turn_ice(self): 10 if self.temperature < 0: 11 print(‘[%s]温度太低结冰了‘ %self.name) 12 elif self.temperature >0 and self.temperature < 100: 13 print(‘[%s]液化成水‘ % self.name) 14 elif self.temperature > 100: 15 print(‘[%s]温度太高变成了水蒸气‘ % self.name) 16 17 class Water(H2O): #继承父类(H2O) 18 pass 19 20 class Ice(H2O): 21 pass 22 23 class Steam(H2O): 24 pass 25 26 w1 = Water(‘水‘, 25) 27 i1 = Ice(‘冰‘, -20) 28 s1 = Steam(‘蒸汽‘, 3000) 29 30 def func(obj): 31 obj.turn_ice() 32 33 func(w1) #---->w1.turn_ice() 34 func(i1) #---->i1.turn_ice() 35 func(s1) #---->s1.turn_ice()
执行结果:
1 [水]液化成水 2 [冰]温度太低结冰了 3 [蒸汽]温度太高变成了水蒸气
面向对象三大特性,封装,多态,继承其实这种逻辑是错的,但很多人都这么说。
类的继承有两层意义:1.改变 2.扩展
1、多态就是类的这两层意义的一个具体的实现机制
3、调用不同的类实例化得对象下的相同的方法,实现的过程不一样。
4、python中的标准类型就是多态概念的一个很好的示范 。
十一、封装
封装是啥,抛开面向对象,你单去想什么是装,装就是拿来一个麻袋,把小猫,小狗,小王八,还有alex一起装进麻袋,什么是封,封就是把麻袋封上口子。
在面向对象中这个麻袋就是你的类或者对象,类或者对象这俩麻袋内部装了数据属性和函数属性,那么对于类和对象来说‘封’的概念从何而来,其实封的概念代表隐藏。
约定一:任何以单下划线开头的名字都应该是内部的,私有的。
示例1
1 class People: 2 _star=‘earth‘ #以单_线开头的,就是隐藏属性(外部看不到他) 3 __star=‘earth1111111111111111‘ #python会自动重命名 4 5 def __init__(self,id,name,age,salary): 6 self.id=id 7 self.name=name 8 self._age=age 9 self._salary=salary 10 11 def _get_id(self): 12 print(‘我是私有方法啊,我找到的id是[%s]‘ %self.id) 13 14 # print(People.__dict__) 15 16 #封装使用者,看不到里面使用的逻辑 17 p1=People(‘123123123123‘,‘alex‘,‘18‘,1000000000) 18 print(p1._star)
执行结果:
1 earth
python并不会真的阻止你访问私有的属性,模块也遵循这种约定,如果模块名以单下划线开头,那么from module import *时不能被导入,但是你from module import _private_module依然是可以导入的。
其实很多时候你去调用一个模块的功能时会遇到单下划线开头的(socket._socket,sys._home,sys._clear_type_cache),这些都是私有的,原则上是供内部调用的,作为外部的你,仍然可以用。
约定二:双下划线开头的名字
双下划线开头的属性在继承给子类时,子类是无法覆盖的(原理也是基于python自动做了双下滑线开头的名字的重命名工作)
示例2:
1 class People: 2 __star=‘earth1111111111111111‘ #python会自动重命名 3 def __init__(self,id,name,age,salary): 4 self.id=id 5 self.name=name 6 self._age=age 7 self._salary=salary 8 9 def _get_id(self): 10 print(‘我是私有方法啊,我找到的id是[%s]‘ %self.id) 11 12 #封装使用者,看不到里面使用的逻辑 13 p1=People(‘123123123123‘,‘alex‘,‘18‘,1000000000) 14 print(p1._People__star)
执行结果:
1 earth1111111111111111
第三个层面的封装:明确区分内外,内部的实现逻辑,外部无法知晓,并且为封装到内部的逻辑提供一个访问接口给外部使用。(这才是真正的封装。)
1 class People: 2 __star=‘earth‘ 3 def __init__(self,id,name,age,salary): 4 self.id=id 5 self.name=name 6 self.__age=age 7 self._salary=salary 8 9 def _get_id(self): 10 print(‘我是私有方法啊,我找到的id是[%s]‘ %self.id) 11 12 class Korean(People): 13 __star = ‘火星‘ 14 pass 15 16 17 print(People.__dict__)#__star存到类的属性字典中被重命名为_People__star 18 print(Korean.__dict__) 19 20 # print(Korean.__star)#傻逼,这么访问当然报错啦,__star被重命名了,忘记了? 21 22 print(Korean._Korean__star) 23 print(Korean._People__star) 24 25 双下划线开头在继承中的应用
总结:
上面提到有两种不同的编码约定(单下划线和双下划线 )来命名私有属性,那么问 题就来了:到底哪种方式好呢?大多数而言,你应该让你的非公共名称以单下划线开 头。但是,如果你清楚你的代码会涉及到子类,并且有些内部属性应该在子类中隐藏 起来,那么才考虑使用双下划线方案。 但是无论哪种方案,其实python都没有从根本上限制你的访问。
十二、面向对象的优点
从编程进化论我们得知,面向对象是一种更高等级的结构化编程方式,它的好处就两点
1:通过封装明确了内外,你作为类的缔造者,你是上帝,上帝造物的逻辑你无需知道(你知道了你tm也成上帝了),上帝想让你知道的你才能知道,这样就明确了划分了等级,物就是调用者,上帝就是物的创造者
2:通过继承+多态在语言层面支持了归一化设计
注意:不用面向对象语言(即不用class),一样可以做归一化(如老掉牙的泛文件概念、游戏行业的一切皆精灵),一样可以封装(通过定义模块和接口),只是用面向对象语言可以直接用语言元素显式声明这些而已;而用了面向对象语言,满篇都是class,并不等于就有了归一化的设计。甚至,因为被这些花哨的东西迷惑,反而更加不知道什么才是设计
十三、Python中关于常用的OOP术语
抽象/实现
抽象指对现实世界问题和实体的本质表现,行为和特征建模,建立一个相关的子集,可以用于 绘程序结构,从而实现这种模型。抽象不仅包括这种模型的数据属性,还定义了这些数据的接口。
对某种抽象的实现就是对此数据及与之相关接口的现实化(realization)。现实化这个过程对于客户 程序应当是透明而且无关的。
封装/接口
封装描述了对数据/信息进行隐藏的观念,它对数据属性提供接口和访问函数。通过任何客户端直接对数据的访问,无视接口,与封装性都是背道而驰的,除非程序员允许这些操作。作为实现的 一部分,客户端根本就不需要知道在封装之后,数据属性是如何组织的。在Python中,所有的类属性都是公开的,但名字可能被“混淆”了,以阻止未经授权的访问,但仅此而已,再没有其他预防措施了。这就需要在设计时,对数据提供相应的接口,以免客户程序通过不规范的操作来存取封装的数据属性。
注意:封装绝不是等于“把不想让别人看到、以后可能修改的东西用private隐藏起来”
真正的封装是,经过深入的思考,做出良好的抽象,给出“完整且最小”的接口,并使得内部细节可以对外透明
(注意:对外透明的意思是,外部调用者可以顺利的得到自己想要的任何功能,完全意识不到内部细节的存在)
合成
合成扩充了对类的 述,使得多个不同的类合成为一个大的类,来解决现实问题。合成 述了 一个异常复杂的系统,比如一个类由其它类组成,更小的组件也可能是其它的类,数据属性及行为, 所有这些合在一起,彼此是“有一个”的关系。
派生/继承/继承结构
派生描述了子类衍生出新的特性,新类保留已存类类型中所有需要的数据和行为,但允许修改或者其它的自定义操作,都不会修改原类的定义。
继承描述了子类属性从祖先类继承这样一种方式
继承结构表示多“代”派生,可以述成一个“族谱”,连续的子类,与祖先类都有关系。
泛化/特化
基于继承
泛化表示所有子类与其父类及祖先类有一样的特点。
特化描述所有子类的自定义,也就是,什么属性让它与其祖先类不同。
多态
多态的概念指出了对象如何通过他们共同的属性和动作来操作及访问,而不需考虑他们具体的类。
多态表明了动态(又名,运行时)绑定的存在,允计重载及运行时类型确定和验证。
举例:
水是一个类
不同温度,水被实例化成了不同的状态:冰,水蒸气,雾(然而很多人就理解到这一步就任务此乃多态,错,fuck!,多态是运行时绑定的存在)
(多态体现在由同一个类实例化出的多个对象,这些对象执行相同的方法时,执行的过程和结果是不一样的)
冰,水蒸气,雾,有一个共同的方法就是变成云,但是冰.变云(),与水蒸气.变云()是截然不同的两个过程,虽然调用的方法都一样
自省/反射
自省也称作反射,这个性质展示了某对象是如何在运行期取得自身信息的。如果传一个对象给你,你可以查出它有什么能力,这是一项强大的特性。如果Python不支持某种形式的自省功能,dir和type内建函数,将很难正常工作。还有那些特殊属性,像__dict__,__name__及__doc__