Python20-Day07

面向对象之继承与派生

什么是继承?

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

  子类会‘遗传’父类的特性,从而解决代码重用问题

python中分为单继承和多继承

  

class ParentClass1: #定义父类
    pass

class ParentClass2: #定义父类
    pass

class SubClass1(ParentClass1): #单继承,父类是ParentClass1,子类是SubClass
    pass

class SubClass2(ParentClass1,ParentClass2): #python支持多继承,用逗号分隔开多个继承的类
    pass

 查看继承

#print(SubClass1.__bases__)
(<class '__main__.ParentClass1'>,)


#print(SubClass2.__bases__)
(<class '__main__.ParentClass1'>, <class '__main__.ParentClass2'>)

经典类与新式类

1、只有python2中才分为经典类和新式类,python3中都是新式类

2、在python2中没有标明继承object类的类,以及该类的子类,都称为经典类

3、在python2中标明继承object类的类,以及该类的子类,称为新式类

4、python3中,无论是否继承object类,默认都继承object类,即python3中都为新式类

 

继承与抽象

继承描述的是子类与父类之间的关系,是什么是什么的关系,要找出这种关系,必须先抽象再继承

属性查找

class Foo:
    def f1(self):
        print('Foo.f1')

    def f2(self):
        print('Foo.f2')
        self.f1()

class Bar(Foo):
    def f1(self):
        print('Foo.f1')


b=Bar()
b.f2()

#得到的结果为:

 Foo.f2
 Foo.f1

 

派生

子类也可以添加自己新的属性或者自己重新定义属性。一旦重新定义了自己的属性且与父类重名,调用新增的属性时,就以自己的为准了。

class ParentClass:
    def __init__(self,name,age,sex):
        self.name = name
        self.age = age
        self.sex = sex
    def tell_info(self):
        print('%s is in old_class!' %self.name)
class SubClass(ParentClass):
    def __init__(self,name,age,sex):
        ParentClass.__init__(self,name,age,sex)
    # def tell_info(self):
    #     print('%s is in subclass!' %self.name)

stu1 = SubClass('zly',25,'female')
stu1.tell_info()

#此时调用结果为:
zly is in old_class!

#如果在SubClass子类中也定义tell_info属性,此时调用结果为:
zly is in subclass!

 

组合与重用性

软件重用的重要方式除了继承之外还有另外一种:组合

组合指的是:在一个类中以另外一个类的对象作为数据属性,称为类的组合

继承:

通过继承建立了派生类与基类之间的关系,是一种什么是什么的关系。

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

组合:

用组合的方式建立了类与组合的类之间的关系,是一种有的关系。

class People:
    def __init__(self,name,age,sex):
        self.name=name
        self.age=age
        self.sex=sex

class Course:
    def __init__(self,name,period,price):
        self.name=name
        self.period=period
        self.price=price
    def tell_info(self):
        print('<%s %s %s>' %(self.name,self.period,self.price))

class Teacher(People):
    def __init__(self,name,age,sex,job_title):
        People.__init__(self,name,age,sex)
        self.job_title=job_title
        self.course=[]
        self.students=[]


class Student(People):
    def __init__(self,name,age,sex):
        People.__init__(self,name,age,sex)
        self.course=[]


egon=Teacher('egon',18,'male','沙河霸道金牌讲师')
s1=Student('牛榴弹',18,'female')

python=Course('python','3mons',3000.0)
linux=Course('python','3mons',3000.0)

#为老师egon和学生s1添加课程
egon.course.append(python)
egon.course.append(linux)
s1.course.append(python)

#为老师egon添加学生s1
egon.students.append(s1)


#使用
for obj in egon.course:
    obj.tell_info()

 

抽象类

抽象类是一个特殊的类,它的特殊之处在于只能被继承,不能被实例化

从设计的角度看,如果类是从现实对象抽象而来的,那么抽象类就是基于类抽象来的。

抽象类中只有抽象方法,没有实现功能,该类不能被实例化,只能被继承,且子类必须实现抽象方法。

#_*_coding:utf-8_*_
__author__ = 'Linhaifeng'
#一切皆文件
import abc #利用abc模块实现抽象类

class All_file(metaclass=abc.ABCMeta):
    all_type='file'
    @abc.abstractmethod #定义抽象方法,无需实现功能
    def read(self):
        '子类必须定义读功能'
        pass

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

# class Txt(All_file):
#     pass
#
# t1=Txt() #报错,子类没有定义抽象方法

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)
print(yingpanwenjian.all_type)
print(jinchengwenjian.all_type)

 

继承实现的原理

1、继承顺序

  如果继承关系为非菱形结果,则会按照先找B这一条分支,然后再找C这一条分支,最后找D这一条分支,

  如果继承关系为菱形结构,属性查找:深度优先和广度优先。

2、继承原理

  定义的每一个类,python会计算出一个方法解析顺序(MRO)列表,这个MRO列表就是一个简单的所有基类的线性顺序列表。

  所有父类的MRO列表遵循以下3条原则:

  1、子类会先与父类被检查

  2、多个父类会根据他们在列表中的顺序被检查

  3、如果对下一个类存在两个合法的选择,选择第一个父类

 

面向对象之封装

如何隐藏?

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

#其实这仅仅这是一种变形操作且仅仅只在类定义阶段发生变形
#类中所有双下划线开头的名称如__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是可以访问到的,
#这种,在外部是无法通过__x这个名字访问到。

 

变形的过程只在类的定义时发生一次,在定义后的赋值操作,不会变形

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

#正常情况
>>> 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

 

封装数据:

  将数据隐藏起来,然后对外提供操作该数据的接口,然后可以在接口附加上对该数据操作的限制,以此完成对数据属性操作的严格限制。

class Teacher:
    def __init__(self,name,age):
        # self.__name=name
        # self.__age=age
        self.set_info(name,age)

    def tell_info(self):
        print('姓名:%s,年龄:%s' %(self.__name,self.__age))
    def set_info(self,name,age):
        if not isinstance(name,str):
            raise TypeError('姓名必须是字符串类型')
        if not isinstance(age,int):
            raise TypeError('年龄必须是整型')
        self.__name=name
        self.__age=age


t=Teacher('egon',18)
t.tell_info()

t.set_info('egon',19)
t.tell_info()

 

封装方法:目的是隔离复杂度

#取款是功能,而这个功能有很多功能组成:插卡、密码认证、输入金额、打印账单、取钱
#对使用者来说,只需要知道取款这个功能即可,其余功能我们都可以隐藏起来,很明显这么做
#隔离了复杂度,同时也提升了安全性

class ATM:
    def __card(self):
        print('插卡')
    def __auth(self):
        print('用户认证')
    def __input(self):
        print('输入取款金额')
    def __print_bill(self):
        print('打印账单')
    def __take_money(self):
        print('取款')

    def withdraw(self):
        self.__card()
        self.__auth()
        self.__input()
        self.__print_bill()
        self.__take_money()

a=ATM()
a.withdraw()

 

特性(property)

什么是特性property?

  property是一种特殊的属性,访问它时会执行一段功能,然后返回值。

class People:
    def __init__(self,name,weight,height):
        self.name=name
        self.weight=weight
        self.height=height
    @property
    def bmi(self):
        return self.weight / (self.height**2)

p1=People('egon',75,1.85)
print(p1.bmi)

为什么要用property?

  将一个类的函数定义成特性后,对象再去使用的时候obj.name,根本无法察觉自己的nameshi执行了一个函数后计算出来的。这种特性的使用方式遵循了统一访问的原则

 

面向对象之绑定方法与非绑定方法

类中定义的函数分成两大类

1、绑定方法(绑定给谁,谁来调用就自动将它本身当作第一个参数传入)

  1、绑定到类的方法:用classmethod装饰器装饰的方法

   为类量身定制

  2、绑定到对象的方法:没有被任何装饰器装饰的方法

   为对象量身定制

           属于类的函数,类可以调用,但是必须按照函数的规则来,没有自动传值一说。

2、非绑定方法:用staicmethod装饰器装饰的方法

  1、不与类或对象绑定,类和对象都可以调用。但是没有自动传值一说,就是一个普通工具。

  

绑定方法:

绑定给类的方法(classmethod)

  classmethod是给类用的,即绑定到类,类在使用时会将类本身当做参数传给类方法的第一个参数,python内置了函数classmethod来吧类中的函数定义成类方法

#settings.py
HOST='127.0.0.1'
PORT=3306
DB_PATH=r'C:\Users\Administrator\PycharmProjects\test\面向对象编程\test1\db'

 

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() #对象也可以调用,但是默认传的第一个参数仍然是类

 

非绑定方法

在类内部用staicmethod装饰的函数即非绑定方法,就是普通函数

staicmethod不与类或对象绑定,谁都可以调用,没有自动传值效果

 

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与staicmethod区别

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__的执行,打印就不告诉你:

 

posted @ 2018-01-25 09:38  LinuxPark  阅读(151)  评论(0编辑  收藏  举报