Python学习(25)--面向对象编程2
这一节我们将继续介绍面向对象编程,主要涉及到的内容有属性的访问权限,特殊属性,类中的继承的编程思想。
1.属性的访问权限
Python类中常用的有3种属性类型,第一种是形如XXX的属性,这种属性可以供对象在类的外部访问,访问权限比较大;第二种是形如_XXX的属性,这种属性也可以供对象在类的外部访问,但是一般不建议这样做,;第三种是形如__XXX的属性,即私有属性,这种属性不能在类的外部访问,仅限于类的内部使用,访问权限最小。以下是三种属性的代码例子:
(1)XXX属性
- class Person():
- def __init__(self,name,age,height,weight,money,score):
- self.name=name
- self._age=age
- self.height=height
- self.weight=weight
- def run(self):
- print("running...")
- def eat(self, food):
- print(self.name + " eat " + food)
- per = Person("zhangsan", 11, 123, 20, 10000, 80)
- print(per.name)
打印结果如下:
通过打印结果可见,在Person类的外部可以访问形如XXX的属性name。
(2)_XXX属性
- class Person():
- def __init__(self,name,age,height,weight,money,score):
- self.name=name
- self._age=age
- self.height=height
- self.weight=weight
- def run(self):
- print("running...")
- def eat(self, food):
- print(self.name + " eat " + food)
- per = Person("zhangsan", 11, 123, 20, 10000, 80)
- print(per._age)
打印结果如下:
虽然可以访问属性_age,但是在编程时请将其当做私有属性看待,在类的内部使用形如_XXX的属性。
(3)__XXX属性
- class Person():
- def __init__(self,name,age,height,weight,money,score):
- self.name=name
- self._age=age
- self.height=height
- self.weight=weight
- self.__money=money
- def run(self):
- print("running...")
- def eat(self, food):
- print(self.name + " eat " + food)
- per = Person("zhangsan", 11, 123, 20, 10000, 80)
- print(per.__money)
打印结果如下:
打印结果含义为:Person类不存在属性__money。这是因为作为私有属性的__money,仅限于类的内部使用,在Person类的外部访问时不允许的。但私有属性并不是绝对的私有,Python只是将私有属性的变量名改变成为"_类名"+"私有属性名"。如私有属性__money,Python将属性名"__money"改变成了"_Person__money",所以在访问私有属性__money不能访问到。如下,我们直接访问Python改变之后的私有属性名"_Person__money",则可以访问到。
- class Person():
- def __init__(self,name,age,height,weight,money,score):
- self.name=name
- self._age=age
- self.height=height
- self.weight=weight
- self.__money=money
- def run(self):
- print("running...")
- def eat(self, food):
- print(self.name + " eat " + food)
- per = Person("zhangsan", 11, 123, 20, 10000, 80)
- print(per._Person__money)
打印结果如下:
如上,我们使用Python改变之后的私有属性名"_Person__money"后可以访问到__money
的值。但是这种访问方式是不建议的,这样做会使数据的安全性降低。
那么如何访问私有属性呢?既然只能在类中访问私有属性,那么类中的私有属性值,可以通过将私有属性作为方法返回结果的方式读取;修改私有属性的值可以调用类中方法,通过传参的方式在类中修改私有属性的值。代码如下:
- class Person():
- def __init__(self,name,age,height,weight,money,score):
- self.name=name
- self._age=age
- self.height=height
- self.weight=weight
- self.__money=money
- def run(self):
- print("running...")
- def eat(self, food):
- print(self.name + " eat " + food)
- def getMoney(self):
- return self.__money
- def setMoney(self,money):
- self.__money=money
- per = Person("zhangsan", 11, 123, 20, 10000, 80)
- print(per.getMoney())
- per.setMoney(999)
- print(per.getMoney())
如上代码,Person类中定义的方法getMoney(self)返回结果为私有属性__money的值,用于读取私有属性__money;调用方法setMoney(self,money),通过传参的方式在类中修改私有属性__money的值。
打印结果如下:
如上,我们使用get和set方法读取和修改对象的私有属性值,这样做的优点就是提高的
代码的安全性和灵活性。
提高安全性体现在当类中的属性只能读不能写时,我们可以把属性设置为私有,只提供
属性的get方法,不提供set方法,避免了因为修改属性的值而使数据不安全。
提高灵活性体现在使用get或者set方法时可以进行其他操作,不完全只是机械的修改
或者读取数据。例如当修改数据时,可以先判断传入的参数是否合法,如果不合法则不予修
改,而通过直接赋值来修改数据更加机械,灵活性不高。
2.特殊属性
Python类中形如__XXX__的属性称为特殊属性,代表着特殊的含义。例如,__class__
表示对象的类型,__name__表示类名,__dict__表示类属性组成的字典等等。代码例子如下:
- class Person():
- def __init__(self,name,age,height,weight,money,score):
- self.name=name
- self._age=age
- self.height=height
- self.weight=weight
- self.__money=money
- per = Person("zhangsan", 11, 123, 20, 10000, 80)
- print(per.__class__)
- print(Person.__name__)
- print(per.__dict__)
代码打印结果如下:
如上为打印类的特殊属性结果,per.__class__表示对象的类型为<class '__main__.Person'>;per.__dict__为对象per属性组成的字典,字典中的每一个元素的键为属性名,值为属性值。更多的特殊属性如下:
3.类的继承
面向对象中继承的编程思想来源于现实,在现实世界中,子女从父母那里继承财产,或者子女与父母长得相像,其实是继承了父母的基因。而面向对象中的继承也是类似,子类继承了父类,就继承了父类的属性和方法,拥有了父类中的属性和方法。以下,我们使用一个小例子来了解面向对象中继承的规则和优点。
在本例中有两个模块,Student.py和Person.py,两个类分别是Student类和Person类,Student类继承自Person类。类图如下:
如上类图所示,Person类中的属性有name,age,__money;方法有eat(),run(),getMoney(),setMoney();Student类中的独有属性有stuId;Student类继承了Person类,便拥有了Person类的属性和方法,可以访问相关属性和方法。
其中模块Person.py的代码如下:
- class Person:
- def __init__(self,name,age,money):
- self.name=name
- self.age=age
- self.__money=money
- def run(self):
- print("running")
- def eat(self,food):
- print("eat "+food)
- def getMoney(self):
- return self.__money
- def setMoney(self,money):
- self.__money=money
以上是Person类定义的属性和方法。
模块Student.py的代码如下:
- from Person import Person#导入父类
- class Student(Person):#父类列表中为Student类的父类,这里继承的父类是Person类
- def __init__(self,name,age,stuId,money):
- Person.__init__(self,name,age,money)#使用父类的构造函数初始化子类对象的属性值
- self.stuId=stuId#子类的独有属性
- def getStuMoney(self):#读取__money属性值
- return self.__money
- #测试代码部分
- stu=Student("zhangsan",12,"0001",100)
- print("name:",stu.name)
- print("age:",stu.age)
- stu.run()
- stu.eat("Apple")
- print("stuId:",stu.stuId)
- print(stu.getStuMoney())
以上为Student类的定义和测试代码。首先如果父类和子类并不位于同一模块中,需要从其他模块导入父类;子类的父类列表中为父类的类名;初始化子类对象时,可以调用父类的构造函数初始化,调用的方式有两种,第一种如上super(Student,self).__init__(name,age,money);第二种方式为Person.__init__(self,name,age,money)。以上两种方式都是使用父类的构造函数,并将当前对象self和属性实参传入,初始化父类和子类共同拥有的属性。
Student.py中测试代码的打印结果如下:
如上所示,Student对象stu可以访问父类的属性name,age和方法eat(),run()都没有报错,这说明Student类作为Person类的子类,继承了父类的这些方法和属性。但是当调用方法getStuMoney(self)时访问继承自父类的私有属性"__money"时,却报错为:" 'Studnet' object has no attribute '__money' ",含义为:Student对象不存在属性'__money'。
这是因为子类Student并没有继承父类Person的私有属性__money。如果继承了父类的私有属性__money,那么Student类便拥有了私有属性__money,当通过get方法访问__money时便不会出错。但是,通过getStuMoney(self)访问私有属性__money时发生错误,说明Student类并没有从父类Person继承私有属性__money。
其实,在继承关系中子类并不能继承父类的私有属性,只能继承父类的非私有属性,这也是继承的规则,应注意这一点。
那么子类对象如何访问没有继承到的父类属性呢?虽然没有继承父类的私有属性,但继承了父类访问私有属性的get和set方法,调用继承到的get和set方法就可以访问父类的私有属性。代码修改模块Student.py的测试代码如下:
- from Person import Person#导入父类
- class Student(Person):#父类列表中为Student类的父类,这里继承的父类是Person类
- def __init__(self,name,age,stuId,money):
- Person.__init__(self,name,age,money)#使用父类的构造函数初始化子类对象的属性值
- self.stuId=stuId#子类的独有属性
- def getStuMoney(self):#读取__money属性值
- return self.__money
- stu=Student("zhangsan",12,"0001",100)
- print(stu.getMoney())
代码打印结果如下:
之前的Student.py的测试代码中,调用Student类中的自定义方法getStuMoney()访问没有从父类Person继承到的私有属性__money,报出错误。而直接调用父类访问私有属性__money的getMoney(),可以访问私有属性__money,没有报错。这说明,子类访问没有从父类继承到的私有属性的方式为:调用父类访问私有属性的get和set方法,因为get和set方法是子类可以继承到的父类方法。
下面来分析下继承的优点。
继承的优点:当有若干个类是属于相同的类型时,可以将多个类的相同属性抽象在父类中,使得每个类只需要继承父类,相同的属性不需在类中重新编写,简化了代码,减少了冗余。
当需要修改类的属性时,只需要修改父类中的属性,而不需要修改子类中的属性,提高了代码的健壮性。
继承时实现多态的前提。
下一节继续介绍面向对象中的多继承和多态,敬请期待。