zoukankan      html  css  js  c++  java
  • 面向对象编程

    Date: 2019-05-18

    Author: Sun

    1. 面向对象的思想

    面向过程:面向处理,更多的是从计算机角度思考,注重计算每一个步骤,程序更像是一本cpu操作手册。

    面向对象:以日常生活的角度思考问题的解决,更接近人的思维方式,让人可以从更高的层面考虑系统的构建

    以你请朋友吃饭

    面向过程 面向对象
    自己买菜 和朋友一块到饭店
    自己摘菜 叫服务员点菜
    自己洗菜 和朋友一块吃
    自己做菜
    和朋友一块吃

    面向对象的优点:

    • 面向对象更加适合做应用的开发 --- 面向实体性编程为主,造飞机,造轮船
    • 面向对象可以使你的代码更加优雅和紧凑
    • 面向对象开发效率更高 ---- 将功能和数据进行隔离
    • 面向对象代码复用度更高、可维护性更好

    ​ 面向对象是一种思维方式,它认为万事万物皆对象,程序是由多个对象协作共同完成功能的,所以以后我们要从面向过程转向面向对象。以面向对象的方式考虑程序的构建。面向对象的核心是:类和对象

    2. 类和对象

    2.1 类和对象的概念

    ​ 类是抽象的,在使用的时候通常会找到这个类的一个具体的存在,使用这个具体的存在。一个类可以找到多个对象。

    ​ 对象是类的行为属性和数据属性进行具体化后的实例。

    生活角度

    • 类:具有相同特征和行为的对象的集合,是一个概念
    • 对象:客观存在的一切事物,是类的实例

    ​ 类: 汽车 超级英雄 电脑 杯子

    ​ 对象: 红色的宝马 美国队长 桌上的mac pro 老王的黑色杯子

    类就相当于制造飞机时的图纸,用它来进行创建的飞机就相当于对象

    程序角度

    • 类:用户自定义的数据类型,是模板,不占用内存
    • 对象:由类定义的变量,占用内存
    类:
      成员属性(成员变量)  描述对象的静态特征,诸如,名字、身高体重
      成员方法  描述对象的动态特征,例如:吃饭、睡觉、打豆豆
    

    类和对象之间的关系

    总之,类就是创建对象的模板

    2.2 类的构成

    类(Class) 由3个部分构成

    • 类的名称:类名
    • 类的属性:一组数据
    • 类的方法:允许对进行操作的方法 (行为)

    举例:

    1)人类设计,只关心3样东西:

    • 事物名称(类名):人(Person)
    • 属性:身高(height)、年龄(age)
    • 方法(行为/功能):跑(run)、打架(fight)

    2)狗类的设计

    • 类名:狗(Dog)
    • 属性:品种 、毛色、性别、名字、 腿儿的数量
    • 方法(行为/功能):叫 、跑、咬人、吃、摇尾巴

    2.3 类的定义

    #语法:
    class  类名:
         类体
    

    注意:

    • 类定义必须以关键字class
    • 类名要符合标识符的规范
    • 类名一般用大驼峰风格: 每个单词首字母大写,其它小写 ,例如MyBook YouMoney
    • 类体必须缩进
    • 在python3中类默认继承object,所以可以这样写 class Dog:,它等价于class Dog(object):
    • 一个文件里只放一个类

    (1) 定义一个Car类

    # 定义类
    class Car:   #经典定义类,如果是Car(object)则为新式类
        # 方法
        def getCarInfo(self):
            print(f'车轮子个数:{self.wheelNum}, 颜色:{self.color}')
    
        def move(self):
            print("车正在移动...")
    

    说明:

    • 定义类时有2种:新式类和经典类,上面的Car为经典类,如果是Car(object)则为新式类
    • 类名 的命名规则按照"大驼峰"

    (2) 成员方法

    成员方法其实就是函数,作用域在类内,成员方法的第一个参数必须是self,self代表当前对象,也就是调用这个方法的对象,这个参数有系统传递。

    class Dog(object):
        def bark(self):  #成员方法,第一个参数必须是self,代表当前调用对象
            print('我是小可爱--丁丁')
    
    dingding = Dog()  #实例化一个对象
    
    #调用方法,不需要传参数,self是系统传递的
    #调用形式: 对象.方法([实参])
    dingding.bark()   #等价调用形式:bark(dingding)
    

    注意:

    • self参数在调用的时候不必传值,由系统传值
    • self只能在实例方法中使用
    • 方法和函数的区别:
      • 方法作用域属于类,所以即便和普通函数重名,也不会被覆盖
      • 方法的第一个参数必须是self,但函数不要求
      • 方法必须通过对象调用,而函数不需要
    • 方法的第一个参数self其实可以使任何合法标识符,不过一般约定俗成都是self

    2.4 创建对象

    ​ 上节2.3中定义了一个Car类;就好比有车一个张图纸,那么接下来就应该把图纸交给生成工人们去生成了。

    python中,可以根据已经定义的类去创建出一个个对象。

    创建对象的格式为:

    对象名 = 类名()
    

    创建对象:

    # 定义类
    class Car:
        # 移动
        def move(self):
            print('车在奔跑...')
    
        # 鸣笛
        def toot(self):
            print("车在鸣笛...嘟嘟..")
    
    # 创建一个对象,并用变量BMW来保存它的引用
    BMW = Car()
    BMW.color = '黑色'
    BMW.wheelNum = 4 #轮子数量
    BMW.move()
    BMW.toot()
    print(BMW.color)
    print(BMW.wheelNum)
    

    说明:

    • BMW = Car(),这样就产生了一个Car的实例对象,此时也可以通过实例对象BMW来访问属性或者方法
    • 第一次使用BMW.color = '黑色'表示给BMW这个对象添加属性,如果后面再次出现BMW.color = xxx表示对属性进行修改
    • BMW是一个对象,它拥有属性(数据)和方法(函数)

    另外说明:

    #语法:  对象  = 类名([实参])
    dingding = Dog()  #实例化一个对象
    print(dingding)   #<__main__.Dog object at 0x00000000023F40B8>
    print(type(dingding)) #<class '__main__.Dog'>
    
    #查看对象的类名
    print(dingding.__class__)
    

    2.5 成员属性

    成员属性描述的是对象的静态特征,比如说狗名字、品种等,其实就是一个变量,作用域属于类,不会和类外的全局变量冲突。python中成员属性可以动态添加,也可以在构造函数中添加。成员属性属于对象,每个对象的成员属性都不同

    • 给对象动态添加的属性只属于这个对象,其它对象没有该属性
    • 使用__ slots__限制属性的动态绑定:
    • 在构造函数中添加的属性属于所有对象(重点)

    理解self

    • 所谓的self,可以理解为自己
    • 可以把self当做C++中类里面的this指针一样理解,就是对象自身的意思
    • 某个对象调用其方法时,python解释器会把这个对象作为第一个参数传递给self,所以开发者只需要传递后面的参数即可

    python中__slots__作用?

    ​ 当一个类需要创建大量实例时,可以通过__slots__声明实例所需要的属性,如果超过了此属性范围进行对象属性赋值,就会限制,起到保护数据作用。

    例如,

    class Foo(object):

    __slots__ = ['foo']

    这样做带来以下优点:

    1. 更快的属性访问速度
    2. 通过取消__dict__的使用减少内存消耗
    3. 保护数据安全性

    ​ Python内置的字典本质是一个哈希表,它是一种用空间换时间的数据结构。为了解决冲突的问题,当字典使用量超过2/3时,Python会根据情况进行2-4倍的扩容。由此可预见,取消__dict__的使用可以大幅减少实例的空间消耗。

    #添加属性语法:
       对象.成员属性  =  值
    #引用方式:对象.成员属性
    
    class Dog(object):
    
        __slots__ = ('gender','name','age','kind')  #这个类的对象这能有这些属性,同时也限制了采用__dict__属性可以用来查看实例属性
        
        def __init__(self,name,kind,age):
            self.name = name
            self.kind = kind
            self.age = age
            
        def bark(tmp):
            print('我是小可爱--丁丁')
    
    dingding = Dog('丁丁','泰迪',3)
    print('我是可爱的%s犬,%s,我今年%d岁了' % (dingding.kind, dingding.name, dingding.age))
    dingding.gender = '公'  #动态添加的属性
    #dingding.weight = 8  #不能添加这个属性,语法错误
    
    #查看实例属性
    print(dingding.__dict__)  #__dict__属性可以用来查看实例属性
    print(dir(dingding))  #查看Dog的属性,包括实例属性
    

    说明:

    ​ python中类如何来保护成员属性和成员方法呢?

    ​ 采用self.__xxx方式来定义类的成员。(两个下划线, 见 保护对象的属性)

    2.6 构造和析构

    (1) 构造方法

    • 目的:构造方法用于初始化对象,可以在构造方法中添加成员属性

    • 时机:实例化对象的时候自动调用

    • 参数:第一个参数必须是self,其它参数根据需要自己定义

    • 返回值:不返回值,或者说返回None,不应该返回任何其他值

      语法:
      	def __init__(self,arg1,arg2....):
      		函数体
      #参数:arg1,agr2...根据需要自己定义
      #如果自己不定义构造方法,系统自动生成一个构造函数
      def __init__(self):
        pass
      
      

    注意:

    • 如果没有定义构造方法,系统会生成一个无参构造方法,如果自己定义了构造方法,则系统不会自动生成

      class 类名:
         def __init__(self):
         	    pass
      
      
    • 一个类只能有一个构造方法,如果定义多个,后面的会覆盖前面的

    • 构造函数由系统在实例化对象时自动调用,不要自己调用

      class Dog(object):
          def __init__(self,name,kind,age):
              self.name = name  #定义对象属性,这个类所有的对象都具有该属性
              self.kind = kind  #成员属性必须通过self.引用,否则是普通变量
              self.age = age
      
          def bark(tmp):
              print('我是小可爱--丁丁')
      
      dingding = Dog('丁丁','泰迪',3)
      print('我是可爱的%s犬,%s,我今年%d岁了' % (dingding.kind, dingding.name, dingding.age))
      
      

    (2) 析构方法

    • 目的:对象销毁时,释放资源

    • 时机:对象销毁时由系统自动调用

    • 参数:除了self外,没有其他参数

    • 返回值:不返回值,或者说返回None。

    • 语法:

      def __del__(self):
          #to do
      
      

      案例

      class Dog(object):
      	#构造
          def __init__(self,name,kind,age):
              self.name = name
              self.kind = kind
              self.age = age
              
          #析构
          def __del__(self):
              print('拜拜了,二十年后又是一条好汉')
              
          def bark(tmp):
            print('我是小可爱--丁丁')
      
      dingding = Dog('丁丁','泰迪',3)
      print('我是可爱的%s犬,%s,我今年%d岁了' % (dingding.kind, dingding.name, dingding.age))
      del dingding  #销毁对象,自动调用析构方法
      
      

    ​ 当删除一个对象时,python解释器也会默认调用一个方法,这个方法为__del__()方法

    3. 保护对象的属性

    如果有一个对象,当需要对其进行修改属性时,有2种方法

    • 对象名.属性名 = 数据 ---->直接修改
    • 对象名.方法名() ---->间接修改

    为了更好的保存属性安全,即不能随意修改,一般的处理方式为

    • 将属性定义为私有属性
    • 添加一个可以调用的方法,供调用
    class People(object):
    
        def __init__(self, name):
            self.__name = name
    
        def getName(self):
            return self.__name
    
        def setName(self, newName):
            if len(newName) >= 5:
                self.__name = newName
            else:
                print("error:名字长度需要大于或者等于5")
    
    xiaoming = People("dongGe")
    print(xiaoming.__name)
    
    

    此时就会出现错误。

    class People(object):
        name = 'Tom'  #公有的类属性
        __age = 12     #私有的类属性
    
    p = People()
    
    print(p.name)           #正确
    print(People.name)      #正确
    print(p.__age)            #错误,不能在类外通过实例对象访问私有的类属性
    print(People.__age)        #错误,不能在类外通过类对象访问私有的类属性
    
    

    4. @property装饰器

    Python中有一个被称为属性函数(property)的小概念,它可以做一些有用的事情。

    • 将类方法转换为只读属性
    • 重新实现一个类的属性的setter和getter方法

    ​ Python内置的@property装饰器就是负责把一个方法变成属性调用的,特别在对属性数据进行校验的时候。

    # -*- coding: utf-8 -*-
    __author__ = 'sun'
    __date__ = '2019/5/28 16:23'
    
    class Student(object):
    
        @property
        def score(self):
            return self._score
    
        @score.setter
        def score(self, value):
            if not isinstance(value, int):
                raise ValueError('score must be an integer!')
            if value < 0 or value > 100:
                raise ValueError('score must between 0 ~ 100!')
            self._score = value
    
        @score.deleter
        def score(self):
            if not hasattr(self, '_score'):
                raise AttributeError('Student object has no attribute "_score"')
            del self._score
    
    s = Student()
    s.score =99
    print(s.score)
    
    del s.score
    
    

    ​ @property广泛应用在类的定义中,可以让调用者写出简短的代码,同时保证对参数进行必要的检查,这样程序运行时就减少了出错的可能性。

    5. 魔法方法

    (1)id() 方法

    ​ 获取对象在内存中的地址

    创建对象,并打印对象地址

    dingding2 = Dog('丁丁', '泰迪', 3)
    print(id(dingding2))
    
    

    (2) __str__()方法

    ​ 自定义打印对象内容,以字符串形式打印出来

    案例:

    class Car:
    
        def __init__(self, newWheelNum, newColor):
            self.wheelNum = newWheelNum
            self.color = newColor
    
        def __str__(self):
            msg = "嘿。。。我的颜色是" + self.color + "我有" + int(self.wheelNum) + "个轮胎..."
            return msg
    
        def move(self):
            print('车在跑,目标:夏威夷')
    
    
    BMW = Car(4, "白色")
    print(BMW)
    
    

    6. 静态方法和类方法

    6.1 类方法

    ​ 是类对象所拥有的方法,需要用修饰器@classmethod来标识其为类方法,对于类方法,第一个参数必须是类对象,一般以cls作为第一个参数(当然可以用其他名称的变量作为其第一个参数,但是大部分人都习惯以'cls'作为第一个参数的名字,就最好用'cls'了),能够通过实例对象和类对象去访问。

    class People(object):
        country = 'china'
    
        #类方法,用classmethod来进行修饰
        @classmethod
        def getCountry(cls):
            return cls.country
    
    p = People()
    print p.getCountry()    #可以用过实例对象引用
    print People.getCountry()    #可以通过类对象引用
    
    

    6.1 静态方法

    需要通过修饰器@staticmethod来进行修饰,静态方法不需要多定义参数

    class People(object):
        country = 'china'
    
        @staticmethod
        #静态方法
        def getCountry():
            return People.country
    
    
    print People.getCountry()
    
    

    综上:

    ​ 从类方法和实例方法以及静态方法的定义形式就可以看出来,类方法的第一个参数是类对象cls,那么通过cls引用的必定是类对象的属性和方法;而实例方法的第一个参数是实例对象self,那么通过self引用的可能是类属性、也有可能是实例属性(这个需要具体分析),不过在存在相同名称的类属性和实例属性的情况下,实例属性优先级更高。静态方法中不需要额外定义参数,因此在静态方法中引用类属性的话,必须通过类对象来引用

    7. 类的继承

    在现实生活中,继承一般指的是子女继承父辈的财产

    在程序中,继承描述的是事物之间的所属关系,例如猫和狗都属于动物,程序中便可以描述为猫和狗继承自动物

    # 定义一个父类,如下:
    class Cat(object):
    
        def __init__(self, name, color="白色"):
            self.name = name
            self.color = color
    
        def run(self):
            print("%s--在跑"%self.name)
    
    # 定义一个子类,继承Cat类如下:
    class Bosi(Cat):
    
        def setNewName(self, newName):
            self.name = newName
    
        def eat(self):
            print("%s--在吃"%self.name)
    
    
    bs = Bosi("印度猫")
    print('bs的名字为:%s'%bs.name)
    print('bs的颜色为:%s'%bs.color)
    bs.eat()
    bs.setNewName('波斯')
    bs.run()
    
    

    说明:

    • 私有的属性,不能通过对象直接访问,但是可以通过方法访问
    • 私有的方法,不能通过对象直接访问
    • 私有的属性、方法,不会被子类继承,也不能被访问
    • 一般情况下,私有的属性、方法都是不对外公布的,往往用来做内部的事情,起到安全的作用

    多重继承

    class base(object):
        def test(self):
            print('----base test----')
    class A(base):
        def test(self):
            print('----A test----')
    
    # 定义一个父类
    class B(base):
        def test(self):
            print('----B test----')
    
    # 定义一个子类,继承自A、B
    class C(A,B):
        pass
    
    
    obj_C = C()
    obj_C.test()
    
    print(C.__mro__) #可以查看C类的对象搜索方法时的先后顺序
    
    

    重写父类方法

    ​ 所谓重写,就是子类中,有一个和父类相同名字的方法,在子类中的方法会覆盖掉父类中同名的方法

    class Cat(object):
        def sayHello(self):
            print("halou-----1")
    
    
    class Bosi(Cat):
    
        def sayHello(self):
            print("halou-----2")
    
    bosi = Bosi()
    
    bosi.sayHello()
    
    

    调用父类的方法

    class Cat(object):
        def __init__(self,name):
            self.__id = id   #私有的,不被继承到子类
            self.name = name
            self.color = 'yellow'
    
    class Bosi(Cat):
    
        def __init__(self,name):
            # 调用父类的__init__方法1(python2)
            #Cat.__init__(self,name)
            # 调用父类的__init__方法2
            #super(Bosi,self).__init__(name)
            # 调用父类的__init__方法3
            super().__init__(name)
    
        def getName(self):
            return self.name
    
    bosi = Bosi('xiaohua')
    
    print(bosi.name)
    print(bosi.color)
    
    

    8 多态

    多态的概念是应用于Java和C#这一类强类型语言中,而Python崇尚“鸭子类型”。

    所谓多态:定义时的类型和运行时的类型不一样,此时就成为多态

    Python “鸭子类型”

    class F1(object):
        def show(self):
            print 'F1.show'
    
    class S1(F1):
    
        def show(self):
            print 'S1.show'
    
    class S2(F1):
    
        def show(self):
            print 'S2.show'
    
    def Func(obj):
        print obj.show()
    
    s1_obj = S1()
    Func(s1_obj) 
    
    s2_obj = S2()
    Func(s2_obj)
    
    

    说明

    python3的面向对象编程其实还有一些高级应用,大部分工作中不会用到

    敬请期待后续课程

  • 相关阅读:
    抽象类
    继承
    面向对象的静态属性,类方法,静态方法
    查找linux系统下的端口被占用进程的两种方法 【转】
    awk学习
    【转】借助LVS+Keepalived实现负载均衡
    ORA-28001: the password has expired解决方法
    ORACLE的还原表空间UNDO写满磁盘空间,解决该问题的具体步骤
    Oracle控制文件多路复用以及Oracle备份重建控制文件
    RedHat6.1通过配置yum server安装软件包
  • 原文地址:https://www.cnblogs.com/sunBinary/p/10940936.html
Copyright © 2011-2022 走看看