python 静态,组合,继承

一.静态
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()   #<class '__main__.home'>          --->1
r1=home('公寓','Alex','100','100','3')
r1.tell_info()   #<class '__main__.home'>          --->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号线')
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值