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

  执行结果:

 说明:

原文地址:https://www.cnblogs.com/wx170119/p/14470568.html