1.python中的类与对象
1 Python中定义类的方式比较简单: 2 3 class 类名: 4 5 类变量 6 7 def __init__(self,paramers): 8 9 def 函数(self,...) 10 11 …... 12 13 其中直接定义在类体中的变量叫类变量,而在类的方法中定义的变量叫实例变量。类的属性包括成员变量和方法,其中方法的定义和普通函数的定义非常类似,但方法必须以self作为第一个参数。
举例:
1 >>>class MyFirstTestClass: 2 3 class Spec="it is a test class" 4 5 def__init__(self,word): 6 7 print"say "+word 8 9 def hello(self,name): 10 11 print"hello "+name 12 13
在Python类中定义的方法通常有三种:实例方法,类方法以及静态方法。这三者之间的区别是实例方法一般都以self作为第一个参数,必须和具体的对象实例进行绑定才能访问,而类方法以cls作为第一个参数,cls表示类本身,定义时使用@classmethod;而静态方法不需要默认的任何参数,跟一般的普通函数类似.定义的时候使用@staticmethod。
1 >>>class MethodTest(): 2 3 count= 0 4 5 defaddCount(self): 6 7 MethodTest.count+=1 8 9 print"I am an instance method,my count is"+str(MethodTest.count),self 10 11 @staticmethod 12 13 defstaticMethodAdd(): 14 15 MethodTest.count+=1 16 17 print"I am a static methond,my count is"+str(MethodTest.count) 18 19 @classmethod 20 21 defclassMethodAdd(cls): 22 23 MethodTest.count+=1 24 25 print"I am a class method,my count is"+str(MethodTest.count),cls 26 27 28 29 >>> 30 31 >>>a=MethodTest() 32 33 >>>a.addCount() 34 35 Iam an instance method,my count is 1 <__main__.MethodTest instanceat 0x011EC990> 36 37 >>>MethodTest.addCount() 38 39 40 41 Traceback(most recent call last): 42 43 File"<pyshell#5>", line 1, in <module> 44 45 MethodTest.addCount() 46 47 TypeError:unbound method addCount() must be called with MethodTest instance asfirst argument (got nothing instead) 48 49 >>>a.staticMethodAdd() 50 51 Iam a static methond,my count is2 52 53 >>>MethodTest.staticMethodAdd() 54 55 Iam a static methond,my count is3 56 57 >>>a.classMethodAdd() 58 59 Iam a class method,my count is4 __main__.MethodTest 60 61 >>>MethodTest.classMethodAdd() 62 63 Iam a class method,my count is5 __main__.MethodTest
从上面的例子来看,静态方法和类方法基本上区别不大,特别是有Java编程基础的人会简单的认为静态方法和类方法就是一回事,可是在Python中事实是这样的吗?看下面的例子:
1 >>>MethodTest.classMethodAdd() 2 3 Iam a class method,my count is5 __main__.MethodTest 4 5 >>>class subMethodTest(MethodTest): 6 7 pass 8 9 >>>b=subMethodTest() 10 11 >>>b.staticMethodAdd() 12 13 Iam a static methond,my count is6 14 15 >>>b.classMethodAdd() 16 17 Iam a class method,my count is7 __main__.subMethodTest 18 19 >>>a.classMethodAdd() 20 21 Iam a class method,my count is8 __main__.MethodTest 22 23 >>>
如果父类中定义有静态方法a(),在子类中没有覆盖该方法的话,Sub.a()仍然指的是父类的a()方法。而如果a()是类方法的情况下,Sub.a()指向的是子类。@staticmethod只适用于不想定义全局函数的情况。
2.python的封装
面向对象程序设计中的术语对象(Object)基本上可以看做数据(特性)以及由一系列可以存取、操作这些数据的方法所组成的集合。传统意义上的“程序=数据结构+算法”被封装”掩盖“并简化为“程序=对象+消息”。对象是类的实例,类的抽象则需要经过封装。封装可以让调用者不用关心对象是如何构建的而直接进行使用。
一个简单的Python类封装如下:
1 _metaclass_=type # 确定使用新式类 2 class Animal: 3 4 def __init__(self): #构造方法 一个对象创建后会立即调用此方法 5 self.Name="Doraemon" 6 print self.Name 7 8 def accessibleMethod(self): #绑定方法 对外公开 9 print "I have a self! current name is:" 10 print self.Name 11 print "the secret message is:" 12 self.__inaccessible() 13 14 def __inaccessible(self): #私有方法 对外不公开 以双下划线开头 15 print "U cannot see me..." 16 17 @staticmethod 18 def staticMethod(): 19 #self.accessibleMethod() #在静态方法中无法直接调用实例方法 直接抛出异常 20 print "this is a static method" 21 22 def setName(self,name): #访问器函数 23 self.Name=name 24 25 def getName(self): #访问器函数 26 return self.Name 27 28 name=property(getName,setName) #属性 可读可写
构造函数和析构函数 Python的构造函数有两种,__init__和__new__,__init__的调用不会返回任何值,在继承关系中,为了保证父类实例正确的初始化,最好显示的调用父类的__init__方法。与__init__不同,__new__实际是个类方法,以cls作为第一个参数。 如果类中同时定义了__init__和__new__方法,则在创建对象的时候会优先使用__new__. class A(object): def __init__(self): print("in init") def __new__(self): print("in new") A() 如果__new__需要返回对象,则会默认调用__init__方法。利用new创建一个类的对象的最常用的方法为:super(currentclass,cls).__new__(cls[, ...]) class A(object): def __new__(cls): Object = super(A,cls).__new__(cls) print "in New" return Object def __init__(self): print "in init" class B(A): def __init__(self): print "in B's init" B() __new__构造函数会可变类的定制的时候非常有用,后面的小节中会体现。 Python由于具有垃圾回收机制,通常不需要用户显示的去调用析构函数,即使调用,实例也不会立即释放,而是到该实例对象所有的引用都被清除掉后才会执行。 >>>class P: def__del__(self): print"deleted" >>>class S(P): def__init__(self): print'initialized' def__del__(self): P.__del__(self) print"child deleted" >>>a=S() initialized >>>b=a >>>c=a >>>id(a),id(b),id(c) (18765704,18765704, 18765704) >>>del a >>>del b >>>del c deleted childdeleted >>>
3. Python中的继承
Python同时支持单继承与多继承,继承的基本语法为class新类名(父类1,父类2,..),当只有一个父类时为单继承,当存在多个父类时为多继承。子类会继承父类的所有的属性和方法,子类也可以覆盖父类同名的变量和方法。在传统类中,如果子类和父类中同名的方法或者属性,在查找的时候基本遵循自左到右,深度优先的原则。如下列:
1 >>>class A: 2 3 defsayhi(self): 4 5 print'I am A hi' 6 7 >>>class B: 8 9 defsayhi(self): 10 11 print'I am B Hi' 12 13 14 15 >>>class C(A,B): 16 17 pass 18 19 >>>d=C() 20 21 >>>d.sayhi() 22 23 Iam A hi 24 25 >>>B.sayhi(d) 26 27 Iam B Hi
单继承
1 _metaclass_=type # 确定使用新式类 2 class Animal: 3 4 def __init__(self): 5 self.Name="Animal" 6 7 def move(self,meters): 8 print "%s moved %sm." %(self.Name,meters) 9 10 class Cat(Animal): #Cat是Animal的子类 11 12 def __init__(self): #重写超类的构造方法 13 self.Name="Garfield" 14 15 ## def move(self,meters): #重写超类的绑定方法 16 ## print "Garfield never moves more than 1m." 17 18 class RobotCat(Animal): 19 20 def __init__(self): #重写超类的构造方法 21 self.Name="Doraemon" 22 23 ## def move(self,meters): #重写超类的绑定方法 24 ## print "Doraemon is flying." 25 26 obj=Animal() 27 obj.move(10) #输出:Animal moved 10m. 28 29 cat=Cat() 30 cat.move(1) #输出:Garfield moved 1m. 31 32 robot=RobotCat() 33 robot.move(1000) #输出:Doraemon moved 1000m.
多重继承
1 class Animal: 2 3 def eat(self,food): 4 print "eat %s" %food 5 6 class Robot: 7 8 def fly(self,kilometers): 9 print "flyed %skm." %kilometers 10 11 class RobotCat(Animal,Robot): #继承自多个超类 12 13 def __init__(self): 14 self.Name="Doraemon" 15 16 robot=RobotCat() # 一只可以吃东西的会飞行的叫哆啦A梦的机器猫 17 print robot.Name 18 19 robot.eat("cookies") #从动物继承而来的eat 20 21 robot.fly(10000000) #从机器继承而来的fly
需要注意的地方,即如果一个方法从多个超类继承,那么务必要小心继承的超类(或者基类)的顺序:
1 class Animal: 2 3 def eat(self,food): 4 print "eat %s" %food 5 6 def move(self,kilometers): #动物的move方法 7 pass 8 9 class Robot: 10 11 def move(self,kilometers): #机器的move方法 12 print "flyed %skm." %kilometers 13 14 class RobotCat(Animal,Robot): #继承自多个超类 如方法名称相同,注意继承的顺序 15 #class RobotCat(Robot,Animal): 16 def __init__(self): 17 self.Name="Doraemon" 18 19 robot=RobotCat() # 一只可以吃东西的会飞行的叫哆啦A梦的机器猫 20 print robot.Name 21 22 robot.eat("cookies") #从动物继承而来的eat 23 24 robot.move(10000000) #本来是要从机器继承move方法,但是因为继承的顺序,这个方法直接继承自动物而pass掉
关于继承的构造函数: 1.如果子类没有定义自己的构造函数,父类的构造函数会被默认调用,但是此时如果要实例化子类的对象,则只能传入父类的构造函数对应的参数,否则会出错 classAddrBookEntry(object): 'addressbook entry class' def__init__(self, nm, ph): self.name= nm self.phone= ph print'Created instance for:', self.name defupdatePhone(self, newph): self.phone = newph print'Updated phone# for:', self.name classEmplAddrBookEntry(AddrBookEntry): 'EmployeeAddress Book Entry class' defupdateEmail(self, newem): self.email= newem print'Updated e-mail address for:', self.name john= EmplAddrBookEntry('John Doe', '408-555-1212') printjohn.name 2.如果子类定义了自己的构造函数,而没有显示调用父类的构造函数,则父类的属性不会被初始化 classAddrBookEntry(object): 'addressbook entry class' def__init__(self, nm, ph): self.name= nm self.phone= ph print'Created instance for:', self.name defupdatePhone(self, newph): self.phone = newph print'Updated phone# for:', self.name classEmplAddrBookEntry(AddrBookEntry): 'EmployeeAddress Book Entry class' def__init__(self, nm, ph, id, em): #AddrBookEntry.__init__(self, nm,ph) self.empid= id self.email= em defupdateEmail(self, newem): self.email= newem print'Updated e-mail address for:', self.name john= EmplAddrBookEntry('John Doe', '408-555-1212',42, 'john@spam.doe') printjohn.email printjohn.empid 输出: john@spam.doe 42 Traceback(most recent call last): printjohn.name AttributeError:'EmplAddrBookEntry' object has no attribute 'name' 3.如果子类定义了自己的构造函数,显示调用父类,子类和父类的属性都会被初始化 classAddrBookEntry(object): 'addressbook entry class' def__init__(self, nm, ph): self.name= nm self.phone= ph print'Created instance for:', self.name defupdatePhone(self, newph): self.phone = newph print'Updated phone# for:', self.name classEmplAddrBookEntry(AddrBookEntry): 'EmployeeAddress Book Entry class' def__init__(self, nm, ph, id, em): AddrBookEntry.__init__(self, nm,ph) self.empid= id self.email= em defupdateEmail(self, newem): self.email= newem print'Updated e-mail address for:', self.name john= EmplAddrBookEntry('John Doe', '408-555-1212',42, 'john@spam.doe') printjohn.email printjohn.empid printjohn.name
4.python的多态
1 class calculator: 2 def count(self, args): 3 return 1 4 5 6 calc = calculator() # 自定义类型 7 8 from random import choice 9 10 obj = choice(['hello,world', [1, 2, 3], calc]) # obj是随机返回的 类型不确定 11 print (type(obj)) 12 print(obj.count('a'))# 方法多态
对于一个临时对象obj,它通过Python的随机函数取出来,不知道具体类型(是字符串、元组还是自定义类型),都可以调用count方法进行计算,至于count由谁(哪种类型)去做怎么去实现我们并不关心。
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 class Duck: 4 def quack(self): 5 print("Quaaaaaack!") 6 7 def feathers(self): 8 print("The duck has white and gray feathers.") 9 10 11 class Person: 12 def quack(self): 13 print("The person imitates a duck.") 14 15 def feathers(self): 16 print("The person takes a feather from the ground and shows it.") 17 18 19 def in_the_forest(duck): 20 duck.quack() 21 duck.feathers() 22 23 24 def game(): 25 donald = Duck() 26 john = Person() 27 in_the_forest(donald) 28 in_the_forest(john) 29 30 31 game()
就in_the_forest函数而言,参数对象是一个鸭子类型和person类型,它实现了两个类型的相同名称的方法,打印的内容不一样,只是因为
下面是运算符号(方法)的多态
1 def add(x,y): 2 return x+y 3 4 print add(1,2) #输出3 5 6 print add("hello,","world") #输出hello,world 7 8 print add(1,"abc") #抛出异常 TypeError: unsupported operand type(s) for +: 'int' and 'str'