初始Python篇(11)—— 面向对象三大特征

找往期文章包括但不限于本期文章中不懂的知识点:

个人主页: 我要学编程(ಥ_ಥ)-CSDN博客

所属专栏: Python

目录

封装

继承的基本概念以及使用

继承

继承的基本概念以及使用

方法重写

多态

多态的概念以及基本使用


封装

继承的基本概念以及使用

封装的概念:隐藏内部实现的细节,只对外提供操作方法(接口)。这个概念我们在上一小节中也已经学习过了,我们主要是去了解其在代码中是如何去实现的:通过有着类似 访问修饰限定符 功能的下划线来实现。

权限控制:通过对属性或方法添加单下划线、双下划线以及首尾双下划线来实现。

|--------|----------------------------------------------------------------|
| 下划线种类 | 功能 |
| 单下划线开头 | 表示protected,受保护的成员,这类成员被视为仅供内部家族使用,允许类本身和子类进行访问,但实际上它可以被外部代码访问 |
| 双下划线开头 | 表示private,私有的成员,这类成员只允许定义该属性或方法的类本身进行访问 |
| 首尾双下划线 | 一般表示特殊的方法 |

代码演示:

python 复制代码
class Dog():
    # 首尾双线划线 ------> 特殊方法
    def __init__(self, name, age):
        # 双下划线开头 ------> private修饰,只能在类内访问
        self.__name = name
        # 单下划线开头 ------> protected修饰,在类内与子类才能访问
        self._age = age

    # 单下划线开头 ------> protected修饰,在类内与子类才能访问
    def _fun1(self):
        print('这是被protected所修饰的方法')
    # 双下划线开头 ------> private修饰,在类内才能访问
    def __fun2(self):
        print('这是被private所修饰的方法')

# 这里是类外了
dog = Dog('大白', 5)
print(dog._age)
# print(dog.__name)
dog._fun1()
# dog.__fun2()
# 和上面一样,直接去访问的话,就会报错,
# 但是我们可以使用 对象.__dir()__ 或者 dir(对象) 先去查看所有的属性与方法
# 然后通过其中的"属性"与"方法名"去调用真正的属性与方法
# print(dog.__dir__())
# print(dir(dog))

print(dog._Dog__name)
dog._Dog__fun2()

运行结果:

根据上面的访问方式,我们可以推测出:被 protected、private 所修饰 方法 与 属性只是在类中对应的名称发生了变化,而我们不知道,但是可以通过特殊手段知晓,从而继续访问。

但是上面的方式不是很推荐,类似与 Java中的反射机制了,有点反常规。除了上面这种方式,Python还提供了两种方式来实现访问 与 修改 私有是属性与方法。

1、在 私有的方法 或者 属性上,进行套壳处理。

2、在1的基础上,通过 @property 装饰器 来修饰方法,使其变为属性,就变为访问与修改属性了,最终也会变的很简单。

代码演示:

python 复制代码
class Dog():
    def __init__(self, name, age):
        self.__name = name
        self.age = age

    # 如果想要去访问除了,使用dir()之外,还有两种方式:

    # 使用实例方法去间接访问与修改
    def get_name(self):
        return self.__name
    def set_name(self, name):
        self.__name = name

    # 使用@property装饰器,将方法转为属性使用
    @property
    def name(self):
        return self.__name
    @name.setter
    def name(self, name):
        self.__name = name

dog = Dog('大白', 5)
# 1、通过实例方法的形式去使用
print(dog.get_name())
dog.set_name('小白')
print('='*15)
# 2、通过@property装饰器,将方法转为属性
print(dog.name)
dog.name = '大白' # 修改属性的样式,去传参
print(dog._Dog__name)
print('='*15)
dog._Dog__name = '小白'
print(dog._Dog__name)

运行结果:

注意:我们在使用 @property 装饰器,将属性转为方法时,取值的方法,必须被 @property 修饰,而修改值的方法,必须被 @取值方法名. setter修饰。

我们的建议是:是对哪个变量进行取值 与 修改值,就将方法名设置为哪个变量,这样修改方法的装饰器也很容易写,负责容易混淆,而且可读性不高,不易于别人读。这里也体现了封装的特性,隐藏内部实现细节,只对外提供接口。

继承

继承的基本概念以及使用

继承是指一个类(子类、派生类)继承另一个类(父类、基类)的属性与方法。

在Python中一个子类可以继承N多个父类,这是与 Java决然不同的一点。Java属于单继承,而Python属于多继承,并且每个类都默认继承自object类。当然,一个子类可以有多个父类,一个父类也可以有多个子类。

语法:

python 复制代码
class 类名([父类列表]):

注意:当一个类的父类只有object类时,默认是可以不写父类的,但是如果一个类有除object类之外的其他类,就需要将这些类全部写到 () 中,当然这个()在只有继承object类的情况下,也是可以不写的,但是建议还是要写上去。

代码演示:

python 复制代码
# 默认继承object类,但是这个可以不写
class Animal(object):
    def __init__(self, name, age, gender,sort):
        self.name = name
        self.age = age
        self.gender = gender
        self.__sort = sort


class Dog(Animal):
    pass

# 子类继承父类,会将父类中除private修饰的方法与属性全部拿过来
# 继承了 __init__方法,以及 name、age、gender属性
dog = Dog('大白', 5, '男', '中华田园犬')
print(dog.name)
print(dog.age)
print(dog.gender)
# 同样需要特殊手段才能访问到
print(dog._Animal__sort)

运行结果:

注意:我们在实例化一个对象时,会先去调用构造方法(init 方法)给对象的属性进行初始化赋值,如果一个类没有构造方法,但是由于这个类是默认继承自object类,因此会调用object类中默认的 init 方法,但如果这个类继承了其他类,就会先在其他类中去搜索这个方法,如果有则调用;反之,则还是去调用object类的。 而在上面的代码中,Animal 类是有 init 方法,因此会去调用Animal 类的。

我们再看看多继承的代码:

python 复制代码
class Person():
    def __init__(self,name,age,gender):
        self.name = name
        self.age = age
        self.gender = gender
    def show(self):
        print(f'我叫{self.name},今年{self.age},性别是{self.gender}')

class Father(Person):
    def __init__(self,name,age,gender):
        # 可以直接调用父类的初始化方法
        super().__init__(name,age,gender)
        # 下面这种方式是错误的
        # super().name = name
        # super().age = age
        # super().gender = gender
        # 下面的方式也行,但是不推荐
        # self.name = name
        # self.age = age
        # self.gender = gender

class Mother(Person):
    def __init__(self, name, age, gender):
        # 可以直接调用父类的初始化方法
        super().__init__(name, age, gender)
        # 下面这种方式是错误的
        # super().name = name
        # super().age = age
        # super().gender = gender
        # 下面的方式也行,但是不推荐
        # self.name = name
        # self.age = age
        # self.gender = gender

class Son(Father, Mother):
    def __init__(self, name, age, gender):
        # 可以直接调用父类的初始化方法
        super().__init__(name, age, gender)
        # 下面这种方式是错误的
        # super().name = name
        # super().age = age
        # super().gender = gender
        # 下面的方式也行,但是不推荐
        # self.name = name
        # self.age = age
        # self.gender = gender

father = Father('A',40,'男')
mother = Mother('B',35,'女')
son = Son('C',10,'男')

father.show()
mother.show()
son.show()

运行结果:

上面的代码中,Son类、Mother类、Father类都是继承自Person类的属性与方法,它们是公用Person类的属性与方法。

注意:上述 Son类继承了多个父类,当我们去使用 super().init 方法初始化子类对象时,默认是按照父类在Son()中的顺序来查找的,这里是Father类定义声明在前,因此这里调用的就是 Father 类的 init 方法,如果想要指定某个类的 init 方法的话,就需要使用 类名.init() 方法。例如:

python 复制代码
# 这里的self代指当前Son类的实例对象
Mother.__init__(self,name,age,gender) # 可替换Son类中是super.init

方法重写

当子类继承父类时,子类就拥有了父类中的 公有成员、方法和受保护的成员、方法。如果我们对父类中有些成员不满意的话,子类就可以重新定义成员。同理,方法也是如此,但是重新定义方法麻烦了,那有什么办法呢?方法重写,即 偷梁换柱,表面上这个方法还是原来的方法,但是其内部实现的结构早就发生变化了。

重写的要求:方法名必须一样,至于方法的返回值类型,参数列表、访问权限这些可以不一样。

代码演示:

python 复制代码
class Animal():
    def __init__(self,name,age):
        self.name = name
        self.age = age

    def eat(self):
        print(f'{self.name}正在吃食物,补充体力')

class Dog(Animal):
    def __init__(self,name,age):
        super().__init__(name,age)
    # 由于Animal类的eat方法只是随便说在吃啥,并不具体,
    # 但我们想要打印出具体的食物,因此可以重写(外壳不变,核心变化)
    def eat(self):
        print(f'{self.name}正在吃狗粮~')

class Cat(Animal):
    def __init__(self,name,age):
        super().__init__(name,age)

dog = Dog('大白', 5)
# 子类重写父类的方法之后,父类再去调用eat方法,就不再是调用父类的,而是自己的
dog.eat()

cat = Cat('小白', 3)
# 子类没有重写父类的eat方法,因此还是去调用父类的eat方法
cat.eat()

运行结果:

其实,重写就是在子类中定义了一个与父类重名的方法,只不过因为 Python中语法的检查不是很严格,因此这里就很容易不理解。

在Python中,我们就将重写看作是子类定义了一个与父类重名的方法即可,在调用时,如果子类没有这个方法,那么就是调用父类的同名方法,如果还没有就会报错。但如果子类有这个方法,那么就是直接调用子类的方法,即使父类有,也不去调用("地头蛇原则")。

多态

多态的概念以及基本使用

多态是指多种形态,当去完成某个行为时,不同的对象可能会产生不同的形态。

例如,我们在类和对象中说的小故事:当刘建明 与 陈永仁 遇到 韩琛 时,两人打招呼的方式不一样,这就是不同对象 在 完成同一个行为时,两者所产生的形态不同。

再比如,我们每天都会吃早餐,但是不同的人 在 面对这件事情时,所表现出的行为就不一样,A 可能吃面条,B可能吃包子、饺子,C可能吃大米饭等。

Python中的多态与Java、C++不同,Python中的多态只需要满足有该方法,然后让不同的对象去调用即可,而 Java 中的多态需要满足三个条件:向上转型(父类引用指向子类对象)、子类重写父类的方法、向上转型的父类调用该方法(被重写的方法),这时就不再是调用父类的方法,而是调用子类的方法。但 Python中,只要这两个类有相同的方法,当两者赋值给同一个对象时,去调用同一个方法就会表现出不同的行为。

其实,多态就是表层对象一样,内部实际的对象不一样,那么在调用同一个方法时,虽然看似是这个表层的对象所调用的,但实际是内部的对象所调用并执行的。

代码演示:

python 复制代码
class Animal():
    def eat(self):
        print('正在吃东西')

class Person():
    def eat(self):
        print('正在干饭')

class Dog():
    def eat(self):
        print('正在吃狗粮')

class Cat():
    def eat(self):
        print('正在吃猫粮')

# 定义一个函数,传入obj对象,并调用该对象的eat方法
def eat(obj):
    obj.eat()

# 方法得通过类或者对象去"."调用,而函数是直接传参调用,不要弄混了
eat(Animal()) # 这里需要传入对象,即:类名()
eat(Person())
eat(Dog())
eat(Cat())

运行结果:

好啦!本期 初始Python篇(11)------ 面向对象三大特征 的学习之旅 就到此结束啦!我们下一期再一起学习吧!

相关推荐
guygg887 分钟前
5G PDSCH信道吞吐量MATLAB仿真实现(含信道生成与解调)
开发语言·5g·matlab
抠头专注python环境配置11 分钟前
基于Python与深度学习的智能垃圾分类系统设计与实现
pytorch·python·深度学习·分类·垃圾分类·vgg·densenet
愈努力俞幸运28 分钟前
flask 入门 token, headers,cookie
后端·python·flask
傻乐u兔39 分钟前
C语音初阶————调试实用技巧2
c语言·开发语言
梦想是成为算法高手41 分钟前
带你从入门到精通——知识图谱(一. 知识图谱入门)
人工智能·pytorch·python·深度学习·神经网络·知识图谱
用什么都重名42 分钟前
Conda 虚拟环境安装配置路径详解
windows·python·conda
阿也在北京44 分钟前
基于Neo4j和TuGraph的知识图谱与问答系统搭建——胡歌的导演演员人际圈
python·阿里云·知识图谱·neo4j
计算机徐师兄1 小时前
Python基于知识图谱的胆囊炎医疗问答系统(附源码,文档说明)
python·知识图谱·胆囊炎医疗问答系统·python胆囊炎医疗问答系统·知识图谱的胆囊炎医疗问答系统·python知识图谱·医疗问答系统
北冥码鲲1 小时前
【保姆级教程】从零入手:Python + Neo4j 构建你的第一个知识图谱
python·知识图谱·neo4j