zoukankan      html  css  js  c++  java
  • Python基础(九)-面向对象二

    一、静态属性,类方法,静态方法

    1.1、静态属性

    未使用静态属性之前:

    class Room:
        def __init__(self,name,owner,width,length,heigh):
            self.name=name
            self.owner=owner
            self.width=width
            self.length=length
            self.heigh=heigh
    
        def cal_area(self):
            print('%s 住的 %s 总面积是%s' % (self.owner,self.name, self.width * self.length))
            # return  self.width * self.length
    
    r1 = Room("room01","小二",10,10,10)
    r1.cal_area()  #小二 住的 room01 总面积是100

    @property ==>使用静态属性

    class Room:
        def __init__(self,name,owner,width,length,heigh):
            self.name=name
            self.owner=owner
            self.width=width
            self.length=length
            self.heigh=heigh
    
        @property  #使用静态属性
        def cal_area(self):
            # print('%s 住的 %s 总面积是%s' % (self.owner,self.name, self.width * self.length)) #TypeError: 'NoneType' object is not callable
            return  self.width * self.length
    
    r1 = Room("room01","小二",10,10,10)
    print(r1.cal_area)  #100  ==>调用方式发生了改变
    print(r1.width)   #10
    

    1.2、类方法

    当我们不想进行实例化,而需要直接获取类的属性时,可以使用类方法@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
    
        @property
        def cal_area(self):
            # print('%s 住的 %s 总面积是%s' % (self.owner,self.name, self.width * self.length))
            return  self.width * self.length
    
        def test(self):
            print('from test',self.name)
    
        @classmethod  #类方法
        def tell_info(cls,x):
            print(cls)
            print('--》',cls.tag,x)  #print('--》',Room.tag)
        # def tell_info(self):
        #     print('---->',self.tag)
    
    print(Room.tag)  #1
    Room.tell_info(10)   #--》 1 10    #不需要进行实例化而直接获取类的属性

    1.3、静态方法

    类的工具包@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
    
        @property
        def cal_area(self):
            # print('%s 住的 %s 总面积是%s' % (self.owner,self.name, self.width * self.length))
            return  self.width * self.length
    
        @classmethod
        def tell_info(cls,x):
            print(cls)
            print('--》',cls.tag,x)#print('--》',Room.tag)
        # def tell_info(self):
        #     print('---->',self.tag)
    
        @staticmethod  #类的工具包,不与类绑定,也不与实例绑定
        def wash_body(a,b,c):  #可以不传参
            print('%s %s %s正在洗澡' %(a,b,c))
    
        def test(x,y):
            print(x,y)
    
    
    print(Room.__dict__)   #==>'wash_body': <staticmethod object at 0x000002BFB0BB4630>
    r1=Room('room12','alex',100,100,100000)
    print(r1.__dict__)    #{'width': 100, 'heigh': 100000, 'length': 100, 'name': 'room12', 'owner': 'alex'}
    

    二、组合

    定义一个人的类,人有头,躯干,手,脚等数据属性,这几个属性有可以是通过一个类实例化的对象,这就是组合

    组合的用途:①做关联②小的组成大的

    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','AAA')
    print(p1.__dict__)
    #{'name': 'AAA', 'foot': <__main__.Foot object at 0x00000273D44547B8>, 'id_num': '111111', 'trunk': <__main__.Trunk object at 0x00000273D44547F0>, 'hand': <__main__.Hand object at 0x00000273D4454780>, 'head': <__main__.Head object at 0x00000273D4454828>}
    

    不建议使用的方式:

    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)

    三、继承

    3.1、初识继承

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

    python中类的继承分为:单继承和多继承

    class ParentClass1: #定义父类
        pass
    
    class ParentClass2: #定义父类
        pass
    
    class SubClass1(ParentClass1): #单继承,基类是ParentClass1,派生类是SubClass
        pass
    
    class SubClass2(ParentClass1,ParentClass2): #python支持多继承,用逗号分隔开多个继承的类
        pass

    查看继承:

    >>> SubClass1.__bases__ #__base__只查看从左到右继承的第一个子类,__bases__则是查看所有继承的父类
    (<class '__main__.ParentClass1'>,)
    >>> SubClass2.__bases__
    (<class '__main__.ParentClass1'>, <class '__main__.ParentClass2'>)

    经典类与新式类:

    1.只有在python2中才分新式类和经典类,python3中统一都是新式类
    2.在python2中,没有显式的继承object类的类,以及该类的子类,都是经典类
    3.在python2中,显式地声明继承object的类,以及该类的子类,都是新式类
    3.在python3中,无论是否继承object,都默认继承object,即python3中所有类均为新式类

    提示:如果没有指定基类,python的类会默认继承object类,object是所有python类的基类,它提供了一些常见方法(如__str__)的实现。

    >>> ParentClass1.__bases__
    (<class 'object'>,)
    >>> ParentClass2.__bases__
    (<class 'object'>,)

    3.2、重用性

    #继承的代码实现
    class Animal:
    
        def eat(self):
            print("%s 吃 " %self.name)
    
        def drink(self):
            print ("%s 喝 " %self.name)
    
        def shit(self):
            print ("%s 拉 " %self.name)
    
        def pee(self):
            print ("%s 撒 " %self.name)
    
    
    class Cat(Animal):
    
        def __init__(self, name):
            self.name = name
            self.breed = '猫'
    
        def cry(self):
            print('喵喵叫')
    
    class Dog(Animal):
    
        def __init__(self, name):
            self.name = name
            self.breed='狗'
    
        def cry(self):
            print('汪汪叫')

    注意:

    class Foo:
        def f1(self):
            print('Foo.f1')
    
        def f2(self):
            print('Foo.f2')
            self.f1()
    
    class Bar(Foo):
        def f1(self):
            print('Foo.f1')
    
    b=Bar()
    b.f2()
    
    #Foo.f2
    #Foo.f1

    3.3、接口与归一化设计

    抽象类

    #一切皆文件
    import abc #利用abc模块实现抽象类
    
    class All_file(metaclass=abc.ABCMeta):
        all_type='file'
        @abc.abstractmethod #定义抽象方法,无需实现功能
        def read(self):
            '子类必须定义读功能'
            pass
    
        @abc.abstractmethod #定义抽象方法,无需实现功能
        def write(self):
            '子类必须定义写功能'
            pass
    
    # class Txt(All_file):
    #     pass
    #
    # t1=Txt() #报错,子类没有定义抽象方法
    
    class Txt(All_file): #子类继承抽象类,但是必须定义read和write方法
        def read(self):
            print('文本数据的读取方法')
    
        def write(self):
            print('文本数据的读取方法')
    
    class Sata(All_file): #子类继承抽象类,但是必须定义read和write方法
        def read(self):
            print('硬盘数据的读取方法')
    
        def write(self):
            print('硬盘数据的读取方法')
    
    class Process(All_file): #子类继承抽象类,但是必须定义read和write方法
        def read(self):
            print('进程数据的读取方法')
    
        def write(self):
            print('进程数据的读取方法')
    
    wenbenwenjian=Txt()
    
    yingpanwenjian=Sata()
    
    jinchengwenjian=Process()
    
    #这样大家都是被归一化了,也就是一切皆文件的思想
    wenbenwenjian.read()
    yingpanwenjian.write()
    jinchengwenjian.read()
    
    print(wenbenwenjian.all_type)
    print(yingpanwenjian.all_type)
    print(jinchengwenjian.all_type)

    3.4、继承的顺序

    class A(object):
        def test(self):
            print('from A')
    
    class B(A):
        def test(self):
            print('from B')
    
    class C(A):
        def test(self):
            print('from C')
    
    class D(B):
        def test(self):
            print('from D')
    
    class E(C):
        def test(self):
            print('from E')
    
    class F(D,E):
        # def test(self):
        #     print('from F')
        pass
    f1=F()
    f1.test()
    print(F.__mro__) #只有新式才有这个属性可以查看线性列表,经典类没有这个属性
    
    #新式类继承顺序:F->D->B->E->C->A
    #经典类继承顺序:F->D->B->A->E->C
    #python3中统一都是新式类
    #pyhon2中才分新式类与经典类
    

    继承顺序原理

    #定义的每一个类,python会计算出一个方法解析顺序(MRO)列表,这个MRO列表就是一个简单的所有基类的线性顺序列表
    
    >>> F.mro() #等同于F.__mro__
    [<class '__main__.F'>, <class '__main__.D'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]

    准则

    1.子类会先于父类被检查
    2.多个父类会根据它们在列表中的顺序被检查
    3.如果对下一个类存在两个合法的选择,选择第一个父类

    3.5、子类调用父类的方法

    3.5.1、方式一

    父类名.父类方法()

    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("中国地铁","180m/s","50人/箱","电",13)
    line13.run()
    

    3.5.2、方法二:super()

    class Vehicle1:
        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('开动啦')
            print('开动啦')
    class Subway(Vehicle1):
            def __init__(self,name,speed,load,power,line):
               # Vehicle.__init__(self,name,speed,load,power)
               # super().__init__(name,speed,load,power)  #super(__class__,self).__init__(name,speed,load,power)
               super(Subway,self).__init__(name,speed,load,power)
               self.line=line
            def show_info(self):
                print(self.name,self.speed,self.load,self.power,self.line)
            def run(self):
                # Vehicle.run(self)
                super().run()
                print('%s %s 线,开动啦' %(self.name,self.line))
    line13=Subway('北京地铁','10km/s',1000000000,'电',13)
    line13.show_info()
    line13.run()
    
    print(line13.__class__)  #<class '__main__.Subway'>

    四、多态

    多态指的是一类事物有多种形态,多态指出了如何通过他们共同的属性和动作来操作及访问,而不需要考虑他们集体的类

    动物有多种形态:人,狗,猪

    import abc
    class Animal(metaclass=abc.ABCMeta): #同一类事物:动物
        @abc.abstractmethod
        def talk(self):
            pass
    
    class People(Animal): #动物的形态之一:人
        def talk(self):
            print('say hello')
    
    class Dog(Animal): #动物的形态之二:狗
        def talk(self):
            print('say wangwang')
    
    class Pig(Animal): #动物的形态之三:猪
        def talk(self):
            print('say aoao')
    class H2O:
        def __init__(self,name,temperature):
            self.name=name
            self.temperature=temperature
        def turn_ice(self):
            if self.temperature < 0:
                print('[%s]温度太低结冰了' %self.name)
            elif self.temperature > 0 and self.temperature < 100:
                print('[%s]液化成水' %self.name)
            elif self.temperature > 100:
                print('[%s]温度太高变成了水蒸气' %self.name)
    
    class Water(H2O):
        pass
    class Ice(H2O):
        pass
    class Steam(H2O):
        pass
    
    w1=Water('水',25)
    i1=Ice('冰',-20)
    s1=Steam('蒸汽',3000)

    五、封装

    5.1、隐藏约定

    在python中使用双下划綫开头的方式将属性隐藏起来(设置成私有的)

    类中所有双下划线开头的名称如__x都会在类定义时自动变形成:_类名__x的形式

    #其实这仅仅这是一种变形操作且仅仅只在类定义阶段发生变形
    #类中所有双下划线开头的名称如__x都会在类定义时自动变形成:_类名__x的形式:
    
    class A:
        __N=0 #类的数据属性就应该是共享的,但是语法上是可以把类的数据属性设置成私有的如__N,会变形为_A__N
        def __init__(self):
            self.__X=10 #变形为self._A__X
        def __foo(self): #变形为_A__foo
            print('from A')
        def bar(self):
            self.__foo() #只有在类内部才可以通过__foo的形式访问到.
    
    #A._A__N是可以访问到的,
    #这种,在外部是无法通过__x这个名字访问到。

    注意

    1)这种机制也并没有真正意义上限制我们从外部直接访问属性,知道了类名和属性名就可以拼出名字:_类名__属性,然后就可以访问了,如a._A__N,即这种操作并不是严格意义上的限制外部访问,仅仅只是一种语法意义上的变形,主要用来限制外部的直接访问

    2)变形的过程只在类的定义时发生一次,在定义后的赋值操作,不会变形

    3)在继承中,父类如果不想让子类覆盖自己的方法,可以将方法定义为私有的

    #正常情况
    >>> class A:
    ...     def fa(self):
    ...         print('from A')
    ...     def test(self):
    ...         self.fa()
    ... 
    >>> class B(A):
    ...     def fa(self):
    ...         print('from B')
    ... 
    >>> b=B()
    >>> b.test()
    from B
     
    
    #把fa定义成私有的,即__fa
    >>> class A:
    ...     def __fa(self): #在定义时就变形为_A__fa
    ...         print('from A')
    ...     def test(self):
    ...         self.__fa() #只会与自己所在的类为准,即调用_A__fa
    ... 
    >>> class B(A):
    ...     def __fa(self):
    ...         print('from B')
    ... 
    >>> b=B()
    >>> b.test()
    from A

    5.2、封装的含义

    封装的真谛在于明确地区分内外,封装的属性可以直接在内部使用,而不能被外部直接使用,然而定义属性的目的终归是要用,外部要想用类隐藏的属性,需要我们为其开辟接口

    1)封装数据:隐藏起来然后对外提供操作该数据的接口,然后我们可以在接口附加上对该数据操作的限制,以此完成对数据属性操作的严格控制

    class Teacher:
        def __init__(self,name,age):
            # self.__name=name
            # self.__age=age
            self.set_info(name,age)
    
        def tell_info(self):
            print('姓名:%s,年龄:%s' %(self.__name,self.__age))
        def set_info(self,name,age):
            if not isinstance(name,str):
                raise TypeError('姓名必须是字符串类型')
            if not isinstance(age,int):
                raise TypeError('年龄必须是整型')
            self.__name=name
            self.__age=age
    
    
    t=Teacher('egon',18)
    t.tell_info()
    
    t.set_info('egon',19)
    t.tell_info()

    2)封装方法:目的是隔离复杂性,可以对外提供接口函数

    #取款是功能,而这个功能有很多功能组成:插卡、密码认证、输入金额、打印账单、取钱
    #对使用者来说,只需要知道取款这个功能即可,其余功能我们都可以隐藏起来,很明显这么做
    #隔离了复杂度,同时也提升了安全性
    
    class ATM:
        def __card(self):
            print('插卡')
        def __auth(self):
            print('用户认证')
        def __input(self):
            print('输入取款金额')
        def __print_bill(self):
            print('打印账单')
        def __take_money(self):
            print('取款')
    
        def withdraw(self):
            self.__card()
            self.__auth()
            self.__input()
            self.__print_bill()
            self.__take_money()
    
    a=ATM()
    a.withdraw()

    5.3、特性property

    5.3.1、property含义

      property是一种特殊的属性,访问它时会执行一段功能(函数)然后返回值

    示例:

    class People:
        def __init__(self,name,weight,height):
            self.name=name
            self.weight=weight
            self.height=height
        @property
        def bmi(self):
            return self.weight / (self.height**2)
    
    p1=People('egon',75,1.85)
    print(p1.bmi)
    
    #++++++++++++++++++++++++++++++++++++++++++++++++++
    import math
    class Circle:
        def __init__(self,radius): #圆的半径radius
            self.radius=radius
    
        @property
        def area(self):
            return math.pi * self.radius**2 #计算面积
    
        @property
        def perimeter(self):
            return 2*math.pi*self.radius #计算周长
    
    c=Circle(10)
    print(c.radius)
    print(c.area) #可以向访问数据属性一样去访问area,会触发一个函数的执行,动态计算出一个值
    print(c.perimeter) #同上
    '''
    输出结果:
    314.1592653589793
    62.83185307179586
    '''
    
    #注意:此时的特性arear和perimeter不能被赋值
    c.area=3 #为特性area赋值
    '''
    抛出异常:
    AttributeError: can't set attribute
    '''

    5.3.2、为什么要使用property

    将一个类的函数定义成特性以后,对象再去使用的时候obj.name,根本无法察觉自己的name是执行了一个函数然后计算出来的,这种特性的使用方式遵循了统一访问的原则

    5.4、封装与扩展性

    封装在于明确区分内外,使得类实现者可以修改封装内的东西而不影响外部调用者的代码;而外部使用用者只知道一个接口(函数),只要接口(函数)名、参数不变,使用者的代码永远无需改变。这就提供一个良好的合作基础——或者说,只要接口这个基础约定不变,则代码改变不足为虑。

    #类的设计者
    class Room:
        def __init__(self,name,owner,width,length,high):
            self.name=name
            self.owner=owner
            self.__width=width
            self.__length=length
            self.__high=high
        def tell_area(self): #对外提供的接口,隐藏了内部的实现细节,此时我们想求的是面积
            return self.__width * self.__length
    
    
    #使用者
    >>> r1=Room('卧室','egon',20,20,20)
    >>> r1.tell_area() #使用者调用接口tell_area
    
    
    #类的设计者,轻松的扩展了功能,而类的使用者完全不需要改变自己的代码
    class Room:
        def __init__(self,name,owner,width,length,high):
            self.name=name
            self.owner=owner
            self.__width=width
            self.__length=length
            self.__high=high
        def tell_area(self): #对外提供的接口,隐藏内部实现,此时我们想求的是体积,内部逻辑变了,只需求修该下列一行就可以很简答的实现,而且外部调用感知不到,仍然使用该方法,但是功能已经变了
            return self.__width * self.__length * self.__high
    
    
    #对于仍然在使用tell_area接口的人来说,根本无需改动自己的代码,就可以用上新功能
    >>> r1.tell_area()
  • 相关阅读:
    iOS-夜间模式(换肤设置)
    iOS-自定义起始时间选择器视图
    [SpriteKit] 系统框架中Cocos2d-x制作小游戏ZombieConga
    [SceneKit] 不会 Unity3D 的另一种选择
    Android WiFi开发
    Java 工具类 —— 定时器(Timer/TimerTask)
    java 语法 —— 数组
    IDEA 的使用(快捷键、括号对齐的方式)
    cmd 高级用法
    MySQL 基本信息的查询(初始化配置信息 my.ini)
  • 原文地址:https://www.cnblogs.com/hujinzhong/p/11487391.html
Copyright © 2011-2022 走看看