Python面向对象 -- 继承和多态、获取对象信息、实例属性和类属性

继承和多态

 继承的好处:

    1,子类可以使用父类的全部功能

    2,多态:当子类和父类都存在相同的方法时,子类的方法会覆盖父类的方法,即调用时会调用子类的方法。这就是继承的另一个好处:多态。

  

多态:

       调用方只管调用,不管细节,当我们新增一种Animal的子类时,只要确保run( )方法编写正确,不用管原来的代码是如何调用的。这就是著名的“开闭”原则:

       对扩展开放:允许新增Animal的子类

       对修改封闭:不需要修改依赖Animal类型的run_twice( )等函数

 

静态语言VS动态语言

    对于静态语言(如Java)来说,如果需要传入Animal类型,则传入的对象必须是Animal类型或者它的子类,否则,将无法调用run( )方法。

    对于Python这样的动态语言来说,如果需要传入Animal类型,则不一定要传入Animal类型,我们只需要保证传入的对象有一个run( )方法就可以了。

   动态语言的“鸭子类型”,它并不要求严格的继承体系,一个对象只要“看起来像鸭子,走起路来像鸭子”,那它就可以被看做是鸭子。

   两个特点:

      外观:看起来像鸭子

     行为动作:走起路来像鸭子

 

       Python的 " file-like object "就是一种鸭子类型。对于真正的文件对象来说,它有一个read( )方法,返回其内容。但是,许多对象,只要有read( )方法,都可以被看做" file-like object ",即你不一定要传入真正的文件对象,可以传入任何实现了read( )方法的对象。

 

下面代码中,

1,对于Dog来说,Animal就是它的父类;对于Animal来说,Dog就是它的子类。

2,在继承关系中,如果一个实例的数据类型是某个子类,那它的数据类型也可以被看做是父类。但是,反过来就不行了。

      Dog的实例d,d的数据类型是Dog,同时也是Animal类型,因为Dog是从Animal继承下来的。

 

 1 class Animal(object):
 2     def run(self):
 3         print('Animal is running...')
 4 
 5 class Dog(Animal):   #继承了Animal类的子类Dog
 6     def run(self):
 7         print('Dog is running...')
 8 
 9 class Cat(Animal):   #继承了Animal类的子类Cat
10     def run(self):
11         print('Cat is running...')
12 
13 class Tor(object):  #虽然不是Animal类型,但是有run方法,“鸭子类型”
14     def run(self):
15         print('tor is running slowly')
16 
17 def run_twice(animal):
18     animal.run()
19     animal.run()
20 
21 
22 
23 a = Animal()
24 d = Dog()
25 c = Cat()
26 t = Tor()
27 
28 print("判断实例a是否是Animal, Dog, Cat")
29 print('a is Animal?', '', isinstance(a, Animal))
30 print('a is Dog?','', isinstance(a, Dog))
31 print('a is Cat?','', isinstance(a, Cat))
32 print("")
33 
34 
35 print("可以看到Dog继承了Animal类,所以Dog的实例的数据类型既是Dog, 也是父类Animal")
36 print('d is Animal?','', isinstance(d, Animal))
37 print('d is Dog?','', isinstance(d, Dog))
38 print('d is Cat?','', isinstance(d, Cat))
39 print("")
40 
41 run_twice(a)
42 run_twice(d)
43 run_twice(c)
44 
45 
46 print("")
47 print("可以看到实例t不是是Animal,但是也能运行run_twice()函数,是因为它有run方法")
48 print('t is Tor?','', isinstance(t,Tor))
49 print('t is Animal?','', isinstance(t,Animal))
50 print("")
51 
52 run_twice(t)

运行结果:

 


 

  小结:

        继承可以把父类所有的功能都继承过来,这样就不必从0开始做起,子类只需要新增自己特有的方法,也可以把父类不适合的方法覆盖重写。

        动态语言的" 鸭子类型 "决定了继承不像静态语言那样数据类型是必须的。

 


 

 

 获取对象信息

  type( )函数、isinstance( )函数、dir( )函数、getattr( )函数、setattr( )函数、hasattr( )函数

 

type( )函数:

(1)判读对象的基本类型、或者直接写int,str

 

(2)一个变量指向函数或者类,进行判断

 

(3)判断两个对象的type类型是否相同

 

(4)判断一个对象是否是函数? 方法:使用types 模块中定义的常量

1 import types
2 def fn():
3     pass
4 
5 print("是否是函数类型:",type(fn)==types.FunctionType)  #判断是否是函数类型
6 print("是否是绝对值类型:",type(abs)==types.BuiltinFunctionType)
7 print("是否是lambda匿名函数类型:",type(lambda x: x)==types.LambdaType)  #判断是否是lambda匿名函数类型
8 print("是否是生成器类型:",type(x for x in range(10))==types.GeneratorType)  #判断是否是生成器类型

 

 

 

使用Isinstance( )函数

(1)判断class类的类型,Isinstance( )函数判断的是一个对象是否是该类型本身,或者位于该类型的父继承链上

(2)能用type( )判断的也能用isinstance( )判断

 

(3)判断一个对象是否是某些类型中的一种

 

 

1 print(isinstance([1,2,3,4],(list,tuple)))
2 print(isinstance((1,2,3,4),(list,tuple)))
3 print(isinstance([1,2,3,4],(int,str)))

 


 

结论:总是优先使用isinstance( )函数来判断类型,可以将指定类型及其子类“一网打尽”

 


 

dir( )函数

功能:可以获得一个对象的所有属性和方法,它返回一个包含字符串的list列表

注:获取属性值的格式,尽量使用 “实例.属性名”,少用getattr( )函数获取属性值

测试对象的属性 , attr, attribute属性缩写  has+attr=hasattr, get+attr=getattr, set+attr=setattr

  hasattr( ):只有两个参数,判断实例变量是否有这个属性或方法,参数中属性或方法都要用引号引起来

  setattr( ):有3个参数,(实例变量,属性名,属性值),为实例变量添加属性值,

  getattr( ):有3个参数,获取实例变量的属性值或方法,   可以设置默认值default,如果有属性不存在,就返回默认值

                   对于属性,getattr返回的是属性值

                   对于方法,getattr返回的是实例变量.方法名

 

  注意:方法名后面不带括号,如果想调用这个方法,就写成这种形式getattr( )( ),在getattr(  )函数的后面加上一个括号

 

 

(1)类似__xxx__这样的属性和方法在Python中是有特殊用途的,比如__len__方法返回长度

(2)在Python中,调用len( )函数获取一个对象的长度,实际上,在len( )函数内部,它自动去调用该对象的__len__( )方法。

 所以,下面的代码是等价的:

 

(3)如果自己写类,想用len( myObj )的话,就自己写一个__len__( )方法

        注意:是__len__( )方法,前后带2个下划线,而不是len( )方法

 

(4)使用hasattr( ), setattr( ), getattr( )操作对象

    测试对象的属性,如果获取不存在的属性,会抛出AttributeError错误,此时可以设置一个default参数,如果属性不存在,就返回默认值

 

 测试对象的方法:

 

 

 


 

 小结

       (1)可以直接写obj.x,就不要写getattr(obj, 'x')

       (2)假设我们从文件流fp中读取图像,首先要判断该fp对象是否存在read方法,如果存在,则该对象是一个流,如果不存在,则无法读取。  hasattr( )就派上用场了。

       注意:在Python这类动态语言中,根据鸭子类型,有read( )方法,不代表该fp对象就是一个文件流,它也可能是网络流,也可能是内存中的一个字节流,但只要read( )方法返回的是有效的图像数据,就不影响读取图像的功能。

            

1 def readImage(fp):
2     if hasattr(fp, 'read'):
3         return readData(fp)
4     return None

 


 

 

 实例属性和类属性

     1、类属性属于类所有,该类的所有实例均可以访问,所有实例共享一个属性。

     2、定义了类属性后,如果实例中的属性名和类中的属性名相同,则访问实例的这个属性值时将会覆盖类中相同属性名的值,如果属性名不相同,则不会覆盖

     3、实例属性属于各个实例,互不干扰

 注:从下面的代码中可以看出,千万不要对实例属性和类属性使用相同的名字,因为相同名称的实例属性将会覆盖掉类属性,但是当删除实例属性后,再访问时,访问到的是类属性。

 

 1 class Student(object):
 2     name='Student'
 3 
 4 
 5 s=Student()
 6 
 7 print('s.name:',s.name)  #实例的name属性值
 8 print('Student.name:',Student.name)  #类Student的name属性值
 9 
10 print("")
11 print("添加了实例的namea属性后:")
12 s.namea='Michael'   #定义了实例的namea属性值,注意后面多了一个a
13 print('s.namea:',s.namea)   #实例中定义了和类Student中name属性,不同属性名的属性namea
14 print('s.name:',s.name)    #此时实例的name属性值
15 print('Student.name:',Student.name)
16 
17 print("")
18 print("添加了实例的name属性后:")
19 s.name='hello'
20 print('s.name:',s.name)   #此时实例的name属性值
21 print('Student.name:',Student.name)   #类Student的name属性值
22 
23 print("")
24 print("删除了实例的name属性后:")
25 del s.name   #删除了实例中的name属性
26 print('s.name:',s.name)
27 print('Student.name:',Student.name)

运行结果如下:

 

 

实战:为了统计学生人数,可以给Student类增加一个类属性,每创建一个实例,该属性自动增加:

 1 class Student(object):
 2     count=0  #初始化计数变量
 3 
 4     def __init__(self,name):
 5         self.name=name
 6         Student.count+=1
 7 
 8 if Student.count != 0:  #先判断计数变量是否初始化为0
 9     print('测试失败!')
10 else:
11     bart = Student('Bart') #创建第一个实例变量
12     if Student.count != 1:    #创建完第一个实例变量后,此时的Student.count应该为1
13         print('测试失败!')
14     else:
15         lisa = Student('Lisa')#又创建第二个实例变量
16         if Student.count != 2:
17             print('测试失败!')
18         else:
19             print('Students:', Student.count)
20             print('测试通过!')

运行结果:

 

 

参考网址:廖雪峰的官方网站

转载于:https://www.cnblogs.com/bravesunforever/p/10447600.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值