python面向对象
面向对象主要内容:
1、类的定义及实例化、对象的方法调用、属性修改
2、静态属性和动态属性
3、类组合
4、三大特性:
继承(子类、父类、super方法(属性和动态方法))
多态(数据类型和传值对应的问题) Java中的多态(多种数据类型的父类)
多继承(格式,C3算法)java中的接口类
封装(隐藏属性和方法,实例应用(如密码问题))
1、类的定义及实例化、对象的方法调用、属性修改
类和对象的定义:
类:具有共同属性和方法的一类事物;
对象:类中的一个例子(具有具体的属性值和方法);
class A: country = 'Chinese' def __init__(self,name): self.name = name def define(self): pass a = A('aname')
诠释:
__init__中定义对象的属性,实例化对象后通过 对象.属性名(如a.name)的方式调用
def 函数名(self):的方式定义方法
思考:self在各个阶段的是什么?
在类的定义中,未调用之前:形参,未传值;
实例化的过程中:将实参传给__init__的形参,执行__init__函数,将由形参传递的参数进行计算或赋值给对象的属性,此处的self表示的是实例化对象本身(因为此处在创建过程中,所以还没将对象赋值出去,所以此处只能以self暂代对象,等创建结束的时候将self赋值给要接收该对象的变量即可;
从外部调用函数的过程中:先将对象(实参)传给self(形参),在执行的过程中以self进行操作,操作完毕后再将self返还给对象。
class B: def __init__(self): self.country ='India' class A(B): country = 'Chinese' def define(self): print('define函数') a = A() print(a.country) print(A.define(a)) print(a.define())
运行结果:
India
define函数
None
define函数
None
其中像Country一样写在class A:与def__init__(self)之间的为静态变量;def 变量名(self),定义的是动态变量。
2、静态属性和动态属性
调用静态变量使用 类名.静态变量 的形式(如A.country),使用 对象.静态变量 的形式也可以调用静态变量(在对象本身没有同名属性的情况下)
调用对象属性 对象.对象属性名 的形式(如a.name),当对象的属性不存在的时候调用父类的属性,如果父类属性中没有,则调用本类的静态属性;
调用对象动态方法 对象.动态方法名 的形式(如a.define),如果对象中没有,则找父类当中的方法;
问题:能不能通过类名能不能调用类中的动态属性(即方法)?可以调用,但要注意给self传对象进去,默认返回值为None
在一个类中,通过self调用自己对象的属性和动态方法,通过类名或对象名调用其他类的属性或方法(调用父类时可使用super()的方法以省略父类的名称)
class Dog: lst = ['',''] #在类的静态变量部分定义一个空列表,用来记录类的两个情况:第一个实例化的对象,最后一个实例化的对象 def __init__(self,name): self.name = name xiaohua = Dog('xiaohua') xiaohua.lst[0] = 'xiaohua' xiaohua.lst[1] ='xiaohua' #结果正确,给list中的某个索引位赋值,改变的是索引位指向的物理地址,并不改变list本身指向的物理地址,python默认没有通过对象改变类的属性,所以不会在对象空间中重新开辟空间 print(Dog.lst) xiaobai =Dog('xiaobai') xiaobai.lst = ['xiaohua','xiaobai']#错误,用对象调用类的静态属性并试图改变静态属性的物理指向,会在对象的空间中增加新的属性 print(Dog.lst,xiaobai.lst) 运行结果: ['xiaohua', 'xiaohua'] ['xiaohua', 'xiaohua'] ['xiaohua', 'xiaobai']
静态属性的改变要慎重,尽量使用类来调用,以免通过对象调用试图改变静态属性物理指向时,在对象的空间中增加新的属性而不真正改变类的静态属性
3、类组合
组合的定义:将一个类对象赋值给另一个类的对象,成为这个对象的属性值
class Circle: #圆类 def __init__(self,r): self.r = r def area(self):#求圆的面积 return 3.14*self.r**2 class Ring: #环类 def __init__(self,bigr,smallr): self.outc = Circle(bigr) #环类对象的属性是一个圆类对象,这就是组合 self.inc = Circle(smallr) def area(self): return self.outc.area() -self.inc.area() ring_1=Ring(10,5) print(ring_1.area()) 运行结果: 235.5
上面的例子中环类对象的属性是一个圆类对象,这就是组合。通过组合,就不需要反复输入pi*r**2计算圆的面积了,简化了代码
4、三大特性:
继承(子类、父类、super方法(属性和动态方法))
多态(数据类型和传值对应的问题) Java中的多态(多种数据类型的父类)
多继承(格式,C3算法)java中的接口类 C3算法放在最后写,最好是能从网上搜出源代码看一下
封装(隐藏属性和方法,实例应用(如密码问题))
继承:
class Person: def __init__(self,name,sex,age): self.name = name self.sex = sex self.age = age def eating(self,foodname): return '%s is eating %s'%(self.name,foodname) class Teacher(Person): def __init__(self,name,sex,age,curriculum): # super(Teacher,self).__init__(name,sex,age) #完整形式 super().__init__(name,sex,age) #简便形式(python3中才有) self.curricuclum =curriculum def teaching(self): return '%s is teaching %s'%(self.name,self.curricuclum) teacha = Teacher('teacha','male',30,'语文') print(teacha.__dict__) print(teacha.eating('bread')) #通过继承,调用父类中的方法 print(teacha.teaching() ) #自己类中有调用的方法时,优先使用自己类中的方法 运行结果: {'name': 'teacha', 'sex': 'male', 'age': 30, 'curricuclum': '语文'} teacha is eating bread teacha is teaching 语文
继承的定义和用途:当存在多个属性和方法相近的类时,将共同属性和方法放到抽象出来的父类中,各子类通过 class 子类名(父类名) 的方式继承父类(如Teacher(Person)),以减少代码的重复、冗余;
子类可以继承父类的属性,也可以继承父类的方法,调用父类属性的方法是super().__init__(实参),子类对象调用方法时优先从自己的类中寻找,找不到时通过类指针查找父类中的方法;
在子类方法中调用父类方法可使用 super().父类方法名(实参) 的方式;
多态
from abc import ABCMeta,abstractmethod class Person(metaclass=ABCMeta): def __init__(self,name): self.name = name @abstractmethod def eating(self,foodname):pass class Teacher(Person): def __init__(self,name,curriculum): super().__init__(name) self.curricuclum =curriculum def eating(self,foodname): return '%s is eating %s'%(self.name,foodname) class Student(Person): def __init__(self,name,learing): super().__init__(name) self.learing =learing def eating(self,foodname): return '%s is eating %s'%(self.name,foodname) class Crew(Person): def __init__(self,name,job): super().__init__(name) self.job =job def eating(self,foodname): return '%s is eating %s'%(self.name,foodname) teacha = Teacher('teacha','语文') studa = Student('studa','VB') crewa = Crew('crewa','打菜阿姨') print(teacha.eating('bread')) print(studa .eating('apple')) print(crewa .eating('banana')) 运行结果: teacha is eating bread studa is eating apple crewa is eating banana
多态是指属于同一父类的对象可以分属不同的子类,如上边的实例代码中,teacha、studa、crewa等子类对象同属于Person这一父类,此处要注意Person这一父类的元类是ABCMethod,且需要子类实现的共同方法eating之前要加@abstractmethod
python崇尚鸭子类型:假设实例代码中的Person为鸭子类型,那么就eating方法而言,teacha、studa、crewa都属于鸭子类型,因为他们跟鸭子类型(Person)一样,均可实现eating方法。
对于强类型语言(如java)中多态的实现方法是,创建父类,将子类对象定义为父类类型,这样就不要区分这个对象具体属于那个类,只要知道它能实现需要实现的方法就行
封装
将一些属性(如用户的密码)通过一定方法转换,使其只能从类的内部调用,但不能从类的外部调用,就称为封装
class Student(): def __init__(self,name,pwd): self.name = name self.__pwd = pwd def checkpwd(self): print(self.__pwd ) studa = Student('studa',123) print(studa.name) studa.checkpwd() 运行结果: studa 123
此处不能载使用studa.__pwd (一般查看属性的方式)查看用户密码
上述示例代码中的__pwd属性就是对属性的封装,同样的以可以使用双下划线开头的方法对方法进行封装,都能实现只能从内部调用,不能从外部调用的效果;
多继承、抽象类、接口类
class A(): def __init__(self,name): self.name = name def amethod(self): print('amethod') class B(A): def __init__(self,name): self.name = name def bmethod(self): print('bmethod') class C(A): def __init__(self,name): self.name = name def cmethod(self): print('cmethod') class D(B,C): def __init__(self,name): self.name = name def dmethod(self): print('dmethod') a=A('a') b=B('b') c=C('c') d=D('d') print(a.name,b.name,c.name,d.name) print(D.mro()) #or D.__mro__() 结果显示: a b c d [<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
多继承是指一个类继承多个类,如上述示例代码中的D类,同时继承了B类和C类;
通过 类名.mro() 方法查看多继承的顺序,在python3中遵循广度优先的原则,本例中D类向上查找父类的顺序是D-B-C-A,在python2中经典类遵循深度优先的原则,新式类(继承object)的类遵循与python3一样广度优先的原则;
抽象类
抽象类就像前边多态中的元类(Person类),自己不能实例化,只是用来规范子类的属性和方法;
java 不支持多继承
python 支持多继承 :通过抽象类的多继承来实现复杂的规范
from abc import ABCMeta,abstractmethod class Fly_Animal(metaclass=ABCMeta): @abstractmethod def fly(self): print('爷会飞') class Swim_Animal(metaclass=ABCMeta): @abstractmethod def swim(self): pass class Walk_Animal(metaclass=ABCMeta): @abstractmethod def walk(self): pass class Swan(Fly_Animal,Swim_Animal,Walk_Animal): def fly(self): super().fly() print('飞') def walk(self):print('走') def swim(self):print('游') class Tiger(Walk_Animal,Swim_Animal): def walk(self):print('走') def swim(self):print('游') class Parrot(Fly_Animal,Walk_Animal): def fly(self):print('飞') def walk(self):print('走') def talk(self):print('说')
java不支持多继承,新的概念 接口 Interface。
java中接口类和抽象类几乎一模一样的功能 :
只定义一个接口名字(基类名),内部定义子类必须实现的方法
接口支持多继承
接口内部的所有方法都不能写具体的代码,只能用pass代替
python中由于有了抽象类的多继承,不需要接口的概念了;
抽象类一般和归一化统一使用,通过抽象类来规范子类的方法,再通过函数统一调用;
私有属性和继承
封装(私有属性和私有方法)能否被继承?
class A: def __init__(self,name): self.__name = name def __show_name(self): return self.__name class B(A): def showname(self): print('到这一步了') # self.__show_name() b = B('lv') # b.showname() #AttributeError: 'B' object has no attribute '_B__show_name' b.showname() 执行结果: 到这一步了
私有属性和私有方法不能被子类继承,本质原因是在定义私有属性和私有方法的时候会自动将 __属性(方法)转化为_类名__属性(方法)
面试题:
情况一: class A: def __init__(self): self.__show_name() #实例化b2时执行 def __show_name(self): print('in A __show_name') class B(A): def __init__(self): if example1 == True: self.__show_name() #实例化b1时执行 else: super().__init__() def __show_name(self): print('in B __show_name') example1 = True b1=B() example1 = False b2=B() 情况二: class A: def __init__(self): self.show_name() #实例化b4时执行 def show_name(self): print('in A __show_name') class B(A): def __init__(self): if example1 == True: self.show_name() #实例化b3时执行 else: super().__init__() def show_name(self): print('in B __show_name') example1 = True b3=B() example1 = False b4=B() 执行结果: in B __show_name in A __show_name in B __show_name in B __show_name
通过情况一中b2和和情况二b4实例化的结果对比可以看出,调用私有属性时,在哪个类中调用就执行那个类中的对应方法,而普通方法优先执行的是实例化对象所在的对象(遵循类的寻址原则),总结起来就是实例化过程中本类没有__init__方法,会通过类指针寻找父类中__init__方法执行,如果父类__init__调用私有方法,则在自己本类中找寻,如果没有则报错;如果父类__init__方法找寻的普通方法,则优先在本类中寻找,没有再在父类中找寻;
上述实例代码中,本质上说实例话过程中都是先调用B类中的__init__,没有则调用父类__init__方法,调用show_name或__show_name方法时也都是从B类开始寻找,只不过__show_name方法在定义时已经变异成 _类名__show_name,故只能在调用的类中找到,所以才有私有方法在哪个类中调用执行哪个类中的方法,没有则报错。