一、类(class)与实例
    在Python中一切皆对象,所有数据类型都可视为对象,也可自定义对象(class)类。
    面向对象编程,是一种程序设计思想,把程序视为一组对象的集合,而每个对象都可以接收其他对象传递的数据,并处理。

    1. int # 为int对象
    2. float # 为float对象
    3. list # 为list对象
    4. dict # 为dict对象
    5. a = 1 # a为一个int对象

    类是自定义对象,抽象的模板,创建实例的模板,比如:人类,动物类,汽车类,飞机类,当然还可以细分为对应子类
    定义一个类

    1. class Car:
    2. def __init__(self,name,weight,color,maxspeed):
    3. self.name = name
    4. self.weight = weight
    5. self.color = color
    6. self.maxspeed = maxspeed
    7. def add_speed(self):
    8. print(f"{self.name}执行加速,最高时速{self.maxspeed}")
    9. def reduce_speed(self):
    10. print(f"{self.name}执行减速"
    11. def back_up(self):
    12. print(f"{self.name}车辆执行倒车")

    上述代码中就通过class关键字定义了一个Car类,该类中包含属性:name, weight, color, maxspeed ;包含的方法(功能):add_speed( ), reduce_speed( ), back_up( )。该Car类就像一个模板,通过实例化这个模板可以源源不断创造出不同的实例来

    1. 宝来 = Car(name = "大众宝来", weight = 1.5, color = "白色", maxspeed = 240)
    2. # 这里就实例化了一辆汽车,定义了车名,重量,颜色,最高时速
    3. 宝来.add_speed() # 大众宝来执行加速,最高时速240
    4. 宝来.reduce_speed() # 大众宝来执行减速
    5. print(宝来.weight) # 1.5
    6. print(宝来.color) # 白色
    7. print(宝来.maxspeed) # 240

    类结构的剖析
    1、自定义类通过关键字 class + 类名来定义
    2、如果要定义该类拥有的属性,通过构造方法 init来实现,第一个参数必须是self必填
    3、类属性的作用域在类的内部,可供类中的方法调用
    4、类中的方法定义了该类提供的功能,方法的定义跟函数是一致的,但实例化方法第一个参数必须是self必填
    image.png
    类中的self参数是在创建实例时指向实例本身,上面代码中,self参数指向的是实例“宝来”,即self定义后用来接收实例变量名称的,当然这个self也可定义为其他如this,that,只要注意位置放在参数第一个,而实例就是抽象类的具体对象,各个实例拥有的数据都是独立,互不影响的。
    把类实例化后,通过“实例.XXX”可访问该实例的属性,以及调用该实例的方法。

    任务:创建一个Student抽象类,包含name, age, country, gender, height属性,以及run( ), sleep( ), eat( ), study( )方法,并进行实例化后调用里面的属性,和方法;
    任务:基于上一任务的实例,尝试修改这个student实例的age,并输出;

    实例属性与类属性
    实例属性:在定义一个类时,self.xxx就是实例属性,当这个类进行实例化时,这个self会指向这个实例变量,实例属性属于各个实例所有,互不干扰。
    类属性:在定义一个类时,没有self的属性就是类属性(固定属性)

    1. class Car:
    2. name = "new_car" # name 就是类属性,类对象可直接调用
    3. def __init__(self,color):
    4. self.color = color
    5. def get_color(self):
    6. print(f"颜色是:{self.color}")
    7. def get_name(self):
    8. print(f"车名是:{self.name}")
    9. c1 = Car("银色")
    10. c1.get_color() # 颜色是:银色
    11. c1.get_name() # 车名是:new_car
    12. c2 = Car("黑色")
    13. c2.get_color() # 颜色是:黑色
    14. c2.get_name() # 车名是:new_car
    15. print(Car.name) # 类属性可通过类对象直接调用

    类属性归属于类所有,所有实例将共享这一属性,类属性可通过类对象直接调用

    类方法@classmethod
    类最基本的作用是实例化出一个对象,但是有的时候再实例化之前,就需要先和类做一定的交互
    一个普通的类,我们要使用其中的方法的话,需要对类进行实例化,而一个类中,某个方法前面加上了staticmethod或者classmethod的话,那么这个函数就可以不通过实例化直接调用,可以通过类名进行调用

    1. class Car:
    2. name = "new_car" # name 就是类属性,类对象可直接调用
    3. def __init__(self,color):
    4. self.color = color
    5. def get_color(self):
    6. print(f"颜色是:{self.color}")
    7. def get_name(self):
    8. print(f"车名是:{self.name}")
    9. @classmethod
    10. def make(cls):
    11. print("生产汽车")
    12. if __name__ == '__main__':
    13. Car.make()

    cls通常用作类方法的第一参数 类似实例方法中的self,即通常用self来传递当前类对象的实例,cls传递当前类对象。
    self 和cls 没有特别的含义,作用只是把实例或类对象绑定到方法上, 不一定非得是self 或cls

    静态方法@staticmethod

    1. class Car:
    2. name = "new_car" # name 就是类属性,类对象可直接调用
    3. def __init__(self,color):
    4. self.color = color
    5. def get_color(self):
    6. print(f"颜色是:{self.color}")
    7. def get_name(self):
    8. print(f"车名是:{self.name}")
    9. @classmethod # 类方法
    10. def make(cls):
    11. print("生产汽车")
    12. @staticmethod # 静态方法
    13. def service():
    14. print("生产商提供贵宾服务")
    15. if __name__ == '__main__':
    16. Car.make()
    17. Car.service()

    静态方法与类方法调用方法基本一致
    如果在方法中不需要访问任何实例方法和属性,纯粹地通过传入参数并返回数据的功能性方法,那么它就适合用静态方法来定义,它节省了实例化对象的开销成本,往往这种方法放在类外面的模块层作为一个函数存在也是没问题的,而放在类中,仅为这个类服务;

    私有变量
    实例化一个类以后得到一个实例对象,可以通过“实例变量名.属性名”来访问这个实例的属性,同样可以通过“实例变量名.属性名 = xxx”来对这个属性进行修改,即外部代码可以通过这种方式修改实例属性。为了让程序健壮以及安全考虑,需要让内部属性不被外部所访问,因此可以在属性名称前加两个“”下划线,在定义类时,以“”开头的实例变量名(实例属性)称为私有变量,只能在类内部访问,外部无法访问

    1. class Car:
    2. def __init__(self,name,weight,color,maxspeed):
    3. self.name = name
    4. self.__weight = weight # 变量名加上“__”变为私有变量,只能内部调用
    5. self.color = color
    6. self.__maxspeed = maxspeed
    7. def get_weight(self):
    8. print(f"{self.name}车重是{self.__weight}")
    9. 蔚来 = Car(name = "蔚来ES6", weight = 1.5, color = "白色", maxspeed = 240)
    10. print(蔚来.__weight) # AttributeError: 'Car' object has no attribute 'weight'
    11. 蔚来.get_weight() # 蔚来ES6车重是1.5

    上述代码,由于weight是私有变量,所以通过“蔚来.weight”外部访问,会提示没有这个对象,而通过 getweight 方法可访问 weight 完成输出;
    需要注意:在Python中,变量名类似
    xxx_
    的,也就是以双下划线开头,并且以双下划线结尾的,是特殊变量,特殊变量是可以直接访问的,不是私有(private)变量。

    任务:定义一个cat类,包含私有变量name, color,定义两个方法,get_color用于获取color并通过return返回,set_name用于设置name;

    二、继承
    定义一个类时,可以通过继承一个现有的类,来拥有这个类的所有功能,而不需要重新额外定义,被继承的类称为父类

    1. class Car:
    2. def add_speed(self):
    3. print("开始加速行驶")
    4. def reduce_speed(self):
    5. print("开始减速行驶")
    6. class limousine(Car): # 定义一个轿车类,继承Car父类
    7. pass
    8. class truck(Car): # 定义一个卡车类,继承Car父类
    9. pass
    10. c = limousine() # limousine拥有父类Car的所有方法
    11. c.add_speed() # 开始加速行驶
    12. c.reduce_speed() # 开始减速行驶
    13. t = truck()
    14. t.add_speed() # 开始加速行驶
    15. t.reduce_speed() # 开始加速行驶

    对于 limousine 来说,Car 就是父类,基类或超类,而 limousine 就是 Car 的子类
    作为子类,可以增加自定义方法

    1. class limousine(Car):
    2. def auto_driver(self):
    3. print("开始进行自动驾驶")
    4. c = limousine()
    5. c.auto_driver() # 开始进行自动驾驶

    上面代码中增加的 auto_driver 方法只存在于 limousine 中,对父类以及继承该父类的其他子类无影响 ;
    对于子类继承的父类的方法,子类可以对继承的方法进行重写修改,这个子类运行调用该方法时,会运行修改后的方法

    1. class limousine(Car):
    2. def add_speed(self):
    3. print("limousine开始加速行驶")
    4. class truck(Car):
    5. def add_speed(self):
    6. print("truck开始加速行驶")
    7. c = Car()
    8. c.add_speed() # 开始加速行驶
    9. l = limousine()
    10. l.add_speed() # limousine开始加速行驶
    11. t = truck()
    12. t.add_speed() # truck开始加速行驶

    当子类和父类都存在相同的add_speed( )方法时,我们说,子类的add_speed( )覆盖了父类的add_speed( ),在代码运行的时候,总是会调用子类的add_speed( )。
    补充:我们自己定义的类,在Python中就属于一种数据类型,这种自定义的数据类型跟 Python自带的 str, list, dict等是一样的,上面代码中我们定义了 class Car,因此这个Car就是一个数据类型
    image.png
    三、继承树
    继承可以一层一层的进行继承,父类-子类-孙类,就好比人类的继承关系,爷-父-子-孙继承,但所有继承都可以向上追溯到一个根类 object,Python所有类都默认继承这个 object

    1. class Car:
    2. pass
    3. class Car(object):
    4. pass

    上述代码,继承关系是一样的,默认继承 object 这个根类可不显示的写出来

    1. class Car:
    2. pass
    3. class limousine(Car):
    4. pass
    5. class truck(Car):
    6. pass

    再看一个继承实例
    子类需要覆写构造方法

    1. class Persion:
    2. rest = '休息日'
    3. work_study = '学习或工作日'
    4. def __init__(self,name,age,gender):
    5. self.name = name
    6. self.age = age
    7. self.gender = gender
    8. def eat(self):
    9. print(self.rest)
    10. print(f"{self.name} 正在吃饭")
    11. def drink(self):
    12. print(f"{self.name}正在喝水")
    13. def walk(self):
    14. print(f"{self.name} 正在走路")
    15. def sleep(self):
    16. print(f"{self.name} 正在睡觉")
    17. @classmethod
    18. def smile(cls):
    19. print("微微一笑")
    20. @staticmethod
    21. def hungry():
    22. print("肚子空空")
    23. class Student(Persion):
    24. def __init__(self,name,age,gender,school,classname,number):
    25. super(Student, self).__init__(name,age,gender)
    26. # Persion.__init__(self,name,age,gender)
    27. self.school = school
    28. self.classname = classname
    29. self.number = number
    30. def sport(self):
    31. print(f"{self.name} 正在学校做运动")
    32. def study(self):
    33. print(f"{self.name}{self.age}岁,正在{self.school}{self.classname}上课")
    34. if __name__ == "__main__":
    35. xiaoMing = Student(name='小明',age=12, gender='boy', school='兴华实验学校', classname='一(2)班',number='10')
    36. xiaoMing.study()
    37. xiaoMing.drink()

    上述Student类继承了Person类,并对Person类的构造方法进行覆写
    注:一定要用super(Student,self).init(name,age,gender)去初始化父类,否则,Student子类将没有name,age,gender三个属性,super(Student,self)将返回当前子类Student继承的父类,即Person

    继承关系树如下
    image.png
    小结:继承可以把父类的所有功能都直接拿过来,这样就不必重零做起,子类只需要新增自己特有的方法,也可以把父类不适合的方法覆盖重写。