Python复习系列:面向对象程序设计

Python进阶: 面向对象程序设计

1. 类的定义与使用

Python使用class关键字来定义类,class关键字之后是一个空格,接下来是类的名字,如果派生自其它基类的话则需要把所有基类放到一对圆括号中并使用逗号分隔,然后是一个冒号,最后换行并定义类的内部实现。

class Car(object):              #定义一个类,派生自object类
    def infor(self):            #定义成员方法
        print("This is a car")
        
car = Car()               #实例化对象
car.infor()               #调用对象的成员方法
This is a car

2. 数据成员与成员方法

创建类时用变量形式表示对象特征的成员称为数据成员(attribute),用函数形式表示对象行为的成员称为成员方法(method),数据成员和成员方法统称为类的成员。

2.1 私有成员和公有成员

私有成员在类的外部不能直接访问,一般是在类的内部进行访问和操作,或者在类的外部通过调用对象的公有成员方法来访问,而公有成员是可以公开使用的,既可以在类的内部进行访问,也可以在外部程序中使用。

从形式上看,在定义类的成员时,如果成员名以两个下划线开头但是不以两个下划线结束则表示是私有成员,否则就不是私有成员。

Python并没有对私有成员提供严格的访问保护机制,通过一种特殊方式“对象名._类名__xxx”也可以在外部程序中访问私有成员,但这会破坏类的封装性,不建议这样做。

>>> 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

在Python中,以下划线开头的变量名和方法名有特殊的含义,尤其是在类的定义中。

_xxx:受保护成员;

__ xxx__:系统定义的特殊成员;

__ xxx:私有成员,只有类对象自己能访问,子类对象不能直接访问到这个成员,但在对象外部可以通过“对象名._类名__xxx”这样的特殊方式来访问。

注意:Python中不存在严格意义上的私有成员。

2.2 数据成员

数据成员可以大致分为两类:属于对象的数据成员和属于类的数据成员。

属于对象的数据成员一般在构造方法**__ init__()**中定义,当然也可以在其他成员方法中定义,在定义和在实例方法中访问数据成员时以self作为前缀,同一个类的不同对象(实例)的数据成员之间互不影响;

属于类的数据成员是该类所有对象共享的,不属于任何一个对象,在定义类时这类数据成员一般不在任何一个成员方法的定义中。

2.3 成员方法

Python类的成员方法大致可以分为公有方法、私有方法、静态方法、类方法这几种类型。

公有方法、私有方法一般是指属于对象的实例方法,私有方法的名字以两个下划线开始,而抽象方法一般定义在抽象类中并且要求派生类必须重新实现。每个对象都有自己的公有方法和私有方法,在这两类方法中都可以访问属于类和对象的成员。公有方法通过对象名直接调用,私有方法不能通过对象名直接调用,只能在其他实例方法中通过前缀self进行调用或在外部通过特殊的形式来调用。

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

在实例方法中访问实例成员时需要以self为前缀,但在外部通过对象名调用对象方法时并不需要传递这个参数。

如果在外部通过类名调用属于对象的公有方法,需要显式为该方法的self参数传递一个对象名,用来明确指定访问哪个对象的成员。

静态方法和类方法都可以通过类名和对象名调用,但不能直接访问属于对象的成员,只能访问属于类的成员。

静态方法和类方法不属于任何实例,不会绑定到任何实例,当然也不依赖于任何实例的状态,与实例方法相比能够减少很多开销。

类方法一般以cls作为类方法的第一个参数表示该类自身,在调用类方法时不需要为该参数传递值,静态方法则可以不接收任何参数。

>>> class Root:
    __total = 0
    def __init__(self, v):    #构造方法
        self.__value = v
        Root.__total += 1

    def show(self):           #普通实例方法
        print('self.__value:', self.__value)
        print('Root.__total:', Root.__total)

    @classmethod              #修饰器,声明类方法
    def classShowTotal(cls):  #类方法
        print(cls.__total)

    @staticmethod             #修饰器,声明静态方法
    def staticShowTotal():    #静态方法
        print(Root.__total)
        
>>> r = Root(3)
>>> r.classShowTotal()              #通过对象来调用类方法
1
>>> r.staticShowTotal()             #通过对象来调用静态方法
1
>>> r.show()
self.__value: 3
Root.__total: 1
>>> rr = Root(5)
>>> Root.classShowTotal()           #通过类名调用类方法
2
>>> Root.staticShowTotal()          #通过类名调用静态方法
2

>>> Root.show()    #试图通过类名直接调用实例方法,失败
TypeError: unbound method show() must be called with Root instance as first argument (got nothing instead)
>>> Root.show(r)   #但是可以通过这种方法来调用方法并访问实例成员
self.__value: 3
Root.__total: 2
>>> Root.show(rr)  #通过类名调用实例方法时为self参数显式传递对象名
self.__value: 5
Root.__total: 2

2.4 属性

属性是一种特殊的成员方法,综合了公开数据成员和成员方法二者的优点,既可以像成员方法那样对值进行必要的检查,又可以像数据成员一样灵活的访问。

如果属性为只读,则无法修改其值,也无法增加与属性同名的新成员,当然也无法删除对象属性。

# 只读属性

>>> class Test:
	    def __init__(self, value):
			self.__value = value		   #私有数据成员

	    @property					   #修饰器,定义属性
	    def value(self):               #只读,无法修改和删除
			return self.__value
    
>>> t = Test(3)
>>> t.value
3
>>> t.value = 5                        #只读属性不允许修改值
AttributeError: can't set attribute
>>> t.v=5                              #动态增加新成员
>>> t.v
5
>>> del t.v                            #动态删除成员
>>> del t.value                        #试图删除对象属性,失败
AttributeError: can't delete attribute
>>> t.value
3

# 可读、可写属性 不可以删除

>>> class Test:
    def __init__(self, value):
        self.__value = value	

    def __get(self):
        return self.__value

    def __set(self, v):
        self.__value = v
    value = property(__get, __set)

    def show(self):
        print(self.__value)

>>> t = Test(3)
>>> t.value      #允许读取属性值
3
>>> t.value = 5  #允许修改属性值
>>> t.value
5
>>> t.show()     #属性对应的私有变量也得到了相应的修改
5
>>> del t.value  #试图删除属性,失败
AttributeError: can't delete attribute

# 可读、可修改、可删除的属性。
>>> class Test:
    def __init__(self, value):
        self.__value = value

    def __get(self):
        return self.__value

    def __set(self, v):
        self.__value = v

    def __del(self):
        del self.__value

    value = property(__get, __set, __del)

    def show(self):
        print(self.__value)
>>> t = Test(3)
>>> t.show()
3
>>> t.value
3
>>> t.value = 5
>>> t.show()
5
>>> t.value
5
>>> del t.value            #删除属性
>>> t.value                #对应的私有数据成员已删除
AttributeError: 'Test' object has no attribute '_Test__value'
>>> t.show()
AttributeError: 'Test' object has no attribute '_Test__value'
>>> t.value = 1            #为对象动态增加属性和对应的私有数据成员
>>> t.show()
1
>>> t.value
1

3. 继承

继承是用来实现代码复用和设计复用的机制,是面向对象程序设计的重要特性之一。设计一个新类时,如果可以继承一个已有的设计良好的类然后进行二次开发,无疑会大幅度减少开发工作量。

在继承关系中,已有的、设计好的类称为父类或基类,新设计的类称为子类或派生类。派生类可以继承父类的公有成员,但是不能继承其私有成员。如果需要在派生类中调用基类的方法,可以使用内置函数super()或者通过“基类名.方法名()”的方式来实现这一目的。

Python支持多继承,如果父类中有相同的方法名,而在子类中使用时没有指定父类名,则Python解释器将从左向右按顺序进行搜索。

例题:设计Person类,并根据Person派生Teacher类,分别创建Person类和Teacher类对象。

class Person(object): 
    def __init__(self, name='', age=20, sex='man'):
        #通过调用方法进行初始化,这样可以对参数进行更好地控制
        self.setName(name)
        self.setAge(age)
        self.setSex(sex)

    def setName(self, name):
        if not isinstance(name, str):
            raise Exception('name must be string.')
        self.__name = name

    def setAge(self, age):
        if type(age) != int:
            raise Exception('age must be integer.')
        self.__age = age

    def setSex(self, sex):
        if sex not in ('man', 'woman'):
            raise Exception('sex must be "man" or "woman"')
        self.__sex = sex

    def show(self):
        print(self.__name, self.__age, self.__sex, sep='\n')
#派生类
class Teacher(Person):
    def __init__(self, name='', age=30,sex='man', department='Computer'):
        #调用基类构造方法初始化基类的私有数据成员
        super(Teacher, self).__init__(name, age, sex)
        #也可以这样初始化基类的私有数据成员
        #Person.__init__(self, name, age, sex)
        #调用自己的方法初始化派生类的数据成员
        self.setDepartment(department)

    #在派生类中新增加的方法
    def setDepartment(self, department):
        if type(department) != str:
            raise Exception('department must be a string.')
        self.__department = department

    #覆盖了从父类中继承来的方法
    def show(self):
        #先调用父类的同名方法,显示从父类中继承来的数据成员
        super(Teacher, self).show()
        #再显示派生类中的私有数据成员
        print(self.__department)

if __name__ =='__main__':
    	#创建基类对象
    	zhangsan = Person('Zhang San', 19, 'man')
    	zhangsan.show()
    	print('='*30)

    	#创建派生类对象
    	lisi = Teacher('Li si', 32, 'man', 'Math')
    	lisi.show()
    	#调用继承的方法修改年龄
    	lisi.setAge(40)
    	lisi.show()

#Zhang San
#19
#man
#==============================
#Li si
#32
#man
#Math
#Li si
#40
#man
#Math

4. 特殊方法

Python类有大量的特殊方法,其中比较常见的是构造函数和析构函数,除此之外,Python还支持大量的特殊方法,运算符重载就是通过重写特殊方法实现的。

Python中类的构造函数是__ init__(),一般用来为数据成员设置初值或进行其他必要的初始化工作,在创建对象时被自动调用和执行。如果用户没有设计构造函数,Python将提供一个默认的构造函数用来进行必要的初始化工作。

Python中类的析构函数是__ del__(),一般用来释放对象占用的资源,在Python删除对象和收回对象空间时被自动调用和执行。如果用户没有编写析构函数,Python将提供一个默认的析构函数进行必要的清理工作。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序猿小张的日常笔记

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值