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())
此示例示意类变量的定义和用法 class_variable1.py
 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)
class_variable2.py
 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)
class_variable3.py

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 岁
slots.py

二、类方法 @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
class_method1.py
 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
class_method2.py
 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)
class_method3.py

三、静态方法 @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
staticmethod.py

练习:
    用类来描述一个学生的信息,(可以修改之前写的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('太累了,今天晚吃啥')
inherit2.py
 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('英雄联盟')
inherit3.py

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]
示例.py

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 被调用
override1.py
 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 被调用
override2.py

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()  # 出错
super.py

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()
super_init.py

面向对象的三大特征:
     封装 继承 多态

练习:

   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度电
练习.py

posted on 2018-10-12 17:39  破天荒的谎言、谈敷衍  阅读(675)  评论(0)    收藏  举报

导航