python基础 - 14 面向对象3

一、继承的简介

1.1 继承的引入

      class Person():
         name = ' '
         age = 
      class Doctor():
            name = ' '
            age = ' '
            def treat(self):
                  print('治疗病人...')
            
      class   Soldier():
            name = ' '
            age = 
            def duty(self):
                 print('保卫国家...')

这两个类中有公共的属性name和age,我们是否可以将这两个共有属性抽象成一个单独类Person,然后让这三个类之间存在一些关联,这个过程就叫做继承。

1.2 继承的优点

1.提高了代码的复用性
2.让类与类之间产生了关系,有了这个关系,才有多态

1.3 继承的应用

定义一个狗类,有以下三种方法:
1.直接修改动物类, 修改起来比较麻烦且会违反OCP
2.直接定义新的狗类,新类比较麻烦,会出现大量的复制粘贴代码,维护起来会比较困难
3.直接从Animal这个类中继承它的属性和方法

 class Animal():
      def run(self):
          print('动物跑.....')
      
      def sleep(self):
          print('动物睡觉...')
 a = Animal()
 a.run()
 
 class Dog(Animal):
       def home(self):
           print('狗看家')
 
 d = Dog()      
 d.run()        #调用父类Animal的方法run

在定义类时,可以在类名后加上一个括号,括号中指定当前类的父类(超类、基类、super)

  r =  isinstance(d,Dog)
  r1 =  isinstance(d,Animal)    
  print(r)          #返回值为True,d是Dog的实例对象
  print(r1)         #返回值为True,d也是父类Animal的实例对象

如果我们在创建类时,没有指定父类,则默认该类的父类为object
object是所有类的父类

      r = issubclass(Dog,Animal)       #检查Dog是否是Animal的子类
      r1 = issubclass(Animal,object)   #检查Animal是否是object的子类
      r2 = issubclass(Dog,object)      #检查Dog是否是object的子类
      print(r)                         #返回结果为True
      print(r1)                        #返回结果为True
      print(r2)                        #返回结果为True

二、方法的重写

如果在子类中有和父类重名的方法,我们通过子类的实例对象去调用该方法时,会调用子类的方法而不是父类的方法,这种特性称为方法的重写或者覆盖(override)

	    class A(object):
		    def test(self):
		        print('A....')
		        
		class B(A):
		    def test(self):
		        print('B....')
		        
		class C(B):
		    def test(self):
		        print('C....')
		        
		c = C()
		c.test()

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

三、super()

子类可以继承父类所有的方法和属性,包括特殊方法,也可以重写特殊方法

		class Animal():
		    def __init__(self,name):
		        self._name = name
		    def run(self):
		        print('动物跑.....')
		
		    def sleep(self):
		        print('动物睡觉...')
		    @property
		    def name(self):
		        return self._name
		    @name.setter
		    def name(self,name):
		        self._name = name
		
		
		class Dog(Animal):
		    def __init__(self,name,age):
		        self._name = name
		        self._age = age
		    def run(self):
		        print('狗跑')
		    def home(self):
		        print('狗看家')
		    @property
		    def age(self):
		        return self._age
		    @age.setter
		    def age(self,age):
		        self._age = age
		
		
		d = Dog('二哈',5)
		print(d.name)
		print(d.age)
		d.name = '凯撒'
		d.age = 10
		print(d.name)
		print(d.age)

在父类中使用__init__特殊方法初始化了实例对象d的一个属性值name,但在子类中,又增加了一个属性age,如果我们在子类的初始化方法中只添加age这个属性,那么实例对象只会调用子类中的初始化方法,即d中只有一个属性age
因此,我们需要在子类对象的初始化方法中添加name和age两个属性
如果父类中的属性有多个,我们采用上述的方法对子类进行重写时,会比较麻烦。希望可以在子类中直接调用父类的__init__方法来初始化子类的属性

     class Animal():
		    def __init__(self,name):
		        self._name = name
		    def run(self):
		        print('动物跑.....')
		
		    def sleep(self):
		        print('动物睡觉...')
		    @property
		    def name(self):
		        return self._name
		    @name.setter
		    def name(self,name):
		        self._name = name
		
		
		class Dog(Animal):
		    def __init__(self,name,age):
		    
		        #直接调用父类的init方法,需要添加self参数
		        Animal.__init__(self,name)
		        self._age = age
		    def run(self):
		        print('狗跑')
		    def home(self):
		        print('狗看家')
		    @property
		    def age(self):
		        return self._age
		    @age.setter
		    def age(self,age):
		        self._age = age
		
		
		d = Dog('二哈',5)
		print(d.name)
		print(d.age)
		d.name = '凯撒'
		d.age = 10
		print(d.name)
		print(d.age)

我们通过该方法来实现子类的初始化时,父类名是固定的。有没有什么方法可以获得子类所指定的父类名,从而使程序更加完善。
super()可以获得子类所指定的父类名,且通过super()返回的对象调用初始化方法时,不需要传递self参数

   class Animal():
		    def __init__(self,name):
		        self._name = name
		    def run(self):
		        print('动物跑.....')
		
		    def sleep(self):
		        print('动物睡觉...')
		    @property
		    def name(self):
		        return self._name
		    @name.setter
		    def name(self,name):
		        self._name = name
		
		
		class Dog(Animal):
		    def __init__(self,name,age):
		       #super()获取子类所指定的父类名
		        super().__init__(name)
		        self._age = age
		    def run(self):
		        print('狗跑')
		    def home(self):
		        print('狗看家')
		    @property
		    def age(self):
		        return self._age
		    @age.setter
		    def age(self,age):
		        self._age = age
		
		
		d = Dog('二哈',5)
		print(d.name)
		print(d.age)
		d.name = '凯撒'
		d.age = 10
		print(d.name)
		print(d.age)

四、多重继承

语法: 类名.__ bases__ 可以获取当前类的所有父类
在Python中是支持多重继承的,也就是我们可以为一个类指定多个父类

    class A(object):
	    def test(self):
	        print('A....')
	class B(object):
	    def test(self):
	        print('B....')
	class C(A,B):
	    def test(self):
	        print('C....')
	
	# c = C()
	print(C.__bases__)     #返回结果为元组,元组中元素就是该类的父类

A,B,C之间的关系
在多重继承中,如果多个父类中有相同的方法,子类在调用父类的方法时,会根据子类传递的顺序进行调用
在该例子中,class C(A,B) C类有两个父类A,B,且A在B的前面,c.test()会先调用A中的test方法,如果A中没有test方法才会调用B中的test方法

	   class A(object):
			    def test(self):
			        print('A....')
			class B(object):
			    def test(self):
			        print('B....')
			class C(A,B):
			    #def test(self):
			       # print('C....')
			      pass 
			
			 c = C()
			#print(C.__bases__) 
			c.test()

在开发中没有特殊情况,不建议使用多重继承,多重继承会让代码过于复杂
test方法的查找顺序

五、多态

多态是面向对象的三大特征之一
多态字面上理解就是多种形态(狗(二哈、萨摩耶、德牧…))
一个对象可以以不同形态去展现

	   class A(object):
		    def __init__(self,name):
		        self._name = name
		
		    @property
		    def name(self):
		        return self._name
		    @name.setter
		    def name(self,name):
		        self._name = name
		
		class B(object):
		    def __init__(self, name):
		        self._name = name
		
		    @property
		    def name(self):
		        return self._name
		
		    @name.setter
		    def name(self, name):
		        self._name = name
		def speak(obj):
		    print('你好,我是%s' %obj.name)
		def speak2(obj):
		    if isinstance(obj,A):
		         print('你好,我是%s' %obj.name)
		
		a = A('葫芦娃')
		b = B('蜘蛛侠')
		
		speak(a)
		speak(b)

在上述的例子中,a和b都可以调用speak函数,因为a和b都有name属性,这个特性就称为多态.如果我们将speak()函数修改为speak2()这个函数,那么就只有A类型的对象能调用该函数,这个函数就违反了多态。
违反了多态的函数,只适用于一种类型的对象,这样导致函数的适用性非常差。
len()函数可以检查字符串的长度,也可以检查列表的长度,这就是多态
之所以len()可以获取对象的长度,是因为这些对象中都封装了__len__特殊方法,就可以通过len()获取长度

面向对象的三大特征

封装 确保对象中的数据更安全
继承 保证了对象的可扩展性
多态 保证了程序的灵活性

六、类中的属性和方法

定义在类中的属性都是类属性,类对象中的属性值只能通过类对象来修改

    class A(object):
	    #定义在类中的属性就是类属性,类对象和实例对象都可以访问
	    count = 0
	
	a = A()
	#修改的是实例对象中的count属性值
	a.count = 5   
	
	#修改的是类对象中的count属性值,类对象和实例对象中的count属性都会改变
	A.count = 8   
	print('A',A.count)
	print('a',a.count)

实例属性是通过实例对象添加的属性,实例属性只能通过实例对象来访问和修改

    class A(object):

	    # count = 0
	    def __init__(self,name):
	        #实例属性,通过实例对象添加的属性
	        self.name = name
	a = A('葫芦娃')
	
	a.name = '钢铁侠'
	# print(A.name)  #报错
	print(a.name)

实例方法在类中直接定义,以self为第一个参数的都是实例方法
通过实例对象调用时,Python会将调用对象作为self传入
通过类对象调用时,不会自动传入self,需手动传入self

      class A(object):
       
       #实例方法在类中直接定义,以self作为第一个参数
	    def test(self):
	         print('这是test方法')
	         
	a = A()
	
    a.test()      #通过实例对象调用实例方法
    A.test(a)     #通过类对象调用实例方法,需传入调用的实例对象作为参数

a.test()等价于A.test(a)
类方法在类中定义需要使用@classmethod装饰器进行装饰
类方法的第一个参数为cls,会在调用时将当前的类对象作为cls自动传入
类方法可以通过类对象调用也可以用实例对象调用

    class A(object):
       
       #类方法在类中定义,需要用classmethod进行装饰,且以cls作为第一个参数
       @classmethod
	    def test1(cls):
	         print('这是test1方法.....')
	         
	a = A()
	
    A.test1()      #通过类对象调用类方法
    a.test1()      #通过实例对象调用类方法

A.test1()等价于a.test1()
静态方法
静态方法,基本上一个与与当前类无关的方法,它只是一个保存在当前类中的函数
静态方法一般都是些工具方法,与当前类无关
静态方法必须用一个对象去调用它,函数可以直接使用

      class A(object):
       
       #静态方法在类中定义,需要用staticmethod进行装饰
       @staticmethod
	    def test2():
	         print('这是test2方法.....')
	         
	a = A()
	
    A.test2()      #通过类对象调用静态方法
    a.test2()      #通过实例对象调用静态方法
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值