zoukankan      html  css  js  c++  java
  • python之面向对象篇6

    一、继承与派生

    什么是继承
    继承一种新建类的方式,新建的类称为子类或者派生类,被继承的类称为父类或基类或超类
    子类会遗传父类的一系列属性
    
    python支持多继承
    注意:
        在python3中,如果没有显式地继承任何类,那默认继承object类
        在python2中,如果没有显式地继承任何类,也不会继承object类
    
    在python中类分为两种:
        python3新式类,广度优先
            但凡继承object的类,以及该类的子类都是新式类
            在python3中所有的类都是新式类
        python2经典类,深度优先
            没有继承object类,以该类的子类都是经典类
            只有在python2中才存在经典类,为何?
            因为在python2中没有没有显式地继承任何类,也不会继承object类
    

    1)继承的示例

    class Parent1(object):
        pass
    
    class Parent2(object):
        pass
    
    class Sub1(Parent1):
        pass
    
    class Sub2(Parent1,Parent2):
        pass
    #  __bases__ 打印继承关系,没有继承,默认继承object
    print(Parent1.__bases__)    
    print(Parent2.__bases__)
    
    print(Sub1.__bases__)
    print(Sub2.__bases__)
    View Code

     2)基于继承减少代码冗余的案例+派生/衍生

    import pickle
    class OldboyPeople:
        school = 'oldboy'
    
        def __init__(self,name,age,sex):
            self.name=name
            self.age=age
            self.sex=sex
    
        def save(self):
            with open('%s' % self.name, 'wb') as f:
                pickle.dump(self, f)
    
    class OldboyStudent(OldboyPeople):
        def choose_course(self,course):
            print('%s is choosing course:%s' %(self.name,course))
    
    class OldboyTeacher(OldboyPeople):
        def __init__(self,name,age,sex,level):
            # self.name = name
            # self.age = age
            # self.sex = sex
            OldboyPeople.__init__(self,name,age,sex)
            self.level=level
    
        def score(self,stu):
            print('%s is score %s' %(self.name,stu.name))
    
    # stu1=OldboyStudent('alex',38,'male')
    # print(stu1.__dict__)
    # stu1.save()
    
    tea1=OldboyTeacher('egon',18,'male',10)
    print(tea1.name,tea1.level)
    # tea1.save()
    # print(stu1.school)
    
    
    # 在子类派生出的新方法中重用父类的功能:
    #方式一:指名道姓地访问某一个类的函数,与继承是没有关系的
    # OldboyPeople.__init__(self,name,age,sex)
    View Code

     3)单继承的属性查找“:对象自己-》对象的类-》父类-》父类

    class Foo:
        def f1(self):
            print('Foo.f1')
    
        def f2(self): #self=obj
            print('Foo.f2')
            self.f1() #obj.f1()
    
    class Bar(Foo):
        def f1(self):
            print('Bar.f1')
    
    obj=Bar()
    obj.f2()
    # 执行父类的 f2 代码,发现到了f1 ,仍然先从自己这里开始找
    View Code

    4)(python2和python3一样的)多继承的属性查找“:对象自己-》对象的类-》从左往右一个一个的分支找下去

    class D:
        # def test(self):
        #     print('D')
        pass
    class E:
        def test(self):
            print('E')
    
    class F:
        def test(self):
            print('F')
    
    class A(D):
        # def test(self):
        #     print('A')
        pass
    
    class B(E):
        # def test(self):
        #     print('B')
        pass
    
    class C(F):
        def test(self):
            print('C')
    
    class G(A,B,C):
        # def test(self):
        #     print('G')
        pass
    
    obj=G()
    obj.test()
    View Code

     5)python3新式类,广度优先查找,从左到右依次查找。但不先找头,最后才头

      print(F.mro())  方法属性查找顺序

      python2经典类,深度优先查找,从左到右依次查找,遇到了头,就找头,再找其他的

    # 新式类:广度优先
    class A(object):
        def test(self):
            print('from A')
    
    class B(A):
        # def test(self):
        #     print('from B')
        pass
    class C(A):
        # def test(self):
        #     print('from C')
        pass
    class D(B):
        # def test(self):
        #     print('from D')
        pass
    class E(C):
        # def test(self):
        #     print('from E')
        pass
    
    class F(D,E):
    View Code

     6)在子类派生出的新方法中重用父类的功能

    #方式一:指名道姓地访问某一个类的函数,与继承是没有关系的
    # OldboyPeople.__init__(self,name,age,sex)
    
    #方式二:super(自己的类名,self).父类中的方法名()
    # 调用super会得到一个特殊的对象,该对象是专门用来引用父类中的方法的,
    #具体的:该对象会严格按照当前类的MRO列表从当前类的父类中依次查找属性,即这种方式是严格依赖于继承的
    #ps:在python3中可以简写为super()
    
    # !!!!!强调:二者使用哪一种都可以,但最好不要混合使用 !!!!

    基于2种方式的实现继承父类方法

    class OldboyPeople:
        def __init__(self,name,age,sex):
            self.name=name
            self.age=age
            self.sex=sex
    
    class OldboyTeacher(OldboyPeople):
        def __init__(self,name,age,sex,level):
            # OldboyPeople.__init__(self,name,age,sex)    # 方式一,访问的函数
            super(OldboyTeacher,self).__init__(name,age,sex)  # 方式二,访问的方法
            self.level=level
    
    tea1=OldboyTeacher('egon',18,'male',10)
    print(tea1.name,tea1.level)
    View Code

    super的应用,无论super定义在哪个位置的方法,都应该当前位置开始查找,查找顺序可以用 mro进行查看

    class A:
        def f1(self):
            print('A')
            super().f2() # super()会基于当前所在的查找位置继续往后查找
        def f2(self):
            print('A')
    class B:
        def f2(self):
            print('B')
    
    class C(A,B):
        def f2(self):
            print('C')
    
    obj=C()
    print(C.mro())
    obj.f1()
    View Code

     总结:最好不要用多继承

    二、类与类的组合

    1)不要组合之前

    import pickle
    class OldboyPeople:
        school = 'oldboy'
        def __init__(self,name,age,sex):
            self.name=name
            self.age=age
            self.sex=sex
        def save(self):
            with open('%s' % self.name, 'wb') as f:
                pickle.dump(self, f)
    
    class OldboyStudent(OldboyPeople):
        def __init__(self,name,age,sex):
            OldboyPeople.__init__(self,name,age,sex)
            self.courses=[]
        def choose_course(self,course):
            print('%s is choosing course:%s' %(self.name,course))
    
    stu1=OldboyStudent('李三泡',18,'male')
    # print(stu1.courses)
    stu1.courses.append({'name':'python','price':8000,'period':'5mons'})
    stu1.courses.append({'name':'linux','price':10000,'period':'3mons'})
    print(stu1.courses)
    for course in stu1.courses:
        print("""
        课程名:%s
        价钱:%s
        周期:%s
        """ %(course['name'],course['price'],course['period']))
    
    stu2=OldboyStudent('李二泡',38,'female')
    stu2.courses.append({'name':'python','price':8000,'period':'5mons'})
    for course in stu2.courses:
        print("""
        课程名:%s
        价钱:%s
        周期:%s
        """ %(course['name'],course['price'],course['period']))
    View Code

    2)组合的使用意义

    1、什么是组合
        对象的属性的值是来自于另外一个类的对象,这就叫类的组合使用
    2、为何要用组合
        组合是用来减少类与类代码冗余的
    
        组合vs继承
            只有在类与类之间有从属的关系的时候才能用继承
            否则的话用组合

    使用组合之后,代码调整

    import pickle
    class OldboyPeople:
        school = 'oldboy'
        def __init__(self,name,age,sex):
            self.name=name
            self.age=age
            self.sex=sex
        def save(self):
            with open('%s' % self.name, 'wb') as f:
                pickle.dump(self, f)
    
    class OldboyStudent(OldboyPeople):
        def __init__(self,name,age,sex):
            OldboyPeople.__init__(self,name,age,sex)
            self.courses=[]
        def choose_course(self,course):
            print('%s is choosing course:%s' %(self.name,course))
        def tell_courses_info(self):
            for course in self.courses:
                course.tell_info()
    
    class Course:
        def __init__(self,name,price,period):
            self.name=name
            self.price=price
            self.period=period
    
        def tell_info(self):
            print("""
                课程名:%s
                价钱:%s
                周期:%s
                """ % (self.name, self.price, self.period))
    
    python=Course('python',8000,'5mons')
    linux=Course('linux',10000,'3mons')
    
    stu1=OldboyStudent('李三泡',18,'male')
    stu1.courses.append(python)
    stu1.courses.append(linux)
    
    stu2=OldboyStudent('李二泡',38,'female')
    stu2.courses.append(python)
    
    stu1.tell_courses_info()
    stu2.tell_courses_info()
    View Code

     三、多态与多态性

    1、什么是多态
        同一种事物的多种形态
    
    2、为何要用多态
        多态性:指的是可以在不用考虑对象具体类型的前提下,直接调用对象的方法

    1)多态方法,类似的事物用同样的方法

    class Animal:
        def talk(self):
            pass
    
    class People(Animal):
        def talk(self):
            print('say hello')
    
    class Dog(Animal):
        def talk(self):
            print('汪汪汪')
    
    class Pig(Animal):
        def talk(self):
            print('哼哼哼')
    
    peo1=People()
    dog1=Dog()
    pig1=Pig()
    #多态性:
    peo1.talk()
    dog1.talk()
    pig1.talk()
    View Code

    2)函性质的数多态方法

    #多态
    class People():
        def talk(self):
            print('say hello')
    
    class Dog():
        def talk(self):
            print('汪汪汪')
    
    class Pig():
        def talk(self):
            print('哼哼哼')
    
    peo1=People()
    dog1=Dog()
    pig1=Pig()
    #多态性:
    def talk(animal):
        animal.talk()
    
    talk(peo1)
    talk(dog1)
    talk(pig1)
    View Code

    3)平时统计自动的长度,使用的多态方法

    l=list([1,2,3])
    s=str('hello')
    t=tuple((4,5,6))
    
    l.__len__()
    s.__len__()
    t.__len__()
    
    
    def len(obj):
        return obj.__len__()
    
    print(len(l))
    print(len(s))
    print(len(t))
    View Code

     4)强制让子类使用多态属性,import abc

    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('汪汪汪')
    
    class Pig(Animal):
        def talk(self):
            print('哼哼哼')
    
    peo1=People()
    dog1=Dog()
    pig1=Pig()
    
    peo1.talk()
    dog1.talk()
    pig1.talk()
    View Code

    四、封装

    1、什么是封装
        “装”的意思就往一个容器中放入一系列属性
        “封”的意思就是藏起来,在内部可以看到,但对外部是隐藏的
    
    2、如何用封装
        但凡是双下划线开头(不能是双下划线结尾)的属性,会被隐藏起来,类内部可以直接使用
        而类外部无法直接使用,即封装是对外不对内的
    
        这种隐藏的特点:
            1、只是一种语法上的变形,会将__开头的属性变形为:_自己的类名__属性名
            2、该变形只在类定义阶段发生一次,在类定义阶段之后新增的__开头的属性并不会发生变形
            3、隐藏是对外不对内的
            4、在继承中,父类如果不想让子类覆盖自己的同名方法,可以将方法定义为私有的

    1)访问封装起来的隐藏内容

    class Foo:
        __n=1 #_Foo__n=1
    
        def __init__(self,name):
            self.__name=name #self._Foo__name=name
    
        def __f1(self): #_Foo__f1
            print('f1')
    # __n 和 __f1() 被隐藏起来,无法直接被访问
    print(Foo.__dict__)     # 查看名称空间,可知道如何被访问
    print(Foo._Foo__n)
    print(Foo._Foo__f1)
    View Code

     2)封装的真正使用意义,内部开辟了一个接口来访问隐藏空间

    案例一

    class Foo:
        __n=1 #_Foo__n=1
    
        def __init__(self,name):
            self.__name=name #self._Foo__name=name
        def __f1(self): #_Foo__f1
            print('f1')
        def f2(self):
            self.__f1() #self._Foo__f1()
            print(self.__name) #self._Foo__name
            print(self.__n) #self._Foo__n
    
    obj=Foo('egon')
    obj.f2()
    View Code

     案例二

    class People:
        def __init__(self,name,age):
            self.__name=name
            self.__age=age
        def tell_info(self):
            print('<%s:%s>' %(self.__name,self.__age))
        def set_info(self,name,age):
            '''
            修改名字,年龄
            :param name:
            :param age:
            :return:
            '''
            if type(name) is not str:
                raise TypeError('用户名必须是str类型')
            if type(age) is not int:
                raise TypeError('年龄必须是int类型')
            self.__name=name
            self.__age=age
    p=People('egon',18)
    p.tell_info()
    p.set_info('egon',22)
    p.tell_info()
    View Code

     3)封装的意图

    1、封装数据属性:将数据数据隐藏起来,从而类的使用者无法直接操作该数据属性
        需要类的设计者在类内部开辟接口,让类的使用者同接口来间接地操作数据,
        类的设计者可以在接口之上附加任意逻辑,从而严格控制类的使用者对属性的操作
    2、封装函数属性:隔离复杂度
    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()
    View Code

     五、特性(property)

    1)类方法,把一个函数伪造成对象的特殊

    class People:
        def __init__(self,name,height,weight):
            self.name=name
            self.height=height
            self.weight=weight
    
        @property
        def bmi(self):
            return self.weight / (self.height ** 2)
    
    # 封装内置函数 property 用法,本该执行函数的用法是 bmi()  变成了 bmi
    egon=People('egon',1.80,75)
    print(egon.bmi)
    View Code

    2)类方法中 property与setter和deleter的使用

    先有 proerty伪装函数,才能有setter修改功能,和删除功能

    class People:
        def __init__(self,name):
            self.__name = name
    
        @property       # 伪装方法成属性
        def name(self):
            return self.__name
    
        @name.setter    # 修改方法的属性
        def name(self,val):
            # print('=====>准备修改名字的值:',val)
            if type(val) is not str:
                raise TypeError('名字的值必须为str类型')
            self.__name=val
    
        @name.deleter
        def name(self):
            # del self.__name
            print('删不得')
    
    p = People('egon')
    print(p.name)
    p.name = 'alex'
    print(p.name)
    del p.name
    View Code

    六、绑定方法与非绑定办法

    1)绑定方法

    绑定方法:
        特点:
            绑定给谁,就应该由谁来调用,谁来调用就会将谁当做第一个参数传入
    
        绑定到对象的方法:
            在类中定义的函数,在没有被任何装饰器装饰的情况下,默认都是绑定给对象的
    
        绑定到类的方法
            在类中定义的函数,在被装饰器classmethod装饰的情况下,该方法是绑定类的

    1)绑定方法使用示例

    class Foo:
        @classmethod
        def f1(cls): #cls=Foo
            print('自动传入的是类:',cls)
    
        def f2(self):
            print('自动传入的是对象:',self)
    
    print(Foo.f1)
    print(Foo.f2)
    print(Foo)
    Foo.f1()
    View Code

    2)绑定类方法常用示例

    配置文件中获取ip和端口连接

    HOST='127.0.0.1'
    PORT=3306
    settings.py

    类方法使用

    import settings
    class MySQL:
        def __init__(self,host,port):
            self.host = host
            self.port = port
    
        @classmethod
        def from_conf(cls):
            return cls(settings.HOST, settings.PORT)
    
    conn=MySQL.from_conf()  # 利用类方法,从配置文件获取到的连接
    print(conn.host,conn.port)
    
    conn1=MySQL('1.1.1.1',3306)  # 传值获取的连接
    print(conn.host,conn.port)
    classmethod使用

     3)非绑定办法。既不与类绑定也不与对象绑定,没有任何自动传值的效果,因为函数体根本也不需要

      示例:

    import settings
    import hashlib
    import time
    
    class MySQL:
        def __init__(self,host,port):
            self.host=host
            self.port=port
    
        @classmethod
        def from_conf(cls):
            return cls(settings.HOST,settings.PORT)
    
        @staticmethod
        def create_id():
            m=hashlib.md5()
            m.update(str(time.clock()).encode('utf-8'))
            return m.hexdigest()
    
    obj=MySQL('1.1.1.1',3306)
    
    print(MySQL.create_id())
    print(obj.create_id())
    View Code

     七、类的内置函数

    1)isinstance和issubclass。判断是否是它的类

    class Foo:
        pass
    
    obj=Foo()
    
    print(isinstance(obj,Foo))
    print(isinstance([],list))
    print(type(obj) is Foo)
    print(type([1,2]) is list)
    print(issubclass(Foo,object))
    View Code

     2)字符串的改造方法:hasattr和getattr和setattr和delattr

    hasattr   # 判断字符串是否在类里面的方法
    getattr   # 找到类里面的字符串
    setattr   # 新增一个属性
    delattr   # 删除属性

    1)使用示例

    class People:
        country='China'
        def __init__(self,name):
            self.name=name
    
    #涉及四个内置函数
    # hasattr   # 判断字符串是否在类里面的方法
    print('country' in People.__dict__)
    print(hasattr(People,'country'))
    
    # getattr  # 找到类里面的字符串
    print(People.__dict__['country'])
    print(getattr(People,'country'))
    print(getattr(People,'country1111',None))   # 找不到,也不报错
    
    # setattr # 新增一个属性
    setattr(People,'x',111)
    print(People.__dict__)
    print(getattr(People,'x'))
    
    # delattr   # 删除属性
    delattr(People,'country')
    print(getattr(People,'country',None))
    View Code

    2)利用字符串的改造方法输入执行相应的技能

    class Ftp:
        def get(self):
            print('get...')
        def put(self):
            print('put...')
        def auth(self):
            print('auth...')
        def run(self):
            while True:
                cmd=input('>>: ').strip() #cmd='get'
                if hasattr(self,cmd):
                    method=getattr(self,cmd)
                    method()
                else:
                    print('输入的方法不存在')
    
    obj=Ftp()
    obj.run()
    View Code

     八、类的内置方法

    1)__str__,用在类中,实印例化对象后,自动触发,返回值必须是字符串格式的类型

    class People:
        def __init__(self,name,age,sex):
            self.name=name
            self.age=age
            self.sex=sex
    
        # 该方法会在对象被打印时自动触发,
        def __str__(self):
            # print('=====run=====')
            return '["%s",%s,"%s"]' %(self.name,self.age,self.sex)
    
    obj=People('egon',18,'male')
    print(obj) # print(obj.__str__())
    View Code

     2)__del__,会在对象被删除前自动触发。适用于回收机制。

    # 伪代码,
    # 因connect连接涉及到了2方面的资源。所有需要在程序结束时,自动触发回收机制
    class Mysql:
        def __init__(self,host,port):
            self.host=host
            self.port=port
            self.conn=connect(host,port)
    
        def __del__(self):
            self.conn.close()
    
    obj=Mysql('1.1.1.1',3306)
    View Code

    3)__call__,调用对象的时候触发

    class Foo:
        def __init__(self):     # 对象实例化的时候触发
            pass
        def __str__(self):      # 打印对象的时候触发
            pass
        def __del__(self):      # 删除对象的时候触发
            pass
        def __call__(self, *args, **kwargs):    # 调用对象的时候触发
            print("__call__")
    obj = Foo()
    obj()   # 自动触发 __call__ 这个方法

    九、元类方法(了解)

    1)exec的使用。把字符串产生的名字丢在名称空间里面

    code="""
    x=0
    y=2
    """
    local_dic={}
    exec(code,{},local_dic)     #  第二个放全局的名称空间。第三个局部的名称空间
    print(local_dic)
    View Code

       exec的作用:自己定义全局名称空间的名字和局部名称空间的名字。函数也可以

    code="""
    global x
    x=0
    y=2
    def f1(self,a,b):
        pass
    """
    global_dic = {"x":100}
    local_dic={}
    exec(code,global_dic,local_dic)     #  第二个放全局的名称空间。第三个局部的名称空间
    print(global_dic)
    print(local_dic)
    View Code

     2)对象的产生过程

    class Chinese:
        country="China"
    
        def __init__(self,name,age,sex):
            self.name=name
            self.age=age
            self.sex=sex
    
        def speak(self):
            print('%s speak Chinese' %self.name)
    
    print(Chinese)
    print(type(Chinese))    # 元类:类的类就是元类,
    p=Chinese('egon',18,'male')
    print(type(p))  # 我们用class定义的类使用来产生我们自己的对象的
    # 内置元类type是用来专门产生class定义的类的
    # 相当于 type 实例化 classclass 再实例化成对象
    View Code

     3)exec 方法 创建类(class)的第二种方式,也是类创建的原理

    class_name='Chinese'
    class_bases=(object,)
    class_body="""
    country="China"
    def __init__(self,name,age,sex):
        self.name=name
        self.age=age
        self.sex=sex
    def speak(self):
        print('%s speak Chinese' %self.name)
    """
    class_dic={}
    exec(class_body,{},class_dic)
    # 类的三大要素
    print(class_name,class_bases,class_dic)
    Chinese=type(class_name,class_bases,class_dic)
    print(Chinese)
    p=Chinese('egon',18,'male')
    print(p.name,p.age,p.sex)
    View Code

     4)自定义类的创建规范

    class Mymeta(type):
        def __init__(self,class_name,class_bases,class_dic):
            if not class_name.istitle():
                raise TypeError('类名的首字母必须大写')
            if not class_dic.get('__doc__'):
                raise TypeError('类中必须写好文档注释')
            super(Mymeta, self).__init__(class_name, class_bases, class_dic)    # 重用父类的功能
    
    # 类 Foo的首字母必须大写,必须有注释。
    class Foo(object,metaclass=Mymeta):
        '''
        管理员类
        '''
        pass
    View Code

     5)自定义控制类的调用

    class Mymeta(type):
        # 控制类的Foo的创建
        def __init__(self,class_name,class_bases,class_dic):
            if not class_name.istitle():
                raise TypeError('类名的首字母必须大写')
            if not class_dic.get('__doc__'):
                raise TypeError('类中必须写好文档注释')
            super(Mymeta, self).__init__(class_name, class_bases, class_dic)    # 重用父类的功能
        # 控制类Foo的调用,即控制实例化Foo的过程
        def __call__(self, *args, **kwargs):
            obj = object.__new__(self)
            self.__init__(obj, *args, **kwargs)
            return obj
    
    class Foo(object,metaclass=Mymeta):
        """
        文档注释
        """
        x=1
        def __init__(self,y):
            self.y=y
        def f1(self):
            print('from f1')
    
    obj=Foo(1111) #Foo.__call__()
    print(obj.y)
    print(obj.__dict__)
    View Code

     6)单实例模式。共用资源,不重复创建

    import settings
    
    class MySQL:
        __instance=None
        def __init__(self,ip,port):
            self.ip=ip
            self.port=port
    
        @classmethod
        def singleton(cls):
            if not cls.__instance:
                obj=cls(settings.IP, settings.PORT)
                cls.__instance=obj
            return cls.__instance
    obj1=MySQL('1.1.1.2',3306)
    obj2=MySQL('1.1.1.3',3307)
    obj3=MySQL('1.1.1.4',3308)
    # 单实例模式,ip和端口也一样
    obj4=MySQL.singleton()
    obj5=MySQL.singleton()
    obj6=MySQL.singleton()
    
    print(obj4 is obj5 is obj6)
    View Code

    原文链接:

    http://www.cnblogs.com/linhaifeng/articles/7340153.html
    http://www.cnblogs.com/linhaifeng/articles/6204014.html
  • 相关阅读:
    高并发处理(一)页面静态化
    高并发处理(二)图片服务器分离
    五百强企业某部门视频面试题汇总
    access和sqlyog的使用
    条件查询
    primary select |||| case primary select
    win10 + ubuntu16 双系统安装
    ubuntu16 下安装python3.6 和Anaconda3
    import nltk nltk.download('reuters') [nltk_data] Error loading reuters: <urlopen error [Errno 11004] [nltk_data] getaddrinfo failed>
    共现矩阵
  • 原文地址:https://www.cnblogs.com/linu/p/9097210.html
Copyright © 2011-2022 走看看