day18
类变量
类变量是类的属性,此属性属于类,不属于类的实例
作用:
通常用来存储该类创建对象的共有属性
说明:
类变量可以通过该类直接访问
类变量,可以通过类的实例直接访问,无法改变
类变量可以通过此类的对象的__class__属性间接访问
示例见: class_variable.py
class_variable2.py
#此实例示意变量的定义和用法
class Human:
total_count = 0 #类变量,用来记录Human对象的个数
pas
# 类变量可以通过该类直接访问
print(Human.total_count) #访问类变量
Human.total_count += 1 #改变类变量
print(Human.total_count) #1
#类变量,可以通过类的实例直接访问,,无法改变
h1 = Human() #创建一个对象
print(h1.total_count) # 0 借助于此类的实例访问类变量(类属性)
h1.total_count = 10000 #添加实例属性(变量),
print(h1.total_count) #10000
print(Human.total_count) # 1
print(dir()) #打印全局变量
# 类变量可以通过此类的对象的__class__属性间接访问
h1.__class__.total_count += 1 #Human.total_count += 1
print(Human.total_count) #2
#此示例示意用类变量示意Human对象的个数
class Human:
total_count = 0 #类变量,用来记录Human对象的个数
def __init__(self,n):
self.name = n
self.__class__.total_count += 1 #类变量加1操作
print(n,'对象被创建')
def __del__(self):
print(self.name,'对象被销毁')
self.__class__.total_count -= 1
L = []
L.append(Human('张飞'))
L.append(Human('关羽'))
print("当前人物个数是:",Human.total_count)
del L[1]
print('当前人物个数是:',Human.total_count)
类的文档字符串
类内第一个没有赋值给任何变量的字符串为类的文档字符串
类的文档字符串可以通过help()函数查看
类的文档字符串绑定在类的__doc__属性上
交互模式
class Human:
'''地球上的主宰者'''
help(Human)
Human.__doc__
Human.__dict__
dir(Human) #查看Human类中的各种属性
类的 __slots__列表
作用:
限定一个类创建的实例只有固定的属性(实例变量)
不允许对象添加列表以外的实例属性(实例变量)
防止用户因写错属性名而发生程序错误
说明:
1. __slots__属性绑定一个字符串列表
2.含有__slots__属性的类所创建的实例对象没有__dict__属性,
即 此实例不用字典来存储对象的实例属性
示例见:slots.py
class Human:
# 限定一个类创建的实例只有固定的属性(实例变量)
__slots__ = ['name','age'] #限定实例变量是能有name,age两种类型
def __init__(self,n,a):
self.name = n
self.age = a
def info(self):
print(self.name,'今年',self.age,'岁')
h1 = Human('小张',8)
h1.info() #小张今年8岁
h1.Age = 9 #此处会出错,__slots__列表限定不能有此属性
h1.info() #小张今年8岁
类方法:(描述类的行为)
类方法是用来描述类的行为的方法,类方法属于类,不属于类的实例对象
说明:
类方法需要使用@classmethod装饰器定义
类方法至少有一个形参,第一个形参用于绑定类.约定写为'cls'
类和该类的实例都可以调用类的方法
类方法不能访问此类创建的对象的实例属性
注:@classmethod把实例自动看成 实例.__class__
不要直接操作类属性,在方法内操作,制定操作规则
示例见:
class_method.py
class A:
v = 0 #类变量(类属性)
@classmethod
def get_v(cls): # 操作类变量
return cls.v #用cls访问类变量v
@classmethod
def set_v(cls,a):
# v = a #方法内的变量
cls.v = a
print('A.v=',A.get_v()) #调用类方法得到类的变量的值
A.set_v(100)
print('A.v=',A.get_v()) #100
静态方法 @staticmethod
静态方法是定义在类的内部的函数,此函数的作用域是类的内部
说明:
静态方法需要使用@staticmethod装饰器定义
静态方法与普通函数定义相同,不需要传入self实例参数和cls类参数
静态方法只能凭借该类或类创建的实例调用
静态方法不能访问类变量和实例变量
示例见:
staticmethod.py
#此示例示意静态方法的定义和使用
class A:
@staticmethod
# 静态方法与普通函数定义相同,不需要传入self实例参数和cls类参数
def mysdd(a,b):
return a + b
# 静态方法只能凭借该类或类创建的实例调用
print(A.myadd(100,200)) #300
a = A()
print(a.myadd(300,400)) #700
练习一:
用类来描述一个学生的信息,(可以修改之前的Student类)
class Student:
....
学生信息有:
姓名 年龄 成绩
将这些学生对象存于列表中,可以任意添加和删除学生
1)打印出这些学生的个数
2)打印出学生的平均成绩
3)打印出学生的平均年龄
(建议用类内的列表来存储学生信息)
class Student:
L = []
def __init__(self,n,a,s=0):
self.name = n
self.age = a
self.score = s
@classmethod
def add_Student(L):
cls.L.append(Student('小张',20,100))
cls.L.append(Student('小照',18,90))
cls.L.append(Student('小里',19,80))
@classmethod
def del_Student(cls):
del cls.L[0]
@classmethod
def get_student_count(cls):
return len(cls.L)
@classmethod
def get_avg_age(cls):
return sum(map(lambda obj:obj.age,L))/len(L)
@classmethod
def get_avg_score(cls):
return sum(map(lambda obj:obj.score,L))/len(L)
Student.add_Student(L)
print('学生个数是:',Student.get_student_count(L))
print('学生的平均成绩是:',,Student.get_avg_score(L))
print('学生的平均年龄是:',,Student.get_avg_age(L))
Student.del_Student(L)
print('学生个数是:',Student.get_student_count(L))
print('学生的平均成绩是:',,Student.get_avg_score(L))
print('学生的平均年龄是:',,Student.get_avg_age(L))
继承 inheritance 和派生 derived
什么是继承/派生
1.继承是从已有的类中派生出新的类,新类具有原类的数据属性和行 为,并能扩展新的行为
2.派生类就是从一个已有的类中衍生出新的类,在新类的基本上添加新的属性和行为
为什么继承和派生
继承的目的是延续旧类的功能
派生的目的是在旧类的基础上改变原有的功能
名词:
基类(base class)-------->原有类
超类(super class)
父类(father class)
派生类(derived class)---->派生出来的新的类
子类(child class)
单继承
语法:
class 类名(基类名):
语句块
说明:
单继承是指派生类由一个基类衍生出来的新类
示例见:
inherit.py
继承派生机的作用:
1.可以将一些共有功能加在基类中,实现代码共享
2.在不改变基类的基础上改变原有功能
例如:
class Mylist(list):
def say(self,what):
print("说:",what)
L = Mylist()
print(L) # L = []
L.append(10)
print(10) #L = [10]
L.say("好")
练习二:
思考:
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]
class MyList(list):
def insert_head(self,x):
self[0:0] = [x]
self.insert(0,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]
继承说明:
python3 任何类都直接或间接的继承自object类
object类是一切类的超类
类的__base__属性
__base__属性用来记录此类的基类
print(object.__base__) #None
python內建的类详见:
>>>help(__builtins__)
覆盖 override
什么事覆盖:
覆盖是指在有继承关系的类中,子类中实现了与基类同名的方法,
在子类的实例调用该方法时,实际调用的是子类中的覆盖版本,
这种现象叫覆盖
示例见:
override.py
问题:
当覆盖发生时,子类对象如何调用父类中的覆盖方法?
调用方式:
基类名.方法名(实例,实际调用传参,....)
b.work() #等同于B.work(b)
super函数
super(cls,obj) 返回绑定超类的实例(要求obj必须为cls类型的实例)
super()
返回绑定超类的实例,等同于:super(__class__,实例方法的第一个参数,必须在方法内调用)
作用:
借助super()返回的实例间接调用父类的覆盖方法
示例见:
super.py
注:#方法外
super(B,b).work()
#B类中的实例b,当做是A类中的,来调用work
#方法内
super().work()#此种方式只能在实例方法内部调用
super(B,self).work()
super(self.__class__,self).work()
#此示例示意用super函数间接调用父类中
class A:
def work(self):
print("A.work被调用")
class B(A):
'''B类继承自A类'''
def work(self):
print("B.work被调用!!!")
def super_work(self):
#调用B类自己的方法work方法
self.work()
#调用父类的work
super(B,self).work()
super(self.__class__,self).work()
super().work()#此种方式只能在实例方法内部调用
b = B()
b.work() #B.work被调用!!
super(B,b).work() #B类中的实例b,当做是A类中的,来调用work
显示调用基类初始化方法
当子类中实现了__init__方法,基类的构造方法并不会被调用,
此时需要显示调用
示例见:
super_init.py
#此示例示意显式调用初始化方法
#子类未改变父类的原有内容,由super在子类中调用父类方法
class Human:
def __init__(self,n,a):
self.name = n #姓名
self.age = a #年龄
print("Human类的初始化方法被调用")
def infos(self):
print("姓名:",self.name)
print("年龄:",self.age)
class Student(Human):
def __init__(self,n,a,s=0):
super(Student,self).__init__(n,a) #自己的属性自己改,父类的属性由父类自己改
super().__init__(n,a)
self.score = s
print("Student的初始化方法被调用")
def infos(self):
super(Student,self).infos()
print("成绩:",self.score)
s1 = Student('张飞',15,80)
s1.infos()
课后练习:
1. 看懂学生信息管理系统的代码.理解实例方法的用法及
实例变量封装的思想及好处
2. 写一个类Bicycle类,有run方法,调用时显示骑行的里程(km)
class 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度电
class Bicycle:
def run(self,km):
print("自行车骑行了",km,'公里')
class EBicycle(Bicycle):
def __init__(self,vol):
self.volume = vol
print('新电动车有',self.volume,'度电')
def fill_charge(self,vol):
'''充电'''
self.volume += vol
print('充了',vol,'度电')
print('充电后有',self.volume,'度电')
def run(self,km):
if km <= self.volume * 10:
self.volume -= km//10
print("电动车骑行了",km,"km","还剩",self.volume,'度电')
else:
print("电动车骑行了",self.volume *10,"km","还剩0度电")
km -= self.volume * 10
super().run(km)
b = EBicycle(5)
b.run(10)
b.run(100)
b.fill_charge(10)
b.run(50)