面向对象的三大特性

面向对象的三大特性:继承、多态、封装

一、继承

1、什么是继承?

继承是一种创建新类的方式,在python中,新建的类可以继承一个或多个父类,父类又可称为基类或超类,新建的类称为派生类或子类。

python中类的继承分为:单继承和多继承。

class A:pass   # 父类,基类,超类
class B:pass   # 父类,基类,超类
class A_son(A,B):pass # 子类,派生类
class AB_son(A,B):pass # 子类,派生类

注:一个类可以被多个类继承;

       一个类可以继承多个类。

2、查看继承

class A:pass   
class B:pass   
class A_son(A,B):pass 
class AB_son(A,B):pass 

print(A_son.__base__)  #__base__只查看从左到右继承的第一个子类,
#(<class '__main__.A'>, <class '__main__.B'>)
print(AB_son.__bases__)  #__bases__则是查看所有继承的父类
#(<class '__main__.A'>, <class '__main__.B'>)
print(A.__bases__)  #python的类会默认继承object类
#(<class 'object'>,)

注:如果没有指定基类,python的类会默认继承object类,object是所有python类的基类,它提供了一些常见方法(如__str__)的实现。

3、继承与抽象(先抽象再继承)

 抽象即抽取类似或者说比较像的部分。

继承:是基于抽象的结果,通过编程语言去实现它,肯定是先经历抽象这个过程,才能通过继承的方式去表达出抽象的结构。

抽象只是分析和设计的过程中,一个动作或者说一种技巧,通过抽象可以得到类。

4、继承与重用性

有两个类,类B的内容与类A大部分相同时,可以用到类的继承,让B继承A,B会继承A的所有属性(数据属性和函数属性),实现代码的重用。

class Animal:
    '''
    人和狗都是动物,所以创造一个Animal基类
    '''
    def __init__(self, name, aggressivity, life_value):
        self.name = name  # 人和狗都有自己的昵称;
        self.aggressivity = aggressivity  # 人和狗都有自己的攻击力;
        self.life_value = life_value  # 人和狗都有自己的生命值;

    def eat(self):
        print('%s is eating'%self.name)

class Dog(Animal):
    pass

class Person(Animal):
    pass

ya = Person('yaya',10,1000)
ha2 = Dog('二愣子',50,1000)
ya.eat()
ha2.eat()
重用性

5、派生

在继承中,子类也可以添加自己新的属性,也可以在不影响父类的情况下重新定义这些属性。

#人狗大战
class Animal:
    def __init__(self,name,aggr,hp):
        self.name = name
        self.aggr = aggr
        self.hp = hp

    def eat(self):
        print('吃药回血')
        self.hp+=100

class Dog(Animal):
    def __init__(self,name,aggr,hp,kind):
        Animal.__init__(self,name,aggr,hp)  #
        self.kind = kind       # 派生属性
    def eat(self):
        Animal.eat(self)   # 如果既想实现新的功能也想使用父类原本的功能,还需要在子类中再调用父类
        self.teeth = 2
    def bite(self,person):   # 派生方法
        person.hp -= self.aggr

jin = Dog('金老板',100,500,'吉娃娃')
jin.eat()
print(jin.hp)

class Person(Animal):
    def __init__(self,name,aggr,hp,sex):
        Animal.__init__(self,name,aggr,hp)
        self.sex = sex       # 派生属性
        self.money = 0       # 派生属性

    def attack(self,dog):
        dog.hp -= self.aggr

    def get_weapon(self,weapon):
        if self.money >= weapon.price:
            self.money -= weapon.price
            self.weapon = weapon
            self.aggr += weapon.aggr
        else:
            print("余额不足,请先充值")
alex = Person('alex',1,2,None)
alex.eat()
print(alex.hp)

jin.bite(alex)
print(alex.hp)
派生

注意:

1)、父类中没有的属性 在子类中出现 叫做派生属性;

2)、父类中没有的方法 在子类中出现 叫做派生方法;

3)、只要是子类的对象调用,子类中有的名字 一定用子类的,子类中没有才找父类的,如果父类也没有报错

4)、如果父类 子类都有 用子类的

              如果还想用父类的,单独调用父类的:   

                          父类名.方法名 需要自己传self参数   

                          super().方法名 不需要自己传self(只在新式类中有,python3中所有类都是新式类)

5)、正常的代码中 单继承 === 减少了代码的重复

6)、继承表达的是一种 子类是父类的关系

7)、如果子类中实现了调用父类的方法

    在类内:super(子类,self).方法名()  super().__init__(参数)

    在类外:super(子类名,对象名).方法名()

通过继承建立了派生类与基类之间的关系,它是一种'是'的关系,比如白马是马,人是动物。

当类之间有很多相同的功能,提取这些共同的功能做成基类,用继承比较好。

继承的实例

class Animal:      #父类  基类  超类
 2     def __init__(self,name,life_value,aggr):
 3         self.name= name
 4         self.life_value = life_value
 5         self.aggr = aggr  #攻击力
 6     def eat(self):
 7         self.life_value += 10  #谁调谁的血量就增加
 8 
 9 class Person(Animal):  #子类  派生类
10     def __init__(self, money, name, life_value, aggr):
11         super().__init__(name, life_value, aggr)
12         self.money = money  #派生出来的一个属性
13     def attack(self,enemy):    #人的派生方法
14         #enemy.life_value = enemy.life_value - self.aggr
15         enemy.life_value -= self.aggr
16 
17 class Dog(Animal): #子类  派生类
18     def __init__(self,name,breed, life_value,aggr):
19         # Animal.__init__(self,name,breed, life_value,aggr)#让子类执行父类的方法 就是父类名.方法名(参数),连self都得传
20         super().__init__(name,life_value,aggr) #super关键字  ,都不用传self了,在新式类里的
21         # super(Dog,self).__init__(name,life_value,aggr)  #上面super是简写
22         self.breed = breed
23     def bite(self,person):   #狗的派生方法
24         person.life_value -= self.aggr
25     def eat(self):  #父类方法的重写
26         super().eat()
27         print('dog is eating')
28 
29 # ha2 = Dog('旺财','哈士奇',20000,100)
30 # egg = Person('egon',500,1000,50)
31 # print(egg.aggr)
32 # print(ha2.aggr)
33 # egg.eat()
34 # print(egg.life_value)
35 #
36 # egg.eat()
37 # print(egg.life_value)
38 #
39 # ha2.eat()
40 # print(ha2.life_value)
41 #
42 # print(egg.life_value)
43 # ha2.bite(egg)
44 # print(egg.life_value)
45 #
46 
47 ha2 = Dog('牛头梗','旺财',20000,100)
48 print(ha2.life_value)
49 ha2.eat()  #如果父类有的方法子类里面也有,那么就叫做方法的重写,就不执行父类的了,去执行子类了
50 print(ha2.life_value)
51 
52 
53 super(Dog,ha2).eat() #生掉父类的方法,但是不推荐这样用
54 print(ha2.life_value)
55 
56 #在继承中,如果子类有的方法就执行子类的
57 # 如果子类没有的方法就执行父类的
实现调用父类的方法

6、抽象类与接口类

6.1、接口类

接口类:python原生不支持

本质:是做代码规范用的,希望在子类中实现和父类方法名字完全一样的方法。

from abc import abstractmethod,ABCMeta
class Payment(metaclass=ABCMeta):  # 元类 默认的元类 type
    @abstractmethod
    def pay(self,money):pass   # 没有实现这个方法
# 规范 :接口类或者抽象类都可以
# 接口类 支持多继承,接口类中的所有的方法都必须不能实现 —— java
# 抽象类 不支持多继承,抽象类中方法可以有一些代码的实现 —— java
class Wechat(Payment):
    def pay(self,money):
        print('已经用微信支付了%s元'%money)

class Alipay(Payment):
    def pay(self,money):
        print('已经用支付宝支付了%s元' % money)

class Applepay(Payment):
    def pay(self,money):
        print('已经用applepay支付了%s元' % money)

def pay(pay_obj,money):  # 统一支付入口
    pay_obj.pay(money)

wechat = Wechat()
ali = Alipay()
app = Applepay()
wechat.pay(100)
ali.pay(200)
接口类

接口类的多继承:

# tiger 走路 游泳
# swan 走路 游泳 飞
# oldying 走路 飞
from abc import abstractmethod,ABCMeta
class Swim_Animal(metaclass=ABCMeta):
    @abstractmethod
    def swim(self):pass

class Walk_Animal(metaclass=ABCMeta):
    @abstractmethod
    def walk(self):pass

class Fly_Animal(metaclass=ABCMeta):
    @abstractmethod
    def fly(self):pass

class Tiger(Walk_Animal,Swim_Animal):
    def walk(self):
        pass
    def swim(self):
        pass
class OldYing(Fly_Animal,Walk_Animal):pass
class Swan(Swim_Animal,Walk_Animal,Fly_Animal):pass
View Code

接口类,  刚好满足接口隔离原则 面向对象开发的思想 、规范。

6.2、抽象类

抽象类:规范

一般情况下 单继承 能实现的功能都是一样的,所以在父类中可以有一些简单的基础实现
多继承的情况 由于功能比较复杂,所以不容易抽象出相同的功能的具体实现写在父类中

利用abc模块实现抽象类

import abc #利用abc模块实现抽象类
class All_file(metaclass=abc.ABCMeta):
    all_type='file'
    @abc.abstractmethod #定义抽象方法,无需实现功能
    def read(self):
        '子类必须定义读功能'
        with open('filaname') as f:
            pass

    @abc.abstractmethod #定义抽象方法,无需实现功能
    def write(self):
        '子类必须定义写功能'
        pass

class Txt(All_file): #子类继承抽象类,但是必须定义read和write方法
    def read(self):
        print('文本数据的读取方法')
    def write(self):
        print('文本数据的读取方法')

class Sata(All_file): #子类继承抽象类,但是必须定义read和write方法
    def read(self):
        print('硬盘数据的读取方法')

    def write(self):
        print('硬盘数据的读取方法')

class Process(All_file): #子类继承抽象类,但是必须定义read和write方法
    def read(self):
        print('进程数据的读取方法')

    def write(self):
        print('进程数据的读取方法')

wenbenwenjian=Txt()    #实例化

yingpanwenjian=Sata()

jinchengwenjian=Process()

#这样大家都是被归一化了,也就是一切皆文件的思想
wenbenwenjian.read()   #文本数据的读取方法
yingpanwenjian.write()  #硬盘数据的读取方法
jinchengwenjian.read()  #进程数据的读取方法

print(wenbenwenjian.all_type)  #file
print(yingpanwenjian.all_type)  #file
print(jinchengwenjian.all_type)   #file
抽象类

补充:

抽象类还是接口类 : 面向对象的开发规范 所有的接口类和抽象类都不能实例化

java :

java里的所有类的继承都是单继承,所以抽象类完美的解决了单继承需求中的规范问题

但对于多继承的需求,由于java本身语法的不支持,所以创建了接口Interface这个概念来解决多继承的规范问题

python:
python中没有接口类 :
python中自带多继承 所以我们直接用class来实现了接口类
python中支持抽象类 : 一般情况下 单继承 不能实例化
且可以实现python代码

6.3、接口类和抽象类的区别:

在java的角度上看 是有区别的
java本来就支持单继承 所以就有了抽象类
java没有多继承 所以为了接口隔离原则,设计了接口这个概念,支持多继承了
python既支持单继承也支持多继承,所以对于接口类和抽象类的区别就不那么明显了
甚至在python中没有内置接口类

7、继承顺序

单继承:子类有的用子类,子类没有用父类;

多继承:子类的对象调用一个方法,默认是就近原则;

经典类中:深度优先

新式类中:广度优先

python2.7中:新式类与经典类共存,新式类要继承object

python3:只有新式类,默认继承object

经典类和新式类中还有一个区别:

mro方法只在新式类中存在;

super方法只在python3中新式类中存在;

super的本质:不是直接找父类,而是根据调用者的节点位置的广度优先顺序来的。

二、多态

1、什么是多态?

多态指的是一类事物有多种形态。

#动物有多种形态:人,狗,猪
import abc
class Animal(metaclass=abc.ABCMeta): #同一类事物:动物
    @abc.abstractmethod
    def talk(self):
        pass

class People(Animal): #动物的形态之一:人
    def talk(self):
        print('say hello')

class Dog(Animal): #动物的形态之二:狗
    def talk(self):
        print('say wangwang')

class Pig(Animal): #动物的形态之三:猪
    def talk(self):
        print('say aoao')
动物的多种形态
#文件有多种形态:文本文件,可执行文件
import abc
class File(metaclass=abc.ABCMeta): #同一类事物:文件
    @abc.abstractmethod
    def click(self):
        pass

class Text(File): #文件的形态之一:文本文件
    def click(self):
        print('open file')

class ExeFile(File): #文件的形态之二:可执行文件
    def click(self):
        print('execute file')
文件有多种形态

2、多态性

多态性是指在不考虑实例类型的情况之下使用实例。

peo=People()
dog=Dog()
pig=Pig()

#peo、dog、pig都是动物,只要是动物肯定有talk方法
#于是我们可以不用考虑它们三者的具体是什么类型,而直接使用
peo.talk()
dog.talk()
pig.talk()

#更进一步,我们可以定义一个统一的接口来使用
def func(obj):
    obj.talk()
多态性

3、鸭子类型

鸭子类型是指不依赖父类的情况下实现两个相似类中的同名方法。

#二者都像鸭子,二者看起来都像文件,因而就可以当文件一样去用
class TxtFile:
    def read(self):
        pass

    def write(self):
        pass

class DiskFile:
    def read(self):
        pass
    def write(self):
        pass

三、封装

1、什么是封装?

隐藏对象的属性的实现细节,仅对外提供公共访问方式。

2、封装的好处?

1)、将变化隔离;

2)、便于使用;

3)、提高复用性;

4)、提高安全性。

3、封装原则?

1)、 将不需要对外提供的内容都隐藏起来;

2)、把属性都隐藏,提供公共方法对其访问。

4、私有变量和私有方法

在python中用双下划线开头的方式将属性隐藏起来(设置成私有的)

4.1、私有变量

#其实这仅仅这是一种变形操作
#类中所有双下划线开头的名称如__x都会自动变形成:_类名__x的形式:

class A:
    __N=0 #类的数据属性就应该是共享的,但是语法上是可以把类的数据属性设置成私有的如__N,会变形为_A__N
    def __init__(self):
        self.__X=10 #变形为self._A__X
    def __foo(self): #变形为_A__foo
        print('from A')
    def bar(self):
        self.__foo() #只有在类内部才可以通过__foo的形式访问到.

#A._A__N是可以访问到的,即这种操作并不是严格意义上的限制外部访问,仅仅只是一种语法意义上的变形

自动变形的特点:

1.类中定义的__x只能在内部使用,如self.__x,引用的就是变形的结果

2.这种变形其实正是针对外部的变形,在外部是无法通过__x这个名字访问到的。

3.在子类定义的__x不会覆盖在父类定义的__x,因为子类中变形成了:_子类名__x,而父类中变形成了:_父类名__x,即双下滑线开头的属性在继承给子类时,子类是无法覆盖的。

注意:

1.这种机制也并没有真正意义上限制我们从外部直接访问属性,知道了类名和属性名就可以拼出名字:_类名__属性,然后就可以访问了,如a._A__N

2.变形的过程只在类的内部生效,在定义后的赋值操作,不会变形

4.2私有方法

在继承中,父类如果不想让子类覆盖自己的方法,可以将方法定义为私有

#正常情况
>>> class A:
...     def fa(self):
...         print('from A')
...     def test(self):
...         self.fa()
... 
>>> class B(A):
...     def fa(self):
...         print('from B')
... 
>>> b=B()
>>> b.test()
from B
 

#把fa定义成私有的,即__fa
>>> class A:
...     def __fa(self): #在定义时就变形为_A__fa
...         print('from A')
...     def test(self):
...         self.__fa() #只会与自己所在的类为准,即调用_A__fa
... 
>>> class B(A):
...     def __fa(self):
...         print('from B')
... 
>>> b=B()
>>> b.test()
from A
私有方法

5、会用到私有的这个概念的场景:

1)、隐藏起一个属性 不想让类的外部调用

2)、我想保护这个属性,不想让属性随意被改变

3)、我想保护这个属性,不被子类继承

原文地址:https://www.cnblogs.com/gaoya666/p/8313191.html