python12-面向对象初阶

Posted liangjiongyao

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了python12-面向对象初阶相关的知识,希望对你有一定的参考价值。

一、静态属性

 实质上就是数据属性
@property跟实例的函数属性绑定后,会把函数属性封装成数据属性。对象仍能访问对象的属性,也可以访问类的属性

技术分享图片
class Room:
    tag=1
    def __init__(self,name,owner,width,length,heigh):
        self.name=name
        self.owner=owner
        self.width=width
        self.length=length
        self.heigh=heigh

    @property #class提供的,该关键词看起来像是把函数属性变成了数据属性
    def cal_area(self): 
        # print(‘%s 住的 %s 总面积是%s‘ % (self.owner,self.name, self.width * self.length))
        return  self.width * self.length


print(r1.cal_area) #return了一个面积,用了@property后就不能用r1.cal_area()去调用,要用r1.cal_area。
View Code

 效果跟调数据属性一样,但是看不出这是个函数,这就能隐藏它背后的逻辑

 

二、类方法

@classmethod跟类绑定,能访问类的函数、数据,不能访问实例的属性

技术分享图片
class Room:
    tag=1
    def __init__(self,name,owner,width,length,heigh):
        self.name=name
        self.owner=owner
        self.width=width
        self.length=length
        self.heigh=heigh

    @classmethod #把它变成一个供类使用的方法,不需要self参数。应用场景:跟实例没有关系,类级别的操作
    def tell_info(cls,x): #cls参数是python自动加入的,接受的是一个类名
        print(cls)
        print(--》,cls.tag,x)#print(‘--》‘,Room.tag)
    
    #类调用方法的折中办法,需要先实例化一个对象
    #def tell_info(self,x): 
    #    print(‘---->‘,self.tag,x)
    
#使用@classmethod关键字
Room.tell_info(10) #直接调用,返回类的内存地址、1以及10

#老方法
r1=Room(厕所,alex,100,100,100000)
Room.tell_info(r1,10)
Room.test(1) #1.name执行不了,self原则上接受任何参数,但是实际上它是有意义的,它已经跟实例绑在一起了

#注意
#r1.tell_info(10)#能够调用,但是不建议。因为@classmethod的场景是:跟实例没有关系,类级别的操作
View Code

 

三、静态方法

@staticmethod跟类、实例都不绑定,它是类的工具包。名义上归属类管理,不能访问类变量和实例变量

技术分享图片
class Room:
    tag=1
    def __init__(self,name,owner,width,length,heigh):
        self.name=name
        self.owner=owner
        self.width=width
        self.length=length
        self.heigh=heigh

    @staticmethod #跟类和实例都剥离开,做一些跟类和实例没关的事
    def wash_body(a,b,c):
        print(%s %s %s正在洗澡 %(a,b,c))

    def test(x,y): #跟@staticmethod的区别
        print(x,y)


Room.wash_body(alex,yuanhao,wupeiqi) #直接类执行,不用通过实例
print(Room.__dict__) #有wash_body的内存地址,而test只是个普通函数

r1=Room(厕所,alex,100,100,100000)

print(r1.__dict__) #没有wash_body、test的内存地址
r1.wash_body(alex,yuanhao,wupeiqi) #也能调用

Room.test(1,2) #能调用
r1.test(1,2) 不能调用,因为r1调用的时候,类会把实例r1传进去,然而这里的test是不需要r1
View Code

 

四、组合

软件重用的重要方式除了继承之外还有另外一种方式,即:组合。
组合指的是,在一个类中以另外一个类的对象作为数据属性

作用:做类与类之间的关联,类与类之间没有共同点,但是有关联就可以用组合
应用场景:当类与类之间有显著差别,并且较小类是较大类的组件时,用组合。如一个机器人,有胳膊类、腿类、身体类,一个大类由一个个小类组合而成

 

用组合写一个人的类

技术分享图片
class Hand:
    pass

class Foot:
    pass

class Trunk:
    pass

class Head:
    pass


class Person:
    def __init__(self,id_num,name):
        self.id_num=id_num
        self.name=name
        self.hand=Hand()#类名加一个小括号就是一个对象
        self.foot=Foot()
        self.trunk=Trunk()
        self.head=Head()
p1=Person(111111,alex)
print(p1.__dict__)# Foot,Hand,Head的函数地址都在属性字典里
View Code

 

用组合写学校的多个校区的课程划分

技术分享图片
class School:
    def __init__(self,name,addr):
        self.name=name
        self.addr=addr

    def zhao_sheng(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(北京大学,北京)
s2=School(南京大学,南京)
s3=School(中山大学,广州)

# c1=Course(‘linux‘,10,‘1h‘,‘北京大学 北京‘) #你现在只是名字跟”北京大学 北京“这个字符串有关系,但是school下面的招生就跟你没什么关系
c1=Course(linux,10,1h,s1) #课程都归属于某个学校,不过这个s1写死了,因为用户应该有自己的选择

print(c1.__dict__)# 有school的对象地址
print(c1.school.name) #相当于s1.name
#print(s1)==print(c1.school)





优化,增加用户选择
class School:
    def __init__(self,name,addr):
        self.name=name
        self.addr=addr


    def zhao_sheng(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(北京大学,北京)
s2=School(南京大学,南京)
s3=School(中山大学,广州)

# c1=Course(‘linux‘,10,‘1h‘,‘oldboy 北京‘)
# c1=Course(‘linux‘,10,‘1h‘,s1)

msg=‘‘‘
1 北京大学 北京
2 南京大学 南京
3 中山大学 广州
‘‘‘
while True:
    print(msg)
    menu={
        1:s1,
        2:s2,
        3:s3
    }
    choice=input(选择学校>>: )
    school_obj=menu[choice]
    name=input(课程名>>: )
    price=input(学费>>: )
    period=input(周期>>: )
    new_course=Course(name,price,period,school_obj)
    print(课程【%s】属于【%s】学校 %(new_course.name,new_course.school.name))
View Code

 

有一种写组合很low的方式

技术分享图片
class School:
    def __init__(self,name,addr):
        self.name=name
        self.addr=addr
        self.course_list=[]
    def zhao_sheng(self):
        print(%s 正在招生 %self.name)
class Course:
    def __init__(self,name,price,period):
        self.name=name
        self.price=price
        self.period=period

s1=School(oldboy,北京)
s2=School(oldboy,南京)
s3=School(oldboy,东京)

c1=Course(linux,10,1h)
c2=Course(python,10,1h)

s1.course_list.append(c1)
s1.course_list.append(c2)
print(s1.__dict__)

for course_obj in s1.course_list:
    print(course_obj.name,course_obj.price)
View Code

 

五、继承

继承是一种创建新类的方式,在python中,新建的类可以继承一个或多个父类,父类又可称为基类或超类,新建的类称为派生类或子类

python中类的继承分为:单继承和多继承,以下是单继承

技术分享图片
class Dad:
    这个是爸爸类
    money=10
    def __init__(self,name):
        print(爸爸)
        self.name=name
    def hit_son(self):
        print(%s 正在打儿子 %self.name)

class Son(Dad):
    money = 1000000000009
    def __init__(self,name,age):
        self.name=name
        self.age=age
        print("儿子")
    def hit_son(self):
        print(来自儿子类)
# print(Son.money)
# Son.hit_son()#不能调用到
# print(Dad.__dict__)
# print(Son.__dict__)
# s1=Son(‘alex‘)#如果自己没有__init__,则执行父类的__init__,相当于把_父类的_init__拿到子类里执行
s1=Son(alex,18)#执行自己的__init__,当子类与父类的初始化函数一样时,子类先找自己的,然后再找父类的 
s1.hit_son()#儿子类自己有就用自己的
View Code

 

六、子类中调用父类的方法

技术分享图片
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):#子类重构__init__
    def __init__(self,name,speed,load,power,line):#子类重构__init__
        Vehicle.__init__(self,name,speed,load,power) #继承父类的__init__
        self.line=line #子类新增的属性
        
#为什么继承父类的__init__要传self?因为是类调用类的方法,所以类要传self(类.方法A(实例)),只有在实例化和实例调用类的方法时,才不需要手动传入自己(实例.方法A())

    def show_info(self):
        print(self.name,self.speed,self.load,self.power,self.line)

    def run(self): #该方法与父类的方法重名,但是不会覆盖父类的方法
        Vehicle.run(self) #在这里能调用父类的方法
        print(%s%s号线,开动啦 %(self.name,self.line))

line4=Subway("广州地铁","60km/h","10000","","4")
line4.show_info()
line4.run() #执行改写后的子类的run方法

line8=Vehicle("广州火车","30km/h","10","汽油") #Subway子类的run方法没有覆盖Vehicle的run方法,这里仍然调用父类的方法
line8.run()
View Code

小结:子类继承了父类的属性,如果子类定义的属性跟父类的重名了(只是在子类的属性字典里新增了一个属性),并不会把父类的属性覆盖

 

七、用super继承父类的属性

技术分享图片
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(Subway,self).__init__(name,speed,load,power) #通过super调用父类的__init__
        self.line=line

    def show_info(self):
        print(self.name,self.speed,self.load,self.power,self.line)

    def run(self):
        super().run() #不用传self,默认给你传进去
        print(%s%s号线,开动啦 %(self.name,self.line))

line4=Subway("广州地铁","60km/h","10000","","4")
line4.show_info()
line4.run() #执行改写后的子类的run方法
View Code

 

八、什么时候用继承

当类之间有很多相同的功能,提取共同的功能做成基类,用继承。如猫狗都有吃喝拉撒,这是动物类的共同特征,狗类、猫类都继承这个动物类
派生:派生就是在继承后,子类衍生出来的新属性,猫继承动物类的属性,但又衍生出"喵喵叫"属性

继承同时具有两种含义:
含义一:继承基类的方法,并且做出自己的改变或者扩展(代码重用),动物类、狗、猫的例子。说是代码重用,但实际上会使得子类和父类的代码出现强耦合,这常常是有害的,因为代码之间应该越独立越好。

含义二:声明某个子类兼容于某基类,定义一个接口类,子类继承接口类,并且实现接口中定义的方法,这又叫做"接口继承",接口继承不会节省代码。
接口继承实质上是要求"做出一个良好的抽象,这个抽象规定了一个兼容接口,使得外部调用者无需关心具体细节,可一视同仁的处理实现了特定接口的所有对象"---这在程序设计上,叫做归一化处理。比如Linux中一切皆文件,连cdrom也是文件,而文件只有读和写两种方法,我们不需要关心底层如何实现,只是去调用。


接口本质上是个函数,父类规定好子类必须实现什么方法,但是父类不实现。子类一旦继承父类就需要具体去实现父类规定的方法,下面来模仿一下Linux的一切皆文件:

不加abc模块进行限制的话,子类可以不具体实现父类规定的方法

技术分享图片
#父类中规定了子类需要实现的方法read和write
class All_file(metaclass=abc.ABCMeta):
    def read(self):
        pass

    def write(self):
        pass

#子类具体实现父类中规定的方法
class Disk(All_file):
    def read(self):
        print(disk read)

    def write(self):
        print(disk write)

class Cdrom(All_file):
    def read(self):
        print(cdrom read)

    def write(self):
        print(cdrom write)


class Mem(All_file):
    def read(self):
        print(mem read)

#Mem没有实现父类中规定的write方法
m1=Mem() #能够初始化
m1.read() #调用自己的方法进行读
m1.write() #Mem类自己没有实现write方法,于是就调用父类,之前说过子类的对象可以调用父类的方法。

#Mem类自己没有实现write方法,于是就调用父类,但是调用的结果没有任何效果,因为父类自己也没有实现该方法。
#为了避免这种情况,父类应该严格限制子类必须实现该方法才对,接下来看真正的接口调用
View Code

加了abc模块,mem没有实现write,实例化mem就会报错

技术分享图片
#真正版
import abc #导入该模块
class All_file(metaclass=abc.ABCMeta):
    @abc.abstractmethod
    def read(self):
        pass

    @abc.abstractmethod
    def write(self):
        pass

class Disk(All_file):
    def read(self):
        print(disk read)

    def write(self):
        print(disk write)

class Cdrom(All_file):
    def read(self):
        print(cdrom read)

    def write(self):
        print(cdrom write)


class Mem(All_file):
    def read(self):
        print(mem read)

#    def write(self):
#        print(‘mem write‘)
        
m1=Mem() #不能够初始化,父类检测到子类并没有实现write方法
m1.read() 
m1.write()
View Code

归一化的好处:方便

 

九、继承顺序

技术分享图片
继承顺序
class A:
    # def test(self):
    #     print(‘A‘)
    pass
class B(A):
    # def test(self):
    #     print(‘B‘)

    pass
class C(A):
    # def test(self):
    #     print(‘C‘)
    pass

class D(B):
    # def test(self):
    #     print(‘D‘)
    pass

class E(C):
    # def test(self):
    #     print(‘E‘)
    pass

class F(D,E):
    # def test(self):
    #     print(‘F‘)
    pass
f1=F()
f1.test()   

结论:
经典类:F->D->B->A-->E-->C 深度优先
新式类:F-->D->B-->E--->C--->A 广度优先
View Code

python对于你定义的每一个类,都会计算出一个方法解析顺序MRO列表,这个MRO列表就是一个简单的所有基类的线性顺序列表
为了实现继承,python会在MRO列表上从左到右开始查找基类,直到找到第一个匹配这个属性的类为止。

MRO列表遵循的三条准则:
1.子类会优先于父类被检查#先从子类自己中找
2.多个父类会根据它们在列表中的顺序被检查 #从MRO中按顺序找
3.如果对下一个类存在两个合法的选择,选择第一个父类#比如class F(D,E),先找D




















以上是关于python12-面向对象初阶的主要内容,如果未能解决你的问题,请参考以下文章

C++ 初阶类和对象

C++ 初阶类和对象

python面向对象

JavaScript初阶

C++初阶---类和对象

随笔--类和对象初阶问题总结(面试)