面向对象编程知识点综合
一、面向对象编程与面向过程编程对比
1、面向过程编程:核心过程二字,过程指的是解决问题的步骤,既先干什么、再干什么、后干什么,基于该思想的编程就好比在生产一条流水线,是一种机械式的思维方式。
优点:复杂的问题流程化进而简单化
缺点:可扩展性差
2、面向对象编程:核心是对象二字,对象是技能与特征的结合体,基于该思想编写程序就好比在创造一个世界,世界是由一个个对象组成的,在上帝眼里任何存在的事物都是对象,任何不存在的事物也都可以创造出来,是一种上帝式的思维方式
优点:可扩展性强
缺点:编程的复杂度要高于面向过程
二、类与对象
对象:对象是技能与特征的结合体
类:对象相同特征与技能的结合体
对象是具体存在的事物,而类则是抽象出来的概念,站在不同角度总结出来的类与对象是不同的
在现实世界中,现有一个个具体存在的对象,然后随着人类文明的发展才总结出类的概念
#在现实世界中,站在老男孩学校的角度:先有对象,再有类 对象1:李坦克 特征: 学校=oldboy 姓名=李坦克 性别=男 年龄=18 技能: 学习 吃饭 睡觉 对象2:王大炮 特征: 学校=oldboy 姓名=王大炮 性别=女 年龄=38 技能: 学习 吃饭 睡觉 对象3:牛榴弹 特征: 学校=oldboy 姓名=牛榴弹 性别=男 年龄=78 技能: 学习 吃饭 睡觉 现实中的老男孩学生类 相似的特征: 学校=oldboy 相似的技能: 学习 吃饭 睡觉
在程序中,是先定义出类的概念后调用类来产生对象
#在程序中,务必保证:先定义(类),后使用(产生对象) PS: 1. 在程序中特征用变量标识,技能用函数标识 2. 因而类中最常见的无非是:变量和函数的定义 #程序中的类 class OldboyStudent: school='oldboy' def learn(self): print('is learning') def eat(self): print('is eating') def sleep(self): print('is sleeping') #注意: 1.类中可以有任意python代码,这些代码在类定义阶段便会执行 2.因而会产生新的名称空间,用来存放类的变量名与函数名,可以通过OldboyStudent.__dict__查看 3.对于经典类来说我们可以通过该字典操作类名称空间的名字(新式类有限制),但python为我们提供专门的.语法 4.点是访问属性的语法,类中定义的名字,都是类的属性 #程序中类的用法 .:专门用来访问属性,本质操作的就是__dict__ OldboyStudent.school #等于经典类的操作OldboyStudent.__dict__['school'] OldboyStudent.school='Oldboy' #等于经典类的操作OldboyStudent.__dict__['school']='Oldboy' OldboyStudent.x=1 #等于经典类的操作OldboyStudent.__dict__['x']=1 del OldboyStudent.x #等于经典类的操作OldboyStudent.__dict__.pop('x') #程序中的对象 #调用类,或称为实例化,得到对象 s1=OldboyStudent() s2=OldboyStudent() s3=OldboyStudent() #如此,s1、s2、s3都一样了,而这三者除了相似的属性之外还各种不同的属性,这就用到了__init__ #注意:该方法是在对象产生之后才会执行,只用来为对象进行初始化操作,可以有任意代码,但一定不能有返回值 class OldboyStudent: ...... def __init__(self,name,age,sex): self.name=name self.age=age self.sex=sex ...... s1=OldboyStudent('李坦克','男',18) #先调用类产生空对象s1,然后调用OldboyStudent.__init__(s1,'李坦克','男',18) s2=OldboyStudent('王大炮','女',38) s3=OldboyStudent('牛榴弹','男',78) #程序中对象的用法 #执行__init__,s1.name='牛榴弹',很明显也会产生对象的名称空间 s2.__dict__ {'name': '王大炮', 'age': '女', 'sex': 38} s2.name #s2.__dict__['name'] s2.name='王三炮' #s2.__dict__['name']='王三炮' s2.course='python' #s2.__dict__['course']='python' del s2.course #s2.__dict__.pop('course')
__init__方法
#方式一、为对象初始化自己独有的特征 class People: country='China' x=1 def run(self): print('----->', self) # 实例化出三个空对象 obj1=People() obj2=People() obj3=People() # 为对象定制自己独有的特征 obj1.name='egon' obj1.age=18 obj1.sex='male' obj2.name='lxx' obj2.age=38 obj2.sex='female' obj3.name='alex' obj3.age=38 obj3.sex='female' # print(obj1.__dict__) # print(obj2.__dict__) # print(obj3.__dict__) # print(People.__dict__) #方式二、为对象初始化自己独有的特征 class People: country='China' x=1 def run(self): print('----->', self) # 实例化出三个空对象 obj1=People() obj2=People() obj3=People() # 为对象定制自己独有的特征 def chu_shi_hua(obj, x, y, z): #obj=obj1,x='egon',y=18,z='male' obj.name = x obj.age = y obj.sex = z chu_shi_hua(obj1,'egon',18,'male') chu_shi_hua(obj2,'lxx',38,'female') chu_shi_hua(obj3,'alex',38,'female') #方式三、为对象初始化自己独有的特征 class People: country='China' x=1 def chu_shi_hua(obj, x, y, z): #obj=obj1,x='egon',y=18,z='male' obj.name = x obj.age = y obj.sex = z def run(self): print('----->', self) obj1=People() # print(People.chu_shi_hua) People.chu_shi_hua(obj1,'egon',18,'male') obj2=People() People.chu_shi_hua(obj2,'lxx',38,'female') obj3=People() People.chu_shi_hua(obj3,'alex',38,'female') # 方式四、为对象初始化自己独有的特征 class People: country='China' x=1 def __init__(obj, x, y, z): #obj=obj1,x='egon',y=18,z='male' obj.name = x obj.age = y obj.sex = z def run(self): print('----->', self) obj1=People('egon',18,'male') #People.__init__(obj1,'egon',18,'male') obj2=People('lxx',38,'female') #People.__init__(obj2,'lxx',38,'female') obj3=People('alex',38,'female') #People.__init__(obj3,'alex',38,'female') # __init__方法 # 强调: # 1、该方法内可以有任意的python代码 # 2、一定不能有返回值 class People: country='China' x=1 def __init__(obj, name, age, sex): #obj=obj1,x='egon',y=18,z='male' # if type(name) is not str: # raise TypeError('名字必须是字符串类型') obj.name = name obj.age = age obj.sex = sex def run(self): print('----->', self) # obj1=People('egon',18,'male') obj1=People(3537,18,'male') # print(obj1.run) # obj1.run() #People.run(obj1) # print(People.run)
类是一系列对象相同特征与技能的结合体,既类体中最常见的就是变量与函数体,但其实类体中是可以存在任意python代码的,
类体中会在定义阶段立即执行,会产生一个类的名称空间,用来将类体代码执行过程中产生的名字都丢进去
总结:①类本质就是一个名称空间,或者说就是一个用来存放变量与函数的容器,
②类的用途之一就是当做名称空间,从其内部取出名字来使用
③类的用途之二是用来调用类产生对象
调用类产生对象:调用类的过程称之为类的实例化,调用类的返回值称之为类的一个对象
调用类发生了:
1、产生一个空对象
2、触发类中的__init__方法,将对象连同调用类括号内指定的参数一同传入__init__
类中定义的变量是类的数据属性,类可以用,对象也可以用,大家都指向同一个地址,类变量值一旦改变,所有对象都跟着改变
类中定义的函数是类的函数属性,类可以用,类来调用就是一个普通的函数,但其实类中定义的函数就是给对象用的,而且是绑定给对象用的
绑定方法:指向类的函数(特殊之处是绑定给谁就应该由谁调用)
类的函数:该传几个参数就传几个参数
三大特性之继承:
利用继承能来解决类与类之间的代码冗余问题
继承是一种新建类的方法,新建的类称之为子类(派生类),被继承的类称之为父类、基类、超类
继承的特性:子类可以遗传、重用父类的属性
python中继承类的特点:
1、在python中一个子类可以同时继承多个父类
2、在继承背景下说,python中类分为两种:新式类和经典类
新式类:但凡继承了object的类以及该类的子类都是新式类
python中一个类即便没有显示的继承任何类,默认就会继承object,即在python3中所有的类都是新式类
经典类:没有继承object的类以及该类的子类都是经典类
在单继承的基础下属性查找的顺序是:对象——》对象类——》父亲——》父类。。。
子类中重用父类的属性:
方式一:
在子类派生出的新方法中指名道姓的引用某一个类中的函数
与继承无关,访问的是父类中的函数没有自动传值功能
继承解决的是类与类之间代码冗余的问题,一定是一个类是另外一个类的子类
总结对象之间的相似之处得到类,总结类之间的相似之处得到的就是父类
多继承背景下属性查找的顺序:对象--》对象的类--》按照从左往右的顺序一个个的分支找下去
一旦出现菱形继承问题,新式类与经典类在属性查找上的区别是:
新式类:广度优先查找,在最后一个分支查找顶级类
经典类:深度优先查找,在第一个分支就查找顶级类
mro()查找的顺序:
在子类派生出的新的方法中重用父类功能的方式二
在子类中用super()方法
python2中:super(自己的类名,对象自己)
python3中:super()
调用super()方法会得到一个特殊的对象,该对象是专门用来引用父类中的属性,完全参照mro()列表 访问是绑定方法,有自动传值的效果
组合:
组合指的是一个对象拥有一个属性,该属性的值属于另外一个类的对象
组合通过一个对象添加属性(属性的值是另外一个类的对象)的方式,可以间接的将两个类关联、整合,从而减少类之间的代码冗余问题
多态:
多态指的是同一事物的不同形态
在多态背景下,可以不用考虑对象具体的类型的前提下直接使用对象多态的精髓:统一
父类只是用来建立规范的,不能用来实例化的,更无需实现内部方法。
python崇尚鸭子类型
封装:
装:往容器、名称空间内存入名字
封:代表存放于名称空间的名字给藏起来,这种隐藏对外不对内
在类内定义的属性前加__开头(没有__结尾)
总结:
1、__开头的属性实现的隐藏仅仅只是一种语法上的变形,并不会真正的限制类外部的访问
2、该变形操作只在类定义阶段检测语法时发生一次,类定义阶段之后新增的__开头的属性不会变形
3、如果父类不想让子类覆盖自己的属性,可以在属性前加__开头
4、peoperty装饰器是用来将类内的函数属性伪装为数据属性
绑定方法与非绑定方法
一:绑定方法(绑定给谁,谁来调用就自动将它本身当作第一个参数传入):
1. 绑定到类的方法:用classmethod装饰器装饰的方法。
为类量身定制
类.boud_method(),自动将类当作第一个参数传入
(其实对象也可调用,但仍将类当作第一个参数传入)
2. 绑定到对象的方法:没有被任何装饰器装饰的方法。
为对象量身定制
对象.boud_method(),自动将对象当作第一个参数传入
(属于类的函数,类可以调用,但是必须按照函数的规则来,没有自动传值那么一说)
import settings class MySQL: def __init__(self,host,port): self.host=host self.port=port @classmethod def from_conf(cls): print(cls) return cls(settings.HOST,settings.PORT) print(MySQL.from_conf) #<bound method MySQL.from_conf of <class '__main__.MySQL'>> conn=MySQL.from_conf() conn.from_conf() #对象也可以调用,但是默认传的第一个参数仍然是类
二:非绑定方法:用staticmethod装饰器装饰的方法
1. 不与类或对象绑定,类和对象都可以调用,但是没有自动传值那么一说。就是一个普通工具而已
注意:与绑定到对象方法区分开,在类中直接定义的函数,没有被任何装饰器装饰的,都是绑定到对象的方法,可不是普通函数,对象调用该方法会自动传值,而staticmethod装饰的方法,不管谁来调用,都没有自动传值一说
import hashlib import time class MySQL: def __init__(self,host,port): self.id=self.create_id() self.host=host self.port=port @staticmethod def create_id(): #就是一个普通工具 m=hashlib.md5(str(time.time()).encode('utf-8')) return m.hexdigest() print(MySQL.create_id) #<function MySQL.create_id at 0x0000000001E6B9D8> #查看结果为普通函数 conn=MySQL('127.0.0.1',3306) print(conn.create_id) #<function MySQL.create_id at 0x00000000026FB9D8> #查看结果为普通函数
classmethod与staticmethod的区别
import settings class MySQL: def __init__(self,host,port): self.host=host self.port=port @staticmethod def from_conf(): return MySQL(settings.HOST,settings.PORT) # @classmethod #哪个类来调用,就将哪个类当做第一个参数传入 # def from_conf(cls): # return cls(settings.HOST,settings.PORT) def __str__(self): return '就不告诉你' class Mariadb(MySQL): def __str__(self): return '<%s:%s>' %(self.host,self.port) m=Mariadb.from_conf() print(m) #我们的意图是想触发Mariadb.__str__,但是结果触发了MySQL.__str__的执行,打印就不告诉你:
三、 元类
元类源自一句话,在python中一切皆对象,而对象都是由类实例化得到的,既然这样那样类也是对象,也是通过实例化得到的,内置类为type
调用关系是:
调用元类——》自定义的类
调用自定义的类——》自定义的对象
自定义类的三个关键组成部分
1 类名
2 类的基类们
3 类的名称空间
class关键字的底层的工作原理
1、先拿到类名(oldboyteacher)
2、再拿到类的基类们:(object,)
3、拿到类的名称空间(执行类体代码,将产生的名字放到类的名称空间也就是一个字典里,补充exec)
4、调用元类实例化得到自定义的类:、oldboyTeacher=type('oldboyTeacher',(object){})
实现代码
class_name="OldboyTeacher" class_bases=(object,) class_dic={} class_body=''' school=self.shcool def __init__(self,...) ............. def score(......) ........... exec('x=1',{},class_dic) print(class_dic) OldboyTeacher_type(class_name,class_bases,class_dic)
自定义元类来控制类的产生:
但凡继承了type的类才能称之为自定义的元类,否则就是一个普通的类
对象之所以可以调用是因为对象有一个函数__call__
实例化发生的三件事:
1、产生一个空对象
2、执行__init__方法,完成对象初始化属性的操作
3、返回初始化好的那个对象
自定义类来控制类的调用