Python类的探讨
我们下面的探讨基于Python3,我实际测试使用的是Python3.2,Python3与Python2在类函数的类型上做了改变
1,类定义语法
Python类定义以关键字class开头,一个类定义例子
1 class MyClass: 2 """this is an example""" 3 i = 123 4 def f(self): 5 return "hello world"
当类定义一进入的时候,也就是class关键字一遇到的时候,就开启了一个新的名字空间(namespace),并且被作为当前的局部域。
只要没有进入新的局部域,类定义里面的赋值语句和函数定义都会把名字绑定在作为当前域的这个class namespace中。而类定义结束的时候,这个当前域就退出了,会重新进入到类定义之前的局部域(基本上是全局域)。
类定义结束的时候,一个类对象(class object)也被创建了,并且这个类对象会以定义时使用的名字绑定在类定义之前的那个局部域中。
上面的类定义在类的namespace中绑定了三个对象
- i : int
- f : 一个函数对象
- __doc__: 一个str对象(第2行三引号对类进行注释)
2,类对象和实例对象(Class Object and Instance Object)
类对象支持两种操作,属性引用(attribute)和实例化(instantiation)。
2.1 属性引用
类名字空间的所有name都是这个类的属性,可以通过ClassName.Attr来直接引用,可以去获取类的属性,也可以对其赋值修改类的属性,也可以为类对象增加新的属性。
如对于上面的类对象,可以如下操作:
1 def add(self, x, y): 2 return x+y 3 4 MyClass.i = 4321 #将属性MyClass.i绑定到一个新的int对象 5 MyClass.add = add #添加一个新的属性,绑定到一个函数对象上
2.2 实例化
类对象还支持的另一个操作是实例化,实例化的语法就像函数调用一样,类名加上括号。
x = MyClass()
这创建了一个实例对象(instance object),并在当前域中用名字x和这个对象绑定。
从一个类对象实例化出一个实例对象后,也引入了一个实例对象名字空间,这个名字空间会用类对象的名字空间去初始化。
当然实例化的时候可以进行初始化设置,可以带一些参数,这就需要类去定义__init__()方法:
1 class Complex: 2 def __init__(self, realpart, imagpart): 3 self.r = realpart 4 self.i = imagpart 5 c = Complex(2, -3) 6 print(c.r, c.i)
输出:(2, -3)
[对实例对象来说,支持属性的哪些操作,下面的的是可以为一个实例的属性重新绑定对象,我记得django view中的request是可以增加属性的 ]??
3,方法对象(Method Object)
可以这么理解:方法是属于某一个对象的函数。(method is a function that "belongs to " an object)
对于Python3来说,在class中的函数属性就是函数对象(function object),而实例中的函数类属性则是方法对象(method object)
- 注:在Python2.7中class和instance中的函数属性都是方法对象,一个未绑定(unbound),一个是绑定了实例对象的
对于我们上面定义的类MyClass以及MyClass的实例x,用Python3.2测试一下
>>> type(MyClass.f) <class 'function'> >>> x = MyClass() >>> type(x.f) <class 'method'>
可以看到在Python3中x.f是method而MyClass.f是function。
使用MyClass.f和x.f
1 print(MyClass.f(x)) 2 print(x.f())
会输出两行一样的:hello world
记住,对于Python3来说,MyClass.f就是一个普通的函数对象,而它要求了一个参数self,根据函数的定义,当然我们可以给self传进去任何一个类型的对象,事实证明也是可以的
print(MyClass.f(5))
依然会输出:hello world
- 注意:如果使用Python2.7的话,这样会报错,因为Python2.7是把MyClass.f作为method的,而method调用第一个参数必须是这个类的实例对象。
现在我们再看看method object,简称为method。
method在调用时其第一个参数必须为method"所属的"对象,但是我们实际使用的时候都是这样做的:
>>>x.f()
其实编译器大概是这么做的,首先它确定x.f是一个method(注:x.f也可以不是一个method而是一个function,下面我们会看到),然后它去搜寻x所属的class的定义,找到f,然后由x和f去生成一个method object,并把x作为第一个参数进行调用。
现在我们试着把x.f给绑定到一个function上去,代码如下:
1 class MyClass: 2 """this is an example""" 3 i = 1234 4 def f(self): 5 return 'hello world' 6 def add(self, x, y): 7 return x+y 8 9 x = MyClass() 10 x.f = MyClass.add 11 12 print(x.f(x,3,4)) 13 print(x.add(8, 8))
输出:7
16
还是这个MyClass,我们在类MyClass中定义了一个add,我们已经知道这是function。
我们把实例属性x.f绑定到函数对象MyClass.add,所以现在x.f是function而不是method了,第12行的调用也说明了这一点,如果没有第一个参数x,是会报错的,同样的实例x也有method add,不用传入x,第13行的调用说明了这点。