一、类(class)与实例
在Python中一切皆对象,所有数据类型都可视为对象,也可自定义对象(class)类。
面向对象编程,是一种程序设计思想,把程序视为一组对象的集合,而每个对象都可以接收其他对象传递的数据,并处理。
int # 为int对象
float # 为float对象
list # 为list对象
dict # 为dict对象
a = 1 # a为一个int对象
类是自定义对象,抽象的模板,创建实例的模板,比如:人类,动物类,汽车类,飞机类,当然还可以细分为对应子类
定义一个类
class Car:
def __init__(self,name,weight,color,maxspeed):
self.name = name
self.weight = weight
self.color = color
self.maxspeed = maxspeed
def add_speed(self):
print(f"{self.name}执行加速,最高时速{self.maxspeed}")
def reduce_speed(self):
print(f"{self.name}执行减速")
def back_up(self):
print(f"{self.name}车辆执行倒车")
上述代码中就通过class关键字定义了一个Car类,该类中包含属性:name, weight, color, maxspeed ;包含的方法(功能):add_speed( ), reduce_speed( ), back_up( )。该Car类就像一个模板,通过实例化这个模板可以源源不断创造出不同的实例来
宝来 = Car(name = "大众宝来", weight = 1.5, color = "白色", maxspeed = 240)
# 这里就实例化了一辆汽车,定义了车名,重量,颜色,最高时速
宝来.add_speed() # 大众宝来执行加速,最高时速240
宝来.reduce_speed() # 大众宝来执行减速
print(宝来.weight) # 1.5
print(宝来.color) # 白色
print(宝来.maxspeed) # 240
类结构的剖析
1、自定义类通过关键字 class + 类名来定义
2、如果要定义该类拥有的属性,通过构造方法 init来实现,第一个参数必须是self必填
3、类属性的作用域在类的内部,可供类中的方法调用
4、类中的方法定义了该类提供的功能,方法的定义跟函数是一致的,但实例化方法第一个参数必须是self必填
类中的self参数是在创建实例时指向实例本身,上面代码中,self参数指向的是实例“宝来”,即self定义后用来接收实例变量名称的,当然这个self也可定义为其他如this,that,只要注意位置放在参数第一个,而实例就是抽象类的具体对象,各个实例拥有的数据都是独立,互不影响的。
把类实例化后,通过“实例.XXX”可访问该实例的属性,以及调用该实例的方法。
任务:创建一个Student抽象类,包含name, age, country, gender, height属性,以及run( ), sleep( ), eat( ), study( )方法,并进行实例化后调用里面的属性,和方法;
任务:基于上一任务的实例,尝试修改这个student实例的age,并输出;
实例属性与类属性
实例属性:在定义一个类时,self.xxx就是实例属性,当这个类进行实例化时,这个self会指向这个实例变量,实例属性属于各个实例所有,互不干扰。
类属性:在定义一个类时,没有self的属性就是类属性(固定属性)
class Car:
name = "new_car" # name 就是类属性,类对象可直接调用
def __init__(self,color):
self.color = color
def get_color(self):
print(f"颜色是:{self.color}")
def get_name(self):
print(f"车名是:{self.name}")
c1 = Car("银色")
c1.get_color() # 颜色是:银色
c1.get_name() # 车名是:new_car
c2 = Car("黑色")
c2.get_color() # 颜色是:黑色
c2.get_name() # 车名是:new_car
print(Car.name) # 类属性可通过类对象直接调用
类属性归属于类所有,所有实例将共享这一属性,类属性可通过类对象直接调用
类方法@classmethod
类最基本的作用是实例化出一个对象,但是有的时候再实例化之前,就需要先和类做一定的交互
一个普通的类,我们要使用其中的方法的话,需要对类进行实例化,而一个类中,某个方法前面加上了staticmethod或者classmethod的话,那么这个函数就可以不通过实例化直接调用,可以通过类名进行调用
class Car:
name = "new_car" # name 就是类属性,类对象可直接调用
def __init__(self,color):
self.color = color
def get_color(self):
print(f"颜色是:{self.color}")
def get_name(self):
print(f"车名是:{self.name}")
@classmethod
def make(cls):
print("生产汽车")
if __name__ == '__main__':
Car.make()
cls通常用作类方法的第一参数 类似实例方法中的self,即通常用self来传递当前类对象的实例,cls传递当前类对象。
self 和cls 没有特别的含义,作用只是把实例或类对象绑定到方法上, 不一定非得是self 或cls
静态方法@staticmethod
class Car:
name = "new_car" # name 就是类属性,类对象可直接调用
def __init__(self,color):
self.color = color
def get_color(self):
print(f"颜色是:{self.color}")
def get_name(self):
print(f"车名是:{self.name}")
@classmethod # 类方法
def make(cls):
print("生产汽车")
@staticmethod # 静态方法
def service():
print("生产商提供贵宾服务")
if __name__ == '__main__':
Car.make()
Car.service()
静态方法与类方法调用方法基本一致
如果在方法中不需要访问任何实例方法和属性,纯粹地通过传入参数并返回数据的功能性方法,那么它就适合用静态方法来定义,它节省了实例化对象的开销成本,往往这种方法放在类外面的模块层作为一个函数存在也是没问题的,而放在类中,仅为这个类服务;
私有变量
实例化一个类以后得到一个实例对象,可以通过“实例变量名.属性名”来访问这个实例的属性,同样可以通过“实例变量名.属性名 = xxx”来对这个属性进行修改,即外部代码可以通过这种方式修改实例属性。为了让程序健壮以及安全考虑,需要让内部属性不被外部所访问,因此可以在属性名称前加两个“”下划线,在定义类时,以“”开头的实例变量名(实例属性)称为私有变量,只能在类内部访问,外部无法访问
class Car:
def __init__(self,name,weight,color,maxspeed):
self.name = name
self.__weight = weight # 变量名加上“__”变为私有变量,只能内部调用
self.color = color
self.__maxspeed = maxspeed
def get_weight(self):
print(f"{self.name}车重是{self.__weight}")
蔚来 = Car(name = "蔚来ES6", weight = 1.5, color = "白色", maxspeed = 240)
print(蔚来.__weight) # AttributeError: 'Car' object has no attribute 'weight'
蔚来.get_weight() # 蔚来ES6车重是1.5
上述代码,由于weight是私有变量,所以通过“蔚来.weight”外部访问,会提示没有这个对象,而通过 getweight 方法可访问 weight 完成输出;
需要注意:在Python中,变量名类似xxx_的,也就是以双下划线开头,并且以双下划线结尾的,是特殊变量,特殊变量是可以直接访问的,不是私有(private)变量。
任务:定义一个cat类,包含私有变量name, color,定义两个方法,get_color用于获取color并通过return返回,set_name用于设置name;
二、继承
定义一个类时,可以通过继承一个现有的类,来拥有这个类的所有功能,而不需要重新额外定义,被继承的类称为父类
class Car:
def add_speed(self):
print("开始加速行驶")
def reduce_speed(self):
print("开始减速行驶")
class limousine(Car): # 定义一个轿车类,继承Car父类
pass
class truck(Car): # 定义一个卡车类,继承Car父类
pass
c = limousine() # limousine拥有父类Car的所有方法
c.add_speed() # 开始加速行驶
c.reduce_speed() # 开始减速行驶
t = truck()
t.add_speed() # 开始加速行驶
t.reduce_speed() # 开始加速行驶
对于 limousine 来说,Car 就是父类,基类或超类,而 limousine 就是 Car 的子类
作为子类,可以增加自定义方法
class limousine(Car):
def auto_driver(self):
print("开始进行自动驾驶")
c = limousine()
c.auto_driver() # 开始进行自动驾驶
上面代码中增加的 auto_driver 方法只存在于 limousine 中,对父类以及继承该父类的其他子类无影响 ;
对于子类继承的父类的方法,子类可以对继承的方法进行重写修改,这个子类运行调用该方法时,会运行修改后的方法
class limousine(Car):
def add_speed(self):
print("limousine开始加速行驶")
class truck(Car):
def add_speed(self):
print("truck开始加速行驶")
c = Car()
c.add_speed() # 开始加速行驶
l = limousine()
l.add_speed() # limousine开始加速行驶
t = truck()
t.add_speed() # truck开始加速行驶
当子类和父类都存在相同的add_speed( )方法时,我们说,子类的add_speed( )覆盖了父类的add_speed( ),在代码运行的时候,总是会调用子类的add_speed( )。
补充:我们自己定义的类,在Python中就属于一种数据类型,这种自定义的数据类型跟 Python自带的 str, list, dict等是一样的,上面代码中我们定义了 class Car,因此这个Car就是一个数据类型
三、继承树
继承可以一层一层的进行继承,父类-子类-孙类,就好比人类的继承关系,爷-父-子-孙继承,但所有继承都可以向上追溯到一个根类 object,Python所有类都默认继承这个 object
class Car:
pass
class Car(object):
pass
上述代码,继承关系是一样的,默认继承 object 这个根类可不显示的写出来
class Car:
pass
class limousine(Car):
pass
class truck(Car):
pass
再看一个继承实例
子类需要覆写构造方法
class Persion:
rest = '休息日'
work_study = '学习或工作日'
def __init__(self,name,age,gender):
self.name = name
self.age = age
self.gender = gender
def eat(self):
print(self.rest)
print(f"{self.name} 正在吃饭")
def drink(self):
print(f"{self.name}正在喝水")
def walk(self):
print(f"{self.name} 正在走路")
def sleep(self):
print(f"{self.name} 正在睡觉")
@classmethod
def smile(cls):
print("微微一笑")
@staticmethod
def hungry():
print("肚子空空")
class Student(Persion):
def __init__(self,name,age,gender,school,classname,number):
super(Student, self).__init__(name,age,gender)
# Persion.__init__(self,name,age,gender)
self.school = school
self.classname = classname
self.number = number
def sport(self):
print(f"{self.name} 正在学校做运动")
def study(self):
print(f"{self.name}{self.age}岁,正在{self.school}{self.classname}上课")
if __name__ == "__main__":
xiaoMing = Student(name='小明',age=12, gender='boy', school='兴华实验学校', classname='一(2)班',number='10')
xiaoMing.study()
xiaoMing.drink()
上述Student类继承了Person类,并对Person类的构造方法进行覆写
注:一定要用super(Student,self).init(name,age,gender)去初始化父类,否则,Student子类将没有name,age,gender三个属性,super(Student,self)将返回当前子类Student继承的父类,即Person
继承关系树如下
小结:继承可以把父类的所有功能都直接拿过来,这样就不必重零做起,子类只需要新增自己特有的方法,也可以把父类不适合的方法覆盖重写。