一、三大编程范式:
编程范式即编程的方法论,是一种编程风格,三者没有高低贵贱之分。
包含有:
1、面向过程编程
基本设计思路就是程序一开始是要着手解决一个大的问题,然后把一个大问题分解成很多个小问题或子过程,这些子过程再执行的过程再继续分解直到小问题足够简单到可以在一个小步骤范围内解决。举个典型的面向过程的例子, 数据库备份, 分三步,连接数据库,备份数据库,测试备份文件可用性。
面向过程核心就是过程,一步一步去实现特定的功能。优点是:易读性,简单化设计。缺点是:一套流程只能解决一个问题,放在其他生产线上没有用,修改一处,全身可能都得动,牵一发动全身。
2、函数式编程
Python是一种伪函数式编程,基本不用
3、面向对象编程
OOP编程是利用“类”和“对象”来创建各种模型来实现对真实世界的描述,使用面向对象编程的原因一方面是因为它可以使程序的维护和扩展变得更简单,并且可以大大提高程序开发效率 ,另外,基于面向对象的程序可以使它人更加容易理解你的代码逻辑,从而使团队开发变得更从容。
二、编程进化论:
1、编程最开始就是无组织无结构的,从基本的控制语言开始的,如汇编语言。
2、将简单的控制语言中提取出重复的代码或者逻辑,组织到一起,比如一个函数,这就实现了代码的重用,并且开始有结构化,变得有逻辑性
3、我们定义函数都是独立于函数外定义变量,然后作为参数传递非函数,这意味着:数据和动作的分离。
4、如果我们把数据和动作整合到一个结构中,如函数或类,那么我们就有了一个对象系统。
三、面向对象设计与面向对象编程
1、面向对象设计:核心就是对象,对象是特征与功能的结合体,基于面向对象设计程序就好比在创造一个世界,存在的就是对象。优点是解决了问题的扩展性,缺点是复杂度高,不易于读。
面向对象设计(Object oriented design):将一类具体事物的数据和动作整合到一起,即面向对象设计
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)
类:把一类事物相同的特征和动作整合到一起就是类,类是一个抽象的概念
对象:就是基于类而创建的一个具体存在的事物,也是特征和动作整合到一起
--注--:
在程序中:务必保证先定义类,后产生对象这与函数的使用是类似的,先定义函数,后调用函数,类也是一样的,在程序中需要先定义类,后调用类
不一样的是,调用函数会执行函数体代码返回的是函数体执行的结果,而调用类会产生对象,返回的是对象
示例:
学校类
特征:name,addr,type
动作:考试,招生,开除学生
1 def school(name,addr,type): 2 def init(name, addr, type): 3 sch = { 4 'name': name, 5 'addr': addr, 6 'type': type, 7 'kao_shi': kao_shi, 8 'zhao_sheng': zhao_sheng, 9 } 10 return sch 11 def kao_shi(school): 12 print('%s 学校正在考试' %school['name']) 13 def zhao_sheng(school): 14 print('%s %s 正在招生' %(school['type'],school['name'])) 15 return init(name,addr,type) 16 17 s1=school('oldboy','沙河','私立学校') 18 print(s1) 19 print(s1['name']) 20 21 s1['zhao_sheng'](s1) 22 23 s2=school('清华','北京','公立学校') 24 25 print(s2) 26 print(s2['name'],s2['addr'],s2['type']) 27 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()
用面向对象语言写程序和一个程序的设计是面向对象的,两者就是两码事。
四、类和对象:
1、什么叫类:类是一种数据结构,好比一个模型,该模型用来表示一类事物(数据和动作的结合体),用他来生产真实的物体。
2、什么叫对象:睁开眼,你看到的一切的事物都是一个个的对象,你可以把对象理解为一个具体的事物(事物即数据和动作的结合体)。
3、类和对象的关系:对象都是由类产生的。
4、什么叫实例化:由类生产对象的过程就是实例化,类实例化的结果就是产生一个对象或者实例。
五、类的相关知识:
1、初识类:
在Python总声明类和声明函数相似:
声明函数:
1 1 def functionName(args): 2 2 '函数文档字符串' 3 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中,上述两种定义方式全都是新式类
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(类名):查出的是一个名字列表
—— 类名.__dic__:查出的是一个字典,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 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 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实例,没有做任何事情
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__,所以一定不要在该函数内定义返回值,会冲突。
3、实例属性
—— 实例只有数据属性(实例的函数属性严格来说是类的函数属性)
—— del 实例 ,只是收回了实例的数据属性。
—— 实例化的过程实际就是执行__init__的过程,这个函数内部只是为实例本身即self设定了一堆数据(变量),所以实例只有数据属性
1 class Chinese: 2 '我们都是中国人,我们骄傲的活着,我们不服任何事和物' 3 government='共产_党' 4 def __init__(self,name,age,gender): 5 self.Name=name # Name 只是一个key 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()或内置函数__init__():
原则就是:实例现在自己的属性里找,找不到了在到类属性里找。
如:
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__':__init__ at 0x0121F0C0>, '__doc__': '这是一个中国人的类', '__module__': '__main__', '__weakref__': '__weakref__' of 'Chinese' objects>, 'sui_di_tu_tan': , 'dang': '共产堂', 'cha_dui': , '__dict__': '__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__': __init__ at 0x0121F108>, 'dang': '共产堂', '__module__': '__main__', 'play_ball': , '__weakref__': '__weakref__' of 'Chinese' objects>, 'country': 'Japan', '__doc__': None, '__dict__': '__dict__' of 'Chinese' objects>} 16 共产堂 17 {'__init__': __init__ at 0x0121F108>, '__module__': '__main__', 'play_ball': , '__weakref__': '__weakref__' of 'Chinese' objects>, '__doc__': None, '__dict__': '__dict__' of 'Chinese' objects>} 18 {'__init__': __init__ at 0x0121F108>, '__module__': '__main__', 'play_ball': , '__weakref__': '__weakref__' of 'Chinese' objects>, '__doc__': None, 'eat': , '__dict__': '__dict__' of 'Chinese' objects>} 19 alex 正在吃虾 20 test
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不稳定。
-------也就是说不能通过__dict__来给属性字典添加键值对,这个是不合理的
5、类属性与实例属性的增删改查
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':, '__init__': __init__ at 0x01085660>, '__doc__': None, '__weakref__': '__weakref__' of 'Chinese' objects>, '__module__': '__main__', '__dict__': '__dict__' of 'Chinese' objects>} 8 9 {'play_ball': , '__init__': __init__ at 0x01085660>, '__doc__': None, 'eat': , '__weakref__': '__weakref__' of 'Chinese' objects>, '__module__': '__main__', '__dict__': '__dict__' of 'Chinese' objects>} 10 11 alex 正在吃虾 12 13 test
5.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__)
--注-- :类名首字母大写
一个点代表一层作用域,obj.x先从自己的作用域找,自己找不到去外层的类的字典中找,都找不到,就会报错
在类中没有使用点的调用,代表调用全局变量。
应用: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']
七、静态属性、类方法、静态方法:
7.1、静态方法:作用就是封装逻辑,把函数属性封装成数据属性----@property的使用
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) # 跟调用数据属性一样的感觉
7.2、类方法:作用是专门给类使用,与实例无关,类方法只能访问类相关的属性,不能访问实例属性(跟实例没有任何关系)-----@classmethod的使用
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 Room.tell_info(1)
1 <class '__main__.Room'> 2 ----> 1
7.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 def test(): 20 print('这可不是静态方法,用类调用没问题,你用一个实例调用看看') 21 Room.wash_body() 22 r1=Room('厕所','alex',10,10) 23 r1.wash_body() 24 #------ 25 Room.test() 26 r1.test() #会报错,因为如果test不是静态方法,那么r1会吧自己传给test的第一个参数self,test无参所以报错 27 静态方法
八、组合
——组合的组成部分
定义一个人的类,人有头,驱赶,手,脚等数据属性,这几个属性又可以是通过一个类实例化的对象,这就是组合
简单理解:就是大类包含小类,就是组合。
用途:
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__) #查看实例的属性字典
{'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 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 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 5 python 9800
实例:选课 法二:
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))
1 1 老男孩 北京校区 2 2 老男孩 上海校区 3 3 老男孩 天津校区 4 5 请选择学校:1 6 课程名: python 7 课程费用: 9800 8 课程周期: 1h 9 课程[python] 属于[oldboy] 学校 10 11 1 老男孩 北京校区 12 2 老男孩 上海校区 13 3 老男孩 天津校区 14 15 请选择学校:
九、面向对象三大特征
继承: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 #继承 2 class Dad: 3 '这个是爸爸类' 4 money=10 5 def __init__(self,name): 6 print('爸爸') 7 self.name=name 8 9 def hit_son(self): 10 print('%s 正在打儿子' %self.name) 11 12 '这个是儿子类' 13 class Son(Dad): 14 money = 10000000000000 15 16 s1=Son('alex') #继承父类的属性 17 print(s1.money) 18 print(Dad.money) 19 # print(Dad.__dict__) 20 # print(Son.__dict__)
1 爸爸
2 10000000000000
3 10
3、什么时候用继承
--1.当类之间有显著不同,并且较小的类是较大的类所需要的组件时,用组合比较好
--2.当类之间有很多相同的功能,提取这些共同的功能做成基类,用继承比较好
如:
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()
4、继承同时具有两种含义
含义一:继承基类的方法,并且做出自己的改变或者扩展(代码重用)
含义二:声明某个子类兼容与某个基类,定义一个接口类,子类继承接口类,并且实现接口中定义的方法
归一化式继承方式实现示例: abc模块使用
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、实践中,继承的第一种含义意义并不很大,甚至常常是有害的。因为它使得子类与基类出现强耦合。
2、继承的第二种含义非常重要。它又叫“接口继承”。
接口继承实质上是要求“做出一个良好的抽象,这个抽象规定了一个兼容接口,使得外部调用者无需关心具体细节,可一视同仁的处理实现了特定接口的所有对象”——这在程序设计上,叫做归一化。
3、归一化使得高层的外部使用者可以不加区分的处理所有接口兼容的对象集合,就好象linux的泛文件概念一样,所有东西都可以当文件处理,不必关心它是内存、磁盘、网络还是屏幕(当然,对底层设计者,当然也可以区分出“字符设备”和“块设备”,然后做出针对性的设计:细致到什么程度,视需求而定)。
4、继承顺序
在不同的版本中继承不同
Python-3:都是新式类
Pyhton-2:经典类 和 新式类
新式类示例:
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 # -*- coding:utf-8 -*- # 在2版本中 2 class A: 3 def test(self): 4 print('A') 5 pass 6 7 class B(A): 8 def test(self): 9 print('B') 10 pass 11 12 class C(A): 13 def test(self): 14 print('C') 15 pass 16 17 class D(B): 18 def test(self): 19 print('D') 20 pass 21 22 class E(C): 23 def test(self): 24 print('E') 25 pass 26 27 class F(D,E): 28 def test(self): 29 print('F') 30 pass 31 f1=F() 32 f1.test() #经典类:F->D->B->A-->E-->C
5、子类调用父类的方法:
子类继承了父类的方法,然后想进行修改,注意了是基于原有的基础上修改,那么就需要在子类中调用父类的方法
方法一:父类名.父类方法() ===>相当于在子类中调用父类的方法 -----不推荐,容错性低,父类名修改 ,所有代码用到父类名都得修改
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 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) # 调用父类的init方法 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()
方法二:用super方法------推荐
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 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 s1='abc' # 字符串对象 2 l=[1,2] # 列表对象 3 s1.__len__() 4 l.__len__()
1 class H2O: 2 def __init__(self,name,temperature): #名字,温度 3 self.name=name 4 self.temperature=temperature 5 def turn_ice(self): 6 if self.temperature < 0: 7 print('[%s]温度太低结冰了' %self.name) 8 elif self.temperature >0 and self.temperature < 100: 9 print('[%s]液化成水' % self.name) 10 elif self.temperature > 100: 11 print('[%s]温度太高变成了水蒸气' % self.name) 12 13 class Water(H2O): #继承父类(H2O) 14 pass 15 16 class Ice(H2O): 17 pass 18 19 class Steam(H2O): 20 pass 21 22 w1 = Water('水', 25) 23 i1 = Ice('冰', -20) 24 s1 = Steam('蒸汽', 3000) 25 26 def func(obj): 27 obj.turn_ice() 28 29 func(w1) #---->w1.turn_ice() 30 func(i1) #---->i1.turn_ice() 31 func(s1) #---->s1.turn_ice()
面向对象的三大特征:封装、多态、继承。其实这种逻辑是错误的,但是很多人都是这么说的。
类的继承有两层意义:1、改变 2、扩展
1、多态就是类的这两层意义的一个具体的实现机制
2、调用不同的类实例化的对象下的相同的方法,实现的过程不一样。
3、Python中的标准类型就是多态概念的一个很好的示范,如上述示例。
十一、封装
封装是啥,抛开面向对象,你单去想什么是装,装就是拿来一个麻袋,把小猫,小狗,小王八,还有alex一起装进麻袋,什么是封,封就是把麻袋封上口子。
在面向对象中这个麻袋就是你的类或者对象,类或者对象这俩麻袋内部装了数据属性和函数属性,那么对于类和对象来说‘封’的概念从何而来,其实封的概念代表隐藏。
约定一:任何以单下划线开头的名字都应该是内部的,私有的(虽然还是可以被在外部调用到)
1 class People: 2 _star='earth' #以单_线开头的,就是隐藏属性(外部看不到他) 3 __star='earth1111111111111111' #python会自动重命名 为: _People__star 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) # earth
Python并不会真的阻止访问私有属性,模块也遵循这种约定,如果模块名以单下划线开头,那么from module import * 是不能被导入的,但是 from module import _private_module 依然可以被导入。
其实很多时候去调用一个模块的功能时会遇到单下划线开头的(socket._socket,sys._home,sys._clear_type_cache),这些都是私有的,原则是供内部调用的,作为外部的你,也是可以用的。
约定二:双下划线开头的名字,在继承给子类时,子类时无法覆盖的(原理也是基于python自动做了双下滑线开头的名字的重命名工作)
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)
15
16
17 ------》
18 earth1111111111111111
第三个层面的封装:明确区分内外,内部的实现逻辑,外部无法知晓,并且为封装到内部的逻辑提供一个访问接口给外部用(这才是真正的封装)
1 class People: 2 __star='earth' # 被重命名为 _类名__start 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
示例:真正的封装
1 class Room: 2 def __init__(self,name,owner,width,length,high): 3 self.name=name 4 self.owner=owner 5 self.__width=width 6 self.__length=length 7 self.__high=high 8 9 def tell_area(self): #此时我们想求的是面积 10 return self.__width * self.__length *self.__high 11 12 def tell_width(self): # 提供一个接口 获取属性 13 return self.__width 14 15 16 r1=Room('卫生间','alex',100,100,10000) 17 18 # arear=r1.__width * r1.__length 19 print(r1.tell_area())
-------》私有属性,在内部调用不会重命名
总结:上面提到有两种不同编码约定(单或双)来命名私有属性,大多数情况下,让非公共名称用单下划线。但是,如果清楚自己的代码会设计子类,并且有些内部属性应该是在子类中隐藏的,那么使用双下划线,但是无论哪种方式,python都没有限制访问。
十二、面向对象的优点
从编程进化论我们得知,面向对象是一种更高等级的结构化编程方式,它的好处就两点
1:通过封装明确了内外,你作为类的缔造者,你是上帝,上帝造物的逻辑你无需知道(你知道了你tm也成上帝了),上帝想让你知道的你才能知道,这样就明确了划分了等级,物就是调用者,上帝就是物的创造者
2:通过继承+多态在语言层面支持了归一化设计
注意:不用面向对象语言(即不用class),一样可以做归一化(如老掉牙的泛文件概念、游戏行业的一切皆精灵),一样可以封装(通过定义模块和接口),只是用面向对象语言可以直接用语言元素显式声明这些而已;而用了面向对象语言,满篇都是class,并不等于就有了归一化的设计。甚至,因为被这些花哨的东西迷惑,反而更加不知道什么才是设计
十三、Python中关于OOP的常用术语:
抽象/实现
抽象指对现实世界问题和实体的本质表现,行为和特征建模,建立一个相关的子集,可以用于 绘程序结构,从而实现这种模型。抽象不仅包括这种模型的数据属性,还定义了这些数据的接口。
对某种抽象的实现就是对此数据及与之相关接口的现实化(realization)。现实化这个过程对于客户 程序应当是透明而且无关的。
封装/接口
封装描述了对数据/信息进行隐藏的观念,它对数据属性提供接口和访问函数。通过任何客户端直接对数据的访问,无视接口,与封装性都是背道而驰的,除非程序员允许这些操作。作为实现的 一部分,客户端根本就不需要知道在封装之后,数据属性是如何组织的。在Python中,所有的类属性都是公开的,但名字可能被“混淆”了,以阻止未经授权的访问,但仅此而已,再没有其他预防措施了。这就需要在设计时,对数据提供相应的接口,以免客户程序通过不规范的操作来存取封装的数据属性。
注意:封装绝不是等于“把不想让别人看到、以后可能修改的东西用private隐藏起来”
真正的封装是,经过深入的思考,做出良好的抽象,给出“完整且最小”的接口,并使得内部细节可以对外透明
(注意:对外透明的意思是,外部调用者可以顺利的得到自己想要的任何功能,完全意识不到内部细节的存在)
合成
合成扩充了对类的 述,使得多个不同的类合成为一个大的类,来解决现实问题。合成 述了 一个异常复杂的系统,比如一个类由其它类组成,更小的组件也可能是其它的类,数据属性及行为, 所有这些合在一起,彼此是“有一个”的关系。
派生/继承/继承结构
派生描述了子类衍生出新的特性,新类保留已存类类型中所有需要的数据和行为,但允许修改或者其它的自定义操作,都不会修改原类的定义。
继承描述了子类属性从祖先类继承这样一种方式
继承结构表示多“代”派生,可以述成一个“族谱”,连续的子类,与祖先类都有关系。
泛化/特化
基于继承
泛化表示所有子类与其父类及祖先类有一样的特点。
特化描述所有子类的自定义,也就是,什么属性让它与其祖先类不同。
多态
多态的概念指出了对象如何通过他们共同的属性和动作来操作及访问,而不需考虑他们具体的类。
多态表明了动态(又名,运行时)绑定的存在,允计重载及运行时类型确定和验证。
举例:
水是一个类
不同温度,水被实例化成了不同的状态:冰,水蒸气,雾(然而很多人就理解到这一步就任务此乃多态,错,fuck!,多态是运行时绑定的存在)
(多态体现在由同一个类实例化出的多个对象,这些对象执行相同的方法时,执行的过程和结果是不一样的)
冰,水蒸气,雾,有一个共同的方法就是变成云,但是冰.变云(),与水蒸气.变云()是截然不同的两个过程,虽然调用的方法都一样
自省/反射
自省也称作反射,这个性质展示了某对象是如何在运行期取得自身信息的。如果传一个对象给你,你可以查出它有什么能力,这是一项强大的特性。如果Python不支持某种形式的自省功能,dir和type内建函数,将很难正常工作。还有那些特殊属性,像__dict__,__name__及__doc__