zoukankan      html  css  js  c++  java
  • Python_基础_(面向对象三大特性)

    一,继承

     ## 在Python中的继承分为单继承和多继承

    class ParentClass1:
        pass
        
    class ParentClass2:
        pass
    
    class SubClass(ParentClass1):    # 单继承
        pass
        
    class SubClass(ParentClass1,ParentClass2)    # 多重继承
        pass

    # 在Python中可以继承多个类,在Java和C#中则只能继承一个类

    # Python类如果继承了多个类,那么其寻找方式有两种,深度优先和广度优先(下方有讲)

    ## 继承例子

    class Dad:
        "这是父类"
        name = "henry_Dad"
        def __init__(self,name):
            self.name = name
            print("父类")
    class Sun(Dad):
        pass
    print(Sun.name)     # henry_Dad
    print(Dad.__dict__)
    # {'__module__': '__main__',
    # '__doc__': '这是父类',
    # 'name': 'henry_Dad',
    # '__init__': <function Dad.__init__ at 0x000001E8916CB8C8>,
    # '__dict__': <attribute '__dict__' of 'Dad' objects>,
    # '__weakref__': <attribute '__weakref__' of 'Dad' objects>}
    
    print(Sun.__dict__) 
    # {'__module__': '__main__', '__doc__': None}

     ## 问题:

    问:子类继承了父类的属性,子类自定义的属性与父类中的属性重名,那么就覆盖了父类?

    答:不是覆盖,当子类调用一个数据属性或函数属性时,子类现在自己的类中找,若找不到则在父类中找,若找到则不在父类中找

    什么时候用到继承:

    1:当类之间有显著的不同,且较小的类为较大的类的组件时,用组合较好

    2:当类之间具有很多相同的功能,提取这些功能作为基类,用继承比较好

    ## 父类:动物(吃,喝,玩,乐)

    ## 子类:小狗(汪汪叫)

    ## 子类:母鸡(喔喔叫)

    ## 继承具有的含义

    1:继承基类的方法(尽量少用,会让类与类之间的耦合度增加,因遵循低耦合 高内聚),并且做出自己的改变或或扩展

    在实践中,该继承的意义不大,甚至是有害的,因为它使基类和子类之间的耦合性加强了

    2:声明某个子类兼容于某个基类,定义一个类的接口,子类继承接口类,并且实现接口中定义的方法

    接口继承实质上要求“做出一个良好的抽象,这个抽象定义一个兼容的接口,使得外部的调用者无需知道具体的实现细节,可一视同仁的处理特定接口的所有对象”

    ## abc模块

    在Python中本身不提供抽象类和接口机制,想要实现抽象类,可以利用abc模块,BAC为 Abstract Base Class

    import abc
    class Animal(metaclass=abc.ABCMeta):
        @abc.abstractmethod     # 声明该适配器,被声明的方法在本类中不用实现
        def eat(self):
            pass
    
        @abc.abstractmethod
        def sleep(self):
            pass
    
    class Cat(Animal):
        def eat(self):
            pass
        def sleep(self):
            pass
    c = Cat()
    
    class Dog(Animal):
        def eat(self):
            pass
    
    d = Dog()       # 当类Dog中没有实现方法sleep时,执行这步会报错(必须实现父类中的抽象方法)

     ## 深度优先 和 广度优先

    深度优先遍历:对每一个可能的分支路径深入到无法深入为止,且每个节点只访问一次

    广度优先遍历:又叫层次遍历,从上往下对每一层依次访问,在每一层中,从左往右(也可以从右往左)访问结点,访问完一层就进入下一层,直到没有结点可以访问为止。

    ## 在Python中如何实现继承

    1:在Python中对于你定义的每个类,Python会计算出一个方法解析顺序列表(MRO),这个MRO列表就是一个简答的所有基类的线性顺序表

    2:为了实现继承,Python会从MRO列表中从左到右开始查找基类,知道找到第一个匹配这个属性的类为止

    # 2.1:子类会先于基类被检查

    # 2.2:如果一个子类有多个基类,则根据它们在MRO列表中的顺序进行检查

    # 2.3:如果对一个子类存在两个合法的基类,则选择第一个基类

    ## 新式类/经典类中的查找顺序

    # Python 3 为新式类

    # Python 2 为经典类和新式类

    class C1:        # 经典类,没有继承关系
        pass
        
    class C2(C1):    # 经典类,因为继承的C1为经典类
        pass

    class B1(object):    # 经典类,Python3基类默认继承了object
        pass
    
    class B2(B1):        # 新式类
        pass

    # 当为经典类时,多继承情况下,会按照深度优先查找
    # 当为新式类时,多继承情况下,会按照广度优先的方法查找

    ## MRO示例

    # 下图为继承关系样例图

     ## 新式类中的查找顺序

    class A:
        def test(self):
            print("A")
    
    class B(A):
        def test(self):
            print("B")
    
    class C(B):
        def test(self):
            print("C")
    
    class D(A):
        def test(self):
            print("D")
    
    class E(D):
        def test(self):
            print("E")
    
    class F(C,E):
        def test(self):
            print("F")
    
    print(F.__mro__)    # 经典类中无此方法
    # (<class '__main__.F'>, <class '__main__.C'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.D'>, <class '__main__.A'>, <class 'object'>)

    ## 经典类中的查找顺序(Python中),经典类中无__mro__方法

    class A:
        def test(self):
            print("A")
    
    class B(A):
        def test(self):
            print("B")
    
    class C(B):
        def test(self):
            print("C")
    
    class D(A):
        def test(self):
            print("D")
    
    class E(D):
        def test(self):
            print("E")
    
    class F(C,E):
        def test(self):
            print("F")
    
    f = F()
    f.test()
    # F->D->B->A->E->C

     ## super方法的使用

    # 方式一,在子类中直接写上基类的名称

    class Animal():
        def __init__(self,name,speed,appetite,master):
            self.name = name
            self.speed = speed
            self.appetite = appetite
            self.master = master
    
        def walk(self):
            print("开始遛了")
    
    class Dog(Animal):
        def __init__(self,name,speed,appetite,master,size):     # 自有属性为size
            Animal.__init__(self,name,speed,appetite,master)
            self.size = size
    
        def Walk_the_dog(self):
            Animal.walk(self)
            print("开始遛狗啦")
    
    d = Dog("small_yellow","10km/h","十碗","henry",30)
    d.Walk_the_dog()
    # 输出
    开始遛了
    开始遛狗啦

     # 以上的方法存在缺陷,当父类名修改时,子类中涉及父类名称的也得进行修改,扩展性差

    # 方式二,利用super

    class Animal:
        def __init__(self,name,speed,appetite,master):
            self.name = name
            self.speed = speed
            self.appetite = appetite
            self.master = master
    
        def walk(self):
            print("开始遛了")
    
    class Dog(Animal):
        def __init__(self,name,speed,appetite,master,size):     # 自有属性为size
            # Animal.__init__(self,name,speed,appetite,master)
            super().__init__(name, speed, appetite, master)
            self.size = size
    
        def Walk_the_dog(self):
            # Animal.walk(self)
            super(Dog,self).walk()
            print("开始遛狗啦")
    
    d = Dog("small_yellow","10km/h","十碗","henry",30)
    d.Walk_the_dog()
    # 输出
    开始遛了
    开始遛狗啦

    ....

    二,多态

     多态:由不同的类实例化得到的对象,这些对象调用同一个方法,执行不同的逻辑

    多态反应一种在执行时候的状态

    >>> str1 = "abc"
    >>> list1 = [1,1,2,3]
    >>> str1.__len__()
    3
    >>> list1.__len__()
    4
    >>>
    # 其中 str1和list1为不同的对象,都去调用方法__len__(),但执行不同的逻辑来得到结构

     ## 多态例子

    class H20:
        def __init__(self,h2o_type,temperature):
            self.h2o_type = h2o_type        # H2O类型
            self.temperature = temperature  # 温度
    
        def translate(self):
            if self.temperature > 100:
                print("当前的温度为:%s 大于100,变成了%s" %(self.temperature,self.h2o_type))
            elif self.temperature > 0 & self.temperature < 100:
                print("当前的温度为:%s 位于0和100之间,变成了%s" % (self.temperature, self.h2o_type))
            elif self.temperature < 0:
                print("当前的温度为:%s 小于0,变成了%s" % (self.temperature, self.h2o_type))
    
    class Water(H20):
        pass
    
    class Ice(H20):
        pass
    
    class Stream(H20):
        pass
    
    w = Water("",20)       # 由不同的类实例化出不同的对象
    i = Ice("",-20)        # 由不同的类实例化出不同的对象
    s = Stream("蒸汽",111)   # 由不同的类实例化出不同的对象
    
    w.translate()           # 调用同一个方法(执行不同的逻辑)
    i.translate()           # 调用同一个方法(执行不同的逻辑)
    s.translate()           # 调用同一个方法(执行不同的逻辑)
    
    #
    当前的温度为:20 位于0和100之间,变成了水
    当前的温度为:-20 小于0,变成了冰
    当前的温度为:111 大于100,变成了蒸汽

    # 注

    注:多态实际上是依附于继承的两种含义,“改变”和“扩展”本身就意味着必须有着一种机制去实现改变和扩展,如果没有多态,两种含义则就不可能实现
    # 多态实际上是继承的实现细节,让多态与封装,继承这两个并列,显然不符合逻辑

    ...

    三,封装

     封装1 普通的封装形式 

    # 第一层封装,类就是麻袋,这本身就是一种封装

    封装2

    # 第二层封装,类中定义私有的,只在类的内部进行使用,而在外部无法进行使用 _ __  (使用单下划线和双下划线)

    # 当属性以单下划线 _ 开头,那么它就是属于内部的属性,不能被外部调用(当你发现有用单下划线开头的属性,则不该调用该属性)
    # 这只是一种约定,Python并不会真正阻止你访问私有的属性,外部还是能调用的

    # 当属性一双下划线开头 __ ,Python会自动做一个重命名的操作,
    # 外部不能访问 __star ,内部可以进行直接访问 __star

    class People:
        __star = "earth"
    # __star被 Python重命名为 _People__star

     例子

    class Dog:
        def __init__(self,name,speed,appetite,master):
            self.name = name
            self.speed = speed
            self.appetite = appetite
            self.master = master
        def walk(self):
            print("开始遛%s" %self.name)
    d = Dog("small_yellow","10km/h","十碗","henry")

    # 当调用其属性时
    print(d.name,d.speed,d.appetite,d.master)   # small_yellow 10km/h 十碗 henry

    # 当调用其方法时
    d.walk()        # 开始遛small_yellow

     # 当将类中的属性隐藏起来

    class Dog:
        def __init__(self,name,speed,appetite,master):
            self.__name = name
            self.__speed = speed
            self.__appetite = appetite
            self.__master = master
    
        def walk(self):
            print("开始遛%s" %self.__name)
    d = Dog("small_yellow","10km/h","十碗","henry")

    # 当调用其属性时 报错
    print(d.name)       # AttributeError: 'Dog' object has no attribute 'name'

    # 同样报错
    print(d.__name)     # AttributeError: 'Dog' object has no attribute '__name'

    # 当调用其方法时,方法还是可以进行调用
    d.walk()        # 开始遛small_yellow

     ## 进行隐藏后如何进行访问

    class Dog:
        def __init__(self,name,speed,appetite,master):
            self.__name = name
            self.__speed = speed
            self.__appetite = appetite
            self.__master = master
    
        def walk(self):
            print("开始遛%s" %self.__name)
    
    d = Dog("small_yellow","10km/h","十碗","henry")

    print(d.__dict__)
    # {'_Dog__name': 'small_yellow', '_Dog__speed': '10km/h', '_Dog__appetite': '十碗', '_Dog__master': 'henry'}
    # 在Python中,当用"__属性名"进行命名时,Python自动将其隐藏的属性的命名格式进行了修改,修改成了“_类名__属性名”

    # 当然也可以利用 _类名__属性名  来访问对应的属性
    print(d._Dog__name)     # small_yellow

     ...

    四,@property的使用

      当在类中添加一个属性时,如果将属性直接暴露在外,让对象可以随意的调用,这就会让属性可以随意的更改

     ## 实例化出的对象可以随意的获取和修改 name

    class Dog:
        def __init__(self,name):
            self.name = name
    
    d =Dog("藏獒")
    print(d.name)           # 藏獒
    
    d.name = "中华田园犬"
    print(d.name)           # 中华田园犬

     ## 在类中添加方法 get_name()来获取名字,添加set_name()来写入名字(现在就不能随心所欲设置名字了)

    class Dog:
        def set_name(self,name):
            if len(name) > 10:
                print("名字的长度超过10个字符")
            else:
                self.name = name
        def get_name(self):
            return self.name
    
    d = Dog()
    d.set_name("中华田园犬")
    print(d.get_name())         # 中华田园犬

    d1 = Dog()
    d1.set_name("我是超级可爱的中华田园犬")     # 名字的长度超过10个字符

     ## @property的作用就是负责将一个方法变成一个属性调用

    class Dog:
        def __init__(self,val):
            self.__NAME = val   # 将数据隐藏起来
    
        @property
        def name(self):
            return self.__NAME  # obj.name访问的是self.__NAME
    
        @name.setter
        def name(self,value):
            if len(value) > 10:
                raise TypeError('名字的长度超过10个字符')
            self.__NAME = value
    
    
    d = Dog("中华田园犬")
    d.name = "藏獒"       # 实际转化为 set_name("藏獒")
    print(d.name)         # 实际转化为 get_name

     ## 可以自定义只读属性,只定以getter方法

    # 由getter方法和setter方法,为可读可写
    class Dog:
        def __init__(self,val):
            self.__NAME = val
            
        @property
        def name(self):
            return self.__NAME
        
        @name.setter
        def name(self,value):
            self.__NAME = value

    # 只有getter方法,为只读
    class Cat:
        def __init__(self,val):
            self.__NAME = val 
            
        @property
        def name(self):
            return self.__NAME

     ...

  • 相关阅读:
    Emoji表情编解码库XXL-EMOJI
    代码生成平台Xxl-Code-Generator
    分布式单点登录框架XXL-SSO
    分布式爬虫框架XXL-CRAWLER
    Java对象和Excel转换工具XXL-EXCEL
    API管理平台XXL-API
    分布式缓存管理平台XXL-CACHE
    不知不觉已经写了多年代码,贴一下12年写的代码,表喷哈
    memcached安装、使用
    MySQL和Redis数据一致性问题
  • 原文地址:https://www.cnblogs.com/Doaoao/p/10149889.html
Copyright © 2011-2022 走看看