当前位置: 首页 > 科技观察

Python继承概念的这些优缺点你知道吗?

时间:2023-03-12 09:27:51 科技观察

作为一个程序员或者准程序员,我对面向对象编程几乎是耳熟能详。作为当今最流行的编程思想之一(也许你可以去掉“一个”),面向对象无论是在面试还是在工作中都是绕不开的话题。对于Python程序员来说,OOP(面向对象编程)的三大特性——数据封装、继承和多态通常是面试的重点,所以大多数人都比较熟悉。然而,你真的了解OOP的优缺点吗?今天的文章将带领大家了解一下三大特性中继承的优缺点。ClassOOP()就是所谓的面向对象编程,这是一种编程思想。OOP把对象作为程序的基本单位,一个对象包含数据和操作数据的函数。面向对象程序设计将计算机程序看作是对象的集合,每个对象都可以接收来自其他对象的消息并处理这些消息。计算机程序的执行是对象之间传递的一系列消息。面向对象最重要的概念就是类(Class)和实例(Instance)。必须牢记,class是抽象的模板,instance是根据class创建的具体“对象”。每个对象都有相同的方法。但各自的数据可能不同。假设我们要创建一个Student类。在Python中,类的定义是通过class关键字:class后面跟着类名,即Student。类名一般是一个大写开头的单词,后面跟(object),表示该类继承自哪个类?稍后我们将讨论继承的概念。通常,如果没有合适的继承类,则使用对象类。这是所有类最终都会继承的类。定义好Student类后,可以基于Student类创建Student实例。创建实例是通过类名+()来实现的:可以看到变量bart指向了一个Student实例,后面的0x10a67a590就是内存地址。每个对象的地址都不一样,Student本身就是一个类。你可以自由的给一个实例变量绑定属性,比如给实例绑定一个name属性bart:既然类可以充当模板,那么你可以强制把一些我们认为在创建实例时必须绑定的属性填进去。通过定义一个特殊的__init__方法,在创建实例时绑定name、score等属性:注意:特殊方法“__init__”前后有两个下划线!!!请注意,__init__方法的第一个参数始终是self,这意味着创建的实例本身。因此,在__init__方法内部,可以给self绑定各种属性,因为self指向创建的实例本身。使用__init__方法,在创建实例时,不能传入空参数。必须传入匹配__init__方法的参数,self则不需要传入,Python解释器会传入实例变量本身。:与普通函数相比,定义在类中的函数只有一个区别,就是第一个参数永远是实例变量self,调用时不需要传这个参数。除此之外,类方法与普通函数没有什么不同,所以你仍然可以使用默认值、可变参数、关键字参数和命名关键字参数。继承什么是继承?继承是一种创建类的方法。在python中,一个类可以继承自一个或多个父类。原始类称为基类或超类。查看继承:何时使用继承?如果已经有几个类,并且类之间有共同的变量属性和函数属性,那么可以把这些变量属性和函数属性提取出来作为基类的属性。特殊的变量属性和函数属性定义在这个类中,这样只需要继承基类就可以访问基类的变量属性和函数属性。可以提高代码的可扩展性。继承与抽象(先抽象再继承)抽象就是把相似的部分提取出来。基类是将多个类的公共属性抽象出来得到的类。Garen类和Riven类都有nickname、aggressivity、life_value、script四个变量属性和attack()函数属性。这里可以抽象出一个Hero类,它包含了这些属性。严格来说,上面的Hero.init(self,...)不能算作子类调用父类的方法。因为如果我们去掉(Hero)的继承关系,代码仍然可以得到预期的结果。总结一下python中继承的特点:在子类中,不会自动调用基类的init(),需要在派生类中手动调用。调用基类的方法时,需要加上基类的类名前缀,需要带上self参数变量。先在这个类中寻找被调用的方法,找不到再去基类中查找。继承的优缺点讨论子类化内置类型的缺点1.内置类型的方法不会调用被子类重写的方法内置类可以被子类化,但是内置类型的方法不会调用被重写的方法子类。下面是继承dict的自定义子类重写__setitem__的例子:从输出可以看出,当a中存入键值对one=1和three=3时,会调用dict的__setitem__,并且只[]操作说明符将调用我们预先覆盖的方法。解决问题的办法不是继承dict,而是继承collections.UserDict。2.集合中的类的子类化用户自定义类应该继承集合模块,如UserDict、UserList、UserString。这些类是专门设计的,以便于扩展。子类化UserDict的代码如下:总结:以上问题只出现在C语言实现的内置类型的子类化中,只影响直接继承内置类型的自定义类。相比之下,用Python编写的子类,如UserDict或MutableMapping则不会有这个问题。多重继承1.方法解析顺序(MethodResolutionOrder,MRO)在多重继承中,存在不相关的祖先类实现同名方法导致的冲突问题。这个问题被称为“钻石问题”。Python依赖于一个特定的顺序来遍历继承图,这个顺序被称为方法解析顺序。如图,左图是类的UML图,右图虚线箭头是方法分析顺序:2.super提到类的属性__mro__时,会提到super:superis一个类,既不是关键字也不是函数和其他数据结构。作用:super被子类用来调用父类的方法。语法:super(a_type,obj);a_type是obj的__mro__,当然也可以是__mro__的一部分,而issubclass(obj,a_type)==true比如有一个MRO:[A,B,C,D,E,object]我们调用likethis:super(C,A).foo()super只会在C之后查找,即:只查找D或E或object中的foo方法。下面构造一个多重继承的菱形问题加深理解:输出结果如下:分析:d.pingpong()执行super.ping(),super根据MRO查找父类的ping方法,得到pingB类后,查询输出B。平()。3.处理多重继承的建议(1)将接口继承与实现继承分开;继承接口:创建子类型,是框架的支柱;继承实现:通过重用避免代码重复,通常改用组合和委托模式。(2)使用抽象基类显式表示接口;(3)通过混入重用代码;混入类为多个不相关的子类提供方法实现,便于复用,但不会实例化。而具体类不能只继承mixin类。(4)在名称中明确标明mixin;在Python中没有正式的方式将类声明为mixin,Luciano建议在名称中添加Mixin后缀。比如Tkinter中的XView应该变成XViewMixin。(5)抽象基类可以作为mix-in,反之则不行;抽象基类和mix-in的异同:抽象基类会定义类型,mix-in做不到;抽象基类可以作为其他类的唯一基类,不能混入;抽象基类实现的具体方法只能与抽象基类及其父类中的方法配合使用,混合时没有这种限制。(6)不要子类化多个具体类;可能没有具体的类,或者最多有一个具体的超类。例如Dish(China,Japan,Tofu)类中,如果Tofu是具体类,那么China和Japan一定是抽象基类或者混入。(7)为用户提供聚合类;聚合类是指一个类的结构主要继承自mixins,本身不添加结构或行为。Tkinter采纳了这个建议。(8)优先使用对象组合而不是类继承。喜欢使用组合可以使设计更加灵活。组合和委托可以代替mixin,但不能代替接口继承来定义类型层次结构。