方法
在上一篇随笔中,简单提到了类的某些方法:__init__()等的调用,并简要说明方法和函数的区别。
方法是在类内部定义的函数,方法也是对象,所以方法是类的属性,这就是为什么说实例的方法存在于类定义中。而在ruby中,方法肯定是存在于类中的,实例的单件方法就存在于单件类中,python中并没有单件类,并且方法本质也是属性,所以实例的方法也可以存在于自身,而在调用方法时,同样遵循命名空间的查找顺序。但是方法和一般是属性任然存在区别:
from types import MethodType
class MyClass:
? ? #@staticmethod
? ? def my_method(self):
? ? ? ? print("This is a class method")
obj = MyClass()
print(‘obj_m‘,id(obj.my_method))
#obj2 = MyClass()
#print(‘obj2_m‘,id(obj2.my_method))
#a = obj.my_method
print(‘cls_m‘,id(MyClass.my_method))
def my_method(self):
? ? print("This is a obj method")
obj.my_method = MethodType(my_method,obj)
print(‘obj_m‘,id(obj.my_method))
print(‘cls_m‘,id(MyClass.my_method))
def my_method(self):
? ? print("This is a obj method2")
obj.my_method = MethodType(my_method,obj)
print(‘obj_m‘,id(obj.my_method))
得到的结果为:
obj_m 4337531000
cls_m 4337792688
obj_m 4337531000
cls_m 4337792688
obj_m 4337531792
可以看到,一开始设定了一个普通函数,结果发现在类中该函数的引用和对象中函数的引用并不相同,而在对象中对该函数进行修改时,第一次修改并没有改变引用,第二次修改时,引用却发生改变了,而类对函数的引用当然是不会变的。由于第二次修改确实是改变了引用,说明函数确实是不可更改类型(这一点可以通过更加简单的实验得到),那第一次对对象改变函数为什么并没有改变其引用呢?其实是因为在这里对象的函数和类的函数并不是同一个引用,当第一次改变对象的函数时,原来的对象的函数引用次数就变为0,地址被系统回收,当我们给对象定义一个同名函数时,又分到了同样的地址,实际上是改变了引用了。可以通过增加该方法的引用次数来验证(第四处注释,如果去掉注释,引用将会不同)。
好了,绕了很多的弯路又回到原点:对象的函数和类的函数一开始就不是同一个引用,这是因为,在对象调用函数(这里强调函数是因为就算去掉self也是一样)时,会给函数传递该对象的绑定,所以对象调用的函数是有绑定的函数,而类调用的是没有绑定的函数,所以他们的引用不一样。方法的引用仅仅是因为调用了一个绑定而变得不同,绑定不同的对象对函数的引用没有影响(去掉第二、三行的注释可以实验)。
为了验证,可以实验以下代码:
class MyClass:
? ? pass
class MyClass2:
? ? pass
print(id(MyClass2.__new__))
print(id(MyClass.__new__))
print(id(type.__new__))
静态方法和类方法
在上述代码的第一行注释中,有@staticmethod装饰器,如果去掉该注释,则会发现类和对象对函数的引用是一样的,这就是静态方法,静态方法对类和对象是平等的,但是并不表示完全相同,因为类对函数的调用仍然不会附带绑定:如果在静态方法中调用self,那么对于对象而言,仍然不需要传入自身,而对于类而言,必须传入一个对象以供调用(可以传入MyClass),因为此时只是调用了MyClass命名空间中的一个函数,并没有获得MyClass的绑定。
之所以对象调用方法时,不用显式传入自身,是因为方法在类中定义时,self的含义就是类实例化后的对象,对象在调用自己类的方法时,解释器会帮助将该对象传给方法。
类方法则指的是第一个参数为类本身,没有像self那样的名字,但是一般使用cls作为变量名:
class MyClass:
? ? @classmethod
? ? def a_method(cls):
? ? ? ? print(cls)
MyClass.a_method()
此时,也可以不用显式地传入对象。
类定制
python中所谓的类定制,就是为类实现某一些特定的方法,使其具有一些特定的属性,或者可以对其进行一些特定操作。这些特殊的方法以双下划线开头并且以双下划线结尾。其实__init__方法就是最基本的定制类的特殊方法,通过__init__方法可以定制类实例的实例化过程。
其中一些可能比较常用的定制有:
1. 定制__str__方法,可以使类的实例可以通过str()转换为字符串,并且由于print打印时其实是调用了str(),所以可以自定义打印的格式;
2. 定制__len__方法,可以自定义调用len()时,返回的值;
3. 定制__iter__方法,可以使类的实例成为一个迭代对象,此时,需要实现__next__方法(python2中为next方法),使迭代过程完整执行,在__next__方法中,指定StopIteration错误,以终止迭代。
4. 定制__cmp__方法,__lt__、__le__、__gt__、__ge__、__eq__、__ne__等方法可以自定义比较大小的规则;
5. 定制__*add__、__*sub__等等数值操作的方法,可以自定义类实例的+-*/等操作,*表示存在__add__、__radd__、__iadd__等多种方法,默认方法从左往右计算,r开头的方法从右往左计算,i开头的表示左结合操作符和赋值的组合,类似于+=
6. 定制__call__方法可以使类的实例能够被调用
等等
如果想得到更加具体的例子,以下文章应该会由很大帮助:
http://www.liaoxuefeng.com/wiki/001374738125095c955c1e6d8bb493182103fac9270762a000/0013946328809098c1be08a2c7e4319bd60269f62be04fa000