文章目录

  • 1. 初识对象
  • 2. 成员方法
  • 3. 类和对象
  • 4. 构造方法
  • 5. 内置方法 / 魔术方法
  • 5.1__str__() 字符串方法
  • 5.2__lt__() 小于比较方法
  • 5.3__le__() 小于等于比较方法
  • 5.4__eq__() 比较运算符实现方法
  • 6. 封装
  • 7. 继承
  • 7.1 继承的概念
  • 7.2 单继承
  • 7.3 多继承
  • 7.3 pass 关键字
  • 7.5 复写父类成员和调用父类成员
  • 8. 类型注解
  • 8.1 变量的类型注解
  • 8.2 函数和方法的类型注解
  • 8.2.1 函数 / 方法形参的类型注解
  • 8.2.2 函数 / 方法返回值的类型注解
  • 8.3 Union 联合类型注解
  • 8.3.1 为什么需要 Union 联合类型注解
  • 8.3.2 Union 联合类型注解的使用
  • 9. 多态
  • 9.1 多态的概念
  • 9.2 抽象类(接口)的编程思想
  • 10. 综合案例
  • 1. 初识对象

    设计类:

    class Student:
    	name = None
    

    创建对象:

    stu_1 = Student()
    stu_2 = Student()
    

    对象属性赋值:

    stu_1.name="tom"
    stu_2.name ="jerry"
    

    【例】

    class Student:
        name = None
        age = None
        gender = None
        nationality = None
    
    stu = Student()
    
    stu.name = "小明"
    stu.age = 11
    stu.gender = "男"
    stu.nationality = "中国"
    
    print(stu.name)
    print(stu.age)
    print(stu.gender)
    print(stu.nationality)
    

    2. 成员方法

    上一节介绍了如何封装一个类,并基于类创建对象来使用。现在来看类更详细的使用语法:

    class 类名称:
    	成员变量
    	成员方法
    

    在类中定义成员方法和在类外定义函数基本一致,但仍有细微区别:

    def 方法名(self,形参1,...,形参N):
    	方法体
    

    在定义方法时,形参列表中的 self 关键字必须填写,用于代表对象自身。
    当使用对象调用方法时,self 会被自动传入,即传参时忽略 self,当它不存在。
    在方法内部访问成员变量,必须使用 self。(Java 中只有方法内的变量与成员变量同名时,才必须使用 this)

    创建对象的语法:

    对象 = 类名()
    

    【例】

    class Student:
        name = None
        age = None
        def say_hi(self):
            print(f"hi, 我是{self.name}")
        def say_hello(self, msg):
            print(f"hello, 我是{self.name}, {msg}")
    
    stu1 = Student()
    stu1.name = "tom"
    stu1.say_hi()
    stu1.say_hello("希望大家多多关照")
    
    stu2= Student()
    stu2.name = "jerry"
    stu2.say_hi()
    stu2.say_hello("很高兴认识大家")
    

    输出结果:

    hi, 我是tom
    hello, 我是tom, 希望大家多多关照
    hi, 我是jerry
    hello, 我是jerry, 很高兴认识大家
    

    3. 类和对象

    类相当于模板,对象就是根据类这个模板生产出来的具体实体。

    class Clock:
        id = None
        price = None
        def ring(self): # 响铃
            import winsound
            # 参数1:频率;参数2:持续时间
            winsound.Beep(2000,3000)
    
    clock1 = Clock()
    clock1.id = 111
    clock1.price = 19.8
    print(f"闹钟{clock1.id}的价格是{clock1.price}")
    clock1.ring()
    
    clock2 = Clock()
    clock2.id = 222
    clock2.price = 32.3
    print(f"闹钟{clock2.id}的价格是{clock2.price}")
    clock2.ring()
    

    输出结果(输出的时候还会有响铃,声音略微感人~~):

    闹钟111的价格是19.8
    闹钟222的价格是32.3
    

    4. 构造方法

    看一段代码:

    class Student:
        name = None 
        age = None
        tel = None
        
    student1 = Student()
    student1.name ="tom"
    student1.age = 20
    student1.tel ="18012340000"
    
    student2 = Student()
    student2.name="jerry"
    student2.age = 30
    student2.tel ="19017840900"
    

    在上面代码中,为对象的属性赋值需要依次进行,略显繁琐。可以使用构造方法简化。

    可以使用构造方法__init__() 初始化对象:在创建对象时,会自动执行 __init__() ,并将传入参数自动传递给__init__()方法使用。

    构造方法使用1:

    class Student:
    	# 这三行定义可以不用写,见“构造方法使用2”
    	# 如果不写的话,__init__()构造方法中的self.name self.age self.gender 三条语句就既有定义作用,也有赋值作用
        name = None
        age = None
        gender = None
        def __init__(self, name, age, gender):
            self.name = name
            self.age = age
            self.gender = gender
            print("创建了Student对象")
    
    stu = Student("tom", 14, "男")
    print(stu.name)
    print(stu.age)
    print(stu.gender)
    

    输出结果:

    创建了Student对象
    tom
    14
    男
    

    构造方法使用2:

    class Student:
        def __init__(self, name, age, gender):
            self.name = name
            self.age = age
            self.gender = gender
            print("创建了Student对象")
    
    stu = Student("tom", 14, "男")
    print(stu.name)
    print(stu.age)
    print(stu.gender)
    

    输出结果:

    创建了Student对象
    tom
    14
    男
    

    5. 内置方法 / 魔术方法

    上文学习的__init__()构造方法,是 Python 类内置的方法之一。
    内置的类方法,各自有各自特殊的功能,也称为:魔术方法。主要介绍下面几个魔术方法:

    5.1__str__() 字符串方法

    print(对象) 或者 str(对象) 时,会自动调用__str__()方法。默认情况下,该方法返回的是对象的内存地址。

    class Student:
        name = None
        age = None
        def __init__(self, name, age):
            self.name = name
            self.age = age
    
    stu = Student("tom", 15)
    print(stu)
    print(str(stu))
    

    输出结果:

    <__main__.Student object at 0x01C6CB68>
    <__main__.Student object at 0x01C6CB68>
    

    但我们基本不关心对象的内存地址。因此,可以通过__str__()方法控制直接输出对象把对象转换成字符串的行为。

    class Student:
        name = None
        age = None
        def __init__(self, name, age):
            self.name = name
            self.age = age
        # 返回值:字符串;内容:自行定义
        def __str__(self):
            return f"Student对象:name={self.name}, age={self.age}"
    
    stu = Student("tom", 15)
    print(stu)
    print(str(stu))
    

    输出结果:

    Student对象:name=tom, age=15
    Student对象:name=tom, age=15
    

    5.2__lt__() 小于比较方法

    不能像下面这样直接比较两个对象:

    class Student:
        name = None
        age = None
        def __init__(self, name, age):
            self.name = name
            self.age = age
    
    stu1 = Student("tom", 15)
    stu2 = Student("jerry", 9)
    print(stu1 < stu2)
    print(stu1 > stu2)
    


    在类中实现__lt__()方法,可以同时完成小于和大于两种比较。(虽然名称是小于号比较方法,但是小于和大于都能比较)

    class Student:
        name = None
        age = None
        def __init__(self, name, age):
            self.name = name
            self.age = age
        # 传入参数:other,即另一个对象
        # 返回值:True或False
        # 内容:自行定义
        def __lt__(self, other):
            return self.age < other.age
    
    stu1 = Student("tom", 9)
    stu2 = Student("jerry", 15)
    print(stu1 < stu2)#True
    print(stu1 > stu2)#False
    

    5.3__le__() 小于等于比较方法

    __le__()可以用于 <= 或 >= 两种比较运算符上。

    class Student:
        name = None
        age = None
        def __init__(self, name, age):
            self.name = name
            self.age = age
        # 传入参数:other,即另一个对象
        # 返回值:True或False
        # 内容:自行定义
        def __le__(self, other):
            return self.age <= other.age
    
    stu1 = Student("tom", 9)
    stu2 = Student("jerry", 15)
    stu3 = Student("smith", 15)
    print(stu1 <= stu2)#True
    print(stu1 >= stu2)#False
    print(stu2 <= stu3)#True
    print(stu2 >= stu3)#True
    

    5.4__eq__() 比较运算符实现方法

    不实现__eq__()方法,对象之间可以进行 “==” 比较,但比较的是内存地址。所以不同对象比较一定是 False。

    class Student:
        name = None
        age = None
        def __init__(self, name, age):
            self.name = name
            self.age = age
    
    stu1 = Student("tom", 9)
    stu2 = Student("tom", 9)
    stu3 = Student("tom", 15)
    print(stu1 == stu2)#False
    print(stu1 == stu3)#False
    

    实现了__eq__()方法后,就可以按照需要来判断两个对象是否相等。

    class Student:
        name = None
        age = None
        def __init__(self, name, age):
            self.name = name
            self.age = age
        # 传入参数:other,即另一个对象
        # 返回值:True或False
        # 内容:自行定义
        def __eq__(self, other):
            return self.age == other.age
    
    stu1 = Student("tom", 9)
    stu2 = Student("tom", 9)
    stu3 = Student("tom", 15)
    print(stu1 == stu2)#True
    print(stu1 == stu3)#False
    

    6. 封装

    私有成员:包括私有成员变量和私有成员方法。

    定义私有成员的方式
    私有成员变量:变量名以__开头(2个下划线)
    私有成员方法:方法名以__开头(2个下划线)

    私有成员无法被类对象使用,但是可以被类中的其它成员使用。

    练习:
    设计一个手机类,内部包含:
    (1) 私有成员变量:__is_5g_enable,类型bool,True表示开启 5g,False表示关闭。
    (2) 私有成员方法:__check_5g(),用于判断私有成员__is_5g_enable 的值。
      若为 True,打印输出:5g开启。
      若为 False,打印输出:5g关闭,使用4g网络。
    (3) 公开成员方法:call_by_5g(),它会:
      调用私有成员方法__check_5g(),判断 5g 网络状态。
      打印输出正在通话中。

    输出样例:
    5g关闭,使用4g网络
    正在通话中

    class Phone:
        # 手机的内部信息不需要用户知道,所以做成私有的
        __is_5g_enable = False
    
        def __check_5g(self):
            if self.__is_5g_enable == True:
                print("5g开启")
            else:
                print("5g关闭,使用4g网络")
        # 用户只需要能够打电话
        def call_by_5g(self):
            self.__check_5g()
            print("正在通话中")
    
    phone = Phone()
    phone.call_by_5g()
    

    输出结果:

    5g关闭,使用4g网络
    正在通话中
    

    7. 继承

    7.1 继承的概念

    继承:从父类继承(复制)非私有的成员变量和成员方法,分为单继承和多继承。

    7.2 单继承

    语法:

    class 类名(父类名):
    	类内容体
    

    【例】

    class Phone:
        IMEI = None         #序列号
        producer = "HM"     #生产厂商
        def call_by_4g(self):
            print("4g通话")
    
    class NewPhone(Phone):
        face_id = "10010"   #面部识别id
        def call_by_5g(self):
            print("5g通话")
    
    phone = NewPhone()
    print(phone.producer)#HM
    phone.call_by_4g()#4g通话
    print(phone.face_id)#10010
    phone.call_by_5g()#5g通话
    

    7.3 多继承

    Python 的类也支持多继承,即:一个类可以继承多个父类。
    多个父类中,如果有同名的成员,那么默认以继承顺序(从左到右)为优先级。即:先继承的保留,后继承的被覆盖。

    语法:

    class 类名(父类1,父类2,...,父类N):
    	类内容体
    

    【例】

    class Phone:
        producer = "ITCAST" #生产厂商
        def call_by_4g(self):
            print("4g通话")
    
    class NFCReader:
        nfc_type = "第五代"
        producer = "HM"
        def read_card(self):
            print("NFC读卡")
        def write_card(self):
            print("NFC写卡")
    
    class Myphone(Phone, NFCReader):
        # 类体中不想写内容了,但是又不能空着,可以用pass占位
        pass
    
    my_phone = Myphone()
    #ITCAST, Phone类中的producer属性
    print(my_phone.producer)#ITCAST
    my_phone.call_by_4g()#4g通话
    print(my_phone.nfc_type)#第五代
    my_phone.read_card()#NFC读卡
    my_phone.write_card()#NFC写卡
    

    7.3 pass 关键字

    pass:占位语句,用来保证函数、方法或类定义的完整性,表示无内容,空的意思。具体用法见 7.4 代码中的 Myphone 类。

    7.5 复写父类成员和调用父类成员

    复写父类成员
    子类继承父类的成员属性和成员方法后,若对其 “不满意”,可以进行复写。即:在子类中重新定义同名的属性或方法。

    class Phone:
        producer = "ITCAST" #生产厂商
        def call_by_5g(self):
            print("父类中的5g通话")
    
    class Myphone(Phone):
        producer = "ITHEIMA" #生产厂商
        def call_by_5g(self):
            print("子类中的5g通话")
    
    my_phone = Myphone()
    print(my_phone.producer)#ITHEIMA
    my_phone.call_by_5g()#子类中的5g通话
    

    调用父类同名成员
    子类复写了父类成员后,子类对象调用成员时,就会调用复写后的新成员。若要使用父类成员,可以用以下两种调用方式:

    方式1:
    使用成员变量:父类名.成员变量
    使用成员方法:父类名.成员方法(self)

    方式2:
    使用成员变量:super().成员变量
    使用成员方法:super().成员方法()

    class Phone:
        producer = "ITCAST" #生产厂商
        def call_by_5g(self):
            print("父类中的5g通话")
    
    class Myphone(Phone):
        producer = "ITHEIMA" #生产厂商
        def call_by_5g(self):
        	# 有时可以通过这样,既添加新功能,又利用父类原有功能
            # 方式1
            print(f"父类中的producer={Phone.producer}")
            Phone.call_by_5g(self)
            # 方式2
            print(f"父类中的producer={super().producer}")
            super().call_by_5g()
    
    my_phone = Myphone()
    my_phone.call_by_5g()
    

    输出结果:

    父类中的producer=ITCAST
    父类中的5g通话
    父类中的producer=ITCAST
    父类中的5g通话
    

    注意:只可以在子类内部调用父类的同名成员,子类对象默认调用子类复写的成员。

    8. 类型注解

    类型注解:在代码中显式地说明数据的类型。

    主要功能:
    (1) 帮助第三方 IDE 工具(如PyCharm)对代码进行类型推断,协助代码提示。
    (2) 帮助开发者对变量进行类型注释。

    类型注解支持:变量的类型注解、函数(方法)形参列表和返回值的类型注解。

    8.1 变量的类型注解

    方式1:

    # 基础数据类型注解
    var_1: int = 10
    var_2: float = 3.1415926
    var_3: bool = True
    var_4: str ="itheima"
    # 对象类型注解
    class Student:
        pass
    stu: Student = Student()
    # 容器简单类型注解
    my_list1: list = [1, 2, 3]
    my_tuple1: tuple = (1, 2, 3)
    my_set1: set = {1, 2, 3}
    my_dict1: dict = {"itheima":666}
    my_str1: str = "itheima"
    # 容器详细类型注解
    my_list2: list[int] = [1, 2, 3]
    my_tuple2: tuple[str, int, bool] = ("itheima", 666, True)
    my_set2: set[int] = {1, 2, 3}
    my_dict2: dict[str, int] = {"itheima": 666}
    

    注意事项:
    元组设置详细类型注解,需要将每一个元素都标记出来。
    字典设置详细类型注解,需要两个类型,第一个是 key,第二个是 value。

    方式2:

    除了上面的方式,也可以在注释中进行变量的类型注解。
    语法:#type:类型


    为变量设置注解时,显式的变量定义一般无需注解。因为就算不写注解,也能够明确知晓变量的类型。


    当无法直接看出变量类型时,会添加变量的类型注解。

    注意:类型注解仅仅是建议性的,不是强制性的。所以,若数据类型与注解类型无法对应,也不会导致错误。如下面代码不会报错:

    var_1: int = "itheima"
    var_2: str = 123
    

    8.2 函数和方法的类型注解

    8.2.1 函数 / 方法形参的类型注解

    如下图所示,编写了函数 / 方法,使用形参 data 的时候,工具没有任何提示。
    在调用函数 / 方法传入参数的时候,工具无法提示参数类型。
    这都是因为,在定义函数 / 方法的时候,没有对形参进行注解。

    函数 / 方法的形参类型注解语法:

    def 函数/方法名(形参名: 类型, 形参名: 类型,....):
    	pass
    

    注解后的效果:

    8.2.2 函数 / 方法返回值的类型注解

    语法(使用->符号):

    def 函数/方法名(形参: 类型,...,形参: 类型) -> 返回值类型:
        pass
    

    【例】

    def func(data: list) -> list:
        return data
    

    另外,注解仅仅是建议性的,不是强制性的。所以,若数据类型与注解类型无法对应,也不会导致错误。

    8.3 Union 联合类型注解

    8.3.1 为什么需要 Union 联合类型注解

    如下面代码,当列表元素中只有一种数据类型、字典的 value 只有一种数据类型时,可以利用前面的方法方便地进行详细的类型注解。

    my_list: list[int] = [1, 2, 3]
    my_dict: dict[str, int] ={"age": 11, "num": 3}
    

    但是,若列表元素中有多于一种数据类型、字典的 value 多余一种数据类型时,利用前面的方法进行详细的类型注解就行不通了。

    my_list = [1, 2, "itcast", "itheima"]
    my_dict= {"name": "周杰轮", "age": 31}
    

    这时,可以使用 Union 定义联合类型注解:Union[类型,...,类型]

    8.3.2 Union 联合类型注解的使用

    变量的联合类型注解:

    from typing import Union
    # Union[str, int]表示类型是str和int中的一种
    my_list: list[Union[str, int]] = [1, 2, "itcast", "itheima"]
    my_dict: dict[str, Union[str, int]]= {"name": "周杰轮", "age": 31}
    

    函数的联合类型注解:

    9. 多态

    9.1 多态的概念

    多态:多种状态,即完成某个行为时,使用不同的对象会得到不同的状态。

    多态常常作用在继承关系上,比如:函数 / 方法的形参声明接收父类对象,但实际传入子类对象进行工作。

    class Animal:
        def speak(self):
            pass
    
    class Cat(Animal):
        def speak(self):
            print("喵喵喵")
    
    class Dog(Animal):
        def speak(self):
            print("汪汪汪")
    
    def make_noise(animal: Animal):
        animal.speak()
    
    cat = Cat()
    dog = Dog()
    make_noise(cat)#喵喵喵
    make_noise(dog)#汪汪汪
    

    9.2 抽象类(接口)的编程思想

    在 9.1 的代码中,父类 Animal 的 speak 方法内容为空。以使父类确定有哪些方法,子类决定方法的具体实现。

    抽象方法:方法体为空(只有pass)的方法称之为抽象方法,如:父类 Animal 的 speak 方法。
    抽象类:含有抽象方法的类称之为抽象类,如:Animal 类。

    为什么要使用抽象类
    举个例子:空调有一定的国家标准,假设为:可以制冷、可以制热、左右摆风……有了这个标准,各个厂家可以自行地去具体实现。

    抽象类就好比定义一个标准,它包含一些抽象方法,要求子类必须实现,从而达到约束子类的目的。
    定义了抽象类之后,并不会直接使用,即:不会直接用抽象类创建对象。真正工作的是抽象类的子类。

    class AC:	#国家标准
        def cool_wind(self):
            pass
        def hot_wind(self):
            pass
        def swing_l_r(self):
            pass
    
    class Midea_AC(AC):	#美的
        def cool_wind(self):
            print("美的冷风")
        def hot_wind(self):
            print("美的热风")
        def swing_l_r(self):
            print("美的左右摆风")
    
    class GREE_AC(AC):	#格力
        def cool_wind(self):
            print("格力冷风")
        def hot_wind(self):
            print("格力热风")
        def swing_l_r(self):
            print("格力左右摆风")
    
    def make_cool(ac: AC):#制冷
        ac.cool_wind()
    
    make_cool(Midea_AC())#美的冷风
    make_cool(GREE_AC())#格力冷风
    

    10. 综合案例

    所需文件:
    链接:https://pan.baidu.com/s/1nI0lFNOxptAmQ3kh5UHMEQ?pwd=sumg
    提取码:sumg

    要求:读取 “2011年1月销售数据.txt” 和 “2011年2月销售数据JSON.txt” 两个文件,统计每天各省的销售额之和,并用柱状图展示。两个文件的内容形式分别为:

    2011年1月销售数据.txt:

    2011年2月销售数据JSON.txt:

    效果图:

    代码(共三个文件):

    data_define.py

    class Record:
        def __init__(self, date, order_id, money, province):
            self.date = date            #订单日期
            self.order_id = order_id    #订单编号
            self.money = money          #订单金额
            self.province = province    #省份
    
        # 自定义输出对象时的输出格式
        def __str__(self):
            return f"{self.date} {self.order_id} {self.money} {self.province}"
    

    file_define.py

    import json
    from data_define import Record
    
    class FileRead:     #文件读取抽象类
        def read_data(self) -> list[Record]:
            pass
    
    class TextFileRead(FileRead):   #文本文件读取
        def __init__(self, path):   #传入文件路径
            self.path = path
    
        def read_data(self) -> list[Record]:
            f = open(self.path, "r", encoding="utf-8")
            records_list: list[Record] = [] #保存文件每行数据对象的列表
            for line in f.readlines():  #f.readlines()返回列表,列表中每个元素是一行数据
                line = line.strip()     #去掉”\n“
                data_list = line.split(",")#每行数据按照”,“拆分,存储在列表中
                # 每行数据的列表生成一个对象,追加到records_list中
                records_list.append(Record(data_list[0], data_list[1], int(data_list[2]), data_list[3]))
            f.close()
            return records_list
    
    class JsonFileRead(FileRead):      #json文件读取
        def __init__(self, path):      #传入文件路径
            self.path = path
    
        def read_data(self) -> list[Record]:
            f = open(self.path, "r", encoding="utf-8")
            records_list: list[Record] = []#保存文件每行数据对象的列表
            for line in f.readlines():      #f.readlines()返回列表,列表中每个元素是一行数据
                data_dict = json.loads(line)#不用去掉”\n“,直接把json字符串转换成字典,为什么?
                # 每行数据的字典生成一个对象,追加到records_list中
                records_list.append(Record(data_dict["date"], data_dict["order_id"], int(data_dict["money"]), data_dict["province"]))
            f.close()
            return records_list
    

    main.py

    from file_define import FileRead, TextFileRead, JsonFileRead
    from data_define import Record
    from pyecharts.charts import Bar
    from pyecharts.options import *
    from pyecharts.globals import ThemeType
    
    #text_file_read = TextFileRead("file/数据分析案例/2011年1月销售数据.txt")
    text_file_read = TextFileRead("E:/pythonProject/testpro01/file/数据分析案例/2011年1月销售数据.txt")
    jan_data_list = text_file_read.read_data()
    json_file_read = JsonFileRead("E:/pythonProject/testpro01/file/数据分析案例/2011年2月销售数据JSON.txt")
    feb_data_list = json_file_read.read_data()
    # 合并两个列表
    all_data_list = jan_data_list + feb_data_list
    date_money_dict = {}
    
    for record in all_data_list:
        if record.date in date_money_dict:
            date_money_dict[record.date] += record.money
        else:
            date_money_dict[record.date] = record.money
    
    # bar = Bar(init_opts=InitOpts(theme=ThemeType.LIGHT))#也可以
    bar = Bar({"theme":ThemeType.LIGHT})
    # date_money_dict.keys()、date_money_dict.values()得到的都是dict_keys类型
    bar.add_xaxis(list(date_money_dict.keys()))
    # label_opts=LabelOpts(is_show=False):每个柱不显示数字
    bar.add_yaxis("销售额",list(date_money_dict.values()), label_opts=LabelOpts(is_show=False))
    bar.set_global_opts(
        # 柱状图标题
        title_opts=TitleOpts(title="每日销售额柱状图", pos_left="center", pos_bottom="1%")
    )
    bar.render("每日销售额.html")
    
    物联沃分享整理
    物联沃-IOTWORD物联网 » 8. Python 面向对象

    发表评论