Python 编程要点 -- 类方法,静态方法,实例方法,mro机制

Python 类方法,静态方法,实例方法

类方法,静态方法,实例方法都可以通过类名和实例对象访问,如下:
需要注意点一点是类名访问实例方法的时候必须传递实例对象。
注意:函数 只能由类名访问(Python3),Python2不能访问。
注意:申明静态方法使用@staticmethod 并且不用加类名或者实例对象作为参数
注意:申明类方法使用@classmethod 并且需要加上cls作为方法参数

class A(object):

    @classmethod
    def cls_func(cls):
        print "classmethod"

    @staticmethod
    def static_func():
        print("staticmethod")

    def instance_func(self):
        print("instance_func")

    def common_func():
        print("common func")

a = A()
a.cls_func()
a.static_func()
a.instance_func()
A.cls_func()
A.static_func()
A.instance_func(a)
#Error python2 中类名不能访问函数,但是Python3可以访问
#A.common_func()
#Error Python2/3中都不能使用实例访问函数
#a.common_func()

>>classmethod
staticmethod
instance_func
classmethod
staticmethod
instance_func
common func

子类中访问父类的方法,以及多重继承的问题

在python中子类访问父类的方法有两种方式:

1.使用类名调用类的方法:

class A(object):
    def __init__(self):
        print("Call A __init__!")

class B(A):
    def __init__(self):
        print("Call B __init__!")
        A.__init__(self)

b = B()

>>Call B __init__!
Call A __init__!

2.使用super调用父类的方法:

class A(object):
    def __init__(self):
        print("Call A __init__!")

class B(A):
    def __init__(self):
        print("Call B __init__!")
        super(B, self).__init__()

b = B()

>>Call B __init__!
Call A __init__!

方法一更直观。推荐使用方法一进行父类的初始化。
注意!注意!注意!这里纠正一下:有的地方说方法二可以一次初始化所有超类,该说法是错误的。super并不是像Java或者其他语言一样调用所有父类的构造方法,它是调用的__class__.mro()的下一个类调用构造方法 如下:
下面的函数并不会调用B类的构造函数,因为 __class__.mro()指向的下一个类是A。

class A(object):
    def __init__(self, age=1):
        print("A __init__!")
        self.age = age

class B(object):
    def __init__(self, name=""):
        print("B __init__!")
        self.name = name

class C(A,B):
    def __init__(self):
        print("C __init__!")
        super(C, self).__init__()

c = C()

>>C __init__!
A __init__!
[<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>]

就算调用两次super也不会初始化B的构造函数,如下:
反而它只是调用了两次A类的构造函数而已。

class A(object):
    def __init__(self, age=1):
        print("A __init__!")
        self.age = age

class B(object):
    def __init__(self, name=""):
        print("B __init__!")
        self.name = name

class C(A,B):
    def __init__(self):
        print("C __init__!")
        super(C, self).__init__()
        super(C, self).__init__()

c = C()

>>C __init__!
A __init__!
A __init__!
[<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>]

下面列举一个更加复杂的例子来说明:

class A(object):
    def __init__(self, age=0):
        print("A __init__!")
        self.age = age 
        print("A leaving!")

class B(A):
    def __init__(self, name=""):
        print("B __init__!")
        super(B, self).__init__()
        self.name = name
        print("B leaving!")

class C(A):
    def __init__(self):
        print("C __init__!")
        super(C, self).__init__()
        print("C leaving !")

class D(B, C): 
    pass

d = D() 
print(d.__class__.mro())

如果是想当然以为super是调用父类的构造函数那么输出的顺序应该是

>>B __init__!
A __init__!
A leaving!
B leaving!
C __init__!
A __init__!
A leaving!
C leaving !

然而情况是:当调用D的初始化函数时按照D类的mro()顺序来选择下一个类的初始化。
这里还可以看出如果你没有写__init__函数,那么python默认的方式是通过super初始化父类

>>B __init__!
C __init__!
A __init__!
A leaving!
C leaving !
B leaving!
[<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <type 'object'>]

结论是:当你的程序里面含有多重继承存在的时候千万要注意尽量避免使用super的方式,而应该直接使用类名初始化的方式,多写几行代码比出错来的划算!上面的例子改写为方法一的方式如下:

class A(object):
    def __init__(self, age=0):
        print("A __init__!")
        self.age = age 
        print("A leaving!")

class B(A):
    def __init__(self, name=""):
        print("B __init__!")
        A.__init__(self, age=12)
        self.name = name
        print("B leaving!")

class C(A):
    def __init__(self):
        print("C __init__!")
        A.__init__(self, age=14)
        print("C leaving !")

class D(B, C):
    def __init__(self):
        print("D __init__!")
        B.__init__(self)
        C.__init__(self)
        print("D leaving!")

d = D() 

>>D __init__!
B __init__!
A __init__!
A leaving!
B leaving!
C __init__!
A __init__!
A leaving!
C leaving !
D leaving!

Python mro 的顺序问题

首先mro是按照深度优先的计算方式,重复类只保留最后一个,这是什么意思呢?就拿上面的例子来说深度优先的原则的话就应该是[D, B, A, object, C, A, object] 这样一个顺序,但是重复类只保留最后一个的原则决定了它应该是[D, B, C, A, object]这样的。所以这样就很好的理解了super为什么会表现出那样的方式。
下面再给出一个例子说明:

class A(object):
    def __init__(self, age=0):
        print("A __init__!")
        self.age = age 
        print("A leaving!")

    def func(self):
        print("A func call!")

class B(A):
    def __init__(self, name=""):
        print("B __init__!")
        A.__init__(self, age=12)
        self.name = name
        print("B leaving!")

class C(A):
    def __init__(self):
        print("C __init__!")
        A.__init__(self, age=14)
        print("C leaving !")

    def func(self):
        print("C func call!")

class D(B, C):
    def __init__(self):
        print("D __init__!")
        B.__init__(self)
        C.__init__(self)
        print("D leaving!")

d = D()
d.func()

>>D __init__!
B __init__!
A __init__!
A leaving!
B leaving!
C __init__!
A __init__!
A leaving!
C leaving !
D leaving!
C func call!

以上程序的输出是C func 被调用了,也证实了mro的顺序规则。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值