python基础(18)——类变量 & 类方法 & 继承 和 派生 & 覆盖
一、类变量
类变量是类的属性,此属性属于类,不属于此类的实例。
作用:
通常用来存储该类创建对象的共有属性
说明:
类变量可以通过该类直接访问
类变量可以通过类的实例直接访问
类变量可以通过此类的对象的__class__属性间接访问
1 class Human: 2 total_count = 0 # 类变量,用来记录Human对象的个数 3 pass 4 5 print(Human.total_count) # 0 访问类变量 6 h1 = Human() # 创建一个对象 7 8 print(h1.total_count) # 0 借助于此类的实例访问类变量(类属性) 9 10 h1.total_count = 10000 # 这一步为实例添加实例属性 11 print(h1.total_count) # 10000 12 print(Human.total_count) # 0 13 14 #类变量可以通过此类的对象的__class__属性间接访问 15 h1.__class__.total_count += 1 # 等同于Human.total_count += 1 16 print(Human.total_count) # 1 17 18 # Human.total_count += 1 # 改变类变量 19 # print(Human.total_count) # 1 20 21 # print(dir())
1 # 此示例示意用类变量来记录Human对象的个数 2 class Human: 3 total_count = 0 # 类变量,用来记录Human对象的个数 4 5 def __init__(self, n): 6 self.name = n 7 self.__class__.total_count += 1 8 print(n, '对象被创建') 9 10 def __del__(self): 11 print(self.name, '对象被销毁') 12 self.__class__.total_count -= 1 13 14 L = [] 15 L.append(Human('张飞')) 16 L.append(Human('关羽')) 17 print("当前人物个数是:", Human.total_count) 18 del L[1] 19 print("当前人物个数是:", Human.total_count)
1 #示意全局变量 2 total_count = 0 # 全局变量 3 4 class Human: 5 6 def __init__(self, n): 7 self.name = n 8 global total_count 9 total_count += 1 10 print(n, '对象被创建') 11 12 def __del__(self): 13 print(self.name, '对象被销毁') 14 global total_count 15 total_count -= 1 16 17 L = [] 18 L.append(Human('张飞')) 19 L.append(Human('关羽')) 20 print("当前人物个数是:", total_count) 21 del L[1] 22 print("当前人物个数是:", total_count)
1、类的文档字符串
类内第一个没有赋值给任何变量的字符串为为类的文档字符串
类的文档字符串可以通过help()函数查看
类的文档字符串绑定在类的__doc__属性上
2、类的 __slots__列表
作用:
限定一个类创建的实例只能有固定的属性(实例变量)
不允许对象添加列表以外的实例属性(实例变量)
访止用户因错写属性名而发生程序错误
说明:
__slots__属性绑定一个字符串列表
含有__slots__属性的类所创建的实例对象没有__dict__属性,即此实例不用字典来存储对象的实例属性
1 #此示例示意类内__slots__列表的用法和作用 2 class Human: 3 __slots__ = ['name', 'age'] 4 5 def __init__(self, n, a): 6 self.name = n 7 self.age = a 8 9 def info(self): 10 print(self.name, '今年', self.age, '岁') 11 12 13 h1 = Human('小张', 8) 14 h1.info() # 小张 今年 8 岁 15 # h1.Age = 9 # <<--此处会出错。__slots__列表限定不能有此属性 16 h1.info() # 小张 今年 8 岁
二、类方法 @classmethod
类方法是用于描述类的行为的方法,类方法属于类,不属于类的实例对象。
说明:
类方法需要使用@classmethod装饰器定义
类方法至少有一个形参,第一个形参用于绑定类,约定写为'cls'
类和该类的实例都可以调用类方法
类方法不能访问此类创建的对象的实例属性
1 #类方法 @classmethod 2 class A: 3 v = 0 # 类变量(类属性) 4 5 @classmethod 6 def get_v(cls): 7 return cls.v # 用cls 访问类变量v 8 9 @classmethod 10 def set_v(cls, a): 11 cls.v = a 12 13 print('A.v = ', A.get_v()) # 调用类方法得到类变量的值 14 A.set_v(100) 15 16 print("A.v =", A.get_v()) # 100 17 print(A.v) # 100
1 class A: 2 v = 0 # 类变量(类属性) 3 4 @classmethod 5 def get_v(cls): 6 return cls.v # 用cls 访问类变量v 7 8 @classmethod 9 def set_v(cls, a): 10 cls.v = a 11 12 print('A.v = ', A.get_v()) # 调用类方法得到类变量的值 13 14 a = A() # a绑定A类型的一个实例对象 15 print(a.get_v()) # 此类的实例也可以调用该类方法 16 a.set_v(999) 17 print('A.v = ', A.get_v()) # 999
1 # 类方法不能访问该类对象的实例属性 2 class A: 3 v = 0 # 类变量(类属性) 4 5 @classmethod 6 def set_v(cls, a): 7 '''此类方法不能访问 a.color 属性''' 8 cls.v = a 9 10 a = A() # a绑定A类型的一个实例对象 11 a.color = '#FF0000' 12 a.set_v(100)
三、静态方法 @staticmethod
静态方法是定义在类的内部的函数,此函数的作用域是类的内部
说明:
静态方法需要使用@staticmethod装饰器定义
静态方法与普通函数定义相同,不需要传入self实例参数和cls类参数
静态方法只能凭借该类或类创建的实例调用
静态方法不能访问类变量和实例变量
1 #静态方法的定义和使用 2 class A: 3 @staticmethod 4 def myadd(a, b): 5 return a + b 6 7 8 print(A.myadd(100, 200)) # 300 9 a = A() 10 print(a.myadd(300, 400)) # 700
练习:
用类来描述一个学生的信息,(可以修改之前写的Student类)
class Stdent:
...此处自己实现
学生信息有:
姓名,年龄,成绩
将这些学生对象存于列表中,可以任意添加和删除学生
1) 打印出学生的个数
2) 打印出学生的平均成绩
3) 打印出学生的平均年龄
(建议用类内的列表来存储学生的信息)
1 #方法1 函数 2 class Student: 3 def __init__(self, n, a, s=0): 4 self.name = n 5 self.age = a 6 self.score = s 7 8 L = [] 9 def add_student(L): 10 L.append(Student("小张", 20, 100)) 11 L.append(Student("小李", 18, 80)) 12 L.append(Student("小赵", 19, 90)) 13 14 def del_student(L): 15 del L[0] 16 17 def get_student_count(L): 18 return len(L) 19 20 def get_avg_score(L): 21 return sum(map(lambda obj: obj.score, L)) / len(L) 22 23 def get_avg_age(L): 24 return sum(map(lambda obj: obj.age, L)) / len(L) 25 26 add_student(L) 27 print("学生个数是: ", get_student_count(L)) 28 print("学生的平均成绩是: ", get_avg_score(L)) 29 print("学生的平均年龄是: ", get_avg_age(L)) 30 del_student(L) 31 print("学生个数是: ", get_student_count(L)) 32 print("学生的平均成绩是: ", get_avg_score(L)) 33 print("学生的平均年龄是: ", get_avg_age(L))
1 #方法2 类 2 class Student: 3 L = [] 4 5 def __init__(self, n, a, s=0): 6 self.name = n 7 self.age = a 8 self.score = s 9 10 @classmethod 11 def add_student(cls): 12 cls.L.append(Student("小张", 20, 100)) 13 cls.L.append(Student("小李", 18, 80)) 14 cls.L.append(Student("小赵", 19, 90)) 15 16 @classmethod 17 def del_student(cls): 18 del cls.L[0] 19 20 @classmethod 21 def get_student_count(cls): 22 return len(cls.L) 23 24 @classmethod 25 def get_avg_score(cls): 26 return sum(map(lambda obj: obj.score, cls.L)) / len(cls.L) 27 28 @classmethod 29 def get_avg_age(cls): 30 return sum(map(lambda obj: obj.age, cls.L)) / len(cls.L) 31 32 Student.add_student() 33 print("学生个数是: ", Student.get_student_count()) 34 print("学生的平均成绩是: ", Student.get_avg_score()) 35 print("学生的平均年龄是: ", Student.get_avg_age()) 36 Student.del_student() 37 print("学生个数是: ", Student.get_student_count()) 38 print("学生的平均成绩是: ", Student.get_avg_score()) 39 print("学生的平均年龄是: ", Student.get_avg_age())
四、继承 inheritance 和 派生 derived
1、什么是继承/派生
1. 继承是从已有的类中派生出新的类,新类具有原类的数据属性和行为,并能扩展新的行为
2. 派生类就是从一个已有的类中衍生出新的类,在新类的基本上添加新的属性和行为
2、为什么继承/派生
继承的目的是延续旧类的功能
派生的目的是在旧类的基础上改变原有的功能
名词:
基类(base class)/ 超类(super class)/ 父类(father class)
派生类(derived class) / 子类(child class)
3、单继承
语法:
class 类名(基类名):
语句块
说明:
单继承是指派生类由一个基类衍生出来新类
示例见:
inherit.py
inherit1.py
1 #此示例示意单继承的定义方法和用法 2 class Human: 3 def say(self, what): 4 print("说:", what) 5 6 def walk(self, distance): 7 print("走了", distance, '公里') 8 9 class Student(Human): 10 def study(self, subject): 11 print("正在学习", subject) 12 13 class Teacher(Human): 14 def teach(self, subject): 15 print("正在教", subject) 16 17 h1 = Human() 18 h1.say('今天天气真好') 19 h1.walk(5) 20 21 s1 = Student() 22 s1.walk(4) 23 s1.say('感觉有点累') 24 s1.study('Python') 25 26 t1 = Teacher() 27 t1.teach("面向对象") 28 t1.walk(6) 29 t1.say('太累了,今天晚吃啥')
1 #此示例示意单继承的定义方法和用法 2 class Human: 3 def say(self, what): 4 print("说:", what) 5 6 def walk(self, distance): 7 print("走了", distance, '公里') 8 9 10 class Student(Human): 11 def study(self, subject): 12 print("正在学习", subject) 13 14 15 class Teacher(Student): 16 def teach(self, subject): 17 print("正在教", subject) 18 19 h1 = Human() 20 h1.say('今天天气真好') 21 h1.walk(5) 22 23 s1 = Student() 24 s1.walk(4) 25 s1.say('感觉有点累') 26 s1.study('Python') 27 28 t1 = Teacher() 29 t1.teach("面向对象") 30 t1.walk(6) 31 t1.say('太累了,今天晚吃啥') 32 t1.study('英雄联盟')
4、继承派生机制的作用:
1. 可以将一些共有功能加在基类中。实现代码的共享
2. 在不改变基类的基础上改变原有的功能
思考:
list类里只有append向末尾加一个元素的方法,但没有向列表头部添加元素的方法。
试想能否为列表在不改变原有功能的基础上添加一个insert_head(x)方法,此方法能在列表的前部添加元素
class MyList(list):
def insert_head(self, x):
....
myl = MyList(range(3, 6))
print(myl) # [3, 4, 5]
myl.insert_head(2)
print(myl) # [2, 3, 4, 5]
myl.append(6)
print(myl) # [2, 3, 4, 5, 6]
1 class MyList(list): 2 def insert_head(self, x): 3 # self.insert(0, x) 4 self[0:0] = [x] 5 6 myl = MyList(range(3, 6)) 7 print(myl) # [3, 4, 5] 8 myl.insert_head(2) 9 print(myl) # [2, 3, 4, 5] 10 myl.append(6) 11 print(myl) # [2, 3, 4, 5, 6]
5、继承说明:
python3 任何类都直接或间接的继承自object类
object类是一切类的超类
类的 __base__ 属性
__base__属性用来记录此类的基类
python内建的类详见:
>>> help(__builtins__)
5、覆盖 override
1、什么是覆盖
覆盖是指在有继承关系的类中,子类中实现了与基类同名的方法,在子类的实例调用该方法时,实际调用的是子类中的覆盖版本,这种现象叫覆盖。
1 此示例示意覆盖的含义及用法 2 class A: 3 def work(self): 4 print("A.work 被调用") 5 6 7 class B(A): 8 '''B类继承自A类''' 9 def work(self): 10 print("B.work被调用!!!") 11 12 b = B() 13 b.work() # B.work被调用!!! 14 15 a = A() 16 a.work() # A.work 被调用
1 #此示例示意覆盖的含义及用法 2 class A: 3 def work(self): 4 print("A.work 被调用") 5 6 class B(A): 7 '''B类继承自A类''' 8 def work(self): 9 print("B.work被调用!!!") 10 11 b = B() 12 b.work() # B.work被调用!!! B.work(b) 13 A.work(b) # A.work 被调用
2、问题:
当覆盖发生时,子类对象如何调用父类中的被覆盖方法?
调用方式:
基类名.方法名(实例, 实际调用传参,....)
3、super函数
super(cls, obj) 返回绑定超类的实例(要求obj必须为cls类型的实例)
super() 返回绑定超类的实例,等同于: super(__class__, 实例方法的第一个参数, 必须在方法内调用)
作用:
借助super()返回的实例间接调用父类的覆盖方法。
1 #用super函数间接调用父类中覆盖版本的方法 super.py 2 class A: 3 def work(self): 4 print("A.work 被调用") 5 6 class B(A): 7 '''B类继承自A类''' 8 def work(self): 9 print("B.work被调用!!!") 10 11 def super_work(self): 12 # 调用B类自己的方work方法怎么调用 13 self.work() 14 # 调用父类的work怎么调用 15 super(B, self).work() 16 super().work() # 此种调用方式只能在实例方式内调用 17 18 b = B() 19 # b.work() # B.work被调用!!! 20 # super(B, b).work() # A.work 被调用 21 b.super_work() 22 # super().work() # 出错
4、显式调用基类的初始化方法
当子类中实现了__init__方法,基类的构造方法并不会被调用,此时需要显式调用。
1 #显式调用初始化方法 2 class Human: 3 def __init__(self, n, a): 4 self.name = n # 姓名 5 self.age = a # 年龄 6 print("Human类的初始化方法被调用 ...") 7 def infos(self): 8 print("姓名:", self.name) 9 print("年龄:", self.age) 10 11 class Student(Human): 12 def __init__(self, n, a, s=0): 13 # super(Student, self).__init__(n, a) 14 super().__init__(n, a) 15 self.score = s 16 print("Stduent的初始化方法被调用...") 17 18 def infos(self): 19 super().infos() # 显式调用父类的方法 20 print('成绩:', self.score) 21 22 s1 = Student('张飞', 15, 80) 23 s1.infos()
面向对象的三大特征:
封装 继承 多态
练习:
1. 看懂学生信息管理系统的代码.理解实例方法的用法及实例变量封装的思想及好处
2. 写一个类Bicycle类,有run方法,调用时显示骑行的里程(km)
def Bicycle:
def run(self, km):
print("自行车骑行了", km, '公里')
再写一个类EBicycle,在Bicycle类的基础上添加电池电量volume属性,和两个方法:
1.fill_charge(vol) 用来充电 vol 为电量(度)
2. run(km) 方法每骑行10km消耗电量1度, 同时显示当前电量,当电量耗尽时,则调用Bicycle的run方法继续骑行
class EBicycle(Bicycle):
....
def fill_charge(self, vol):
'''充电'''
def run(self, km):
...
b = EBicycle(5) 新买的电动车内有5度电
b.run(10) # 电动骑行了10km还剩4度电
b.run(100) # 电动骑行了40km还剩0度电, 用脚登骑行了60km
b.fill_charge(10) # 充电10度
b.run(50) # 电动骑行了50km还剩5度电
1 class Bicycle: 2 def run(self, km): 3 print("自行车骑行了", km, '公里') 4 5 class EBicycle(Bicycle): 6 def __init__(self, vol): 7 self.volume = vol # 初始电量 8 9 def fill_charge(self, vol): 10 '''充电''' 11 self.volume += vol 12 13 def run(self, km): 14 e_km = min(km, self.volume * 10) # 算出电能走的最大公里数 15 if e_km > 0: 16 self.volume -= e_km / 10 17 print("电动骑行了%dkm,还剩%.1f度电" % (e_km, self.volume)) 18 if km > e_km: 19 super().run(km - e_km) 20 21 b = EBicycle(5) # 新买的电动车内有5度电 22 b.run(10) # 电动骑行了10km还剩4度电 23 b.run(100) # 电动骑行了40km还剩0度电, 用脚登骑行了60km 24 b.fill_charge(10) # 充电10度 25 b.run(50) # 电动骑行了50km还剩5度电
posted on 2018-10-12 17:39 破天荒的谎言、谈敷衍 阅读(675) 评论(0) 收藏 举报
浙公网安备 33010602011771号