精华内容
下载资源
问答
  • python面向对象

    2018-09-13 21:49:34
    python面向对象精彩讲解视频,详细讲解了python在面向对象中的用途。
  • Python面向对象

    千次阅读 多人点赞 2019-12-24 09:25:39
    Python面向对象1. 面向对象基础语法2. 初始化方法__init__3. 属性查找与绑定方法4. 跑步案例5. 家具案例6. 私有属性7. 继承7.1 面向对象的三大特性7.2 单继承7.2.1 继承的概念7.2.2 继承的语法7.2.3 方法的重写 1. ...

    1. 面向对象基础语法

    # -*- coding: utf-8 -*-
    # @Time    : 2019/12/9 14:33
    # @Author  : 我就是任性-Amo
    # @FileName: 1.面向对象基础语法.py
    # @Software: PyCharm
    # @Blog    :https://blog.csdn.net/xw1680
    """
    1.类: 类是对一群具有相同特征或者行为的事物的一个统称,是抽象的,不能直接使用
        特征被称为属性
        行为被称为方法
    类就相当于制造飞机时的图纸,是一个模板,是复制创建对象的
    
    2.对象: 对象是由类创建出来的一个具体存在,可以直接使用
        由哪一个类创建出来的对象,就拥有在哪一个类中定义的: 属性和方法
    对象就相当于用图纸制造的飞机
    在程序开发中,应该先有类,再有对象
    
    3.类和对象的关系
        类是模板,对象是根据类这个模板创建出来的,应该先有类,再有对象
        类只有一个,而对象可以有很多个
        不同的对象之间属性 可能会各不相同
        类中定义了什么属性和方法,对象中就有什么属性和方法,不可能多,也不可能少
    
    4.类的设计
        在程序开发中,要设计一个类,通常需要满足一下三个要素:
            类名: 这类事物的名字,满足大驼峰命名法(每一个单词的首字母大写并且单词与单词之间没有下划线)
            属性: 这类事物具有什么样的特征
            方法: 这类事物具有什么样的行为
        4.1 类名的确定
            名词提炼法 分析整个业务流程,出现的名词,通常就是找到的类
        4.2 属性和方法的确定
            对对象的特征描述,通常可以定义成属性
            对象具有的行为(动词),通常可以定义成方法
            提示: 需求中没有涉及的属性或者方法在设计类时,不需要考虑
    """
    
    
    # 第一个面向对象程序: 小猫爱吃鱼 小猫要喝水
    # 这里的话按照需求首先是不需要定义属性的
    class Cat:
        """这是一个猫类"""
    
        def eat(self):
            # self: 由哪一个对象调用的方法,方法内的self就是哪一个对象的引用
            # 1.在类封装的方法内部,self就表示当前调用方法的对象自己
            # 2.调用方法时,我们是不需要传递self参数的 解释器会自动帮我们传入
            # 3.在方法内部 可以通过self.访问对象的属性 也可以通过self.调用其他的对象方法
            print("%s 爱吃鱼" % self.name)
    
        def drink(self):
            print("小猫在喝水")
    
    
    # 创建对象
    tom = Cat()
    # tom.eat()  # 在设置属性之前 调用方法报错
    tom.drink()
    # 在类的外部是可以直接通过.的方式去给对象添加属性的,但是不推荐
    # 因为对象属性的封装应该封装在类的内部
    tom.name = "Tom"
    # print(tom.name)
    tom.eat()  # tom.eat(tom)
    # print(tom.__dict__)  # 可以查看对象绑定了哪些属性
    # print(Cat.__dict__)  # 可以查看类绑定了哪些命名空间
    
    lazy_cat = Cat()
    lazy_cat.name = "大懒猫"
    lazy_cat.eat()
    # 在类的外部通过变量名.访问对象的属性和方法
    # 在类的封装方法中,通过self.访问对象的属性和方法
    

    2. 初始化方法__init__

    # -*- coding: utf-8 -*-
    # @Time    : 2019/12/9 14:40
    # @Author  : 我就是任性-Amo
    # @FileName: 2.初始化方法.py
    # @Software: PyCharm
    # @Blog    :https://blog.csdn.net/xw1680
    
    """
    初始化方法:
        1.当使用类名()创建对象时,会自动执行以下操作:
          为对象在内存中分配空间--创建对象
          为对象的属性设置初始值--初始化方法(init)
        2.__init__方法是专门用来定义一个类具有哪些属性的方法
    """
    
    
    # 在Cat中增加__init__方法,验证该方法在创建对象时会被调用
    class Cat:
        """这是一个猫类"""
    
        # def __init__(self):
        #     print("这是一个初始化方法")
        #     # 定义用Cat类创建的猫对象都有一个name的属性
        #     self.name = "Tom"
    
        # 在开发中,如果希望在创建对象的同时,就设置对象的属性,可以对_init__方法进行改造
        # 把希望设置的属性值,定义成__init__方法的参数,在方法内部使用
        # self.属性 = 形参 接收外部传递的参数
        # 在创建对象时,使用类名(属性1, 属性2...)
    
        def __init__(self, name):
            self.name = name
    
        def eat(self):
            print("%s 爱吃鱼" % self.name)
    
        # 如果在开发中,希望使用print输出对象变量时,能够打印自定义的内容,就可以利用__str__这个内置方法了
        def __str__(self):
            return "我是小猫: %s" % self.name
    
        # def __del__(self):
        # __del__:如果希望在对象被销毁前,再做一些事情,可以考虑此方法
        def __del__(self):
            print("准备删除~~~")
    
    
    tom = Cat("Tom")  # 创建对象时 会自动调用初始化__init__方法
    tom.eat()
    lazy_cat = Cat("大懒猫")
    lazy_cat.eat()
    # 在Python中,使用print输出对象变量,默认情况下,会输出这个变量引用的对象是由哪一个类创建的对象,
    # 以及在内存中的地址(十六进制表示)
    print(tom)  # <__main__.Cat object at 0x103959bd0>
    
    del lazy_cat
    print("-" * 50)
    

    3. 属性查找与绑定方法

    # -*- coding: utf-8 -*-
    # @Time    : 2019/12/9 15:00
    # @Author  : 我就是任性-Amo
    # @FileName: 3.属性查找与绑定方法.py
    # @Software: PyCharm
    # @Blog    :https://blog.csdn.net/xw1680
    
    
    # 属性查找与绑定方法
    
    class Person:
        """这是一个猫类"""
    
        # nationality = "中国"
    
        # def __init__(self, name, age, address, nationality):
        #     self.name = name
        #     self.age = age
        #     self.address = address
        #     self.nationality = nationality 给对象绑定国籍属性
        def __init__(self, name, age, address):
            self.name = name
            self.age = age
            self.address = address
    
        def eat(self):
            print(f"{self.name}正在吃饭~~~")
    
    
    # 1.分别创建amo对象和paul对象
    # amo = Person("amo", 18, "重庆市沙坪坝区", "美国")
    # paul = Person("paul", 22, "重庆市沙坪坝区", "中国")
    # 属性: 首先会从对象本身先去查找,如果对象本身没有绑定该属性,则会从类中去找
    # print(id(amo.nationality))  # 4406863312
    # print(id(paul.nationality))  # 4406863504
    
    amo = Person("amo", 18, "重庆市沙坪坝区")
    paul = Person("paul", 22, "重庆市沙坪坝区")
    # 如果对象身上没有绑定该属性 则去在类中查找 类属性是被所有对象共用的 所以返回的地址值是一致的.
    # print(id(amo.nationality))  # 4461503952
    # print(id(paul.nationality))  # 4461503952
    # 如果在类中查找该属性 还是没有 程序就会报错
    # print(id(paul.nationality))  # AttributeError: 'Person' object has no attribute 'nationality'
    
    
    # 函数属性:是绑定给对象使用的,绑定到不同的对象是不同的绑定方法,对象调用绑定方法时,会把对象本身作为一个参数进行传入,传给self
    print(amo.eat)  # <bound method Person.eat of <__main__.Person object at 0x10e9c7350>>
    print(paul.eat)  # <bound method Person.eat of <__main__.Person object at 0x10e9c73d0>>
    # 可以看到 两个的绑定方法的内存地址值是不一致的
    
    print(Person.__dict__)
    print(id(Person.eat(amo)))  # 4393136216
    print(id(Person.eat(paul)))  # 4393136216
    

    4. 案例

    4.1 跑步案例

    # -*- coding: utf-8 -*-
    # @Time    : 2019/12/10 16:28
    # @Author  : 我就是任性-Amo
    # @FileName: 4.练习1.py
    # @Software: PyCharm
    # @Blog    :https://blog.csdn.net/xw1680
    
    # 封装是面向对象编程的一大特点
    # 面向对象编程的第一步:将属性和方法封装到一个抽象的类中
    # 外界使用类创建对象,然后让对象调用方法
    # 对象方法的细节都被封装在类的内部
    
    """
    需求:amo和jerry都爱跑步
    amo体重75.0公斤
    jerry体重45.0 公斤
    每次跑步都会减少0.5公斤
    每次吃东西都会增加1公斤
    """
    
    
    class Person:
        def __init__(self, name, weight):
            self.name = name
            self.weight = weight
    
        def run(self):
            self.weight -= 0.5
            print("%s 爱锻炼,跑步锻炼身体" % self.name)
    
        def eat(self):
            self.weight += 1
            # 注意: 在对象的方法内部,是可以直接访问对象的属性的
            print("%s 是吃货,吃完这顿在减肥" % self.name)
    
        def __str__(self):
            return "我的名字是%s,体重%.2f公斤" % (self.name, self.weight)
    
    
    amo = Person("amo", 75.0)
    amo.run()
    amo.eat()
    amo.eat()
    print(amo)
    print("-" * 50)
    jerry = Person("jerry", 45.0)  # 同一个类创建的多个对象之间,属性互不干扰
    jerry.run()
    jerry.eat()
    jerry.eat()
    print(jerry)
    

    代码运行结果如下:
    在这里插入图片描述

    4.2 家具案例

    # -*- coding: utf-8 -*-
    # @Time    : 2019/12/20 09:26
    # @Author  : 我就是任性-Amo
    # @FileName: 5.家具案例.py
    # @Software: PyCharm
    # @Blog    :https://blog.csdn.net/xw1680
    
    """
    需求:
        1.房子(House)有户型、总面积和家具名称列表
            新房子没有任何的家具
        2.家具(HouseItem)有名字和占地面积,其中
            席梦思(bed)占地4平米
            衣柜(chest)占地2平米
            餐桌(table)占地1.5平米
        3.将以上三件家具添加到房子中
        4.打印房子时,要求输出:户型、总面积、剩余面积、家具名称列表
    """
    
    
    # 分析: 要去添加家具 首先肯定要有一个家具类 被使用的类 通常应该先被开发
    class HouseItem:
        def __init__(self, name, area):
            self.name = name  # 家具名称
            self.area = area  # 占地面积
    
        def __str__(self):
            return "[%s] 占地面积 %.2f" % (self.name, self.area)
    
    
    bed = HouseItem("bed", 4)  # 席梦思
    chest = HouseItem("chest", 2)  # 衣柜
    table = HouseItem("table", 1.5)  # 餐桌
    print(bed)
    print(chest)
    print(table)
    
    
    class House:
    
        def __init__(self, apartment, area):
            self.apartment = apartment  # 户型
            self.total_area = area  # 总面积
            self.free_area = area  # 剩余面积开始的时候和总面积是一样的
            self.furniture_list = []  # 家具名称列表 默认是没有放置任何家具的
    
        def add_item(self, furniture):
            """添加家具"""
            print(f"添加:{furniture.name}")
            # 判断家具的面积是否超过剩余面积,如果超过,提示不能添加这件家具
            if furniture.area > self.free_area:
                print("房间剩余面积不够,不能添置这件家具...")
                return
            self.furniture_list.append(furniture.name)  # 将家具名称追加到家具名称列表中
            self.free_area -= furniture.area  # 用房子的剩余面积-家具面积
    
        def __str__(self):
            return f"房子户型为:{self.apartment},总面积为:{self.total_area}," \
                   f"剩余面积为{self.free_area},家具列表为:{self.furniture_list}"
    
    
    if __name__ == "__main__":
        my_house1 = House("两室一厅", 60)
        my_house1.add_item(bed)
        my_house1.add_item(chest)
        my_house1.add_item(table)
        print(my_house1)
    
        my_house2 = House("一室一厅", 10)
        my_house2.add_item(bed)
        my_house2.add_item(chest)
        my_house2.add_item(table)
        my_house2.add_item(bed)
        print(my_house2)
    

    代码运行结果如下:
    在这里插入图片描述

    6. 私有属性

    # -*- coding: utf-8 -*-
    # @Time    : 2019/12/20 10:08
    # @Author  : 我就是任性-Amo
    # @FileName: 6.私有属性.py
    # @Software: PyCharm
    # @Blog    :https://blog.csdn.net/xw1680
    
    """
    应用场景:
      在实际开发中,对象的某些属性或方法可能只希望在对象的内部被使用,而不希望在外部被访问到
      私有属性就是对象不希望公开的属性
      私有方法就是对象不希望公开的方法
    定义的方式: 在定义属性或方法时,在属性名或者方法名前增加两个下划线,定义的就是私有属性或方法
    在java中的话是使用private关键字
    """
    
    
    class Women:
        def __init__(self, name):
            self.name = name
            self.__age = 18
    
        def __secret(self):
            print("我的年龄是 %d" % self.__age)
    
    
    xiao_fang = Women("小芳")
    # 私有属性外界不能直接访问
    # print(xiao_fang.__age) 报错:AttributeError: 'Women' object has no attribute '__age'
    # 私有方法,外部不嫩直接调用
    # xiao_fang.__secret()
    
    # 在日常开发中,不要使用这种方式,访问对象的私有属性或私有方法
    # 在Python中,并没有真正意义的私有
    # 在给属性、方法 命名时,实际是对名称做了一些特殊处理,使得外界无法访问到
    # 处理方式:在名称前面加上 _类名 => _类名__名称
    
    
    print(xiao_fang.__dict__)  # 查看名称空间
    print(xiao_fang._Women__age)
    # print(Women.__dict__)
    xiao_fang._Women__secret()
    

    程序运行结果如下:
    在这里插入图片描述

    7. 继承

    7.1 面向对象的三大特性

    • 封装: 根据职责将属性和方法封装到一个抽象的类中
    • 继承: 实现代码的重用,相同的代码不需要重复的编写
    • 多态: 不同的对象调用相同的方法,产生不同的执行结果,增加代码的灵活度

    7.2 单继承

    7.2.1 继承的概念

    继承的概念: 子类拥有父类的所有方法和属性

    在这里插入图片描述

    # ---------不使用继承开发动物和狗---------------
    class Animal:
    
        def eat(self):
            print("吃")
    
        def drink(self):
            print("喝")
    
        def run(self):
            print("跑")
    
        def sleep(self):
            print("睡")
    
    
    class Dog:
    
        def eat(self):  # 不使用继承重复书写的代码太多
            print("吃")
    
        def drink(self):
            print("喝")
    
        def run(self):
            print("跑")
    
        def sleep(self):
            print("睡")
    
        def bark(self):
            print("汪汪叫")
    
    
    # 创建一个狗对象
    wang_cai = Dog()
    
    wang_cai.eat()
    wang_cai.drink()
    wang_cai.run()
    wang_cai.sleep()
    wang_cai.bark()
    # # ---------使用继承开发动物和狗---------------
    class Animal:
        def eat(self):
            print("吃---")
    
        def drink(self):
            print("喝---")
    
        def run(self):
            print("跑---")
    
        def sleep(self):
            print("睡---")
    
    
    class Dog(Animal):
        def bark(self):
            print("汪汪叫---")
    
    
    # 创建一个狗对象
    wang_cai = Dog()
    wang_cai.eat()
    wang_cai.drink()
    wang_cai.run()
    wang_cai.sleep()
    wang_cai.bark()  # 直接享受父类中已经封装好的方法 不需要再次开发
    

    7.2.2 继承的语法

    class 类名(父类名):
    	pass
    
    1. 子类继承自父类,可以直接享受父类中已经封装好的方法,不需要再次开发
    2. 子类中应该根据职责,封装子类特有的属性和方法
    3. 专业术语
      • Dog类是Animal类的子类,Animal类是Dog类的父类,Dog类从Animal类继承
      • Dog类是Animal类的派生类,Animal类是Dog类的基类,Dog类从Animal类派生
    4. 继承的传递性
      C类从B类继承,B类又从A类继承,那么C类就具有B类和A类的所有属性和方法
    # -*- coding: utf-8 -*-
    # @Time    : 2019/12/21 10:52
    # @Author  : 我就是任性-Amo
    # @FileName: 9.继承的传递性.py
    # @Software: PyCharm
    # @Blog    :https://blog.csdn.net/xw1680
    
    
    class Animal:
    
        def eat(self):
            print("吃---")
    
        def drink(self):
            print("喝---")
    
        def run(self):
            print("跑---")
    
        def sleep(self):
            print("睡---")
    
    
    class Dog(Animal):
    
        def bark(self):
            print("汪汪叫")
    
    
    class XiaoTianQuan(Dog):
    
        def fly(self):
            print("我会飞")
    
    
    # 创建一个哮天犬的对象
    xtq = XiaoTianQuan()
    
    xtq.fly()
    xtq.bark()
    xtq.eat()
    

    程序运行结果如下:
    在这里插入图片描述

    7.2.3 方法的重写

    1. 子类拥有父类的所有方法和属性
    2. 子类继承自父类,可以直接享受父类中已经封装好的方法,不需要再次开发
    3. 应用场景: 当父类的方法实现不能满足子类需求时,可以对方法进行 重写(override)
      在这里插入图片描述
    4. 重写父类的方法有两种情况
    • 覆盖父类的方法

    如果在开发中,父类的方法实现和子类的方法实现,完全不同。就可以使用覆盖的方式,在子类中重新编写父类的方法实现。具体的实现方式,就相当于在子类中定义了一个和父类同名的方法并且实现。重写之后,在运行时,只会调用子类中重写的方法,而不再会调用父类封装的方法

    # -*- coding: utf-8 -*-
    # @Time    : 2019/12/21 10:55
    # @Author  : 我就是任性-Amo
    # @FileName: 10.覆盖父类方法.py
    # @Software: PyCharm
    # @Blog    :https://blog.csdn.net/xw1680
    
    
    class Animal:
    
        def eat(self):
            print("吃---")
    
        def drink(self):
            print("喝---")
    
        def run(self):
            print("跑---")
    
        def sleep(self):
            print("睡---")
    
    
    class Dog(Animal):
    
        def bark(self):
            print("汪汪叫")
    
    
    class XiaoTianQuan(Dog):
    
        def fly(self):
            print("我会飞")
    
        def bark(self):
            print("叫得跟神一样...")
    
    
    xtq = XiaoTianQuan()
    
    # 如果子类中,重写了父类的方法
    # 在使用子类对象调用方法时,会调用子类中重写的方法
    xtq.bark()
    
    • 对父类方法进行扩展

    如果在开发中,子类的方法实现中包含父类的方法实现。父类原本封装的方法实现是子类方法的一部分。就可以使用扩展的方式:
    1. 在子类中重写父类的方法
    2. 在需要的位置使用super().父类方法来调用父类方法的执行
    3. 代码其他的位置针对子类的需求,编写子类特有的代码实现

    1. 关于super
      • Pythonsuper是一个特殊的类
      • super()就是使用super类创建出来的对象
      • 最常使用的场景就是在重写父类方法时,调用在父类中封装的方法实现
      • 在Python 2.x时,如果需要调用父类的方法,还可以使用以下方式:父类名.方法(self)
      • 这种方式,目前在 Python 3.x 还支持这种方式
      • 这种方法不推荐使用,因为一旦父类发生变化,方法调用位置的类名同样需要修改
      • 在开发时,父类名和 super()两种方式不要混用
      • 如果使用当前子类名调用方法,会形成递归调用,出现死循环
    # -*- coding: utf-8 -*-
    # @Time    : 2019/12/21 11:03
    # @Author  : 我就是任性-Amo
    # @FileName: 11.扩展父类方法.py
    # @Software: PyCharm
    # @Blog    :https://blog.csdn.net/xw1680
    
    
    class Animal:
    
        def eat(self):
            print("吃---")
    
        def drink(self):
            print("喝---")
    
        def run(self):
            print("跑---")
    
        def sleep(self):
            print("睡---")
    
    
    class Dog(Animal):
    
        def bark(self):
            print("汪汪叫")
    
    
    class XiaoTianQuan(Dog):
    
        def fly(self):
            print("我会飞")
    
        def bark(self):
            # 1.针对子类特有的需求,编写代码
            print("神一样的叫唤...")
            # 2.使用super().调用原本在父类中封装的方法
            # super().bark()
            # 父类名.方法(self)
            Dog.bark(self)  # 但是一旦父类名改变 这里也要随之改变
            # 注意: 如果使用子类调用方法,会出现递归调用-死循环!
            # XiaoTianQuan.bark(self)
            # 3. 增加其他子类的代码
            print("$%^*%^$%^#%$%")
    
    
    xtq = XiaoTianQuan()
    
    # 如果子类中,重写了父类的方法
    # 在使用子类对象调用方法时,会调用子类中重写的方法
    xtq.bark()
    

    程序运行结果如下:
    在这里插入图片描述

    7.2.4 父类的私有属性和私有方法

    • 子类对象不能在自己的方法内部,直接访问父类的私有属性或私有方法
    • 子类对象可以通过父类的公有方法间接访问到私有属性或私有方法
    • 示例:
      在这里插入图片描述
    • B的对象不能直接访问 __num2属性
    • B的对象不能在demo方法内访问 __num2属性
    • B的对象可以在demo方法内,调用父类的test方法
    • 父类的test方法内部,能够访问__num2属性和__test方法
    # -*- coding: utf-8 -*-
    # @Time    : 2019/12/21 11:11
    # @Author  : 我就是任性-Amo
    # @FileName: 13.父类的公有方法.py
    # @Software: PyCharm
    # @Blog    :https://blog.csdn.net/xw1680
    
    
    class A:
    
        def __init__(self):
            self.num1 = 100
            self.__num2 = 200
    
        def __test(self):
            print("私有方法 %d %d" % (self.num1, self.__num2))
    
        def test(self):
            print("父类的公有方法 %d" % self.__num2)
    
            self.__test()
    
    
    class B(A):
    
        def demo(self):
            # 1. 在子类的对象方法中,不能访问父类的私有属性
            # print("访问父类的私有属性 %d" % self.__num2)
    
            # 2. 在子类的对象方法中,不能调用父类的私有方法
            # self.__test()
    
            # 3. 访问父类的公有属性
            print("子类方法 %d" % self.num1)
    
            # 4. 调用父类的公有方法
            self.test()
            pass
    
    
    # 创建一个子类对象
    b = B()
    print(b)
    
    b.demo()
    # 在外界访问父类的公有属性/调用公有方法
    # print(b.num1)
    # b.test()
    
    # 在外界不能直接访问对象的私有属性/调用私有方法
    # print(b.__num2)
    # b.__test()
    

    7.3 多继承

    7.3.1 概念及语法

    • 子类可以拥有多个父类,并且具有所有父类的属性和方法
    • 例如:孩子会继承自己 父亲和母亲的特性
      在这里插入图片描述
    • 语法:
    class 子类名(父类名1, 父类名2...):
    	pass
    
    class A:
    
        def test(self):
            print("test 方法")
    
    
    class B:
    
        def demo(self):
            print("demo 方法")
    
    
    class C(A, B):
        """多继承可以让子类对象,同时具有多个父类的属性和方法"""
        pass
    
    
    # 创建子类对象
    c = C()
    
    c.test()
    c.demo()
    

    7.3.2 多继承使用注意事项

    • 如果不同的父类中存在同名的方法,子类对象在调用方法时,会调用哪一个父类中的方法呢?
    提示:开发时,应该尽量避免这种容易产生混淆的情况。
    如果父类之间存在同名的属性或者方法,应该尽量避免使用多继承
    

    在这里插入图片描述

    class A:
    
        def test(self):
            print("A --- test 方法")
    
        def demo(self):
            print("A --- demo 方法")
    
    class B:
    
        def test(self):
            print("B --- test 方法")
    
        def demo(self):
            print("B --- demo 方法")
    
    
    class C(B, A):
        """多继承可以让子类对象,同时具有多个父类的属性和方法"""
        pass
    
    
    # 创建子类对象
    c = C()
    
    c.test()
    c.demo()
    
    # 确定C类对象调用方法的顺序
    print(C.__mro__)
    

    7.3.3 新式类与旧式(经典)类

    object是Python为所有对象提供的基类,提供有一些内置的属性和方法,可以使用dir函数查看

    在Python3中,使用dir函数查看效果图如下:
    在这里插入图片描述
    在Python2中,使用dir函数查看效果如下:
    在这里插入图片描述

    • 新式类:以object为基类的类,推荐使用
    • 经典类:不以object为基类的类,不推荐使用
    • 在Python3.x中定义类时,如果没有指定父类,会默认使用object 作为该类的基类——Python 3.x 中定义的类都是新式类
    • 在Python2.x中定义类时,如果没有指定父类,则不会以object作为基类
    • 为了保证编写的代码能够同时在Python 2.x和Python3.x运行!今后在定义类时,如果没有父类,建议统一继承自object

    7.3.4 继承的实现原理

    python到底是如何实现继承的,对于你定义的每一个类,python会计算出一个方法解析顺序(MRO)列表,这个MRO列表就是一个简单的所有基类的线性顺序列表,例如

    (<class '__main__.F'>, <class '__main__.D'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)
    

    为了实现继承,python会在MRO列表上从左到右开始查找基类,直到找到第一个匹配这个属性的类为止。而这个MRO列表的构造是通过一个C3线性化算法来实现的。我们不去深究这个算法的数学原理,它实际上就是合并所有父类的MRO列表并遵循如下三条准则:

    • 子类会先于父类被检查
    • 多个父类会根据它们在列表中的顺序被检查
    • 如果对下一个类存在两个合法的选择,选择第一个父类
    • 在Java和C#中子类只能继承一个父类,而Python中子类可以同时继承多个父类,如果继承了多个父类,那么属性的查找方式有两种,分别是:深度优先和广度优先
      在这里插入图片描述
      在这里插入图片描述
      示例代码:
    # -*- coding: utf-8 -*-
    # @Time    : 2019/12/24 09:30
    # @Author  : 我就是任性-Amo
    # @FileName: 15.新式类与经典类.py
    # @Software: PyCharm
    # @Blog    :https://blog.csdn.net/xw1680
    
    
    class A(object):
        def test(self):
            print('from A')
    
    
    class B(A):
        def test(self):
            print('from B')
    
    
    class C(A):
        def test(self):
            print('from C')
    
    
    class D(B):
        def test(self):
            print('from D')
    
    
    class E(C):
        def test(self):
            print('from E')
    
    
    class F(D, E):
        # def test(self):
        #     print('from F')
        pass
    
    
    f1 = F()
    f1.test()
    print(F.__mro__)  # 只有新式才有这个属性可以查看线性列表,经典类没有这个属性
    # import inspect  # 使用inspect模块中的getmro()方法可以查看python2.x的mro顺序
    

    MRO顺序如下:

    # 新式类继承顺序如下:
    # <class '__main__.F'>, 
    # <class '__main__.D'>, 
    # <class '__main__.B'>, 
    # <class '__main__.E'>, 
    # <class '__main__.C'>, 
    # <class '__main__.A'>, 
    # <class 'object'>
    
    # 经典类继承顺序如下:
    # <class __main__.F at 0x1033b3c18>,
    # <class __main__.D at 0x1033b3b48>,
    # <class __main__.B at 0x1033b3a78>,
    # <class __main__.A at 0x1033b3a10>,
    # <class __main__.E at 0x1033b3bb0>,
    # <class __main__.C at 0x1033b3ae0>
    

    8. 多态

    不同的子类对象调用相同的父类方法,产生不同的执行结果

    • 多态可以增加代码的灵活度
    • 以继承和重写父类方法为前提
    • 是调用方法的技巧,不会影响到类的内部设计
      在这里插入图片描述

    8.1 多态案例演练

    • 在Dog类中封装方法 game 普通狗只是简单的玩耍
    • 定义XiaoTianDog继承自Dog,并且重写game方法 哮天犬需要在天上玩耍
    • 定义 Person 类,并且封装一个和狗玩的方法 在方法内部,直接让狗对象调用game方法在这里插入图片描述
    # -*- coding: utf-8 -*-
    # @Time    : 2019/12/24 15:20
    # @Author  : 我就是任性-Amo
    # @FileName: 17.多态.py
    # @Software: PyCharm
    # @Blog    :https://blog.csdn.net/xw1680
    
    
    class Dog(object):
    
        def __init__(self, name):
            self.name = name
    
        def game(self):
            print("%s 蹦蹦跳跳的玩耍..." % self.name)
    
    
    class XiaoTianDog(Dog):
    
        def game(self):
            print("%s 飞到天上去玩耍..." % self.name)
    
    
    class Person(object):
    
        def __init__(self, name):
            self.name = name
    
        def game_with_dog(self, dog):
            print("%s 和 %s 快乐的玩耍..." % (self.name, dog.name))
    
            # 让狗玩耍
            dog.game()
    
    
    # 1. 创建一个狗对象
    # wang_cai = Dog("旺财")
    wang_cai = XiaoTianDog("飞天旺财")
    
    # 2. 创建一个小明对象
    xiao_ming = Person("小明")
    
    # 3. 让小明调用和狗玩的方法
    xiao_ming.game_with_dog(wang_cai)
    

    程序运行结果如下:
    在这里插入图片描述

    8.2 案例总结

    1. Person类中只需要让狗对象调用game方法,而不关心具体是什么狗 game方法是在Dog父类中定义的
    2. 在程序执行时,传入不同的狗对象实参,就会产生不同的执行效果
    3. 多态更容易编写出通用的代码,做出通用的编程,以适应需求的不断变化!

    9. 类属性和类方法

    9.1 类的结构

    9.1.1 术语——实例

    1. 使用面相对象开发,第1步是设计类
    2. 使用类名()创建对象,创建对象的动作有两步:
      • 在内存中为对象分配空间
      • 调用初始化方法__init__为对象初始化
    3. 对象创建后,内存中就有了一个对象的实实在在的存在—实例
      在这里插入图片描述
      因此,通常也会把:
      1. 创建出来的对象叫做类的实例
      2. 创建对象的动作叫做实例化
      3. 对象的属性叫做实例属性
      4. 对象调用的方法叫做实例方法

    在程序执行时:对象各自拥有自己的实例属性 调用对象方法,可以通过 self.访问自己的属性和调用自己的方法

    结论:

    • 每一个对象都有自己独立的内存空间,保存各自不同的属性
    • 多个对象的方法,在内存中只有一份,在调用方法时,需要把对象的引用传递到方法内部

    9.1.2 类是一个特殊的对象

    Python中一切皆对象,class AAA: 定义的类属于类对象 obj1 = AAA() 属于实例对象

    • 在程序运行时,类同样会被加载到内存
    • 在Python中,类是一个特殊的对象–类对象
    • 在程序运行时,类对象在内存中只有一份,使用一个类可以创建出很多个对象实例
    • 除了封装实例的属性和方法外,类对象还可以拥有自己的属性和方法
      • 类属性
      • 类方法
    • 通过类名.的方式可以访问类的属性或者调用类的方法
      在这里插入图片描述

    9.2 类属性和实例属性

    9.2.1 概念和使用

    • 类属性就是给类对象中定义的属性
    • 通常用来记录与这个类相关的特征
    • 类属性不会用于记录具体对象的特征

    示例需求:

    1. 定义一个工具类
    2. 每件工具都有自己的name
    3. 需求——知道使用这个类,创建了多少个工具对象?

    在这里插入图片描述

    class Tool(object):
    
        # 使用赋值语句,定义类属性,记录创建工具对象的总数
        count = 0
    
        def __init__(self, name):
            self.name = name
    
            # 针对类属性做一个计数+1
            Tool.count += 1
    
    
    # 创建工具对象
    tool1 = Tool("斧头")
    tool2 = Tool("榔头")
    tool3 = Tool("铁锹")
    
    # 知道使用 Tool 类到底创建了多少个对象?
    print("现在创建了 %d 个工具" % Tool.count)
    

    在这里插入图片描述

    9.2.2 属性的获取机制

    在Python中属性的获取存在一个向上查找机制

    在这里插入图片描述
    因此,要访问类属性有两种方式:

    • 类名.类属性
    • 对象.类属性 (不推荐)

    如果使用对象.类属性 = 值赋值语句,只会给对象添加一个属性,而不会影响到类属性的值

    9.3 类方法和静态方法

    9.3.1 类方法

    • 类属性就是针对类对象定义的属性
      • 使用赋值语句在class关键字下方可以定义类属性
      • 类属性用于记录与这个类相关的特征
    • 类方法就是针对类对象定义的方法
      • 在类方法内部可以直接访问类属性或者调用其他的类方法
    • 语法如下
    @classmethod
    def 类方法名(cls):
    	pass
    
    • 类方法需要用@classmethod来标识,告诉解释器这是一个类方法
    • 类方法的第一个参数应该是cls
      • 由哪一个类调用的方法,方法内的cls就是哪一个类的引用
      • 这个参数和实例方法的第一个参数是self类似
      • 提示使用其他名称也可以,不过习惯使用cls
    • 通过类名.调用类方法,调用方法时,不需要传递cls参数
    • 在方法内部
      • 可以通过cls.访问类的属性
      • 也可以通过cls.调用其他的类方法
        在这里插入图片描述
    # 定义一个工具类 每件工具都有自己的name 需求—在类封装一个show_tool_count的类方法,输出使用当前这个类,创建的对象个数
    @classmethod
    def show_tool_count(cls):
    	"""显示对象工具的总数"""
    	print("工具对象的总数 %d" % cls.count)
    # 在类方法内部,可以直接使用cls访问类属性或者调用类方法
    

    9.3.2 静态方法

    • 在开发时,如果需要在类中封装一个方法,这个方法:
      • 既不需要访问实例属性或者调用实例方法
      • 也不需要访问类属性或者调用类方法
    • 这个时候,可以把这个方法封装成一个静态方法
    • 语法如下
    @staticmethod
    def 静态方法名():
        pass
    
    • 静态方法需要用@staticmethod来标识,告诉解释器这是一个静态方法
    • 通过类名. 调用静态方法
    class Dog(object):
        
        # 狗对象计数
        dog_count = 0
        
        @staticmethod
        def run():
            
            # 不需要访问实例属性也不需要访问类属性的方法
            print("狗在跑...")
    
        def __init__(self, name):
            self.name = name
    

    9.3.3 方法综合案例

    • 设计一个Game类
    • 属性:
      • 定义一个类属性top_score记录游戏的历史最高分
      • 定义一个实例属性player_name记录当前游戏的玩家姓名
    • 方法:
      • 静态方法show_help显示游戏帮助信息
      • 类方法show_top_score显示历史最高分
      • 实例方法start_game开始当前玩家的游戏
    • 主程序步骤
      1. 查看帮助信息
      2. 查看历史最高分
      3. 创建游戏对象,开始游戏在这里插入图片描述
    class Game(object):
    
        # 游戏最高分,类属性
        top_score = 0
    
        @staticmethod
        def show_help():
            print("帮助信息:让僵尸走进房间")
            
        @classmethod
        def show_top_score(cls):
            print("游戏最高分是 %d" % cls.top_score)
    
        def __init__(self, player_name):
            self.player_name = player_name
    
        def start_game(self):
            print("[%s] 开始游戏..." % self.player_name)
            
            # 使用类名.修改历史最高分
            Game.top_score = 999
    
    # 1. 查看游戏帮助
    Game.show_help()
    
    # 2. 查看游戏最高分
    Game.show_top_score()
    
    # 3. 创建游戏对象,开始游戏
    game = Game("小明")
    
    game.start_game()
    
    # 4. 游戏结束,查看游戏最高分
    Game.show_top_score()
    # 如果方法内部即需要访问实例属性,又需要访问类属性,应该定义成什么方法?
    # 应该定义实例方法
    # 因为,类只有一个,在实例方法内部可以使用类名.访问类属性
    

    9.3.4 案例小结

    • 实例方法 —— 方法内部需要访问实例属性
      • 实例方法内部可以使用类名.访问类属性
    • 类方法 —— 方法内部只需要访问类属性
    • 静态方法 —— 方法内部,不需要访问实例属性和类属性
    展开全文
  • python 面向对象

    2016-09-19 13:44:02
    一.[python] 类常用的内置方法 二.Python 面向对象编程(一) 三.Python面向对象编程(二)
    展开全文
  • Python 面向对象

    万次阅读 2020-02-04 13:13:33
    Python从设计之初就已经是一门面向对象的语言,正因为如此,在Python中创建一个类和对象是很容易的。本章节我们将详细介绍Python面向对象编程。 如果你以前没有接触过面向对象的编程语言,那你可能需要先了解一些...

    Python从设计之初就已经是一门面向对象的语言,正因为如此,在Python中创建一个类和对象是很容易的。本章节我们将详细介绍Python的面向对象编程。

    如果你以前没有接触过面向对象的编程语言,那你可能需要先了解一些面向对象语言的一些基本特征,在头脑里头形成一个基本的面向对象的概念,这样有助于你更容易的学习Python的面向对象编程。

    接下来我们先来简单的了解下面向对象的一些基本特征。

    面向对象技术简介

    • 类(Class): 用来描述具有相同的属性和方法的对象的集合。它定义了该集合中每个对象所共有的属性和方法。对象是类的实例。
    • 类变量:类变量在整个实例化的对象中是公用的。类变量定义在类中且在函数体之外。类变量通常不作为实例变量使用。
    • 数据成员:类变量或者实例变量, 用于处理类及其实例对象的相关的数据。
    • 方法重写:如果从父类继承的方法不能满足子类的需求,可以对其进行改写,这个过程叫方法的覆盖(override),也称为方法的重写。
    • 局部变量:定义在方法中的变量,只作用于当前实例的类。
    • 实例变量:在类的声明中,属性是用变量来表示的。这种变量就称为实例变量,是在类声明的内部但是在类的其他成员方法之外声明的。
    • 继承:即一个派生类(derived class)继承基类(base class)的字段和方法。继承也允许把一个派生类的对象作为一个基类对象对待。例如,有这样一个设计:一个Dog类型的对象派生自Animal类,这是模拟"是一个(is-a)"关系(例图,Dog是一个Animal)。
    • 实例化:创建一个类的实例,类的具体对象。
    • 方法:类中定义的函数。
    • 对象:通过类定义的数据结构实例。对象包括两个数据成员(类变量和实例变量)和方法。
    展开全文

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 56,998
精华内容 22,799
关键字:

python面向对象

python 订阅