共86 次浏览

第七章 面向对象程序设计

7.1 认识面向对象

对象是类的实例化。对象分为静态特征和动态特征两种。静态特征指的是对象的外观、性质、属性等。动态特征则指的是对象具有的功能。在我们之前学习java时基本上都是面向对象编程。所以在这里就很好理解了。对象基本上可以理解为相关变量和方法的软件集。对象主要由两部分组成,一组包含各种类型数据的属性,对属性中的数据进行相关操作的相关方法。 由于Python语言时面向对象的语言,所以再Python语言中,一切都是对象,包括字符串、函数等都是对象。 面向对象中常用的技术术语如下:

  • 类(Class): 用来描述具有相同的属性和方法的对象的集合。它定义了该集合中每个对象所共有的属性和方法。对象是类的实例。
  • 类变量:类变量在整个实例化的对象中是公用的。类变量定义在类中且在函数体之外。类变量通常不作为实例变量使用。
  • 数据成员:类变量或者实例变量, 用于处理类及其实例对象的相关的数据。
  • 方法重写:如果从父类继承的方法不能满足子类的需求,可以对其进行改写,这个过程叫方法的覆盖(override),也称为方法的重写。
  • 局部变量:定义在方法中的变量,只作用于当前实例的类。
  • 实例变量:在类的声明中,属性是用变量来表示的。这种变量就称为实例变量,是在类声明的内部但是在类的其他成员方法之外声明的。
  • 继承:即一个派生类(derived class)继承基类(base class)的字段和方法。继承也允许把一个派生类的对象作为一个基类对象对待。例如,有这样一个设计:一个Dog类型的对象派生自Animal类,这是模拟”是一个(is-a)”关系(例图,Dog是一个Animal)。
  • 实例化:创建一个类的实例,类的具体对象。
  • 方法:类中定义的函数。
  • 对象:通过类定义的数据结构实例。对象包括两个数据成员(类变量和实例变量)和方法。

面向对象的三大特点:封装性、继承性、多态性

7.2 定义类

之前我们已经介绍过类是什么。这一步我们将介绍如何定义一个类。
类是一个用户定义类型,与大多数计算机语言一样。Python语言使用关键字class来定义类,
类的帮助信息可以通过ClassName.doc查看。 定义类的语法格式入下:

 class <ClassName>:
     '类的帮助信息'    #类文档字符串
 class_suite          #类体

下面我们直接定义一个学生类来帮助大家理解:

 class Students:
     "这是一个定义学生类的例子"
     name="赵日天"
     def disStudents():
         print("这个学生类的名字是:"+name)

其中类名称为Students。name是一个类变量,它的值将在这个类的所有例之间共享。用户可以在内部类或者外部类使用Students.name来进行访问。disStudents是此类的方法,属于方法对象。

7.3 类的构造方法和内置属性

7.3.1 构造方法

构造方法是指创建对象时其本身所运行的函数。Python语言使用 init() 函数作为对象的构造方法。当用户要在对象内指向对象本身时,可以使用self关键字,这与JavaScript以及Java中的this关键字一样,都是代表对象本身。def init(self)语句定义Goods类的构造方法,self时必要的参数且为第一个参数。用户可以在里面加入许多参数,在创建类时同时设置类的属性值。

 #类定义
 class Goods:
     #定义基本属性
     name = ' '
     factory= ' '
     #定义私有属性,私有属性在类外部无法直接进行访问
     __price= 0
     #定义构造方法
     def __init__(self,n,f,p):
         self.name = n
         self.factory = f
         self.__price = p
     def disGoods (self):
         print("%s生产的%s质量非常不错。最新款的价格是%s元。" %( self. factory,self.name, self.__price))
 ​
 # 实例化类
 g = Goods ('小米11pro','小米',5557)
 g.disGoods()
 ​

7.3.2 内置属性

所有的Python都具有下面的内置属性

  1. classname.dict :l类的属性是以字典对象的方式存储的。_dict _属性为盖子点对象的值
  2. classname.doc _:doc _属性返回此类的文件夹字符串。
  3. classname. name : _name _属性返回的时此类的名称。
  4. classname.module: module属性返回包含此类的模块名称

7.4 类例

7.4.1 创建类例

要创建一个类例,只需要指定变量与类名即可。使用id( )内置函数,可以返回类的识别码;使用type( )内置函数,额可以返回类的对象类型。下面我们通过创建一个简单类,并设置类的三个属性:

 class Goods:
        def __init__(self, name=None, factory =None, price= None):
               self.name = name
               self.factory = factory
               self.price = price
   
 #创建一个类的实例变量
 g = Goods ("小米11pro", "小米", 4860)
 print(g.name, g.factory, g.price)
 d = Goods("小米汽车", "小米", 8600) 
 print(d.name, d.factory, d.price)
 ​

在这个类的构造方法中,设置name、factoort与price的默认值均为None。 在创建类的时候,可以不必声明属性。等到创建类的实例后,在动态创建类的属性。例如:

 >>> classs myGoods:
   pass
 >>> x = myGoods()
 x.name="电脑"

如果想测试一个类例 y 是否是类 x 的例,可以使用内置函数instance( y,x)。这与JavaScript中的instanceof作用一样,都是测试前者是否是后者的实例化对象,返回值是一个Boolean值。 用户也可以在类内定义类变量,同时这些类变量可以被所有该类的例变量所共享。下面创建一个类,并定义类变量

 >>> class Vegetables:
     default_price = 3.66
     def _init _(self):
       self.price = Vegetables.default_price     #例变量的变量

7.4.2 类例的内置属性

所有的Python语言程序的类例都具有下面内置属性

  1. obj._ dict_:类例内的属性是以字典对象的方式存储的。
  2. obj._class _: _class _属性返回创建此类例所用的类名称
     >>>class Goods:
      def _init_(self,name=None,city=None,price=None):
      self.name = name
      self.city = city
      self.price = price
     >>> g = Goods()
     >>> g._dict_
     {'name':None,'city':None,'price':None}
     >>> g._class_
      <class '_main_.Goods'>

7.5 类的继承

类的继承就是新类继承旧类的属性与方法,这种行为称为派生子类。继承的新类称为派生类,被继承的旧类则称为基类。当用户创建派生类后,就可以在派生类内新增或改写基类的任何方法。这里的继承与Java中的继承类似,但是语句却略有不同。

 class <类名称> [(基类1,基类2,……)]:
   ["文件字符串"]
 <语句>

一个派生类可同时继承自多个基类,即一个子类可以同时有多个父类,基类直接用逗号(,)隔开。
下面举例说明:

先定义一个基类:

 class Cars:
     def __init__(self, name, price, city):
         self.name = name
         self.price = price
         self.city = city
     def printData(self):
         print ("名称: ", self.name)
         print ("价格: ", self.price)
         print ("产地: ", self. city)

下面创建一个Cars类的派生类:

 class bk(Cars):
 def _init_(self,name,price,city):     #派生类的构造方法
 Cars_init_(self,name,price,city)     #调用基类的构造方法

下一步创建一个派生类bk的例变量,并且调用基类Cars的函数printData( )打印出数据。

 >>>b=bk("别克",128000,上海)
 >>> b.printData()

当用户在类内编写函数时,要记得类函数名称空间的搜索顺序是类的实例——>类——>基类

7.6 类的多态

所谓多态,就是指类可以有多个名称相同、参数类型却不同的函数。Python中给并没有明显的多态特性,因为Python函数的参数不必声明数据类型。但是Python利用动态数据类型仍然可以处理对象的多态。

因为是动态数据类型,所以Python必须等到运行该函数时,才能知道该函数的类型,这种特性称为运行期绑定。像C++以及Java语言允许类内有多个名称相同、参数却不同的函数存在。但是在Python语言中却不允许这样做,如果用户在Python的类内声明多个名称相同、参数却不同的函数,那么Python语言程序会使用类内最后一个声明的函数。要解决这个问题必须使用下面的方法,即我们之前学到的可变参数。代码如下:

 class myClass:
   def _init_(self):
       pass
   def handle(self,*arg):
     if len(arg)==1:
         self.handle1(*arg)
      elif len(arg)==2:
          self.handle2(*arg)
      elif len(arg)==3
          self.handle3(*arg)
       else:
         print("Wrong arguments")
     def handle(self,x):
       print("1 arguments")
     def handle2(self,x,y):
       print("2 arguments")
     def handle3(self,x,y,z):
       print("3 arguments")
       
      >>> x=myClass()
      >>>x.handle()
       Wrong arguments
      >>>x.handle(1)
       1 arguments
       >>>x.handle(1,2)
       2 arguments
       >>>x.handle(1,2,3)
       3 arguments
       >>>x.handle(1,2,3,4)
       Wrong argument

7.7 类的封装

类的封装是指将其属性(变量与方法)封装在该类内,只有该类中的成员,才可以使用该类中的其它成员。这种被封装的变量与方法,称为该类的私有变量与私有方法。Python语言中所有变量与方法都是公用的。只要知道该类的名称与该变量或方法的名称,任何外部对象都可以直接存取类中的属性与方法。 要做到类的封装,必须做到以下几点: ① 如果属性名称的第一个字符是下划线,那么该属性视为类的内部变量,外边的变量不可以引用该变量。 ②如果属性名称前两个字符都是单下划线,那么在编译时属性名称_arrtibuteName会被改成 _className _attributeName,className是该类的名称。由于属性名称之前加上了类的名称,因此与类中原有的属性名称有差异。

以上两个原则只是作为参考,Python语言程序类中的所有属性仍然是公用的,只要知道类与属性的名称,就可以存取类中的所有属性。

7.8 Python的优势——垃圾回收机制

Python使用了引用计数这一简单技术来跟踪和回收垃圾。在Python内部有一个跟踪变量,记录着所有使用中的对象各有多少引用,称为一个引用计数器。

当对象被创建时,就同时创建了一个引用计数。当这个对象不再需要,其引用计数变为0 时,就被垃圾回收了。但回收不是”立即”的,而是由解释器在适当的时机将垃圾对象占用的内存空间回收。例如:

 class Vegetables:
    def __init__( self, name="西红四", price=6.88):
       self.name = name
       self. price = price
    def __del__(self):
       class_name = self.__class__.__name__
       print (class_name, "销毁对象")
  
 v= Vegetables ()
 g = v
 s= v
 print (id(v), id(g), id(s))    # 打印对象的id
 del v
 del g
 del s
 # 运行结果     1838495355848   1838495355848  1838495355848 
 #             Vegetables 销毁对象