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,故只能在调用的类中找到,所以才有私有方法在哪个类中调用执行哪个类中的方法,没有则报错。
 

 

 
 
 
 
 
 
posted @ 2018-11-25 13:01  海予心  阅读(169)  评论(0编辑  收藏  举报