24.python中类的方法

  类中的方法,其实就是类中的函数,可以分为:实例方法,类方法,静态方法。方法和字段一样,也是属于类的属性,所以也具有运行中修改的特效, 但一般不推荐这样做。

  我在类的基本语法中,介绍了构造器方法:__init__ 、__new__;解构器方法:__del__;

  注意,这里虽然是以两个下划线(__)开头,但同时以两个下划线(__)结尾,这里表明其是一个‘魔法方法’,关于类中的魔法方法,将起一篇进行说明。

  但是,如果单纯只以两个下划线开始,则依然是私有化的意思,看代码示例:

class Test(object):

    def __scolia__(self):   # 一个类似魔术方法,并不是私有化
        return 'scolia'

    def __good(self):   # 私有方法
        return 'good'

a = Test()
print a.__scolia__()   # 魔法方法可以在直接访问
print a.__good()    # 私有方法不能直接访问

 

  同样的,和字段私有化一样,我们也可能同特殊手段进行强制访问:

print a._Test__good()  # 强制访问

 

  当然,私有方法也可以在类的内部访问,和私有字段一样。

  所以说,属性的私有化都是对访问入口进行混淆,同样的,也不建议强制访问私有属性。

  也许这里的‘魔法方法’看起来并不‘魔法’,详情将以后解释。


实例方法:

  在 __init__ 构造器中,提起过其是一个实例方法,实例方法的特点就是:

  1.方法的第一个参数必须是 self,当然这是约定俗成的写法,你可以将 self 换成 abc 之类的,但是为了别的程序员能看得懂,还是统一用 self 吧。这里的 self 代表实例本身,也就是说如果我实例化时使用的是: a = Test() ,那么 self 就代表 a 这个实例,我们可以在很多构造器中看到类似 self.scolia = 'good' 的写法,其实这个写法和在类外面 a.scolia = 'good' 效果一样,是为了添加属性,只不过 __init__ 方法是实例化时自动调用的函数,所以适合进行初始属性的创建。

  2.实例方法在调用的时候,self 是自动传递的,所以不需要我们再处理。

  3.实例方法一般要有实例才能调用,当然也有特殊的调用方法。

代码示例:

class Test(object):

    def __init__(self, a, b):   # 构造器在实例创建时进行属性的初始化
        self.a = int(a)
        self.b = int(b)

    def abc(self, c):  # 实例方法
        print self.a + self.b + int(c)  # 因为self是自动传递的,所以我们可以在实例方法中调用实例的属性

a = Test(123, 321)  # 我们只要为 a 和 b 传参就行了
a.abc(666)  # 同样的,只要为 c 传参

 

  这里,将引入一个绑定 (binding) 的概念,其主要和方法的调用有关。

  首先,我们知道方法是类的属性,而不是实例的属性,在上篇博文类的属性和实例的属性中我们也讨论过这个问题。

  其次,方法只有在其所属的类拥有实例时,才能被调用。当一个类存在实例后,方法才被认为是绑定到这个实例。没有实例的时候,方法是未绑定的。

  最后,任何一个方法定义的第一个参数都是变量 self ,它表示调用此方法的实例对象。

  很明显这里的绑定针对的是实例方法。因为如果没有实例的话,self 就无法传递,这将导致参数的不足,当然就无法调用了。

  但是,我们可以自己传递 self 来调用未绑定的方法。调用未绑定的方法通常是在我们继承了一个父类后, 我们覆盖了父类中的某个方法,但是为了实现代码重用,我们又想在子类中调用父类的方法。单纯的复制父类中的代码明显不是一个好选择, 除了浪费系统资源之外,还有可能在复制的时候出错,而且以后修改父类的代码之后,还要修改相应子类中的代码,实在太低效,这个时候就是调用未绑定方法的场景。

代码示例:

class abc(object):
    def __init__(self, a):
        self.a = -int(a)

class Test(abc):
    def __init__(self, a, b):
        abc.__init__(self, a)   # 调用父类的构造器,并手动传递 self
        self.b = b

    def fangfa(self):
        print self.a + self.b   # 属性 a 由父类的构造器创建,b 由子类构造器创建

a = Test(123, 321)  # 我们只创建了子类的实例,而没有创建父类的实例
a.fangfa()

 

  本来我们没有创建父类的示例,是无法调用父类的实例方法的,但是我们手动传递了实例方法需要的 self 参数,就可以实现调用了。

  这里的顺序是,我们创建了 Test 的实例,其 self 是自动传递的,故 Test 的构造方法 __init__(self, a, b) 中 self 就代表实例 a,而我们又调用了父类的 abc.__init__(self, a) 这里的 self 就是子类的实例 a ,参数 a 就是我们传的 123,而父类中 self.a = -int(a) ;最后我们可在子类的方法中调用 self.a 这个属性。

 


 

类方法:

  类方法其实和实例方法类似,不过其第一个参数一般是 cls (约定俗成)而不是 self。但是,如果我们直接将 self 换成 cls 来创建类方法是不对的,因为实例方法的首个参数也是任意的,只是统一使用 self 。python的解释器并没有说看见第一个参数是 cls 就知道这个是类方法,它还是将其当作是实例方法来对待,所以我们需要通过内建函数: classmethod() 来创建类方法。

代码示例:

class Test(object):

    def abc(cls):
        print cls.__name__  # 打印类名

    abc = classmethod(abc)  # 通过普通的函数传参的方式创建类方法

a = Test()  
Test.abc()  # 类能调用
a.abc()     # 实例也能调用

 

  当然,有同学在看到这段代码的时候想到了装饰器,实际上我们也可以使用装饰器的方法来创建类方法:

class Test(object):
    @classmethod
    def abc(cls):
        print cls.__name__  # 打印类名

 

  结果也是一样的,具体选择看个人喜好。

  注意,因为使用了  classmethod()函数,其第一个参数 cls 是固定传递的,而且是代表当前的类。并没有实例方法中非绑定方法的调用形式。

 


 

静态方法:

  静态方法其实就是类中的一个普通函数,它并没有默认传递的参数,在创建静态方法的时候,需要用到内置函数: staticmethod()

代码示例:

class Test(object):

    def abc():
        print 'abc'
        
    abc = staticmethod(abc)

    @staticmethod
    def xyz(a, b):
        print a + b

Test.abc()  # 类调用
Test.xyz(1, 2)  # 类调用
a = Test()
a.abc()     # 实例调用
a.xyz(3, 4)  # 实例调用

 

  注意,虽然静态方法没有默认参数, 但并不代表不能有参数。


 

总结:

  1.实例方法需要至少一个默认参数 self。

  2.类方法需要至少一个默认参数 cls。

  3.静态方法不需要默认参数。

  另外:方法也是属性,所以在类的内部,我们可通过 self.abc()、cls.abc() 的方式来调用类中的其他方法,当然要注意传参的问题。

 


用方法冒充字段:

  有时候,我们的一个方法在经过一系列处理以后,返回的是一个数据,例如:

class Test:

    def __init__(self, a, b):
        self.a = a
        self.b = b

    def fangfa(self):
        c = self.a + self.b
        return c    # 返回处理的结果数据


a = Test(1, 2)
b = a.fangfa()    # 调用方法,得到返回值
print b

 

  但是,懒惰的程序员们想:我想要的只是和字段类似的数据,却要调用一个方法,有时候容易搞错,能不能用字段的形式获取数据呢?这样更加符合直觉。

  可以,只有使用 property() 函数就可以了。同样的,这里也有两种创建方式,这里只演示装饰器的方式:

class Test:

    def __init__(self, a, b):
        self.a = a
        self.b = b

    @property
    def fangfa(self):
        c = self.a + self.b
        return c


a = Test(1, 2)
b = a.fangfa    # 不用带括号了
print b

 

  这样是实现了方法伪装成字段了。其实就是懒惰的程序员们不愿意多写一个括号,当然还有一些其他好处。

  另外,函数要用返回值,不然就默认为 None 了。

  如果在经典类中,我们就只能做到这样了。

  但是,使用新式类的话,就能有更多的功能:

class Test(object):

    def __init__(self, a, b):
        self.a = a
        self.b = b

    @property
    def fangfa(self):
        c = self.a + self.b
        return c

    @fangfa.setter
    def fangfa(self, value):
        self.a = value

    @fangfa.deleter
    def fangfa(self):
        print '属性已删除'

a = Test(1, 2)
b = a.fangfa   # 获得方法的返回值
print b
a.fangfa = 100    # 执行 fangfa.setter 修饰的方法,并让value = 100
print a.a
del a.fangfa    # 执行 fangfa.deleter 修饰的方法

  注意后面两个装饰器的名字

  另外,方法必须要先经过 property()函数的装饰后,才有后面两个装饰器的用法。

  如果使用非装饰器的形式的话:

class Test(object):

    def __init__(self, a, b):
        self.a = a
        self.b = b

    def fangfa_get(self):
        c = self.a + self.b
        return c

    def fangfa_set(self, value):
        self.a = value

    def fangfa_del(self):
        print '属性已删除'

    fangfa = property(fangfa_get, fangfa_set, fangfa_del)

 

    property(fget=None, fset=None, fdel=None, doc=None)  

  最后的 doc 是说明文档,看个人需要添加,可通过 Test.fangfa.__doc__ 进行访问。


  关于方法就先说这么多,以后有需要再进行修改补充。

 

posted @ 2016-06-19 16:36  scolia  阅读(23717)  评论(0编辑  收藏  举报