继承 1 2 3 4 5 6 7 8 9 class Person (object ): def __init__ (self, name, gender ): self .name = name self .age = gender class Student (Person ): def __init__ (self, name, gender, score ): super (Student, self ).__init__(name, gender) self .score = score
super(Student, self).__init__(name, gender) 初始化父类,继承自 Person 的 Student 才会有 name 和 gender属性,函数 super(Student, self) 将返回当前类继承的父类,即 Person ,然后调用其 __init__()方法
可以使用isinstance()来判断继承关系:
1 2 3 4 5 p1 = Person("p_name" , 'f' ) s1 = Student("s_name" , 'm' , 90 ) print (isinstance (p1, Person))print (isinstance (p1, Student))print (isinstance (s1, Person))
多态 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 class Person (object ): def __init__ (self, name, gender ): self .name = name self .age = gender def whoAmI (self ): return "class Person:" , self .name class Student (Person ): def __init__ (self, name, gender, score ): super (Student, self ).__init__(name, gender) self .score = score def whoAmI (self ): return "class Student:" , self .name class Teacher (Person ): def __init__ (self, name, gender, course ): super (Teacher, self ).__init__(name, gender) self .score = course def whoAmI (self ): return "class Teacher:" , self .name def testpolymorphism (obj ): return obj.whoAmI() p1 = Person("p_name" , 'f' ) s1 = Student("s_name" , 'm' , 90 ) t1 = Teacher("t_name" , 'f' , 'english' ) print (testpolymorphism(p1)) print (testpolymorphism(s1)) print (testpolymorphism(t1))
s1 是Student类型,它实际上拥有自己的 whoAmI() 方法以及从 Person继承的 whoAmI 方法,但调用 s1.whoAmI() 总是先查找它自身的定义,如果没有定义,则顺着继承链向上查找,直到在某个父类中找到为止。
由于Python是动态语言,所以,传递给函数 testpolymorphism(obj) 的参数 obj 不一定是 Person 或 Person 的子类型。任何数据类型的实例都可以,只要它有一个whoAmI()的方法即可:
1 2 3 class Book (object ): def whoAmI (self ): return 'I am a book'
这是动态语言和静态语言(例如Java)最大的差别之一。动态语言调用实例方法,不检查类型,只要方法存在,参数正确,就可以调用。
举例:
Python提供了 open() 函数来打开一个磁盘文件,并返回 File 对象。File对象有一个 read() 方法可以读取文件内容:
例如,从文件读取内容并解析为JSON结果:
1 2 3 import jsonf = open ('/path/to/file.json' , 'r' ) print json.load(f)
由于Python的动态特性,json.load() 并不一定要从一个File对象读取内容。任何对象,只要有 read() 方法,就称为File-like Object,都可以传给 json.load() 。
尝试编写一个File-like Object,把一个字符串 r’[“Tim”, “Bob”, “Alice”]’包装成 File-like Object 并由 json.load() 解析。
1 2 3 4 5 6 7 8 9 10 import jsonclass TestRead (object ): def read (self ): return r'["Tim", "Bob", "Alice"]' t = TestRead() print (json.load(t)) print (type (json.load(t)))
多继承 从多个父类继承,称为多重继承。多重继承的继承链就不是一棵树了,像这样:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class A (object ): def __init__ (self, a ): print 'init A...' self .a = a class B (A ): def __init__ (self, a ): super (B, self ).__init__(a) print 'init B...' class C (A ): def __init__ (self, a ): super (C, self ).__init__(a) print 'init C...' class D (B, C): def __init__ (self, a ): super (D, self ).__init__(a) print 'init D...'
像这样,D 同时继承自 B 和 C ,也就是 D 拥有了 A、B、C 的全部功能。多重继承通过 super()调用__init__()方法时,A 虽然被继承了两次,但__init__()只调用一次:
1 2 3 4 5 >>> d = D('d' )init A... init C... init B... init D...
多重继承的目的 是从两种继承树中分别选择并继承出子类,以便组合功能 使用。
举个例子,Python的网络服务器有TCPServer、UDPServer、UnixStreamServer、UnixDatagramServer ,而服务器运行模式有 多进程ForkingMixin 和 多线程ThreadingMixin 两种。
要创建多进程 模式的 TCPServer :
1 2 class MyTCPServer (TCPServer, ForkingMixin) pass
要创建多线程 模式的 UDPServer :
1 2 class MyUDPServer (UDPServer, ThreadingMixin): pass
如果没有多重继承,要实现上述所有可能的组合需要 4x2=8 个子类。
获取对象信息 除了用 isinstance() 判断它是否是某种类型的实例外,还有别的方法获取到更多的信息。
例如,已有定义:
1 2 3 4 5 6 7 8 9 10 11 class Person (object ): def __init__ (self, name, gender ): self .name = name self .gender = gender class Student (Person ): def __init__ (self, name, gender, score ): super (Student, self ).__init__(name, gender) self .score = score def whoAmI (self ): return 'I am a Student, my name is %s' % self .name
首先可以用 type() 函数获取变量的类型,它返回一个 Type 对象:
1 2 3 4 5 >>> type (123 )<type 'int' > >>> s = Student('Bob' , 'Male' , 88 )>>> type (s)<class '__main__.Student' >
其次,可以用 dir() 函数获取变量的所有属性:
1 2 3 4 5 >>> dir (123 ) ['__abs__' , '__add__' , '__and__' , '__class__' , '__cmp__' , ...] >>> dir (s)['__class__' , '__delattr__' , '__dict__' , '__doc__' , '__format__' , '__getattribute__' , '__hash__' , '__init__' , '__module__' , '__new__' , '__reduce__' , '__reduce_ex__' , '__repr__' , '__setattr__' , '__sizeof__' , '__str__' , '__subclasshook__' , '__weakref__' , 'gender' , 'name' , 'score' , 'whoAmI' ]
对于实例变量,dir() 返回所有实例属性,包括 __class__ 这类有特殊意义的属性。注意到方法 whoAmI 也是 s 的一个属性。
如何去掉 __xxx__ 这类的特殊属性,只保留我们自己定义的属性?使用 filter() 函数的用法。
dir() 返回的属性是字符串列表,如果已知一个属性名称,要获取或者设置对象的属性,就需要用 getattr() 和 setattr() 函数了:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 >>> getattr (s, 'name' ) 'Bob' >>> setattr (s, 'name' , 'Adam' ) >>> s.name'Adam' >>> getattr (s, 'age' ) Traceback (most recent call last): File "<stdin>" , line 1 , in <module> AttributeError: 'Student' object has no attribute 'age' >>> getattr (s, 'age' , 20 ) 20
拓展举例:使用 setattr
对于Person类的定义:
1 2 3 4 class Person (object ): def __init__ (self, name, gender ): self .name = name self .gender = gender
希望除了 name和gender 外,可以提供任意额外的关键字参数,并绑定到实例,请修改 Person 的 __init__()定 义,完成该功能。
1 2 3 4 5 6 7 8 9 10 11 12 class Person (object ): def __init__ (self, name, gender, **kwargs ): self .name = name self .gender = gender for k, v in kwargs.items(): setattr (self , k, v) p1 = Person("zyp" , 'male' , age=25 , love="reading" ) print (p1.age) print (p1.love)