开始

Python学习_12_方法和类定制

方法

在上一篇随笔中,简单提到了类的某些方法:__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

posted @ 2015-05-24 22:21  Lyon2014  阅读(283)  评论(0编辑  收藏  举报