Python学习系列之面向对象的三大特征(封装、继承、多态)(二十七)
面向对象的三大特征
1、封装:提高程序的安全性
- 将数据(属性)和行为(方法)包装到类对象中。在方法内部对属性进行操作,在类对象的外部调用方法。这样,无需关心方法内部的具体实现细节,从而隔离了复杂度。
- 在Python中没有专门的修饰符用于属性的私有,如果该属性不希望在类对象外部被访问,前边使用两个“_”。
2、继承:提高代码的复用性
3、多态:提高程序的可扩展性和可维护性
一、封装
一个类就是一个封装,将类的属性、变量、方法全部封装在类里面
代码示例:
'''封装'''
class Car:
    def __init__(self,brand):
        self.brand=brand
    def start(self):
        print('汽车已启动....')
car=Car('宝马X5')
car.start()
print(car.brand)
执行结果:

如果属性不想在类外部使用,则只需要在属性前加"__",示例代码如下:
#对象不想在类外部使用,在属性前使用__进行标记
class Student:
    def __init__(self,name,age):
        self.name=name
        self.__age=age
    def show(self):
        print(self.name,self.__age)
stu=Student('张三',20)
stu.show()
#在类之外使用name和age
print(stu.name)
# print(stu.__age)    #直接使用会报错AttributeError: 'Student' object has no attribute '__age'
#print(dir(std))
print(stu._Student__age)    #此种方法调用,能正确执行并返回
执行结果:

说明:__age可以在外部被使用,此种写法就是告诉开发者不要在外部使用该属性
二、继承及其实现方式
继承
- 语法格式
class 子类类名(父类1,父类2...):
pass
- 如果一个类没有继承任何类,则默认继承Object
- Python支持多继承
- 定义子类时,必须在其构造函数中调用父类的构造函数
代码示例:
'''继承'''
class Person():     #父类
    def __init__(self,name,age):
        self.name=name
        self.age=age
    def info(self):
        print(self.name,self.age)
class Student(Person):      #子类
    def __init__(self,name,age,stu_no):
        super().__init__(name,age)
        self.stu_no=stu_no
class Teacher(Person):      #子类
    def __init__(self,name,age,tea_no):
        super().__init__(name,age)
        self.tea_no=tea_no
stu=Student('张三',20,'1001')
teacher=Teacher('李四',30,10)
stu.info()      #info方法是从父类Person类继承过来的
teacher.info()
执行结果:

说明:stu调用的info()方法都是从父类中继承过来的
上面示例中的继承关系图如下:

- Python支持多继承
'''多继承'''
class A():
    pass
class B():
    pass
class C(A,B):
    pass
上述代码的示意图:

说明:class C(A,B)表示C类既继承了A类,也继承了B类
三、方法重写
方法重写
- 如果子类对继承父类的某个属性或方法不满意,可以在子类中对齐(方法体)进行重新编写
- 子类重写后的方法中可以通过super().xxx()调用父类中被重写的方法
要输出学生的学生编号和教师的教龄,示例代码:
'''重写
重写父类的方法'''
class Person():     #父类
    def __init__(self,name,age):
        self.name=name
        self.age=age
    def info(self):
        print(self.name,self.age)
class Student(Person):      #子类
    def __init__(self,name,age,stu_no):
        super().__init__(name,age)
        self.stu_no=stu_no
    def info(self):     #重写了父类的info()方法
        super().info()
        print(self.name+'的学生编号是:'+self.stu_no)
class Teacher(Person):      #子类
    def __init__(self,name,age,teachofyear):
        super().__init__(name,age)
        self.teachofyear=teachofyear
    def info(self):     #重写了父类的info()方法
        super().info()
        print(self.name+'的教龄是:',self.teachofyear)
stu=Student('张三',20,'1001')
teacher=Teacher('李四',30,10)
stu.info()
print('-----------------------')
teacher.info()
执行结果:

说明:重写是子类在自己的类中重新定义父类的方法,即在子类中定义和父类一样的方法名的方法,然后添加自己的业务代码,这段示例代码中,Student类和Teacher类重写了父类的info()方法,重写父类的方法时需要使用super().info()来对父类方法进行调用,学生编号和教龄是通过重写父类方法来输出的
四、object类
object类
- object类是所有类的父类,因此所有类都有object类的属性和方法。
- 内置函数dir()可以查看指定对象所有的属性
- object类有一个__str__()方法,用户返回一个对于“对象的描述”,对应于内置函数str()经常用于print()方法,帮我们查看对象的信息,所以我们经常会对__str__()进行重写
1. 查看object类的所有属性和方法
class Student:
    pass
stu=Student()
print(dir(stu))
执行结果:

说明:使用dir()方法可以查看Object类的所有属性和方法,stu是Student类的实例对象,Student类默认继承自Object类,所以执行dir(stu)会将object类的属性和方法全部查询出来,这些属性和方法都不是Student类里定义的
2. __str__()方法,用户返回一个对于“对象的描述”
class Student:
    pass
stu=Student()
print(stu)
执行结果:

说明:print(stu) 调用的是object类的 __str__()方法,输出的是stu这个对象的描述,包括对象类型和内存地址
我们可以通过重写__str__()方法,来输出自己的描述
'''重写__str__()方法来返回自己的描述'''
class Student:
    def __init__(self,name,age):
        self.name=name
        self.age=age
    def __str__(self):      #重写object类的__str__()方法
        return '我的名字是{0},今年{1}岁'.format(self.name,self.age)
stu=Student('张三',20)
# print(dir(stu))
print(stu)
print(type(stu))
执行结果:

说明:__str__()方法默认返回的是对象的描述,在Student类中重写了object的__str__()方法,返回了自己的描述
五、多态的实现
示例代码:
'''多态'''
class Animal:
    def eat(self):
        print('动物会吃')
class Dog(Animal):
    def eat(self):
        print('狗吃骨头....')
class Cat(Animal):
    def eat(self):
        print('猫吃鱼')
class Person:
    def eat(self):
        print('人吃五谷杂粮')
#定义一个函数
def fun(obj):
    obj.eat()
#开始调用函数
fun(Dog())
fun(Cat())
fun(Animal())
print('--------------')
fun(Person())
执行结果:

说明:代码中Cat类和Dog类继承了Animal类,重写了Animal中的eat()方法,所以当输入Dog和Cat的对象时,会去执行Dog类和Cat类中的eat方法,但是Person类并没有继承Animal类,只是因为它有eat()方法,所以也会去执行Person类中的eat()方法,这就是和其它变成语言不一样的地方,这叫鸭子类型,Python是一门动态语言,可以动态地去绑定属性和绑定方法。
什么是动态语言
- 动态语言的多态崇尚“鸭子类型”,当看到一只鸟走起来像鸭子,游泳起来像鸭子,收起来像鸭子,那么这只鸟就可以被称为鸭子。在鸭子类型中,不需要关系对象是什么类型,到底是不是鸭子,只关心对象的行为。
静态语言和动态语言的区别:
静态语言实现多态的三个必要条件
- 继承
- 方法重写
- 父类引用指向子类对象
java是静态语言,实现多态必须满足继承、方法重写、父类引用指向子类对象
六、特殊属性和特殊方法
| 名称 | 描述 | |
| 特殊属性 | __dict__ | 获得类对象或示例对象所绑定的所有属性和方法的字典 | 
| 特殊方法 | __len__() | 通过重写__len__()方法,让内置函数len()的参数可以是自定义类型 | 
| __add__() | 通过重写__add__()方法,可使用自定义对象具有“+”功能 | |
| __new__() | 用于创建对象 | |
| __init__() | 对创建的对象进行初始化 | 
1. __add__()
'''__add__''' a=20 b=100 c=a+b #两个整数类型的对象的相加操作 print(c) d=a.__add__(b) print(d)
执行结果:

说明:当计算a+b时,底层其实执行的是a.__add__(b)
练习:使两个字符串相加
class Student:
    def __init__(self,name):
        self.name=name
stu1=Student('张三')
stu2=Student('李四')
s=stu1+stu2
print(s)
执行结果:

说明:stu1和stu2是两个字符串,不能直接进行相加操作
如果要使两个字符串相加,则需要自己重新定义相加的方法
class Student:
    def __init__(self,name):
        self.name=name
    def __add__(self, other):
        return self.name+other.name
stu1=Student('张三')
stu2=Student('李四')
s=stu1+stu2    #实现了两个对象的加法运算(因为在Student类中,编写__add()__特殊的方法)
print(s)
s1=stu1.__add__(stu2)
print(s1)
执行结果:

2.__len()__方法
'''__len()__''' lst=[11,22,33,44] print(len(lst)) #len()是内置函数 print(lst.__len__())
执行结果:

说明:使用len()和__len()__方法的执行结果一样,说明特殊方法__len()__和内置函数len()是对应的
联系:输出字符串的长度
'''__len()__'''
class Student:
    def __init__(self,name):
        self.name=name
    def __add__(self, other):
        return self.name+other.name
    def __len__(self):
        return len(self.name)
stu1=Student('jack')
print(len(stu1))       #会报错,提示TypeError: object of type 'Student' has no len()
#如果想要计算string类型的长度,则需要自己的手动写len()方法
执行结果:

3. new和init方法
class Person(object):
    def __new__(cls, *args, **kwargs):
        print('__new__被调用执行了,cls的id值为{0}'.format(id(cls)))
        obj=super().__new__(cls)
        print('创建的对象的id为:{0}'.format(id(obj)))
        return obj
    def __init__(self,name,age):
        print('__init__被调用了,self的id值为:{0}'.format(id(self)))
        self.name = name
        self.age = age
print('object这个类对象的id为:{0}'.format(id(object)))
print('Person这个类对象的id为:{0}'.format(id(Person)))
#创建Person类的实例对象
p1=Person('张三',20)
print('p1这个Person类的实例对象的id:{0}'.format(id(p1)))
执行结果:

说明:
 
                    
                     
                    
                 
                    
                
 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号