[关闭]
@yanglt7 2018-12-03T07:03:48.000000Z 字数 4162 阅读 600

Python18_继承和多态、获取对象信息

Python


继承和多态

在 OOP 程序设计中,当我们定义一个 class 的时候,可以从某个现有的 class 继承,新的 class 称为子类(Subclass),而被继承的 class 称为基类、父类或超类(Base class、Super class)。子类可获得父类的全部功能。当然,也可以对子类增加一些方法。

  1. >>> class Animal(object):
  2. ... def run(self):
  3. ... print('Animal is running...')
  4. ...
  5. >>> class Dog(Animal):
  6. ... pass
  7. ...
  8. >>> dog=Dog()
  9. >>> dog.run()
  10. Animal is running...
  11. >>> class Dog(Animal):
  12. ... def run(self):
  13. ... print('Dog is running...')
  14. ...
  15. >>> dog=Dog()
  16. >>> dog.run()
  17. Dog is running...

当子类和父类都存在相同的 run() 方法时,子类的 run() 覆盖了父类的 run(),在代码运行的时候,总是会调用子类的 run()。这样,我们就获得了继承的另一个好处:多态。

当我们定义一个 class 的时候,我们实际上就定义了一种数据类型。我们定义的数据类型和 Python 自带的数据类型,比如 str、list、dict 没什么两样:

  1. a = list() # a 是 list 类型
  2. b = Animal() # b 是 Animal 类型
  3. c = Dog() # c 是 Dog 类型

判断一个变量是否是某个类型可以用 isinstance() 判断:所以,在继承关系中,如果一个实例的数据类型是某个子类,那它的数据类型也可以被看做是父类。但是,反过来就不行:

  1. >>> isinstance(a, list)
  2. True
  3. >>> isinstance(b, Animal)
  4. True
  5. >>> isinstance(c, Dog)
  6. True
  7. >>> isinstance(c, Animal)
  8. True
  9. >>> b = Animal()
  10. >>> isinstance(b, Dog)
  11. False

要理解多态的好处,我们还需要再编写一个函数,这个函数接受一个 Animal 类型的变量:

  1. >>> def run_twice(animal):
  2. ... animal.run()
  3. ... animal.run()
  4. ...
  5. >>> run_twice(Animal())
  6. Animal is running...
  7. Animal is running...
  8. >>> run_twice(Dog())
  9. Dog is running...
  10. Dog is running...

再定义一个Tortoise类型,也从Animal派生:

  1. >>> class Tortoise(Animal):
  2. ... def run(self):
  3. ... print('Tortoise is running slowly...')
  4. ...
  5. >>> run_twice(Tortoise())
  6. Tortoise is running slowly...
  7. Tortoise is running slowly...

“开闭”原则:

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

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

获取对象信息

使用 type()

基本类型都可以用 type() 判断,如果一个变量指向函数或者类,也可以用 type() 判断:

  1. >>> type(123)
  2. <class 'int'>
  3. >>> type('str')
  4. <class 'str'>
  5. >>> type(None)
  6. <class 'NoneType'>
  7. >>> type(abs)
  8. <class 'builtin_function_or_method'>
  9. >>> a=Animal()
  10. >>> type(a)
  11. <class '__main__.Animal'>

type() 函数返回对应的 Class 类型。如果我们要在 if 语句中判断,就需要比较两个变量的 type 类型是否相同:

  1. >>> type(123)==type(456)
  2. True
  3. >>> type(123)==int
  4. True
  5. >>> type('abc')==type('123')
  6. True
  7. >>> type('abc')==str
  8. True
  9. >>> type('abc')==type(123)
  10. False

判断基本数据类型可以直接写 int,str 等,但如果要判断一个对象是否是函数怎么办?可以使用 types 模块中定义的常量:

  1. >>> import types
  2. >>> def fn():
  3. ... pass
  4. ...
  5. >>> type(fn)==types.FunctionType
  6. True
  7. >>> type(abs)==types.BuiltinFunctionType
  8. True
  9. >>> type(lambda x:x)==types.LambdaType
  10. True
  11. >>> type((x for x in range(10)))==types.GeneratorType
  12. True

使用 isinstance()

对于 class 的继承关系来说,使用 type() 就很不方便。我们要判断 class 的类型,可以使用 isinstance() 函数。

  1. object -> Animal -> Dog -> Husky
  2. >>> a=Animal()
  3. >>> d=Dog()
  4. >>> h=Husky()
  5. >>> isinstance(h,Husky)
  6. True
  7. >>> isinstance(h,Dog)
  8. True
  9. >>> isinstance(h,Animal)
  10. True
  11. >>> isinstance(d,Dog) and isinstance(d,Animal)
  12. True

能用 type() 判断的基本类型也可以用 isinstance() 判断:

  1. >>> isinstance('a',str)
  2. True
  3. >>> isinstance(123,int)
  4. True
  5. >>> isinstance(b'a',bytes)
  6. True

并且还可以判断一个变量是否是某些类型中的一种,比如下面的代码就可以判断是否是 list 或者 tuple:

  1. >>> isinstance([1,2,3],(list,tuple))
  2. True
  3. >>> isinstance((1,2,3),(list,tuple))
  4. True

使用 dir()

如果要获得一个对象的所有属性和方法,可以使用 dir() 函数,它返回一个包含字符串的 list,比如,获得一个 str 对象的所有属性和方法:

  1. >>> dir('ABC')
  2. ['__add__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__gt__', '__hash__', '__init__', '__iter__', '__le__', '__len__', '__lt__', '__mod__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmod__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'capitalize', 'casefold', 'center', 'count', 'encode', 'endswith', 'expandtabs', 'find', 'format', 'format_map', 'index', 'isalnum', 'isalpha', 'isdecimal', 'isdigit', 'isidentifier', 'islower', 'isnumeric', 'isprintable', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'maketrans', 'partition', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']

配合 getattr()、setattr() 以及 hasattr(),我们可以直接操作一个对象的状态:

  1. >>> class MyObject(object):
  2. ... def __init__(self):
  3. ... self.x=9
  4. ... def power(self):
  5. ... return self.x*self.x
  6. ...
  7. >>> obj=MyObject()
  8. >>> hasattr(obj,'x')
  9. True
  10. >>> obj.x
  11. 9
  12. >>> hasattr(obj,'y')
  13. False
  14. >>> setattr(obj,'y',19)
  15. >>> hasattr(obj,'y')
  16. True
  17. >>> getattr(obj,'y')
  18. 19
  19. >>> obj.y
  20. 19
  21. >>> getattr(obj,'z')
  22. Traceback (most recent call last):
  23. File "<stdin>", line 1, in <module>
  24. AttributeError: 'MyObject' object has no attribute 'z'
  25. >>> getattr(obj,'z',404)
  26. 404
  27. >>> hasattr(obj,'power')
  28. True
  29. >>> getattr(obj,'power')
  30. <bound method MyObject.power of <__main__.MyObject object at 0x000001B3F75461D0>>
  31. >>> fn=getattr(obj,'power')
  32. >>> fn
  33. <bound method MyObject.power of <__main__.MyObject object at 0x000001B3F75461D0>>
  34. >>> fn()
  35. 81
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注