类,作为一个比较大的整体,大规模程序的编写难免少得了类的使用,相当于一个大包,大包里包括各部分的代码,类之间通过继承来联系起来,通过封装进行打包,形成一个整体。
a. 明确内外
b. 通过继承和多态在语言层面支持归一化的设计
a. 抽象实现 b. 封装/接口 c. 组合(合成)
d. 派生(子类出新的特性)/继承/继承结构
e. 泛化(子类与父类相同特点)/特化(子类自定义,与父类不同)
f. 自省/反射
① 声明函数 def fun(args): 函数体
② 声明类 class 类名: 类体 (class Chinese:pass)
③ 实例化 p1 = Chinese()
④ 分类 新式类Chinese(object): 经典类Chinese:
⑥ 属性 数据属性(函数外) 与 函数属性(函数内)
⑦ 查看属性字典 p = Chinese.dict()
⑧ 查看属性名称 Chinese.dict[‘name’] or p.name
import time
t = time.asctime()
class School:
def __init__(self,name,address,type):
self.name = name
self.address = address
self.type = type
def eaxm(self):
print('%s学校正在考试' %self.name)
def zhaosheng(self):
print('%s%s学校正在%s招生,时间%s' %(self.name,self.type,self.address,t))
school = School('清华大学','北京','公立') #实例化
school.eaxm()
school.zhaosheng()
# 类名
print('类名:',School.__name__)
# 类的文档字符串
print('类的文档字符串:',School.__doc__)
# 类的第一个父类
print('类的第一个父类:',School.__base__)
# 类的属性
print('类的属性:',School.__dict__)
# 类定义所在的模块
print('类定义所在的模块:',School.__module__)
# 增
School.add = 'add'
# 删
del School.eaxm
# 改
School.eaxm = 'eaxms'
# 查
print(School.eaxm)
print(School.add)
# 查
print(school.name)
print(school.address)
# 改
school.name = '北京大学'
print('类的属性:',School.__dict__)
print(school.name)
# 删
school.zhaosheng()
del school.name
# school.zhaosheng() name属性值已经删除,无法再调用函数
class Room:
tag = 1
def __init__(self,name,width,length,height,owner):
self.name = name
self.width = width
self.length = length
self.height = height
self.owner = owner
@property # @property装饰器来创建只读属性,@property装饰器会将方法转换为相同名称的只读属性,可以与所定义的属性配合使用,这样可以防止属性被修改。
def cal_area(self):
return self.width*self.height
@classmethod # classmethod修饰符对应的函数不需要实例化,不需要 self 参数,但第一个参数需要是表示自身类的 cls 参数,可以来调用类的属性,类的方法,实例化对象等。
# 默认自动传一个参数cls
def tell_info(cls):
print(cls)
print('--->',cls.tag)
# 可以自己添加参数,后面调用需自己传入数据
def tell_info1(cls,x):
print(cls)
print('--->', x)
@staticmethod #返回函数的静态方法
# 静态方法只是名义上的归属类管理,不能使用类变量和实例变量,是类的工具包
def wash(a,b,c):
print('%s,%s,%s正在洗漱' %(a,b,c))
p = Room('房子',100,100,1000,'alex')
print(p.cal_area)
p.tell_info()
p.tell_info1(10)
# 两种调用方法
Room.wash('a','b','c')
p.wash('a','b','c')
class Father:
money = 10
def __init__(self,name):
print('father')
self.name = name
def hit_son(self):
print('%s正在看儿子' %self.name)
class Mother:
money = 100
def __init__(self,name):
print('mother')
self.name = name
def kid_son(self):
print('%s正在看儿子' %self.name)
class Son(Father):
money = 20
def __init__(self,name,age):
self.name = name
self.age = age
s1 = Son('alex',5)
# 父类与子类含同一属性时
# 调用子类信息
print(s1.money,s1.name)
# 调用父类信息
print('父类信息:',Father.money)
class Son1(Father,Mother):
money = 30
def __init__(self,name,age):
self.name = name
self.age = age
s2 = Son1('kim',6)
# 父类与子类含同一属性时
# 调用子类信息
print(s2.money,s2.age)
# 调用父类信息
print('父类1信息:',Father.money,'父类2信息:',Mother.money)
注
a. 子类继承父亲的所有属性,若父亲与子类重名,不存在覆盖这一说,先找子类的,再找父类的
b. 方法改写,以本身类为准
# 子类继承父类,子类必须有父类方法才可实例化
import abc
class All_file(metaclass=abc.ABCMeta):
def read(self):
pass
def write(self):
pass
class Disk(All_file):
def read(self):#必须有
print('Disk')
def write(self):#必须有
print('Disk')
p = Disk()
p.write()
p.read()
# 接口(函数)继承,定义一个基类,基类中的方法定义成接口函数,只要来一个子类继承他,那子类必须实现这个方法
# 接口类方法不用去实现,不用实例化,只是规范
a. python2—深度优先
b. python3—广度优先
c. MRO列表,存放继承顺序
print(Son.mro) #基类(最小的)放在最后
a.子类会先于父类被检查
b.多个父类会根据他们在列表中的顺序被检查
c.如果对下一个类存在两个合法的选择,选择第一个父类
class Vehicle:
country = 'China'
def __init__(self,name,speed,load,power):
self.name = name
self.speed = speed
self.load = load
self.power = power
def run(self):
print('开动了')
class Subway(Vehicle):
def __init__(self,name,speed,load,power,line):
# Vehicle.__init__(self,name,speed,load,power)
super().__init__(name, speed, load, power) # super调用父类方法,与上等同
self.line = line
def show_info(self):
print(self.name,self.line)
def run(self):
# Vehicle.run(self)
super().run() # super调用父类方法,与上等同
print('%s开动啦' %self.name)
line = Subway('北京线','10km/h',10000,'电',12)
line.show_info()
line.run()
a. 当类之间显著不同,并且较小的类是较大的类所需要的组件时,用组合比较好。
b. 当类之间有很多相同的功能,提取这些共同的功能做成基类,用继承比较好。
调用相同的父类,运用相同的方法,得到不同的结果,多态实质上是继承的实现细节。
class H2O:
def __init__(self,name,tem):
self.name = name
self.tem = tem
def turn_ice(self):
if self.tem<0:
print('[%s]温度太低,结成了冰' %self.name)
elif self.tem>=0 and self.tem<=100:
print('[%s]温度正常,状态为水' %self.name)
else:
print('[%s]温度太高,形成水蒸气' %self.name)
class Water(H2O):
pass
class Ice(H2O):
pass
class Steam(H2O):
pass
w1 = Water('水',25)
w1.turn_ice()
i1 = Ice('水',-12)
i1.turn_ice()
s1 = Steam('水',120)
s1.turn_ice()
在另一个文件中,使用import调用,封装的本质就是将用户用不到的属性进行隐藏(可以使用接口函数来调用)
a. 第一层面的封装:类就是麻袋,这本身就是一种封装。
b. 第二层面的封装:类中定义私有的,只在类的内部使用,外部无法访问。
c. 第三层面的封装:明确区分内外,内部实现逻辑,外部无法实现。并且为封装到内部的逻辑提供一个访问接口给外部使用(真正意义)。
类的继承有两层意义:改变 扩展,多态就是类的这两层意义的一个具体的实现机制。即:调用不同的类实例化得到对象下的相同方法,实现的过程不一样,Python中的标准类型就是多态概念的一个很好的示范
class test:
status = 'abc'
def __init__(self,name,tem):
self.name = name
self.tem = tem
def turn_ice(self):
if self.tem<0:
print('[%s]温度太低,结成了冰' %self.name)
elif self.tem>=0 and self.tem<=100:
print('[%s]温度正常,状态为水' %self.name)
else:
print('[%s]温度太高,形成水蒸气' %self.name)
c = test('水',120)
print(c.name,c.tem)
print(hasattr(c,'name'))
print(getattr(c,'name'))
print(getattr(c,'nam','abc'))
if hasattr(c,‘turn_ice’):
getattr(c,‘turn_ice’)()
import time
setattr(c,'times',time.asctime())
print(c.__dict__)
delattr(c,'tem')
print(c.__dict__)
if hasattr(c,'turn_ice'):
getattr(c,'turn_ice')()
p = Foo(100)
# 调用不存在的参数,执行__getattr__
p.sss
# 调用存在的参数,不执行__getattr__
p.y
# 删除已有属性时,执行__delattr__方法;若属性没有则会报错
print(p.__dict__)
del p.y
print(p.__dict__)
# 赋值修改时执行__setattr__
p1 = Foo(120)
import time
class Open:
def __init__(self,filename,mode='r',encoding='utf-8'):
self.file = open(filename,mode,encoding=encoding)
self.mode = mode
self.encoding = encoding
# 重新授权一种新的write方式,具有新的格式(定制)
def write(self,line):
t = time.strftime('%Y-%m-%d %x')
self.file.write('%s %s' %(t,line))
# 如果没有函数则放开所有权限(通用性)
def __getattr__(self, item):
return getattr(self.file,item)
f = Open('a.txt','w+')
f.write('cpu负载\n')
f.write('内存不足\n')
# isinstance(obj,cls) 判断obj是否为cls的实例,跨族谱也可以
print(isinstance(f,Open))
# issubclass(sub,super) 判断sub是否为super的子类
class Foo:
pass
class Goo(Foo):
pass
print(issubclass(Goo,Foo))
class Foo:
def __getitem__(self, item):
print('getitem')
return self.__dict__[item]
def __setitem__(self, key, value):
print('setitem')
self.__dict__[key]=value
def __delitem__(self, key):
print('delitem')
self.__dict__.pop(key)
# 只有按照字典方法才可显示提示信息
# 改变对象的字符串显示
class Foo:
def __init__(self,name,age):
self.name = name
self.age = age
def __str__(self):
return '姓名%s,年龄%s' %(self.name,self.age)
f = Foo('Li',12)
print(f)
# 1.平常,只会显示f是Foo实例得来的,其他无任何信息
# 2.加__str__,可以定义显示内容,无则为默认__str__,,如平常显示
# 迭代器协议
class Foo:
def __init__(self,n):
self.n = n
def __iter__(self):
return self
def __next__(self):
if self.n == 16:
raise StopIteration('终止')
self.n+=1
return self.n
f = Foo(12)
print(f.__next__())
next(f)
print(next(f))
f.__next__()
print(next(f))
# 斐波那契数列(相邻两个数相加等于第三个数)
class Fib:
def __init__(self):
self.a = 1
self.b = 1
def __iter__(self):
return self
def __next__(self):
if self.a > 12:
raise StopIteration('终止')
self.a,self.b = self.b,self.a+self.b
return self.a
f = Fib()
print(f.__next__())
print(next(f))
print('---')
for i in f:
print(i)
简要概述
# 实质上是一个新式类,至少实现了__get__(),__set__(),__delete__()
# __get__()调用属性触发
# __set__()为一个属性赋值时触发
# __delete__()采用del删除属性时触发
# 包含这三个方法的新式类称为描述符,由这个类产生的实例进行属性的调用/赋值/删除,并不会触发这三个方法(在实例调用中才可触发)
class Foo:
def __get__(self,instance,owner):
print('触发get')
def __set__(self,instance,owner):
print('触发set')
def __delete__(self, instance):
print('触发delete')
p = Foo()
p.name = 'abc'
p.name
del p.name
描述符str
# 为描述符的对象的类属性进行服务
class Str:
def __get__(self, instance, owner):
print('Str调用...')
print(instance, owner)
def __set__(self, instance, value):
print('Str设置...')
print(instance, value)
def __delete__(self, instance):
print('Str删除...')
print(instance)
class People:
name = Str() # name 属性被代理,将这个类作用于另外一个类的属性来使用
def __init__(self, name): # name被Str类代理
self.name = name
p1 = People('long')
# 描述符Str的使用
p1.name
p1.name = 'sss'
del p1.name
print(p1.__dict__)
print(People.__dict__)
描述符种类
# 描述符分为两种
# 1.数据描述符,至少实现__get__和__set__
class Foo:
def __set__(self, instance, value):
print('set')
def __get__(self, instance, owner):
print('get')
# 2.非数据描述符,没有__set__的实现
class Foo:
def __get__(self, instance, owner):
print('get')
# 1.描述符本身应该定义成新式类,被代理的类也应该是新式类
# 2.必须把描述符定义成这个类的类属性,不能定义到构造函数中
# 3.要严格遵循该优先级,优先级由高到底:类属性>数据描述符>实例属性>非数据描述符>找不到的属性触发__getattr__()
类这个大的整体在各个程序中都普遍被使用,而且是代码显得比较规整,看起来逻辑比较清晰明了,简单来说就是重要的一批。