单继承和多继承在python中的区别和应用场景
单继承指的是一个子类只继承自一个父类。这简化了继承关系,使得代码易于理解和维护。大多数情况下,单继承足以处理常见的场景,如扩展基类的功能或者覆盖某些方法。
多重继承允许在一个类同时继承多个父类的属性和方法。python也支持多重继承,这提供了极大的灵活性,允许创建更复杂的关系和行为。然而,他也带来了更高的复杂度和潜在的冲突,比如当多个父类有同名方法是的决策问题。多重继承长用于需要从多个源继承代码的情况,比如使用混合(Mixin)来组合多个类的功能
在子类中保留父类功能的同时添加或者修改功能
使用super() 函数可以在子类中调用父类方法,这样即保留了父类的功能,有可以在子类中添加或者修改功能。这种方式常用于初始化的过程中确保父类也被正常初始化。或者在重写方法是扩展父类的行为
class Vehicle :
def __init__ ( self, brand, model) :
self. brand = brand
self. model = model
def describe ( self) :
return f"This is a { self. brand} { self. model} ."
class Car ( Vehicle) :
def __init__ ( self, brand, model, horsepower) :
super ( ) . __init__( brand, model)
self. horsepower = horsepower
def describe ( self) :
original_description = super ( ) . describe( )
return f" { original_description} It has { self. horsepower} horsepower."
my_car = Car( "Tesla" , "Model S" , 670 )
print ( my_car. describe( ) )
Python中的继承解析顺序(MRO)及其对多重继承的影响
MRO的作用:当在一个类的实力上调用方法或者访问属性的时候,Python需要确定从那个类中获取该方法或者属性。如果使用的是多重继承,即一个类继承多个父类,这个查找过程可能会变得复杂。MRO就是python用来决定这种情况下如何进行查找的规则。
MRO的计算:python中的MRO是根据C3线性化算法计算的。这个算法的目的是保证子类总是在父类之前出现,且保持父类的声明顺序。从Python2.3开始,所有的新式类都继承object的类,都是用这种算法来决定MRO
如何查看MRO:可以使用类的“mro ”属性或者内置的mro()方法来获取任何类的mro列表,这方返回一个类包含和他所有的父类的元组,按照方法解析顺序排列
如何理解MRO
线性化:MRO为类和他的基类提供了一个明确的线性顺序。这意味着Python解释器在查找方法或者属性的时候有一个明确的清晰的路径
保证一致性:无论从哪个类继承,MRO都保证了一致性,确保了在复杂的继承关系中,方法调用的行为是可预测的
解决了多重继承的问题:通过C3线性算法,Python的MRO帮助解决了多重继承中可能遇到的复杂情况,如菱形继承(钻石问题)
class A :
pass
class B ( A) :
pass
class C ( A) :
pass
class D ( B, C) :
pass
print ( D. __mro__)
print ( D. mro( ) )
( < class '__main__.D' > , < class '__main__.B' > , < class '__main__.C' > , < class '__main__.A' > , < class 'object' > )
[ < class '__main__.D' > , < class '__main__.B' > , < class '__main__.C' > , < class '__main__.A' > , < class 'object' > ]
解决钻石问题
钻石问题或者称之为菱形继承发生在多重继承的场景中,当两个父类继承自同一个祖先类,而子类同时继承这两个父类的时候,Python通过C3线性化解决了这个问题,确保每个类在MRO中只出现一次,并且父类的顺序得到了正确的维护。
接口与抽象类在python中的实现
概念上的区别:
抽象类:通常用于定义一个基本的模板,它可以包含一些基础的方法实现。抽象类可以有抽象方法和非抽象方法。抽象类的主要目的是被其他类继承,并提供一部分实现。
接口:是一种特殊的抽象类,完全没有方法实现,只定义方法的签名,接口定义了一套协议或者行为规范,任何实现接口的类都必须实现接口中的所有方法
使用场景上的区别:
放你想要多个类按照相同的方式行动,但他们之间没有共同的基础实现的时候,你应该使用接口。接口更关注行为的规范化,而不是实现共享
当你想要定义一个类的部分或者全部实现,并希望子类基于这个定义进行扩展的时候,你应该使用抽象类。抽象类允许你定义一些子类必须实现的抽象方法,同时提供其他方法的默认实现
在Python中的具体实现
尽管Python的abc模块让抽象类和接口类在技术上看起来很相似,但你可以通过他们的用途和定义来区分
使用只包含“@abstractmethod”的类作为接口,意味着任何继承该类的子项都必须实现所有定义的方法。
使用即包含“@abstractmethod”有包含常规方法的类作为抽象类,提供一个或者多个方法的默认实现,要求子类实现剩余的抽象方法
from abc import ABC, abstractmethod
class Shape ( ABC) :
@abstractmethod
def area ( self) :
pass
@abstractmethod
def perimeter ( self) :
pass
class Rectangle ( Shape) :
def __init__ ( self, width, height) :
self. __width = width
self. __height = height
def area ( self) :
return self. __width * self. __height
def perimeter ( self) :
return 2 * ( self. __width + self. __height)
r = Rectangle( 5 , 10 )
print ( r. area( ) )
print ( r. perimeter( ) )
from abc import ABC, abstractmethod
class IWorker ( ABC) :
@abstractmethod
def work ( self) :
pass
class Engineer ( IWorker) :
def work ( self) :
print ( "Solving problems" )
class Manager ( IWorker) :
def work ( self) :
print ( "Managing projects" )
engineer = Engineer( )
engineer. work( )
manager = Manager( )
manager. work( )
多态性及其在继承中的作用
多态性是面向对象编程中的一个核心概念,他指的是能够使用相同的接口对不同的数据类型进行操作。在继承中,多态性允许子类以自己的方式实现父类或者接口中定义的方法。意味着即使每个子类的行为可能不同,使用父类类型的应用调用这些方法时候,具体调用哪个类的方法将根据对象的实际类型在运行时决定
多态性的作用
提高代码的可复用性:通过多态,可以编写更通用的代码,这些代码可以与多种数据类型一起工作,而不是仅限于一个特定的类型
提高代码的可扩展性:多态性使得当新增加子类时候,原有的代码无需修改或者仅需要少量修改即可适应新的数据类型,从而简化了系统的扩展。
增强了代码的可维护性:多态性通过减少代码的重复和提高代码的抽象层次,使得代码更加易于理解和维护
实现接口的多种实现:在设计模式中,多态性常常用于实现一个接口的多种实现方式,使得程序可以在运行是动态的选择最适合的实现
多态性的实现
在Python中,多态是隐式的提供,因为Python是动态语言,不需要显示的通过继承或者接口来实现多态性。
class Bird :
def fly ( self) :
print ( "Some birds can fly." )
class Sparrow ( Bird) :
def fly ( self) :
print ( "Sparrow flies low." )
class Eagle ( Bird) :
def fly ( self) :
print ( "Eagle flies high." )
def let_bird_fly ( bird) :
bird. fly( )
sparrow = Sparrow( )
eagle = Eagle( )
let_bird_fly( sparrow)
let_bird_fly( eagle)
使用组合代理继承解决特定设计问题
在面向对象设计中,组合优于继承是一个常见的原则,意味着使用组合来组织或者复用代码比使用继承更加灵活。这个原则鼓励开发者在设计软件是考虑组合对象来实现功能,而不是通过严格的继承结构。使用组合可以减少代码的耦合度,增强代码的复用性,提供系统的灵活性和可维护性
组合的优点
提高代码的复用性:组合允许你将功能封装在不同的对象中,然后再多个地方复用这些对象
减少类间的耦合,使用组合可以减少类直接的直接以来,因为组合对象通常是通过接口与外部世界教书,而不是通过继承获得的功能。
提高代码的灵活性:通过替换或者修改内部的组合对象,额可以轻松改变对象的行为,而不需要修改类的继承结构
简化系统设计:组合可以帮助避免创建复杂的继承关系,使系统设计更加简介和易于理解
class Logger :
def log ( self, message) :
print ( f"Log: { message} " )
class Calculator :
def __init__ ( self) :
self. logger = Logger( )
def add ( self, a, b) :
result = a + b
self. logger. log( f"Adding { a} + { b} = { result} " )
return result
class Printer :
def __init__ ( self) :
self. logger = Logger( )
def print_document ( self, document) :
self. logger. log( f"Printing document: { document} " )
Calculator( ) . add( 1 , 3 )
Printer( ) . print_document( "asd" )
在子类中重写方法时决定是否调用父类方法
在子类中重写方法时,是否嗲用父类方法取决于子类方法的目的。如果子类方法皆在完全替换父类方式,则可能不需要调用父类的方法。如果子类方法只是扩展或者修改父类方法的行为,则应该使用super()调用父类的方。
class A :
def do_something ( self) :
print ( "Method defined in A" )
class B ( A) :
def do_something ( self) :
print ( "Method defined in B" )
class C ( A) :
def do_something ( self) :
print ( "Method defined in C" )
class D ( B, C) :
def do_something ( self) :
super ( ) . do_something( )
A. do_something( self)
所有Python类继承自object类的特殊意义
在python中,所有的新式类都隐式地继承自“object”类。这为所有的对象提供了一组基本方法,包括"str ,repr "等这种设计确保了所有类都共享一组通用的基础功能,促进了一致性和可预测性
说一下继承好处和潜在的缺点
好处:包括代码重用,逻辑封层和规范化。继承允许子类复用父类的方法或者属性,简化的代码的维护和扩展
潜在缺点:包括过度使用继承可能导致复杂和脆弱的设计。如果继承层次过深或者设计不当,可能会导致代码难以理解和维护。此外,过度依赖继承可能限制了代码的灵活性,因为子类与父类之间紧密耦合。