面向对象编程是一种程序设计范型,同时也是程序开发方法。程序中包含各种独立而又能互相调用的对象,每个对象都能接受、处理数据并将数据传递给其它对象

面向对象:

思想:以模块化思想解决工程问题
(接触到任意一个任务,首先就应该想到的是任务这个世界的构成,是由模型构成的)
由面向过程转向面向对象
常用名词:OO:面向对象
ooa;分析 ood:设计 oop:编程 ooi:实现
ooa–>ood–>ooi–>oop
类 vs 对象
类:抽象,描述的是一个集合,侧重于共性
对象:具象,描述的是个体
类的内容:动作,函数
对象的概念:
“万物皆对象”,python中所有事务都是以对象形式存在的,从简单的数值类型到复杂的代码模块都是对象
对象即表示客观世界问题空间中某个具体事务,又表示软件系统解空间中的基本元素
对象=属性+方法
对象以id作为标识、即包含数据(属性),又包含代码(方法),是某一类具体事务的特殊案例。
创建对象
对象是类的实例,(拥有自己的属性和方法),是程序的基本单元。
要创建一个新的对象,首先必须定义一个类,用以指明该类型的对象所包含的内容(属性+方法)
同一类(class)的对象具有相同的属性和方法,但对于具体的属性值和生成的对象的id不相同
赋值语句给了对象以名称,对象可以有多个名称,但只有一个id
引入id函数
id():返回对象的唯一标识符,标识符是一个整数
格式:id(obj)——返回值:返回对象的内存地址

对象实现了属性和方法的封装,是一种数据抽象的机制。
(要对数据对象进行处理,只需调用它的方法即可)
对象属性和方法的引用
格式:<对象>.<属性名>/<方法>——点操作
若为单个属性名,可以跟一般的变量一样用在赋值语句和表达式中

str="abc"
t=str.upper()
print(t)

引用:可当作变量或和变量一样进行使用
python语言动态化特征,使得对象可以随时增加或删除属性和方法

类的定义与调用
什么是类;(class)类是对象的模板,封装了对应现实实体的性质和行为
实例对象是类的具体化
把类比作摸具,而对象则是用摸具制作出来的零件
类的出现为OOP的三个重要特性提供了实现的手段——封装性、多态性、继承性
一、 封装:就是对对象的成员进行访问限制
封装的三个级别:公开、受保护的、私有的
判别对象的位置:对象内部、对象外部、子类中
1、 私有成员是最高级别的封装,只能在当前类或对象中访问,在成员面前添加两个下划线即可。

#定义一个空类
class Stu:
    name="wangfei"
    __age=20
A=Stu()
print(A.name)
print(A.__age)#会报错
A._Stu__age=18
print(A._Stu__age)
#对象名._类名__方法

python中的私有不是真的私有,是一种改名策略,可以使用[对象名._类名__方法]

2、受保护的封装是将对象成员进行一定级别的封装,然后,在类中或者子类中都可以进行访问,在外部不可以
受保护封装方法:在成员名称前添加一个下划线即可
(下划线的使用具体见另一篇文章)
3、公开的封装
二、 ——继承见下方的类的继承的介绍

和函数相似,类也是一系列代码的封装

类的命名

–遵守变量命名的规范,为和函数命名区分类的首字母要大写(函数命名首字母要小写),尽量避开跟系统命名相似的命名
定义类
必须使用class关键字,类由属性和方法构成,其它不允许出现,成员属性可以直接使用变量赋值。
语法:

clas <类名(首字母一定要大写)>:
#冒号一定切勿忘记
	<一系列方法的调用>

类的初始化
类基本都有一个初始化的方法(名称是固定)

class <类名>:
	def __init__(self,<参数表>):
	#还可以定义一些其他方法
	def <方法名>(self,<参数表>):

init 是一个特殊的函数名,用于根据类的定义创建实例对象.
对于所有的创建方法第一个参数必须为self.
调用类

<类名>(参数)——调用类会创建一个对象,(注意括号)

和函数调用不同,函数会返回一个值,类调用,会创建一个对象的实例,一般把这个对象实例的数据对象赋值给一个变量
实例化类

obj=<类名>(参数)——返回一个对象实例

使用点(.)操作符来调用对象里的方法

案例:
1、

class Force:
    def __init__(self,x,y):
        self.fx=x
        self.fy=y
    def show(self):
        print(self.fx,self.fy)
    def add(self,Force2):
        x=self.fx+Force2.fx
        y=self.fy+Force2.fy
        return Force(x,y)
    def __rmul__(self,n):
        
        x,y=self.fx*n,self.fy*n#两个力对象是没有办法乘的,但可以乘一个数
        
        return Force(x,y)
    def __eq__(self,Force2):
        return (self.fx==self.Force2.fx) and (self.fy==self.Force2.fy)

      
f1=Force(3,4)
f1.show()
f2=Force(5,6)
f3=f1.add (f2)

f4=3*f1#使用特殊方法的便利(因为特殊方法rmul为左乘,即把n当作左参数传进去参与运算)
f4.show()
f6=(1,2)
f7=(4,5)
print(f6==f7)

2、

#定义一个空类
class Stu:
    pass
#再定义一个类
class Py:
    #用None给不确定的值赋值
    name=None
    age=18
    coruse=99
    def home(self):
        print("ofidsfnoofwe0")
        return None

yue=Py()
yue.home()
print(yue.name)

3、定义一个类,以所在city进行排列,若搬家显示出所住地址

class People:
    #各def不对齐会发生程序报错
    def __init__(self,name,city):
        self.name,self.city=name,city
    def __lt__(self,other):
        return self.city<other.city
    def show(self):
        return print(self.name,"现居住地",self.city)
    
    def moveto(self,newcity):
        name=newcity.name
        city=newcity.city
        return People(name,city)
    def __str__(self):
        return "(%s,%s)" % (self.name,self.city)
    __repr__=__str__
s=list()
s.append(People("tom","上海"))
s.append(People("dim","北京"))
s.append(People("fim","菏泽"))
s.append(People("kim","济南"))
print(s)
s.sort()
print(s)
#类方法的调用
f1=People("tom","上海")
f2=People("tom","淄博")
f3=f1.moveto(f2)
f3.show()

关于self

self在对象的方法中表示当前对象本身,如果通过对象调用一个方法,那么该对象会自动传入到当前的第一个参数中
self并不是关键字,只是一个用于接受对象的普通参数,理论上可以用任何一个普通变量名替代
方法中有self形参的方法成为非绑定类的方法,可以通过对象访问。
没有self的是绑定类的方法

使用类名访问绑定类
<类名>.<方法>()

使用类访问绑定类的方法时,如果类方法中需要访问当前类的成员,可以通过__class__成员名来访问

#定义一个空类
class Stu:
    name="wangfei"
    age=20
    def asy():
        print(__class__.name)
        print(__class__.age)
        return None
 #使用类名访问绑定类
Stu.asy()

类定义中的特殊方法(魔术方法)

-----在类定义中实现一些特殊方法,可以方便的使用python中一些内置操作
所有特殊方法的名称以两个下划线开头可结尾

构造与解构

对象构造器:
	__init__(self,<参数表>)

对象构造器,实例化对象时调用‘
实例化对象时会自动调用这个方法,而且会把这个类调用的后边的这个参数表,把它传递给init的方法,init的方法就会自动读取这个参数表,然后同时给它赋值,赋给这个对象实例即self

析构器
_del(self,<参数表>)

当你销毁一个对象的时候,它就会被自动的调用
算术运算

算术操作符:

使用何种操作符
 __add__(self,other)              +
 __sub__(self,other)              -
 __mul__(self,other)              *
 __div__(self,other)               /

是括号中两个引号引用属性。
other作为左操作数传进来被调用
调用时 如:self.fx+… (+)加号右边就会当作other传进来

#定义一个空类
class Stu:
    def __init__(self,shu1,shu2):
        self.shu1=shu1
        self.shu2=shu2
    def __add__(self,other):
        shu1=self.shu1+other.shu1
        shu2=self.shu2+other.shu2
        return Stu(shu1,shu2)
    def show(self):
        return print("(%d,%d)"%(self.shu1,self.shu2))
f1=Stu(1,2)
f2=Stu(1,4)
#这是调用特殊方法的便利,具体见上述案例1,可见没调用时的调用方法
f3=f1+f2
f3.show()

上述的加减乘除,进行的为右运算,即other被当作右操作符传进去的。若想进行反运算在其前加一个"r"即可。
如果我们定义add的话若想用f1+f2,则__add__=add 即可

#定义一个空类
class Stu:
    
    def __init__(self,shu1,shu2):
        self.shu1=shu1
        self.shu2=shu2
    def show(self):
        return print("(%d,%d)"%(self.shu1,self.shu2))
    def __add__(self,other):
        shu1=self.shu1+other.shu1
        shu2=self.shu2+other.shu2
        return Stu(shu1,shu2)
    def __rmul__(self,n):
        shu1,shu2=self.shu1*n,self.shu2*n
        return Stu(shu1,shu2)
'''f1=Stu(1,2)
f2=Stu(1,4)
#这是调用特殊方法的便利,具体见上述案例1,可见没调用时的调用方法
f3=f2+f1
f3.show()
'''
f1=Stu(1,2)
f2=6*f1
f2.show()

大小比较

__eq__(self,other)			    使用==操作符
__ne__(self,other)  			使用!=操作符
__gt__(self,other)   		    使用>操作符
__ge__(self,other)  			使用>=操作符
__lt__(self,other)    			使用<操作符
__le__(self,other)   			使用<=操作符
#定义一个空类
class Stu:
    
    def __init__(self,shu1,shu2):
        self.shu1=shu1
        self.shu2=shu2
    def show(self):
        return print("(%d,%d)"%(self.shu1,self.shu2))
    def __add__(self,other):
        shu1=self.shu1+other.shu1
        shu2=self.shu2+other.shu2
        return Stu(shu1,shu2)
    def __rmul__(self,n):
        shu1,shu2=self.shu1*n,self.shu2*n
        return Stu(shu1,shu2)
    def __eq__(self,se):
        return print((self.shu1==se.shu1) or (self.shu2==se.shu2))
f1=Stu(7,8)
f2=Stu(7,9)
f1==f2

其它特殊操作

__str__(self)  ——自动转换为字符
     __repr__(self)  ——返回一个用来表示对象的字符串
     __len__(self)  ——返回元素个数
     __new__(self):对象实例化方法,此函数较特殊,一般不需要使用
     __call__:对象当函数使用时触发
     __getattr__:访问一个不存在的属性触发
     __setattr__:对成员属性进行设置的时候触发。参数;self用来获取当前对象。
     另一个参数为:被设置的属性名称,以字符串形式出现。
     第三个参数:需要对属性名称设置的值。作用:进行属性设置的时候进行验证或者修改
class Per:
    def __init__(self):
        pass
    def __setattr__(self,name,value):
        print("设置属性:{}".format(name))
        #下面语句会导致问题,死循环
        self.name=value
    def __getattr__(self):
        print("meiyou")
p=Per()
print(p.__dict__)
p.age=18
class Per:
    def __call__(self):
        print("您好")
    def __getattr__(self):
    	print("没有这个属性")
p1=Per()
p1()
p1.add

自定义对象的排序

列表排序(注意列表是可变的序列)
sort()方法——对原列表进行排序,改变原列表内容
如果列表元素是数字,默认为升序排列,若想降序排列添加参数
添加:reverse=True——降序

格式:list.sort(reverse=True)

如果元素是字符串,则会按照字母表顺序排列(首字母)
sorted()方法——引起对象的重排(不会改变原列表内容)

格式:变量名=sorted(原列表)

只有当列表中所有元素都是同一种类型时,sort()和sorted()才会正常工作

num=[1,5,3,6,2,5]
num.sort(reverse=True)
print(num)
s=sorted(num)
print(s)
print(num)

特殊方法:

__lt__

由于python的可扩展性,每种数据类型可定义特殊方法

def __lt__(self,y):

self:实例化对象
y:用于比较的另一个对象

若返回True视为比y小,排在前。返回False则反之。
只要类定义了特殊方法,任何自定义类都可以使用x<y这样的比较

对于 lt这个特殊方法,里定义时有内置的sort方法可以直接使用

#例子 student
class Student:

    def __init__(self,name,grade):
        self.name,self.grade=name,grade
#内置sort函数只引用<比较符判断前后
    def __lt__(self,other):
        return self.grade>other.grade
#student的易读字符串表示
    def __str__(self):
        return "(%s,%d)" % (self.name,self.grade)
    __repr__=__str__
#构造一个列表,加入sudent对象

#一直错误原因是因为类名没有大写,导致后面一直出错
s=list()
#添加Student对象到列表中(用法)
#append()的方法用于在列表末尾添加一个元素
s.append(Student("Tob",79))
s.append(Student("Dc",94))
s.append(Student("Fv",82))
s.append(Student("Gi",64))
s.append(Student("Qt",99))
print(s)
#对list进行排序,注意这是内置sort方法
s.sort()
#正因为调用了Sudent类所以对这个列表中的成绩进行了排序
print(s)
#也可以调用Student类对名字进行按名字顺序进行排序

类的继承

类的继承机制
继承(inheritance):如果一个类A继承自另一个类B,把A称为子类,B称为父类、超类等。
1、所谓的继承,即把父类所拥有的属性和方法,全都在子类当中,并不需要重复写,就能在子类自动拥有。
2、子类一旦继承父类,则可以使用父类中除私有成员的所有内容
3、子类继承父类后并没有完全赋值到子类中,而是通过引用关系访问调用
4、子类中可以单独定义多有的属性和方法
5、子类中定义的成员和父类相同,优先使用子类

class Per:
    name="wangfei"
    age=18
    def sleep(self):
        print("aini")
class Tea(Per):
    pass
t=Tea()
print(t.name)

代码复用:利用继承可以从已有类中衍生出新的类,添加或修改部分功能。新类具有旧类的各种属性和方法,而不需要任何的复制。

类继承格式;class <类名>:
<一些定义的方法>
class <子类名>(父类民):
<新定义的方法或对父类的修改>
所继承的子类拥有父类全部的属性和方法,子类不需要把父类中的代码复制下来,子类中的代码可能是添加或对父类的修改

子类与父类
定义:如果两个类具有一般特殊的关系,那么特殊类就可以作为一般类的父类。
覆盖:子类对象可以调用父类方法,除非这个方法在子类中重新定义了
如果子类中定义的成员和父类相同,优先使用子类
如果子类同名方法覆盖了父类的方法,任然可以调用父类的方法

子类可以添加父类中没有的方法——super()方法
通常要给子类更多的属性(即__init__(self,原参,新参)——增加一些新的参数)。
方法:

#以下是在子类中的操作
def __init__(self,原参数,新参数):
	super().__init__(原参数)
	#也可以按照下面书写
	#super(self,原参数).__init__()
	self.新参=新参 #类似self.fx=fx

super()方法:在子类调用父类的方法之一
无super()时在子类中调用父类,需要在子类中重新初始化。
该方法的好处:当你父类的名字改变之后,只需要在()中把继承的名字改一下即可。
使用super()方法,调用父类,因为在子类中已经把父类中的属性和方法已经覆盖了,所以需要重新定义。
super不是一个关键字而是一个类

class animal:
    def __init__(self,name,sex,leg):
        self.name = name
        self.sex = sex
        self.leg = leg
    def eat(self,food):
        print('%s likes to eat %s'%(self.name,food))
class cat(animal):  #cat类继承animal类
    def __init__(self,name,sex,leg,tail):  #定义初始化构造函数,但是比父类多一个参数
        #animal.__init__(self,name,sex,leg)
        super().__init__(name,sex,leg)  #调用父类的初始化构造函数
        self.tail=tail
        print('cat has %s legs,and %s tail'%(self.leg,self.tail))
    def eat(self,food):  #定义eat函数,但是增加一行输出
        #animal.eat(self,food)
        super().eat(food)  #调用父类的方法
        print('%s also likes to eat %s' % (self.name, food))

cat1=cat('cat1','male',4,1)
cat1.eat('mouse')
'''
#案例2
class People:
    #各def不对齐会发生程序报错
    def __init__(self,name,city):
        self.name,self.city=name,city
    def __lt__(self,other):
        return self.city<other.city
    def show(self):
        return print(self.name,"现居住地",self.city)
    
    def moveto(self,newcity):
        name=newcity.name
        city=newcity.city
        return People(name,city)
    def __str__(self):
        return "(%s,%s)" % (self.name,self.city)
    __repr__=__str__
s=list()
s.append(People("tom","上海"))
s.append(People("dim","北京"))
s.append(People("fim","菏泽"))
s.append(People("kim","济南"))
print(s)
s.sort()
print(s)
#类方法的调用
f1=People("tom","上海")
f2=People("tom","淄博")
f3=f1.moveto(f2)
f3.show()
#类的继承与super()方法的应用
class Teacher(People):
    def __init__(self,name,city,school):
        super().__init__(name,city)
        self.school=school
    def moveto(self,newschool):
        name=newschool.name
        city=newschool.city
        school=newschool.school
        return Teacher(name,city,newschool)
    
    def show(self):
        super().show()
        #调用父类的方法
        return print(self.name,self.city,self.school)
    
    def __lt__(self,other):
        return self.school<other.school
    def __str__(self):
        return "(%s,%s,%s)" % (self.name,self.city,self.school)
    __repr__=__str__
#生成一个list    
t=list()
t.append(Teacher("WANG","lu","山大"))
t.append(Teacher("LI","he","郑大"))
t.append(Teacher("YAN","yu","内大"))
t.append(Teacher("ZHANG","hei","清华"))
#内置sort()排序
t.sort()
print(t)
f7=Teacher("WANG","lu","山大")
f8=Teacher("WANG","hei","黑大")
f9=f7.moveto(f8)
f9.show()
'''

子类如果想扩充父类的方法,可以在定义新方法的同时访问父类成员来进行代码重用,可以使用:(父类名.父类成员)此格式来调用父类成员,也可以使用(super().父类成员)进行调用
继承变量函数的查找顺序问题

优先查找自己的变量
没有查找自己的父类
构造函数如果本类中没有定义,则自动查找调用父类的构造函数,如果本类中有定义,则不再进行向上查找

构造函数
定义:是一类的特殊的函数,在类进行实例化之前进行调用
所谓的构造函数即前所述的初始化
实例化的时候,括号内的参数要和构造函数的参数一样
构造函数的调用顺序:如果子类没有写构造函数,则自动向上查找,直到找到位置

单继承和多继承

**单继承:**每个类只能继承一个类
**多继承:**每个类允许继承多个类
单继承和多继承优缺点:
单继承——传承有序逻辑清晰语法简单隐患少,但功能不能无限扩展,只能在当前唯一的继承链中扩展
多继承——类的功能方便扩展,但继承关系混乱
菱形继承/钻石继承问题(简单了解即可):——多个子类继承自同一个父类,这些子类又被同一个类继承,因此形成一个菱形
子类永远在父类前面
如果多个父类,则根据继承语法中括号内类的书写顺序存放
如果多个类继承了同一个父类,孙子类中只会选取继承语法括号中第一个父类的父类
多继承时,相对于使用类名.__init__方法,要把每一个父类全部写一遍,而super()只用一句话,执行了全部父类的方法,这也是为何多继承需要全部传参的一个原因
2、在python3中,有一种处理多继承的方法,叫c3算法,用来确保每一个类只调用一次的算法。使用__mro__就可以直观的看到算法规定的顺序

类名.__mro__

多态

多态就是同一个对象在不同情况下有不同的状态出现
多态不是语法,是一种设计思想
多态:同一事物的多种形态,动物分为人类,狗类,猪类
多态和多态性(可见博客园)
mixin设计模式
主要采用多继承方式对类的功能进行扩展
使用多继承语法来实现Mixin
使用Mixin实现多继承的时候必须小心

  1. 首先必须表示某一单一的功能,而不是某个物品
  2. 职责必须单一,如果由多个功能,则写多个Mixin
  3. Mixin不能依赖于子类实现
  4. 子类即使没有继承这个Mixin类,也能照样工作,只是缺少个某个工作

优点

  1. 使用Mixin可以在不对类进行任何修改的情况下,扩充功能
  2. 可以方便的组织和维护不同功能组件的划份
  3. 可以根据需要任意调整功能类的组合
  4. 可以避免创建很多的新类,导致类的继承混乱
class Per:
    name="wang fei"
    age=18
    def eat(self):
        print("hei l love ")
    def dink(self):
        print("kele")
    def sleep(self):
        print("sss")
class Tea(Per):
    def work(self):
        print("work")
class Stu(Per):
    def study(self):
        print("faye w")
class Tutor(Tea,Stu):
    pass
t=Tutor()
print(Tutor.__mro__)
print(t.__dict__)
print(Tutor.__dict__)
'''
以下为mro列表
(<class '__main__.Tutor'>, <class '__main__.Tea'>, <class '__main__.Stu'>, <class '__main__.Per'>, <class 'object'>)
{}
'''

class TeaMixin():
    def work(self):
        print("work")
class StuMixin(Per):
    def study(self):
        print("faye w")
class TutorM(Tea,Stu):
    pass
p=TutorM()
print(TutorM.__mro__)
print(p.__dict__)
print(TutorM.__dict__)

类相关函数

issubclass(子类名,父类名)——检测一个类是否是另一个类的子类

isinstance(对象实例,类名)——检测一个对象是否是一个类的实例

hasattr(对象实例,成员)——检测一个对象是否有成员XXX

getattr()
setattr()
delattr()
dir()——获取对象的成员列表
可以同通过——help()进行查找其用法

class A:
    name="Faye W"
    pass
class B(A):
    pass
class C:
    pass
a=A()
print(issubclass(B,A))
print(issubclass(C,object))
print(isinstance(a,A))
print(hasattr(a,"name"))
print(help(setattr))
dir(A)

类的成员描述符

类的成员描述符是为了在类中对类的成员属性进行相关操作而创建的一种方式
具体可见下方案例

#属性案例
#创建Student类,描述学生类
#学生具有Student.name属性
#但name格式并不统一
class Stu():
    def __init__(self,name,age):
        self.name,self.age=name,age
        #如果不想修改代码(这里党的代码是指下方调用的代码)——这是关键之处
        self.setname(name)
    def intro(self):
        print("Hai my name is {}".format(self.name))
    def setname(self,name):
        self.name=name.upper()
s1=Stu("wang fei",19)
s2=Stu("wang",22)
s1.intro()
s2.intro()
  1. get;获取属性的操作
  2. set:修改或添加属性的操作
  3. delete:删除属性的操作

如果想使用类的成员描述符有三种方法
4. 使用类实现描述器
5. 使用属性描述符
6. 使用property函数(主要对此方法进行概述)
property函数很简单:

property的四个参数的顺序是固定的

  1. 第一个代表读取的时候调用的参数
  2. 第二个代表写入时调用
  3. 第三个代表删除时调用
    具体见某度君
#x=property(fget,fset,fdel,doc)
class Per:
	#此功能,是对类变量进行读取操作的时候应该执行的功能
    def fget(self):
        return self._name * 2#复制两个
     #模拟的是对变量进行写操作的时候执行的功能
    def fset (self,name):
        self._name=name.upper()
     #模拟的是删除变量的时候进行的操作
    def fdel(self):
        set._name="noname"
    name=property(fget,fset,fdel,"对name进行下列操作")
p1=Per()
p1.name="wang fei "name调用的为上面那个赋值语句的name
print(p1.name)

属性的三种用法

  1. 赋值
  2. 读取
  3. 删除

无论是哪种修饰符都是为了对成员属性进行相应的控制
类的方式:适合多个类中的多个属性共用一个描述符
property:适用当前类中使用,可以控制一个类中的多个属性
属性修饰符:适用于当前类中使用,控制一个类中的一个属性

类的内置属性

'''
__dict__:以字典的方式显示类的成员组成
__doc__:获取类的文档信息
__name__:获取类的名称,如果在模块中使用,获取模块的名称
__bases__:获取某个类的所有父类,以元组的方式显示

案例:

class Per:
    '''
     这是一个文档
     '''
    def fget(self):
        return self._name * 2#复制两个
    def fset (self,name):
        self._name=name.upper()
    def fdel(self):
        set._name="noname"
    name=property(fget,fset,fdel,"对name进行下列操作")
p1=Per()
p1.name="wang fei "
print(p1.name)
print(Per.__dict__)
print(Per.__doc__)
print(Per.__name__)
print(Per.__bases__)