一.静态
1.静态属性property:把函数属性封装成数据属性的形式;可访问类/实例的属性
本质就是实现了get,set,delete三种方法
class home:
def __init__(self,name,owner,width,length,height):
self.name=name
self.owner=owner
self.width=width
self.length=length
self.height=height
@property #将函数属性作为数据属性调用 #与实例绑定
def cal_area(self,name,owner,width,length):
#print('%s住的%s的面积是%s'%(self.owner,self.name,self.width*self.length))
return self.width*self.length
r1=home('公寓','Alex','100','100','3')
#r1.cal_area() #'NoneType' object is not callable #调用方法(函数属性)
#r1.cal_area #Alex住的公寓的面积是10000 #调用(数据)属性
print(r1.cal_area) #10000 #类似于r1.name
利用描述符原理完成一个自定制@property(本质是把一个函数属性利用装饰器原理做成一个描述符,类的属性字典中key=函数名,value=描述符类产生的对象)
class Lazyprop:
def __init__(self,func):
self.func=func
def __get__(self, instance, owner):
print('这是我们自己定制的静态属性,r1.area实际是要执行r1.area()')
if instance is None:
#使用默认property时类调用属性返回的是property(属性)对象
#类调用方法时instance=None,area方法缺少参数width/length
return self
return self.func(instance) #instance是r1,owner是Room类
class Room:
def __init__(self,name,width,length):
self.name=name
self.width=width
self.length=length
@Lazyprop #area=Lazyprop(area);相当于给类增加了一个描述符;触发__get__()
#@Lazyprop()=运行Lazyprop+(area=Lazyprop(area))
def area(self):
return self.width * self.length
r1=Room('alex',1,1)
print(r1.area)
#类调用:
print(Room.area) #__get__中instance参数为None,缺少参数width/length
#使用默认property时类调用属性返回的是property
实现延迟计算:
class Lazyproperty:
def __init__(self,func):
self.func=func
def __get__(self, instance, owner):
print('这是我们自己定制的静态属性,r1.area实际是要执行r1.area()')
if instance is None:
return self
else:
print('--->')
value=self.func(instance)
setattr(instance,self.func.__name__,value) #计算一次就缓存到实例的属性字典中
return value
class Room:
def __init__(self,name,width,length):
self.name=name
self.width=width
self.length=length
@Lazyproperty #area=Lazyproperty(area) 相当于'定义了一个类属性,即描述符'
def area(self):
return self.width * self.length
r1=Room('alex',1,1)
print(r1.area) #先从自己的属性字典找,没有再去类的中找,然后触发了area的__get__方法
print(r1.area)
#先从自己的属性字典找,找到了,是上次计算的结果,不用每执行一次都计算
#非数据描述符优先级低于实例属性
对数据描述符,缓存失效:
class Lazyproperty:
def __init__(self,func):
self.func=func
def __get__(self, instance, owner):
print('这是我们自己定制的静态属性,r1.area实际是要执行r1.area()')
if instance is None:
return self
else:
value=self.func(instance)
instance.__dict__[self.func.__name__]=value
return value
def __set__(self, instance, value):
print('hahahahahah')
class Room:
def __init__(self,name,width,length):
self.name=name
self.width=width
self.length=length
@Lazyproperty #area=Lazyproperty(area) 相当于定义了一个类属性,即描述符
def area(self):
return self.width * self.length
print(Room.__dict__)
r1=Room('alex',1,1)
print(r1.area)
print(r1.area)
print(r1.area)
print(r1.area)
#缓存功能失效:描述符实现了set方法,变成数据描述符,数据描述符优先级高于实例属性,所有的属性操作都去找描述符了
.setter/.deleter:
class Foo:
@property
def AAA(self):
print('get的时候运行我啊')
@AAA.setter #设置AAA的值时被触发
def AAA(self,value):
print('set的时候运行我啊')
@AAA.deleter #删除AAA时被触发
def AAA(self):
print('delete的时候运行我啊')
#只有在属性AAA定义property后才能定义AAA.setter,AAA.deleter
f1=Foo()
f1.AAA #如果只定义property,无法赋值/删除属性
f1.AAA='aaa'
print(f1.__dict__)
print(f1.AAA)
del f1.AAA
#效果同上一段代码
class Foo:
def get_AAA(self):
print('get的时候运行我啊')
def set_AAA(self,value):
print('set的时候运行我啊')
def delete_AAA(self):
print('delete的时候运行我啊')
AAA=property(get_AAA,set_AAA,delete_AAA)
#内置property三个参数与get/set/delete一一对应,顺序不可更改
应用:
class Goods:
def __init__(self):
# 原价
self.original_price = 100
# 折扣
self.discount = 0.8
@property
def price(self):
# 实际价格 = 原价 * 折扣
new_price = self.original_price * self.discount
return new_price
@price.setter
def price(self, value):
self.original_price = value
@price.deleter
def price(self):
del self.original_price
obj = Goods()
obj.price # 获取商品价格
obj.price = 200 # 修改商品原价
print(obj.price)
del obj.price # 删除商品原价
#类型检查
class People:
def __init__(self,name):
self.name=name #实例化就触发property
@property
def name(self):
# return self.name #无限递归
print('get------>')
return self.DouNiWan
@name.setter
def name(self,value):
print('set------>')
if not isinstance(value,str):
raise TypeError('必须是字符串类型')
self.DouNiWan=value
@name.deleter
def name(self):
print('delete------>')
del self.DouNiWan
p1=People('alex') #self.name实际是存放到self.DouNiWan里
p1.name=1
2.类方法classmethod:可访问类的属性
class home:
tag=1
def __init__(self,name,owner,width,length,height):
self.name=name
self.owner=owner
self.width=width
self.length=length
self.height=height
def test(self):
print('from test',self.name)
@classmethod #将方法变为专门供类使用 #与类绑定
def tell_info(cls): #cls参数由python自动补全,接收一个类名
print(cls)
print('--->',cls.tag)
home.tell_info() # --->1
r1=home('公寓','Alex','100','100','3')
r1.tell_info() # --->1 #不要这么用
利用描述符原理自定制@classmethod:
class ClassMethod:
def __init__(self,func):
self.func=func
def __get__(self, instance, owner): #类来调用,instance为None,owner为类本身,实例来调用,instance为实例,owner为类本身,
def feedback():
print('在这里可以加功能啊...')
return self.func(owner)
return feedback
class People:
name='linhaifeng'
@ClassMethod # say_hi=ClassMethod(say_hi)
def say_hi(cls):
print('你好啊,帅哥 %s' %cls.name)
People.say_hi()
p1=People()
p1.say_hi()
#疑问,类方法如果有参数呢,好说,好说
class ClassMethod:
def __init__(self,func):
self.func=func
def __get__(self, instance, owner): #类来调用,instance为None,owner为类本身,实例来调用,instance为实例,owner为类本身,
def feedback(*args,**kwargs):
print('在这里可以加功能啊...')
return self.func(owner,*args,**kwargs)
return feedback
class People:
name='linhaifeng'
@ClassMethod # say_hi=ClassMethod(say_hi)
def say_hi(cls,msg):
print('你好啊,帅哥 %s %s' %(cls.name,msg))
People.say_hi('你是那偷心的贼')
p1=People()
p1.say_hi('你是那偷心的贼')
实例可以调用类方法,但实际上还是由类调用:
>>> class a():
... char=1
... def __init__(self,char):
... self.char=char
... @classmethod
... def cla(self):
... print("cla"*self.char)
...
>>> a.cla()
cla
>>> a2=a(2)
>>> a2.char
2
>>> a2.cla()
cla
3.静态方法staticmethod:不能访问类/实例属性
class home:
tag=1
def __init__(self,name,owner,width,length,height):
self.name=name
self.owner=owner
self.width=width
self.length=length
self.height=height
def test(x,y): #类可以调用;实例自动传入self,无法调用
print('from test',x,y)
@staticmethod #不与类绑定也不与实例绑定
#只是名义上归属类管理,不能使用类变量/实例变量,只是类的工具包,在类的属性字典中
def wash(a,b,c): #写在类外边实例
print('%s%s%s在洗澡'%(a,b,c))
home.wash('A','WP','SD') #A WP SD在洗澡
r1=home('公寓','Alex','100','100','3')
r1.wash('A','WP','SD') #A WP SD在洗澡
利用描述符原理自定制@staticmethod
class StaticMethod:
def __init__(self,func):
self.func=func
def __get__(self, instance, owner): #类来调用,instance为None,owner为类本身,实例来调用,instance为实例,owner为类本身,
def feedback(*args,**kwargs):
print('在这里可以加功能啊...')
return self.func(*args,**kwargs)
return feedback
class People:
@StaticMethod# say_hi=StaticMethod(say_hi)
def say_hi(x,y,z):
print('------>',x,y,z)
People.say_hi(1,2,3)
p1=People()
p1.say_hi(4,5,6)
二.组合:类之间有显著不同且较小的类是较大的类的组件时使用
1.定义一个类,有各种数据属性,这些属性可以是通过类实例化得到的对象,这就是组合
class Hand:
pass
class Foot:
pass
class Trunk:
pass
class Head:
pass
class Person:
def __init__(self,id_num,name,hand,foot,trunk,head):
self.id_num=id_num
self.name=name
self.hand=hand()
self.foot=foot()
self.trunk=trunk()
self.head=head()
2.用途:类与类之间做关联(类与类没有共同点,有联系);小的组成大的
class School:
def __init__(self,name,address):
self.name=name
self.address=address
def enrolling(self):
print('%s正在招生'%self.name)
class Course:
def __init__(self,name,price,period,school):
self.name=name
self.price=price
self.period=period
self.school=school
s1=School('oldboy','BJ')
s2=School('oldboy','NJ')
s3=School('oldboy','DJ')
#c1=Course('Linux','1000','1h',s1)
#print(c1.school.address) #BJ
#选课系统:
msg='1 oldboy BJ\n2 oldboy NJ\n3 oldboy DJ\n'
menu={
'1':s1,'2':s2,'3':s3}
while True:
choice=input(msg+'\n请选择学校:')
sch_obj=menu[choice]
name=input('请选择课程名:')
price=input('请选择课程费用:')
period=input('请选择课程周期:')
new_course=Course(name,price,period,sch_obj)
print('课程%s属于%s'%(new_course.name,new_course.school.name))
三.继承:类之间有很多相同功能,提取共同功能做成基类时使用
1.类的继承:类似父->子->孙…的继承关系(父类又称基类)
2.分类:单继承;多继承(Java,C#只能继承一个类)
class ParentClass1:
pass
class ParentClass2:
pass
class SubClass(ParentClass1): #单继承
pass
class SubClass(ParentClass1,ParentClass2): #多继承
pass
3.继承的属性:子类的属性字典中没有父类的属性,但仍可调用父类的属性;优先在子类的属性中寻找
class dad:
money=10
def __init__(self,name):
print('爸爸')
self.name=name
def hit(self):
print('%s正在打儿子'%self.name)
class son(dad):
money=100
pass
s1=son('A') #爸爸
print(s1.name) #A
print(s1.money) #100
4.继承同时具有两种含义
其1是继承基类的方法,并做出自己的扩展——号称解决了代码重用问题
其2是声明某子类兼容于某基类(或者说接口上完全兼容于基类),又叫"接口继承"
定义1个接口类,子类继承接口类,并实现接口中定义的方法
要求做出1个良好的抽象,这个抽象规定了1个兼容接口,使外部调用者可以不加区分地处理实现了特定接口的所有对象(归一化)
可以理解为1种规范/约定,即在接口中定义的方法必须在子类中被实现(或采用同一接口的子类都具有相同的处理逻辑)
可以避免在循环调用中由于某个子类没有该方法而报错
#一切皆文件,所有接口兼容的对象都可以当文件处理:
import abc
class All_file(metaclass=abc.ABCMeta):#接口类无需实例化
#父类目的在于规定所有子类必须实现的方法(定义接口)
@abc.abstractmethod
def read(self):#一个接口(就是一个具体的方法)
'子类必须实现读功能'
pass
@abc.abstractmethod
def write(self):#接口类中方法无需实现
'子类必须实现写功能'
pass
class disk(All_file):
pass
class RAM(All_file):
def read(self):
print('RAM_read')
def write(self):
print('RAM_write')
d1=disk()#报错(必须实现父类定义的方法)
R1=RAM()#不报错
##########################################################
class Animal(metaclass=abc.ABCMeta):
@abc.abstractmethod
def shout():
pass
class dog(Animal):
def shout(self):
print("shout")
class cat(Animal):
def shout(self):
print("shout")
class tiger():
pass
list=[dog,cat,tiger]
for i in list:
i().shout()
#结果:
shout
shout
Traceback (most recent call last):
File "G:\programm\1.txt", line 21, in <module>
i.shout()
AttributeError: 'tiger' object has no attribute 'shout'
#如果tiger类继承了Animal类,即可避免此类情况,因为强制要求实现shout()
第1种意义并不大,甚至常常有害,因为其使子类与基类出现强耦合
第2种非常重要,归一化可以大大简化使用者的处理逻辑(可以不加区分的处理所有接口兼容的对象集合)
5.继承顺序:
如果python类继承了多个类,寻找方法有深度优先和广度优先
0.二者都要求每个节点,走且只走1次(不重复寻找)
1.广度优先:在1条路径搜索到倒数第2层时,如果别的路径能到达最终的基类,就切换路径,否则继续搜索这个类
2.深度优先:走完1条路径(到达最终的基类)或路径上某个节点已经走过时才切换路径
新式类按广度优先,经典类按深度优先(见下图:A继承B/C,B/C继承D,数字指查找顺序)
每定义1个类,Python会自动通过C3线性化算法得到1个方法解析顺序(MRO)列表
就是1个所有基类的线性顺序列表,也就是合并所有父类的MRO列表
该表遵循如下原则:
1.子类先于父类被检测
2.多个父类根据在列表中顺序被检查
3.如果对下一个类存在两个合法的选择,选择第一个父类(找到后就停止寻找)
print(A.__mro__) #查看A类的MRO列表
6.子类中调用父类方法
#父类名.父类方法()
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) #调用父类方法
self.line=line
def run(self):
print('地铁%s号线欢迎您'%self.line)
vehicle.run(self) #调用父类方法
line13=subway('北京地铁','30m/s','1000人','电力','13号线')
super():提高可扩展性;self无需传入
#每次在方法中使用super()时,都需要处于实例方法或类方法中
base():需传入self
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):
super().__init__(name,speed,load,power) #调用父类__init__()方法
#super(subway,self).__init__(name,speed,load,power) 与上一行相同
self.line=line
def run(self):
print('地铁%s号线欢迎您'%self.line)
super().run() #调用父类run()方法
line13=subway('北京地铁','30m/s','1000人','电力','13号线')