17. 面向对象的特征

news/2024/10/21 20:26:05

一、面向对象的三大特征

  面向对象的三大特征指的是 封装继承多态

  封装(encapsulation,有时称为数据隐藏)是处理对象的一个重要概念。从形式上看,封装就是将数据和行为组合在一个包中,并对对象的使用者隐藏具体的实现方式。

  继承(inheritance)的基本思想是,可以基于已有的类创建新类。继承以存在的类就是复用(继承)这些类的方法,而且可以增加一些新的属性和方法,使新类能够适应新的情况。

  多态(polynirphic)就是一个类的多种形态。

二、封装性

2.1、什么是封装性

  所谓的封装,就是把客观事物封装成抽象概念的类,并且类可以把自己的数据和方法只向可信的类或对象开放,向没必要开放的类或对象异常信息。封装性就是隐藏内部对象的复杂性,只对外公开简单的接口。便于外部调用,从而提高系统的可扩展性、可维护性。通俗的来说,把该隐藏的隐藏起来,该暴露的暴露出来。

  当我们创建一个类时,我们可以通过“对象.属性”的方式,对对象的属性进行赋值。此时,赋值操作要受到属性的数据类型和存储范围的制约。除此之外,没有其它制约条件。但是,在实际问题中,我们往往需要给属性赋值加入额外的限制条件。这个条件就不能在属性声明时体现,只能通过方法进行限制条件的添加。同时,我们需要避免用户在使用“对象.属性”的方式对属性进行赋值,则需要将属性声明为私有的(private)。此时,针对于属性就体现了封装性。

  使用封装后,确实增加了类的定义的复杂程度,但是它也确保了数据的安全性。我们隐藏了属性名,使调用者无法任意修改对象中的属性。并且我们增加了 getter() 和 setter() 方法,可以很好的控制属性是否为只读的。如果希望属性是只读的,则可以直接去掉 setter() 方法,如果希望属性不能被外部访问,则可以直接去掉 getter() 方法。而且在使用 setter() 方法设置属性时,可以增加数据的验证,确保数据的值是正确的。在使用 getter() 方法获取属性,使用 setter() 方法设置属性时,可以在读取属性和修改属性的同时做一些其它的处理。

2.2、封装性的体现

  我们将类的属性(xxx)隐藏,同时,提供的公共的方法类获取(getter())和设置(setter(self))此属性的值。在 Python 中,我们可以为对象的属性使用双下划线开头 __xxx,双下划线开头的属性,是对象的隐藏属性,隐藏属性值只能在类的内部访问,无法通过对象访问。

class Person:def __init__(self,name,age):self.__name = nameself.__age = agedef get_name(self):return self.__namedef set_name(self,name):self.__name = namedef get_age(self):return self.__agedef set_age(self,age):if age >= 0:self.__age = agedef __show_info(self):print(f"name: {self.__name}, age: {self.__age}")

  如果我们直接访问类中的隐藏属性,会报以下错误:

p1 = Person("Sakura",10)
print(p1.__name)
AttributeError: 'Person' object has no attribute '__name'

  此时,我们可以通过公共的 getter() 方法和 setter() 方法调用类的私有属性:

p1 = Person("Sakura",10)
print("name: ", p1.get_name() , ", age: ", p1.get_age())p1.set_age(12)
print("name: ", p1.get_name() , ", age: ", p1.get_age())p1.set_age(-10)
print("name: ", p1.get_name() , ", age: ", p1.get_age())

  其实,隐藏属性是假隐藏,只不过是 Python 自动为属性改了一个名字。实际上,Python 将名字修改为 _类型__属性名

p1 = Person("Sakura",10)
p1._Person__age = 12
print(p1._Person__age)
p1._Person__show_info()

使用双下划线开头的属性,实际上依然可以在外部访问。在开发中,我们可以将一些私有属性以下划线开头(实际上是公开的属性),告诉开发人员没有特殊需要不要修改私有属性。

2.3、property装饰器

  property 装饰器,用来将一个 getter() 方法,转换为对象的属性,添加了 property 装饰器以后,我们就可以向调用属性一样使用 getter() 方法。使用 property 装饰的方法必须和属性名是一样的。setter() 方法的装饰器为:@属性名.setter;

class Person:def __init__(self,name):self.__name = name@propertydef name(self):print("get_name(self)方法执行了")return self.__name@name.setterdef name(self,name):print("set_name(self,name)方法执行了")self.__name = name@name.deleterdef name(self):print("del_name(self,name)方法执行了")p = Person("Sakura")
print(p.name,end="\n\n")p.name = "Mikoto"
print(p.name,end="\n\n")del p.name

  在一些较老版本的 Python 解释器中,我们可以通过如下的方法实现。

class Person:def __init__(self,name):self.__name = namedef get_name(self):print("get_name(self)方法执行了")return self.__namedef set_name(self,name):print("set_name(self,name)方法执行了")self.__name = namedef del_name(self):print("del_name(self,name)方法执行了")name = property(get_name, set_name, del_name)p = Person("Sakura")
print(p.name,end="\n\n")p.name = "Mikoto"
print(p.name,end="\n\n")del p.name

三、继承性

3.1、什么是继承性

  继承,其基本思想就是基于某个父类的扩展,制定出一个新的子类,子类可以继承父类原有的属性和方法,也可以增加原来父类所不具备的属性和方法,或者直接重写父类中的某些方法。

  通过继承可以直接让子类获取父类的方法和属性,避免编写重复性的代码,并且也符合 OCP 原则。所以,我们经常需要通过继承来对一个类进行扩展。

3.2、继承性的应用

  使用继承后,我们可以减少代码的冗余,提高了代码的复用性,并且使用继承后,便于功能的拓展。继承的出现让类与类之间产生了 is-a 的关系。

  在 Python 中,继承性的格式如下:

class A(父类):pass

  其中,A 类代表 子类(派生类、subclass),B 类代表 父类(基类、superclass)。一旦 子类 A 继承 父类 B 之后,子类 A 中就获取 父类 B 中声明的所有的结构:属性、方法(包括特殊方法);子类继承父类以后,还可以声明自己特有的属性和方法,实现功能的拓展;

class Animal:def run(self):print("动物在跑")def sleep(self):print("动物在睡觉")class Dog(Animal):def bark(self):print("汪汪汪")dog = Dog()dog.run()
dog.sleep()
dog.bark()
print()# isinstance()检查一个对象是否是一个类的实例
# 如果这个类是这个对象的父类,也会返回true
print(isinstance(dog,Dog))
print(isinstance(dog,Animal))
print()# issubclass()检查一个类是或否是另一个类的子类
print(issubclass(Dog,Animal))

如果在创建类时,省略了父类,则默认父类为 object,它是所有类的父类,所有类都继承 object;

3.3、方法重写

  如果在子类中有和父类同名的方法,则通过子类实例调用方法时,会调用子类的方法而不是父类的方法,这个特点我们成为 方法重写(覆盖,override)。

  当我们调用一个对象的方法时,它会优先去当前对象中寻找是否具有该方法,如果有则直接调用,如果没有则取当前对象的父类中寻找,如果父类中有则直接调用父类中的方法,如果没有则取父类中的父类中寻找,以此类推,直到找到 object,如果依然没有找到则报错。

class Animal:def run(self):print("动物在跑")def sleep(self):print("动物在睡觉")class Dog(Animal):def run(self):print("狗在跑")def bark(self):print("汪汪汪")dog = Dog()dog.run()
dog.sleep()
dog.bark()

3.4、super()方法的使用

  父类中所有结构:属性 和 方法(包括特殊方法)都会被子类继承。如果我们希望直接调用父类中的结构,我们可以使用 super() 方法获取当前类的父类,并且通过 super() 调用父类方法时,不需要传递 self。

class Animal:def __init__(self,name):self.__name = name@propertydef name(self):return self.__name@name.setterdef name(self,name):self.__name = nameclass Dog(Animal):def __init__(self,name,age):# 直接调用父类中的__init__(self)来初始化父类中定义的属性super().__init__(name)self.__age = age@propertydef age(self):return self.__age@age.setterdef age(self,age):self.__age = agedog = Dog("旺财",3)
print("name: ", dog.name, ", age: ", dog.age)

3.5、多重继承

  在 Python 中是支持多重继承的,也就是我们可以为一个类同时指定多个父类。我们可以在类名的 () 后面添加多个类,来实现多重继承。多重继承会使子类同时拥有多个父类,并且或获取所有父类中的方法。如果多个父类中有同名的方法,则会先在第一个父类中寻找,然后找第二个,然后找第三个,...,前面父类的方法会覆盖后面父类的方法。

class A:def test(self):print("AAA")class B:def test(self):print("BBB")class C(A,B):pass# 类名.__bases__这个属性可以获取当前类的所有父类
print(C.__bases__)
# 我们可以通过类.mro()查看属性的查找顺序
print(C.mro())c = C()
c.test()

super() 在调用父类的时候,它需要计算出当前到底调用哪个父类,在 Python 中实现这个功能的算法叫 C3 算法;

  在 Python 中子类可以同时继承多个父类的属性,这样可以最大限度地重用代码,但是使用多继承会导致扩展性变差,并且有可能会导致棱形问题(钻石问题)。棱形问题(钻石问题)指的是一个子类继承的多个父类汇集到一个非 object 类的身上。
棱形问题

class A:def test(self):print("from A")class B(A):def test(self):print("from B")class C(A):def test(self):print("from C")class D(B):def test(self):print("from D")class E(C):def test(self):print("from E")class F(A):def test(self):print("from F")class G(D,E,F):pass# 类名.__bases__这个属性可以获取当前类的所有父类
print(G.__bases__)
# 我们可以通过类.mro()查看属性的查找顺序
print(G.mro())
obj = G()
obj.test()

  如果真的涉及到一个子类不可避免使用多个父类的属性,应该使用 Mixins 机制。Mixin 类表示某一功能,而不是某个物品,Python 对于Mixin 类的命名方式一般以 Mixin,able,ible 为后缀。我们可以将相关的功能放在一个 Mixin 类中,如果有多个不同功能,那可以写多个 Mixin 类,一个类可以继承多个 Mixin 类,但应该只继承一个表示其归属含义的父类,以确保遵循继承的 "is-a" 原则。Mixin 类不应该依赖于子类的实现,子类即便没有继承这个 Mixin 类,也可以照常工作,只是缺少了某个功能而已。

class Vehicle:passclass FlyerMixin:def fly(self):passclass CivilAircraft(FlyerMixin,Vehicle):passclass Helicopter(FlyerMixin,Vehicle):passclass Car(Vehicle):pass

四、多态性

  多态性指的是一个事物的多种形态;同样的行为(函数),传入不同的对象,得到不同的状态;多态性指的是可以在不考虑对象具体类型的情况下而直接使用对象。

  它的格式如下:

class A:def __init__(self,name):self.__name = name@propertydef name(self):return self.__name@name.setterdef name(self,name):self.__name = nameclass B:def __init__(self,name):self.__name = name@propertydef name(self):return self.__name@name.setterdef name(self,name):self.__name = namea = A("Sakura")
b = B("Mikoto")# 对于say_hello()这个函数来说,只要对象中含有name属性,它就可以作为参数传递
# 这个函数并不会考虑对象的类型,只要有name属性即可
def say_hello(obj):print("你好,%s" %obj.name)say_hello(a)
say_hello(b)
import abc# 使用模块abc统一所有子类的标准
class Animal(metaclass=abc.ABCMeta):@abc.abstractmethoddef speak(self):passclass Dog(Animal):def speak(self):print("汪汪汪!")class Cat(Animal):def speak(self):print("喵喵喵!")def make_noise(animal):animal.speak()#animal = Animal()       # 不能实例化抽象类自己dog = Dog()
cat = Cat()make_noise(dog)
make_noise(cat)

看上去调用相同的方法,但实际上需要这个看这个对象是父类还是子类创建的对象,如果是父类创建的对象,一定调用父类中定义的方法,如果是子类创建的对象,那么就要看子类是否重写了父类的方法,如果子类要是重写了父类的方法,那么会调用子类的方法,如果子类没有重写父类方法,那么会调用父类的方法。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.ryyt.cn/news/72392.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈,一经查实,立即删除!

相关文章

20222315 2024-2025-1 《网络与系统攻防技术》实验二实验报告

1.实验内容 1.使用netcat进行虚拟机和主机的连接,cron启动周期性定时任务。 2.使用socat让虚拟机操作主机, 并调用提前准备的程序,启动任务计划。 3.使用MSF meterpreter(或其他软件)生成后门程序,利用ncat传送到主机让主机运行后门程序,虚拟机获取主机shell。 4.使用…

Obsidian之模板的简单使用

前言:在使用Obsidian时经常对每次新建的文件输入相同的内容是否有更好的解决方法呢,以下是我使用Obsidian模板的一些经验总结用到的插件Templater quickadd banner在开始前确保已经安装了以上的插件 首先简单的介绍下Templater的功能自定义指定文件夹的新建文件的模板 配合qu…

基于.NET8 + Vue/UniApp前后端分离的快速开发框架,开箱即用!

前言 今天大姚给大家分享一款基于.NET8 + Vue/UniApp前后端分离的快速开发框架,开箱即用:ZR.Admin.NET。开源免费(基于MIT License开源协议)、代码量少、学习简单、通俗易懂、功能强大、易扩展、轻量级,让 web 开发更快速、简单高效(从此告别 996),解决 70%的重复工作,…

Mininet问题合集

我的环境:Ubuntu 22.04.5 LTS liu@liu-Ubuntu-Desktop:~/桌面$ ovs-vsctl -V ovs-vsctl (Open vSwitch) 2.17.9 DB Schema 8.3.0liu@liu-Ubuntu-Desktop:~/桌面$ mn --version 2.3.0liu@liu-Ubuntu-Desktop:~/桌面$ python3 Python 3.10.12 (main, Sep 11 2024, 15:47:36) [GC…

计算机网络——第一章

@目录1.计算机网络在信息时代中的作用2.互联网概述2.1网络的网络2.2互联网发展的三个阶段3.互联网的组成3.1互联网的边缘部分3.2互联网的核心部分4.计算机网络的性能指标5.计算机网络体系结构5.1计算机网络的五层体系结构5.2 TCP/IP协议族6.本章重点概念 本文首先介绍计算机网络…

计算机基础(cpu,内存,硬盘)

计算机基础(cpu,内存,硬盘) 内存:负责硬盘等硬件上的数据与CPU之间数据交换处理; 缓存系统中的临时数据。 断电后数据丢失。硬盘: ​ 存储资料和软件等数据的设备,有容量大,断电数据不丢失的特点。 流程 ​ 简单来说,硬盘用来存储程序和数据,当运行程序时,CPU首先接…

Java中网络编程的学习

Java 网络编程学习总结 本章目标了解计算机网络基础知识 了解 OSI 七层参考模型 熟悉 TCP/IP 协议 熟悉常见网络协议 掌握 socket 套接字编程计算机网络 什么是计算机网络 计算机网络是通过传输介质、通信设施和网络通信协议,把分散在不同地点的计算机设备互连起来,实现资源共…

高等数学 5.5 反常积分的审敛法 Γ函数

目录一、无穷限反常积分的审敛法二、无界函数的反常积分审敛法三、\(\Gamma\) 函数 一、无穷限反常积分的审敛法 定理1 设函数 \(f(x)\) 在区间 \([a, +\infty)\) 上连续,且 \(f(x) \geqslant 0\).若函数 \[F(x) = \int_a^x f(t) \mathrm{d}t \]在 \([a, +\infty)\) 上有上界,…