草庐IT

Python面向对象三大特征之继承

Alex是大佬 2023-03-28 原文

前言

我们都知道Python面向对象编程有三大特征,继承,封装和多态,下面几篇问题,我们会分别讲述着几大特征。
今天说的是继承,如果有编程基础的人对这个词应该不会陌生,继承是一种创建新类的方式,新建的类可以继承一个或多个父类(python支持多继承),父类又可称为基类或超类,新建的类称为派生类或子类,而子类会“”遗传”父类的属性(数据属性和函数属性),从而解决代码重用问题。

上面这段话就是继承的概念和使用继承所要达到的目的。

下面我们来看看具体代码案例,继续的大致写法就是

子类名(父类1,父类2,。。。)

Python是可以实现对继承的,有些编程语法是只能单继承。

class ParentClass:
    name= '父类'
    def __init__(self,size,color):
        self.size = size
        self.color = color

    def fun(self):
        print('我来自父类')


class SubClass(ParentClass):
        name = '子类'
        pass
#实例化对象
s1 = SubClass('30','red')

#{'size': '30', 'color': 'red'}  如果子类没有构造方法的话,子类属性会找到父类的构造方法,继承父类的数据属性
print(s1.__dict__)

#(<class 'object'>,)  python3中统一都是新式类,所以如果不加继承的父类,默认的父类是object类
print(ParentClass.__bases__)

#(<class '__main__.ParentClass'>,)  子类的父类
print(SubClass.__bases__)

# "我来自父类"   子类对象调用父类的方法
s1.fun()

#子类对象调用数据属性,会先在子类中找,如果找不到会去父类的作用域里面找
#这里子类里面定义了name属性所以结果是: 子类
#如果子类里面没有定name,结果就是:父类
print(s1.name)

这段是继承大致的用法,子类可以继承父类的数据属性和函数属性。下面我们说说使用继承的好处及代码重用和重写,组合的用法

1.代码重用

我们现在有2个类猫和狗,它们都是动物,那如果我们要描述猫和狗这2个类的话,我们会存在大量的重复代码,如下:

class Cat:
    def cry(self):
        print('喵喵')
    def  eat(self):
        print('吃')
    def run(self):
        print('跑')
    def jump(self):
        print('跳')


class Dog:
    def cry(self):
        print('汪汪')
    def eat(self):
        print('吃')
    def run(self):
        print('跑')
    def jump(self):
        print('跳')

cat1 =  Cat()
dog1 = Dog()
cat1.cry()
dog1.cry()

上面2个类也许除了cry方法,其他的方法都是一样的,都猫和狗同意的动作,那像上面那样写,就会出现大量的重复代码,下面我们用继承来改写:

class animal:
    def cry(self):
        print('动物叫')
    def eat(self):
        print('吃')
    def run(self):
        print('跑')
    def jump(self):
        print('跳')

class Cat(animal):
    def cry(self):
        print('喵喵')

class Dog(animal):
    def cry(self):
        print('汪汪')

cat1 =  Cat()
dog1 = Dog()
cat1.cry()  #喵喵
dog1.cry()  #汪汪
cat1.eat() #吃
dog1.eat() #吃

上面我们定义了一个animal的父类,把子类的相同部分放入父类,子类就可以用继承的方式来调用父类的函数属性或者说方法了,而不用再类里面再重复编码。

2. 类的方法重写

其实上面的例子我们已经用到重写,就是父类定义了一个方法,但是子类和父类的方法实现不一样,要达到另外一个功能,比较上面

父类的cry方法时: ‘动物叫’

子类Cat的cry要实现:‘喵喵叫’

子类Dog的cry要实现:‘汪汪叫’

这种情况就要使用重写

class animal:
    def cry(self):
        print('动物叫')
    def eat(self):
        print('吃')
    def run(self):
        print('跑')
    def jump(self):
        print('跳')

class Cat(animal):
    def cry(self):
        print('喵喵')

class Dog(animal):
    def cry(self):
        print('汪汪')

cat1 =  Cat()
dog1 = Dog()
cat1.cry()  #喵喵  重写父类cry方法
dog1.cry()  #汪汪 重写父类cry方法

3.子类的方法派生

就是子类可以定义自己的方法

class animal:
   def cry(self):
       print('动物叫')
   def eat(self):
       print('吃')
   def run(self):
       print('跑')
   def jump(self):
       print('跳')

class Cat(animal):
   def cry(self):
       print('喵喵')
   def  swoop(self): #定义子类的方法
       print('飞扑')


cat1 =  Cat()
cat1.swoop()

类的组合

组合指的是,在一个类中以另外一个类的对象作为数据属性,称为类的组合,当类之间有显著不同,并且较小的类是较大的类所需要的组件时,用组合比较好。

class School:
    def __init__(self,name,addr):
        self.name=name
        self.addr=addr


    def recruit(self):
        print('%s xxx计算机学校正在招生' %self.name)

class Course:
    def __init__(self,name,price,period,School):
        self.name=name
        self.price=price
        self.period=period
        self.school=School  #实现Course和School的组合
s1=School('xxx计算机学校','北京')
s2=School('xxx计算机学校','南京')
s3=School('xxx计算机学校','上海')

msg='''
1 xxx计算机学校 北京校区
2 xxx计算机学校 南京校区
3 xxx计算机学校 上海校区
'''
while True:
    print(msg)
    menu={
        '1':s1,
        '2':s2,
        '3':s3
    }
    choice=input('选择学校>>: ')
    school_obj=menu[choice]
    name=input('课程名>>: ')
    price=input('课程费用>>: ')
    period=input('课程周期>>: ')
    new_course=Course(name,price,period,school_obj)
    print('课程【%s】属于【%s】学校' %(new_course.name,new_course.school.name))  #实现Course和School的组合

5.Python实现接口

接口就是定义抽象函数,不做具体函数实现,起到规范子类的作用,让子类必须实现接口的抽象函数。接口提取了一群类共同的函数,可以把接口当做一个函数的集合。然后让子类去实现接口中的函数。这么做的意义在于归一化,什么叫归一化,就是只要是基于同一个接口实现的类,那么所有的这些类产生的对象在使用时,从用法上来说都一样。

归一化的好处在于:

归一化让使用者无需关心对象的类是什么,只需要的知道这些对象都具备某些功能就可以了,这极大地降低了使用者的使用难度。

归一化使得高层的外部使用者可以不加区分的处理所有接口兼容的对象集合。

上代码:

import abc #利用abc模块实现抽象类
class Interface(metaclass=abc.ABCMeta):
    @abc.abstractclassmethod  #抽象方法,不做具体实现
    def test1(self):
        pass
    @abc.abstractclassmethod
    def test2(self):
        pass
class SubClass(Interface):
    def test1(self):
        print('实现抽象方法1')
    def test2(self):
        print('实现抽象方法2')

s1 = SubClass()

如果我们子类里面不实现具体的抽象函数,会报错

import abc #利用abc模块实现抽象类
class Interface(metaclass=abc.ABCMeta):
    @abc.abstractclassmethod  #抽象方法,不做具体实现
    def test1(self):
        pass
    @abc.abstractclassmethod
    def test2(self):
        pass
class SubClass(Interface):
    def test1(self):
        print('实现抽象方法1')
#不实现test2方法
s1 = SubClass()

报错信息:

Traceback (most recent call last):
File "C:/Users/aryin/Desktop/mysite2/继承.py", line 16, in
s1 = SubClass()
TypeError: Can't instantiate abstract class SubClass with abstract methods test2

6.类继承的顺序,根据类的MRO属性

class A:
   def test(self):
       print('A')
class B(A):
   def test(self):
       print('B')

class C(A):
   def test(self):
       print('C')

class D(B):
   def test(self):
       print('D')

class E(C):
   def test(self):
       print('E')

class F(D,E):
   def test(self):
       print('F')
       
# (<class '__main__.F'>, <class '__main__.D'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)
print(F.__mro__) 

类继承是是按照MRO属性里面的顺序去调用父类的属性

7.子类调用父类的方法

是通过super函数来实现的,在复杂的类继承关系中,super()的取值是按照上面的MRO里面的顺序来定的。

class animal:
    def __init__(self,name,type,size,color):
        self.name = name
        self.type = type
        self.size = size
        self.color = color
    def cry(self):
        print('动物叫')
    def eat(self):
        print('吃')

class Cat(animal):
    def __init__(self,name,type,size,color,age):
        super().__init__(name,type,size,color)  #调用父类的构造函数,这是super最常用的地方
        self.age = age
    def cry(self):
        super().cry()  #子类重新了父类的方法,但是同时又要实现父类中cry方法
        print('喵喵')

cat1 = Cat('毛球','波斯猫',10,'白色','5岁')

# 动物叫
# 喵喵
cat1.cry()

有关Python面向对象三大特征之继承的更多相关文章

  1. ruby - 如何从 ruby​​ 中的字符串运行任意对象方法? - 2

    总的来说,我对ruby​​还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用

  2. python - 如何使用 Ruby 或 Python 创建一系列高音调和低音调的蜂鸣声? - 2

    关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。

  3. ruby-on-rails - 按天对 Mongoid 对象进行分组 - 2

    在控制台中反复尝试之后,我想到了这种方法,可以按发生日期对类似activerecord的(Mongoid)对象进行分组。我不确定这是完成此任务的最佳方法,但它确实有效。有没有人有更好的建议,或者这是一个很好的方法?#eventsisanarrayofactiverecord-likeobjectsthatincludeatimeattributeevents.map{|event|#converteventsarrayintoanarrayofhasheswiththedayofthemonthandtheevent{:number=>event.time.day,:event=>ev

  4. ruby-on-rails - 如何验证非模型(甚至非对象)字段 - 2

    我有一个表单,其中有很多字段取自数组(而不是模型或对象)。我如何验证这些字段的存在?solve_problem_pathdo|f|%>... 最佳答案 创建一个简单的类来包装请求参数并使用ActiveModel::Validations。#definedsomewhere,atthesimplest:require'ostruct'classSolvetrue#youcouldevencheckthesolutionwithavalidatorvalidatedoerrors.add(:base,"WRONG!!!")unlesss

  5. Ruby 写入和读取对象到文件 - 2

    好的,所以我的目标是轻松地将一些数据保存到磁盘以备后用。您如何简单地写入然后读取一个对象?所以如果我有一个简单的类classCattr_accessor:a,:bdefinitialize(a,b)@a,@b=a,bendend所以如果我从中非常快地制作一个objobj=C.new("foo","bar")#justgaveitsomerandomvalues然后我可以把它变成一个kindaidstring=obj.to_s#whichreturns""我终于可以将此字符串打印到文件或其他内容中。我的问题是,我该如何再次将这个id变回一个对象?我知道我可以自己挑选信息并制作一个接受该信

  6. ruby-on-rails - 如果 Object::try 被发送到一个 nil 对象,为什么它会起作用? - 2

    如果您尝试在Ruby中的nil对象上调用方法,则会出现NoMethodError异常并显示消息:"undefinedmethod‘...’fornil:NilClass"然而,有一个tryRails中的方法,如果它被发送到一个nil对象,它只返回nil:require'rubygems'require'active_support/all'nil.try(:nonexisting_method)#noNoMethodErrorexceptionanymore那么try如何在内部工作以防止该异常? 最佳答案 像Ruby中的所有其他对象

  7. ruby-on-rails - 未在 Ruby 中初始化的对象 - 2

    我在Rails工作并有以下类(class):classPlayer当我运行时bundleexecrailsconsole然后尝试:a=Player.new("me",5.0,"UCLA")我回来了:=>#我不知道为什么Player对象不会在这里初始化。关于可能导致此问题的操作/解释的任何建议?谢谢,马里奥格 最佳答案 havenoideawhythePlayerobjectwouldn'tbeinitializedhere它没有初始化很简单,因为你还没有初始化它!您已经覆盖了ActiveRecord::Base初始化方法,但您没有调

  8. ruby - 如何在 Rails 4 中使用表单对象之前的验证回调? - 2

    我有一个服务模型/表及其注册表。在表单中,我几乎拥有服务的所有字段,但我想在验证服务对象之前自动设置其中一些值。示例:--服务Controller#创建Action:defcreate@service=Service.new@service_form=ServiceFormObject.new(@service)@service_form.validate(params[:service_form_object])and@service_form.saverespond_with(@service_form,location:admin_services_path)end在验证@ser

  9. ruby - 如何根据特征实现 FactoryGirl 的条件行为 - 2

    我有一个用户工厂。我希望默认情况下确认用户。但是鉴于unconfirmed特征,我不希望它们被确认。虽然我有一个基于实现细节而不是抽象的工作实现,但我想知道如何正确地做到这一点。factory:userdoafter(:create)do|user,evaluator|#unwantedimplementationdetailshereunlessFactoryGirl.factories[:user].defined_traits.map(&:name).include?(:unconfirmed)user.confirm!endendtrait:unconfirmeddoenden

  10. ruby - 一个 YAML 对象可以引用另一个吗? - 2

    我想让一个yaml对象引用另一个,如下所示:intro:"Hello,dearuser."registration:$introThanksforregistering!new_message:$introYouhaveanewmessage!上面的语法只是它如何工作的一个例子(这也是它在thiscpanmodule中的工作方式。)我正在使用标准的ruby​​yaml解析器。这可能吗? 最佳答案 一些yaml对象确实引用了其他对象:irb>require'yaml'#=>trueirb>str="hello"#=>"hello"ir

随机推荐