飞道的博客

Python —— 第四部分 面向对象程序设计

357人阅读  评论(0)

4.1 基本概念

面向对象程序设计(Object Oriented Programming,OOP)

  1. 针对大型软件设计而提出
  2. 使得软件设计更加灵活
  3. 能够更好地支持代码复用和设计复用
  4. 使得代码具有更好的可读性和拓展性

基本原则:计算机程序由多个能够起到子程序作用的单元或对象组合而成。

创建类时用变量形式表示的对象属性称为数据成员或成员属性,用函数形式表示的对象行为称为成员函数或成员方法
成员属性和成员方法统称为类的成员。

4.2 类的定义与使用

python 使用 class 关键字来定义类,约定类名首字母大写(若类名由多个单词拼接而成,各单词首字母也大写)
e.g.

class Car:
    def infor(self):
        print("This is a car")

实例化对象:

>>> car = Car()
>>> car.infor()
This is a car

python内置了方法 isinstance() 来测试一个对象是否为某个类的实例:

>>> isinstance(car, Car)
True
>>> isinstance(car, str)
False

占位符 pass 可以在没有确定如何实现功能、或为以后的软件升级预留空间时使用。

4.3 self 参数

类的所有实例方法都必须至少有一个名为 self 的参数,并且必须是方法的第一个形参(如果有多个形参),self 参数代表对象本身。

方法__init__()是一个特殊的方法,每当根据某个类创建新实例时,python 都会自动运行它。

注:在类中定义实例方法时将第一个参数定义为 self 只是一个习惯。

class A:
    def __init__(self, v):
        self.value = v
    def show(self):
        print('value=', self.value)

等价于

class A:
    def __init__(hahaha, v):
        hahaha.value = v
    def show(hahaha):
        print('value=', hahaha.value)

二者运行结果:

>>> a = A(3)
>>> a.value		#访问属性
3
>>> a.show()	#调用方法
value= 3

4.4 类成员与实例成员

实例属性:一般是指在构造函数__init__()中定义的,定义和使用必须以self为前缀。
在主程序(或类的外部),实例属性属于实例(对象),只能通过对象名访问。

类属性:是在类中所有方法之外定义的数据成员。
类属性属于类,可以通过类名或对象名访问。
类中的每个属性都必须由初始值,哪怕这个只是0或空字符串。

class Car:
    price = 100000
    def __init__(self, c):
        self.color = c

创建多个实例:

>>> car1 = Car("Red")# 实例化对象car1
>>> car2 = Car("Blue")# 实例化对象car2
>>> print(car1.color, Car.price)# 通过“ 对象名.成员 ”访问类中的数据成员或成员方法
								# 通过“ 类名.类属性 ”访问类属性
Red 100000
>>> Car.price = 110000			# 修改类属性
>>> Car.name = 'QQ'				# 增加类属性
>>> car1.color = "Yellow"		# 修改实例属性
>>> print(car2.color, Car.price, Car.name)
Blue 110000 QQ
>>> print(car1.color, Car.price, Car.name)
Yellow 110000 QQ

👆这里要注意,即使指定相同参数,python 依然会根据相应类创建另一个实例。

4.4.1 修改属性的值
  • 可以以三种不同的方式修改属性的值:
    ①直接通过实例进行修改
    ②通过方法进行设置
    ③通过方法进行递增(增加特定的值)

①直接修改属性的值
“对象.属性 = 新值”

class Car():
    price = 100000
    def __init__(self, color, speed):
        self.color = color
        self.speed = speed
    def show(self):
        print('this is a/an ' + self.color + ' car whose speed can reach', self.speed)

>>> my_car = Car('blue', 100)
>>> my_car.show()
this is a/an blue car whose speed can reach 100
>>> my_car.color = 'yellow'
>>> my_car.speed = 120
>>> my_car.show()
this is a/an yellow car whose speed can reach 120

②通过方法修改属性的值
如果有一个替你更新属性的方法,将大有裨益。这样就无需直接访问属性,而可将值传递给一个方法,由它在内部进行更新。

class Car():
    price = 100000
    def __init__(self, color, speed):
        self.color = color
        self.speed = speed
    def update_speed(self, new_speed):
        self.speed = new_speed
    def show(self):
        print('this is a/an ' + self.color + ' car whose speed can reach', self.speed)

>>> my_car = Car('green', 150)
>>> my_car.show()
this is a/an green car whose speed can reach 150
>>> my_car.update_speed(200)
>>> my_car.show()
this is a/an green car whose speed can reach 200

还可对方法update_speed()进行拓展,检查读数是否合理:

class Car():
	--snip--
    def update_speed(self, new_speed):
        '''检查速度更新数据是否合法'''
        if new_speed <= self.speed:
            print('This speed has already contained!')
        else:
            self.speed = new_speed
    def show(self):
        print('this is a/an ' + self.color + ' car whose speed can reach', self.speed)

>>> my_car = Car('red', 120)
>>> my_car.update_speed(100)
This speed has already contained!

③通过方法对属性的值进行递增
有时候需要将属性值递增特定的量,而不是将其设置为全新的值。

class Car():
    --snip--
    def update_speed(self, new_speed):
        --snip--
    def increment_speed(self, accelerate):
        ''''将速度提升指定的量'''
        self.speed += accelerate 
    def show(self):
        print('this is a/an ' + self.color + ' car whose speed can reach', self.speed)

>>> my_car = Car('red', 120)
>>> my_car.increment_speed(20)
>>> my_car.show()
this is a/an red car whose speed can reach 140

※python中比较特殊的是,可以动态地为类和对象增加成员:

#承接上述例子
def setSpeed(self, s):
    self.speed = s
>>> import types
>>> car1 = Car("Red")
>>> car1.setSpeed = types.MethodType(setSpeed, car1)# 动态为对象增加成员方法
>>> car1.setSpeed(50)						# 调用对象的成员方法
>>> print(car1.speed)
50

在 python 中,函数和方法是有区别的。
方法一般指与特定实例绑定的函数,通过对象调用方法时,对象本身将被作为第一个参数传递过去,普通函数并不具备这个特点。

>>> class Demo:
	pass

>>> t = Demo()
>>> def test(self, v):
	self.value = v

>>> t.test = test
>>> t.test
<function test at 0x000001CC6388B3A0>
>>> t.test(t, 3)
>>> print(t.value)
3
>>> t.test = types.MethodType(test, t)
>>> t.test
<bound method test of <__main__.Demo object at 0x000001CC6387D3D0>>
>>> t.test(5)
>>> print(t.value)
5

4.5 成员

python 并没有对私有成员提供严格的访问保护机制。在定义类的属性时,如果属性名以两个下划线“__”开头则表示私有属性。私有属性在类的外部不能直接访问,需要通过调用对象的公有成员方法来访问,或者通过python支持的特殊方式来访问。 python 提供了访问私有属性的特殊方式,可用于程序的测试和调试,对于成员方法也具有同样的性质。

私有属性是为了数据封装和保密而设的属性,一般只能在类的成员方法(类的内部)中使用访问,虽然python支持一种特殊的方式来从外部直接访问类的私有成员,但是并不推荐这样做。公有属性是可以公开使用的,既可以在类的内部进行访问,也可以在外部程序中使用。

class A:
    def __init__(self, value1=0, value2=0):
        self._value1 = value1
        self.__value2 = value2
    def setValue(self, value1, value2):
        self._value1 = value1
        self.__value2 = value2
    def show(self):
        print(self._value1)
        print(self.__value2)
>>> a = A()
>>> a._value1
0
>>> a._A__value2		# 在外部访问对象的私有数据成员
0
>>> a.setValue(1, 2)
>>> a.show()
1
2

在 python 中,以下划线开头的变量名和方法名有特殊的含义,尤其是在类的定义中,用下划线作为变量名和方法名前缀和后缀来表示类的特殊成员。

  1. _ xxx
    保护成员
    不能用“from module import *”导入,只有类对象和子类对象能访问这些成员。
  2. __ xxx __
    系统定义的特殊成员
  3. __ xxx
    类中的私有成员
    只有类对象自己能访问,子类对象也不能访问到这个成员,但在外部对象可以通过“ 对象名._ 类名 __ xxx ”这样的特殊方式来访问。python 中不存在严格意义上的私有成员。

技巧补充:

  • 查看成员(可以理解成辅助输入)
    1. 在IDLE环境中,在对象或类后面加上一个圆点“.”,可以自动列出所有公开成员
    2. 倘若再在后面加一个下划线,就会列出该对象或类的所有成员,包括私有成员
  • 在IDLE环境下,一个下划线“_”表示解释器中最后一次显示的内容或最后一次语句正确执行的输出结果。
    >>> 3 + 5
    8
    >>> _ + 2
    10
    >>> _ * 3
    30
    >>> _ / 5
    6.0
    >>> 1 / 0
    Traceback (most recent call last):
      File "<pyshell#54>", line 1, in <module>
        1 / 0
    ZeroDivisionError: division by zero
    >>> _
    6.0
    

4.6 方法

类中定义的方法可粗略分为四大类:公有方法、私有方法、静态方法、类方法。

4.7 继承机制

继承是为代码复用和设计复用而设计的,是面向对象程序设计的重要特性之一。

在继承关系中,已有的、设计好的类称为父类基类超类(superclass),新设计的类称为子类派生类
子类继承了其父类的所有属性和方法,同时还可以定义自己的属性和方法~

派生类可以继承父类的公有成员,但是不能继承其私有成员。
如果要在派生类中调用基类的方法,可以使用内置函数super()或者通过“基类名.方法名()”的方式实现。

4.7.1 子类的方法__init__()

例如要模拟电动汽车,电动汽车是汽车的一种特殊子类,因此我们可以在原先创建的 Car 类的基础上创建新类ElectricCar子类,这样就可以只为电动汽车编写其特有的属性和方法。

class Car():
    price = 100000
    def __init__(self, color, speed):
        self.color = color
        self.speed = speed
    def update_speed(self, new_speed):
        '''检查速度更新数据是否合法'''
        if new_speed <= self.speed:
            print('This speed has already contained!')
        else:
            self.speed = new_speed
    def increment_speed(self, accelerate):
        ''''将速度提升指定的量'''
        self.speed += accelerate 
    def show(self):
        print('this is a/an ' + self.color + ' car whose speed can reach', self.speed)

class ElectricCar(Car):
    '''电动汽车'''
    def __init__(self, color, speed):
        '''初始化父类的属性'''
        super().__init__(color, speed)

>>> my_tesla = ElectricCar('grey', 200)
>>> my_tesla.show()
this is a/an grey car whose speed can reach 200

注:创建子类时,父类必须包含在当前文件中,且位于子类前面。
super()是一个特殊函数,帮助python将父类和子类关联起来。父类也称超类(superclass),super因此而得名。

4.7.2 给子类定义属性和方法

现在,我们确认电动汽车具备普通汽车的行为了,下面就可以添加区分子类和父类所需的新属性和方法。

class ElectricCar(Car):
    '''电动汽车'''
    def __init__(self, color, speed):
        '''初始化父类的属性'''
        super().__init__(color, speed)
        self.battery_size = 70

    def describe_battery(self):
        print('This car has a ' + str(self.battery_size) + '-kwh battery.')

>>> my_tesla = ElectricCar('grey', 200)
>>> my_tesla.describe_battery()
This car has a 70-kwh battery.
4.7.3 重写父类的方法

只要父类的方法不符合子类模拟实物的行为,都可以对其进行重写。
为此,可在子类中定义一个与父类中要重写的同名方法,这样,在执行时,父类中的该方法将会失效~

假设 Car 类中有一个名为fill_gas_tank()的方法,但是这对纯电动汽车来说毫无意义。

class ElectricCar(Car):
	--snip--
	def fill_gas_tank():
		'''电动汽车没有油箱'''
		print('This car doesn't need a tank!')

此时,如果有人对电动汽车调用方法fill_gas_tank(),python将忽略Car类中的该方法,转而运行上述代码。

4.7.4 将实例用作属性

属性和方法清单以及文件都越来越长。在这种情况下,可能需要将类的一部分作为一个独立的类提取出来。

例如,在不断给电动汽车的电瓶增加细节时,可以将这些属性和方法提取出来,放到另一个名为 Battery 的类中,并将 Battery 实例作为 ElectricCar 类的一个属性:

class Car():
    --snip--

class Battery():
    def __init__(self, battery_size=70):
        '''初始化电瓶的属性'''
        self.battery_size = battery_size

    def describe_battery(self):
        '''描述电瓶信息'''
        print('This car has a ' + str(self.battery_size) + '-kwh battery.')

class ElectricCar(Car):
    '''电动汽车'''
    def __init__(self, color, speed):
        '''初始化父类的属性,再初始化电瓶汽车特有的属性'''
        super().__init__(color, speed)
        self.battery = Battery()

>>> my_tesla = ElectricCar('grey', 200)
>>> my_tesla.battery.describe_battery()
This car has a 70-kwh battery.

在这里,定义了一个心累(新类)Battery,它没有继承任何类。

在 ElectricCar 类中,添加了一个名为 self.battery 的属性,并创建一个新的 Battery 实例。每当方法__init__()被调用时,都将执行该操作,因此现在每个 ElectricCa 实例都包含一个自动创建的 Battery 实例~

这看似做了很多工作,但是现在我们想多详细的描述电瓶都可以,不会导致 ElectricCar 类混乱不堪。

下面再给 Battery 类添加一个方法,它将根据电池容量报告汽车续航里程:

class Car():
    --snip--

class Battery():
    def __init__(self, battery_size=70):
        '''初始化电瓶的属性'''
        self.battery_size = battery_size

    def describe_battery(self):
        '''描述电瓶信息'''
        print('This car has a ' + str(self.battery_size) + '-kwh battery.')

    def get_range(self):
        if self.battery_size <= 0:
            print('You have to charge your battery!')
        else:
            distance = self.battery_size * 3
        message = 'This car can go approximately ' + str(distance) + ' miles on a full charge.'
        print(message)

class ElectricCar(Car):
    --snip--

>>> my_tesla = ElectricCar('grey', 200)
>>> my_tesla.show()
this is a/an grey car whose speed can reach 200
>>> my_tesla.battery.describe_battery()
This car has a 70-kwh battery.
>>> my_tesla.battery.get_range()
This car can go approximately 210 miles on a full charge.

4.8 导入类

随着不断给类增加新的功能,文件可能会变得很长,即使你妥善地使用了继承亦如此。为遵循python的总体理念,应让文件尽可能整洁~
在这方面,python允许将类存储在模块中,然后在主程序中导入所需的模块。


转载:https://blog.csdn.net/weixin_42764266/article/details/105877025
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场