Python 类和对象

233 阅读9分钟

1.初识对象:什么是数据的组织呢?组织数据就是把数据按照类别分到一起,例如要记录学生的信息,可以做一个表格:

Screenshot_20231109_122708_edit_950696026385819.jpg 这样把所有学生的姓名数据放到一起,进而实现数据的组织。在程序中,是可以做到和生活中的那样,设计表格,做表格,填写表格的。在程序中设计表格就是设计类(class),

class  Student:
    name None     # 记录学生的姓名

在程序中做表格叫做创建对象:

# 基于类创建对象
stu_1 = Student()
stu_2 = Student()

在程序中填写表格就是对象属性赋值:

stu_1.name = "周杰伦"       # 为学生1对象赋予名称属性值
stu_2.name = "林俊杰"       # 为学生2对象赋予名称属性值

2.类的定义和使用:

Screenshot_20231109_131446_edit_953615222036146.jpg 可以看出:类不仅可以定义属性用来记录数据,也可以定义函数用来记录行为;其中类定义的属性(变量),可称之为:成员变量,类定义的行为(函数),可称之为成员方法。

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

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

可以看到,在类的成员方法定义的参数列表中,有一个:self关键词,这个关键词是成员方法定义时必须填写的,它用来表示类对象自身的意思,当使用类对象调用方法的是,self会自动被python传入,在方法内部,想要访问类成员的成员变量,必须使用self。但是,self关键字尽管在参数列表中,传参时依然可以忽略它。如下面代码:

class Student:
    name = None
    
    def say_hi(self):
        print("Hello 大家好!")
    def say_hi2(self,msg):
        print(f"Hello 大家好, {msg}")
stu = Student()        
stu.say_hi()        # 调用的时候不需传参
stu.say_hi2("很高兴认识大家")      # 调用的时候,需要传入msg参数   

可以看到,在传入参数的时候,self是透明的,可以忽略的。

3.类和对象

在程序中我们要有用类描述现实世界事物的思想:现实事物也有属性和方法;事物名就是类的属性,事物要执行的行为就是类当中的方法,所以现实世界中的很多事物都可以用代码来表现出来。类和对象的关系: 类只是一种程序内的“设计图纸”,需要基于图纸生产实体(即对象),才能正常工作;这种套路就称之为:面向对象编程(使用对象进行编程)。如以下代码:

# 设置一个闹钟类
class clock:            # 属性
    id = None       # 序列号
    price = None        # 价格
    def ring(self):     # 行为 响铃
        import winsound         #
        winsound.Beep(2000, 3000)       # 2000是闹钟的频率,3000是响铃的持续时间
# 构建2个闹钟
clock1 = clock()        # 基于类创建对象
clock1.id = "0032"
clock1.price = "19.9"
print(f"闹钟id:{clock1.id},价格:{clock1.price}")
clock1.ring()
clock2 = clock()        # 基于类创建对象
clock2.id = "0033"
clock2.price = "19.9"
clock2.ring()
print(f"闹钟id:{clock2.id},价格:{clock2.price}")

上面的代码创建了2个闹钟并实现它们的响铃行为。 4.构造方法:

Python类可以使用_init_()方法,称之为构造方法。使用这种方法它可以实现以下操作:(1)在创建类对象(即构造类的时候),会自动执行。(2)在创建类的时候,将传入的参数自动传递__init__()方法使用。 如以下的代码:

class Student:
    name = None
    age = None
    telephone = None
    def __init__(self, name, age, telephone):
        self.name = name
        self.age = age
        self.telephone = telephone
        print("Student创建了一个类对象")
stu = Student("周杰伦", 31, "190732162")
print(stu.name)
print(stu.age)
print(stu.telephone)

它的运行结果是:

IMG20231110204901_edit_1029770771995993.jpg 从运行结果中我们可以看到“Student创建了一个类对象”放在最前面,说明了__init__()它有自动执行功能,而下面的“周杰伦,31,190732162”的出现说明了它会自动传入参数。 注意事项:1.构造方法名称:init,init前后都有2个下划线。2.构造方法也是成员方法,在参数列表中需要使用self关键字。3.在构造方法定义内定义成员变量,需要使用self关键字。

5.魔术方法:上面的__init__方法是python的内置方法之一,这些内置方法各有各的特殊功能,这些内置方法我们统称为:魔术方法。以下是要学习的一些常见的内置方法:

Screenshot_20231110_211744_edit_1031179932164154.jpg (1)字符串的方法:

Screenshot_20231110_212150.jpg 内存地址没有多大的作用,可以通过__str__方法,来控制类转换为字符串的行为。

(2)lt()小于符号比较方法:

Screenshot_20231110_230923_edit_1037211443713437.jpg 直接对2个对象比较是行不通的,但是在类中实现__lt__方法,就可以同时完成小于符号和大于符号2种比较了。方法名:lt;传入参数:other,另一个类对象;返回值:True或False;内容:自定义就好。

(4)__le__小于等于比较符号方法: __le__可用于>=和<=2种比较运算符上;方法名:le;传入参数:other,另一个类对象;返回值:True或False;内容:自定义即可。

Screenshot_20231110_232318_edit_1038016920159331.jpg (5)__eq__比较运算符实现方法

Screenshot_20231110_232318_edit_1038016920159331.jpg 方法名:eq;传入参数:other,另一个类对象;返回值:True和False;内容:自行定义。注意:不实现__eq__方法,对象之间可以比较,但是比较的是内存地址,也就是不同对象比较一定是False。实现了__eq__方法,就可以按照自己的想法来决定2个对象是否相等了。 最后如以下代码:

    def __init__(self, name, age):
        self.name = name
        self.age = age
    def __str__(self):      # 字符串方法
        return f"Student类对象,name:{self.name}, age:{self.age}"       # 返回字符串
    def __lt__(self, other):        # 小于符号方法
        return self.age < other.age     
    def __le__(self, other):        # 小于等于符号方法       other是另一个类对象
        return self.age <= other.age
    def __eq__(self, other):        # 等于符号方法
        return self.age == other.age
stu =   Student("周杰伦", 31)
print(stu)
print(str(str))
stu1 = Student("李荣浩", 30)
stu2 = Student("关晓彤", 25)
print(stu1 < stu2)      # 结果是True
print(stu1 <= stu2)      # 结果是True
print(stu1 == stu2)          # 结果是True

面向对象的三大特性:封装,继承和多态。 面向对象编程是许多编程语言都支持的一种编程思想;即基于模板(类)去创建实体(对象),使用对象完成功能开发。

6.封装:封装表示的是,将现实世界事物的属性和行为封装到类中,描述为:成员变量和成员方法,从而完成程序对现实世界事物的描述。 私有成员:在现实生活中有些现实事物不能公开属性和行为,而在程序中类提供了私有成员的形式来支持。私有成员变量和方法:变量名和方法名都以2个下划线开头。注意:私有方法无法直接被类对象使用会报错,同样的私有变量无法赋值(即使赋值了不报错但也无效)也无法获取值。虽然私有成员无法被类对象使用,但是可以被其它的成员使用。如以下代码:

    __current_voltage = 0.5       # 设置一个私有成员变量,当前手机运行电压
    def __keep_single_core(self):       # 设置一个私有成员方法
        print("让cpu以单核模式运行")
    def call_by_5g(self):       # 设置类中另一个成员
        if self.__current_voltage >= 1:     # 在类的另一个成员中使用私有成员变量
            print("5g通话已开启")
        else:
            self.__keep_single_core()       # 在类的另一个成员中使用私有成员方法
            print("电量不足,无法使用5g通话,并以设置为单核运行进行")
phone = Phone()
phone.call_by_5g()

7.继承 继承分为单继承和多继承。继承就是在原有的类上做出修改调整,复制父类的成员变量和方法(不含私有)。单继承语法:class 类名(父类名): 类内容体。多继承(即一个类,可以继承多个父类), 语法:class 类名(父类1,父类2,......,父类n):类内容体。如以下的代码:

# 单继承的演示
class Phone:
    IMEI = None
    producer = "HE"
    def call_by_4g(self):
        print("4g通话")
class Phone2022(Phone):
    face_id = "1011"
    def call_by_5g(self):
        print("2022年新功能:5g")
phone = Phone2022()
print(phone.producer)
phone.call_by_4g()
phone.call_by_5g()
# 多继承的演示
class NFCReader:
    nfc_type = "第五代"
    producer = "HE"
    def read_card(self):
        print("NFC读卡")
    def write_card(self):
        print("NFC写卡")
class RemoteControl:
    rc_type = "红外遥控"
    def control(self): 
        print("红外遥控开启了")
class MyPhone(Phone, NFCReader, RemoteControl):
    pass    # pass只是一个普通的站位语句,用来保证函数或方法或者说类定义的完整性,表示无内容,空的意思。
phone = MyPhone()
phone.call_by_4g()
phone.read_card()
phone.write_card()
phone.control()
        

复写:子类继承父类的成员属性和成员方法后,如果对其"不满意",那么就可以进行复写,即在子类中重新定义同名的属性或方法即可。如下图所示:

Screenshot_20231112_184259_edit_1126166179734739.jpg 调用复写父类的成员:一旦复写父类成员,那么类对象调用成员的时候,就会调用复写后的新成员,如果需要使用被复写的父类成员,需要特殊的调用方式:(1)父类名.成员变量,父类名.成员方法(self) (2)super().成员变量,super().成员方法()

    IMEI = None
    producer = "ITCAST"
    def call_by_5g(self):
        print("使用5g网络进行通话")
# 定义子类,复写父类成员
class MyPhone(Phone):
    producer = "ITHEIMA"
    def call_by_5g(self):
        print("开启CPU单核模式,确保通话的时候省电")
        # 调用父类成员
        # 方式一
        print(f"父类的厂商是:{phone.producer}")
        Phone.call_by_5g(self)
        # 方式二:
        print(f"父类的厂商是:{super().producer}")
        super().call_by_5g()
        print("使用5g网络进行通话")
        print("关闭CPU单核模式,确保性能")
phone = MyPhone()
phone.call_by_5g()
print(phone.producer)

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

Screenshot_20231112_194155_edit_1129312113998003.jpg

Screenshot_20231112_194452_edit_1129484379181398.jpg 如下面代码的演示:

    def speak(self):
        pass
class Dog(Animal):
    def speak(self):
        print("汪汪汪")
class Cat(Animal):
    def speak(self):
        print("喵喵喵")
def make_noise(animal:Animal):
    animal.speak()
# 演示多态,使用2个子类对象调用函数
dog = Dog()
cat = Cat()
make_noise(dog)
make_noise(cat)

从上面的代码中我们可以看到父类Animal的speak方法,是空实现;这种设计的含义是:父类用来确定有哪些方法和具体的方法实现由子类自行决定。这种写法,就叫做抽象类(也是接口)。那么什么是抽象类呢?含有抽象方法的类就称之为抽象类,同样的,方法体是空实现的(pass)就称之为抽象方法。

抽象类的作用: 多用于做顶层设计(设计标准),以便子类做具体实现,也是对子类的一种软性约束,要求子类必须复写(实现)父类的一些方法并且配合多态去使用,从而获得不同的工作状态。