继承与多态
继承
当定义一个class
的时候,可以从某个现有的class
继承,新的class
称为 子类(Subclass),而被继承的class称为 基类、父类或超类(Base class、Super class)。
举个例子:
class animal(object):
def run(self):
print('animal is running')
class dog(animal):
pass
class cat(animal):
pass
上例中animal
是dog
和cat
的 父类
而继承就可以使得 子类 获得了 父类 的全部功能。因此:
d = dog()
print(d.run())
# result is 'animal is running'
当然,也可以对子类增加一些方法,而当子类和父类都存在相同的run()
方法时,我们说,子类的run()
覆盖了父类的run()
,在代码运行的时候,总是会调用子类的run()
多态
比如上个例子中的变量d
,d
既是dog
的实例,又是animal
的实例
多态有个著名的 开闭原则
- 对扩展开放 :允许新增新的子类
- 对修改封闭 :不需要修改依赖父类的某一个函数,子类可直接调用
获取对象信息
使用type():
基本类型和指向变量的函数或者类都可以使用type()
来判断
>>> type(123)
<class 'int'>
>>> type('str')
<class 'str'>
>>> type(None)
<type(None) 'NoneType'>
>>> type(abs)
<class 'builtin_function_or_method'>
>>> type(a)
<class '__main__.Animal'>
使用isinstance()
但是要判断一个有继承关系的class
怎么办?,这时候就要使用isinstance()
了
class Husky(dog)
h = Husky()
print(isinstance(h, Husky))
# result is True
print(isinstance(h, dog)
# result is True
isinstance()
还可以判断一个变量是否是某些类型中的一种,比如下面的代码就可以判断是否是list或者tuple:
>>> isinstance([1, 2, 3], (list, tuple))
True
>>> isinstance((1, 2, 3), (list, tuple))
True
使用dir()
如果要获得一个对象的所有属性和方法,可以使用dir()
函数,它返回一个包含字符串的list,比如,获得一个str对象的所有属性和方法:
>>> dir('ABC')
['__add__', '__class__',..., '__subclasshook__', 'capitalize', 'casefold',..., 'zfill']
类似__xxx__
的属性和方法在Python中都是有特殊用途的,比如__len__
方法返回长度。在Python中,如果你调用len()
函数试图获取一个对象的长度,实际上,在len()
函数内部,它自动去调用该对象的__len__()
方法,所以,下面的代码是等价的:
print(len('ABC'))
print('ABC'.__len__())
# boths' results are '3'
同理,如果我们自己写的类想要使用len()
函数的话,我们就可以自己写一个__len__()
的方法:
class MD(object):
def __len__(self):
return 11
d=MD()
print (len(d))
#result is 11
getattr()
、setattr()
以及hasattr()
class Mo(object):
def __init__(self):#赋予类属性的def
self.x=9
a=Mo()
print(hasattr(obj, 'x')) #有属性'x'吗?
# result is ture
print(hasattr(obj, 'y')) #有属性'y'吗?
# result is flase
setattr(obj, 'y', 19) #设置一个属性'y'
print(hasattr(obj, 'y')) #有属性'y'吗?
# result is ture
getattr(obj,'y',404) # 获取属性'y',如果不存在,返回默认值404
# result is 19
getattr(obj,'z',404) # 获取属性'z',如果不存在,返回默认值404
# result is 404
类属性
给实例绑定属性的方法是通过实例变量,或者通过self变量
>>> class Student(object):
... name = 'Student'
...
>>> s = Student() # 创建实例s
>>> print(s.name) # 打印name属性,因为实例并没有name属性,所以会继续查找class的name属性
Student
>>> print(Student.name) # 打印类的name属性
Student
>>> s.name = 'Michael' # 给实例绑定name属性
>>> print(s.name) # 由于实例属性优先级比类属性高,因此,它会屏蔽掉类的name属性
Michael
>>> print(Student.name) # 但是类属性并未消失,用Student.name仍然可以访问
Student
>>> del s.name # 如果删除实例的name属性
>>> print(s.name) # 再次调用s.name,由于实例的name属性没有找到,类的name属性就显示出来了
Student
从上面的例子可以看出,在编写程序的时候,千万不要对实例属性和类属性使用相同的名字,因为相同名称的实例属性将屏蔽掉类属性,但是当你删除实例属性后,再使用相同的名称,访问到的将是类属性。