类方法和实例方法

[toc]

类属性与类方法

code

def out_fun(a):
    print("out_fun", a)


def deco():
    pass


class Test(object):
    out_fun_var, dec_fun = out_fun, deco

    @dec_fun  # 能成功引用
    def in_fun1(self):
        self.out_fun_var(1)  # TypeError: out_fun() takes 1 positional argument but 2 were given

        Test.out_fun_var(1)  # 1

        self.out_fun_var(2222, 1)  # 1 


ins = Test()
ins.in_fun1()

分析

术语
  • 类属性:类里面定义的变量,如a=1

  • 示例属性:def __init__ 里定义的变量如self.a=1

  • 类方法:Test.in_fun1(xxx)

  • 实例方法:ins.in_fun1()

  • 类方法属性:out_fun_var, dec_fun = out_fun, deco

  • code[12]:代码的第12行

问题
  1. code[12] 类属性方法可以直接在类里面使用,而不是 类名.属性 这样使用

这是因为在类内部使用变量会在当前类(不考虑继承的情况)作用域下寻找变量,如果找到则使用,否则抛出异常

  1. code[14]使用实例调用方法属性和code[16]使用类调用方法属性不一样

首先需要注意几点:

①类与实例相关属性不是指向同一块内存的,实际上在类实例化过程中,Python解释器先使用类方法__new__( )为类分配空间,返回类实例,之后通过调用该对象的__init__( )方法初始化变量(在验证过程中,又出现另一个问题,类与实例方法内存地址相同:Python采用的是垃圾回收的机制, 当一个内存, 没有对象对其引用的话, 就会立刻销毁这块内存然后对其复用. 这个问题发生的关键就在于这个复用阶段. 所以可以推测, 如果不销毁这块内存, Python自然就会开辟新的内存空间去创建对象了)

PS:借用一张图

②类定义的方法属性实际上也等同于类方法,即在code中 out_fun_var, dec_fun = out_fun, deco相当于

```python class Test(object):

   def out_fun_var(a):
       print("out_fun", a)

   def dec_fun(func):
       def wrapper(*args, **kwargs):
           print("in deco")
           return func(*args, **kwargs)
       return wrapper

```

注意虽然类方法中的参数没有显式使用self参数,但是如果实例中调用的话,第一个参数必然是self,如果是类调用的话则第一个参数就是自定义的,不是self。

基于上面几点,self.out_fun_var(1) # TypeError: out_fun() takes 1 positional argument but 2 were given 是会有异常抛出的,只要把参数去掉即可,因为第一个参数必然是self,而使用类调用的话就可以像调用一般方法一样调用,无需添加实例。

super()

code

class Base(object):
    def __init__(self):
        pass
        # print("Base init")

    def method(self):
        print(self.a)
        print("Base method")


class Medium1(Base):
    def __init__(self):
        super(Medium1, self).__init__()
        # print("Medium1 init")

    def method(self):
        print("Medium1 method")


class Medium2(Base):
    a = 2
    def __init__(self):
        super(Medium2, self).__init__()
        # print("Medium2 init")

    # def method(self):
    #     print("Medium2 method")


class Leaf(Medium1, Medium2):
    def __init__(self):
        super(Leaf, self).__init__()
        print(Leaf.__mro__)


leaf = Leaf()
leaf.method()

分析

  • 钻石继承

上述代码的情况如果使用普通方式实例化(即不是使用super而是Medium1.__init()__)实例化后父类Base会被实例化两次,类似这种情况就是钻石继承,很多语言都有这个问题,比如java,它的做法是只能单继承。

  • super()的初始化__init()__作用

C3算法、广度优先算法:(<class '__main__.Leaf'>, <class '__main__.Medium1'>, <class '__main__.Medium2'>, <class '__main__.Base'>, <class 'object'>),解决钻石继承问题,即找方法或者变量的流程是:

graph LR
当前类 --> 继承类左 
继承类左 --> 继承类右
继承类右 --> ...
  • super()的指定继承方法

注意:

下面的代码表明如果想调用Medium2中的方法,应该从mro(<class '__main__.Leaf'>, <class '__main__.Medium1'>, <class '__main__.Medium2'>, <class '__main__.Base'>, <class 'object'>)中指定Medium2前面一个类

class Medium1(Base):
    def __init__(self):
        super(Medium1, self).__init__()
        # print("Medium1 init")

    def method(self):
        print("Medium1 method")


class Medium2(Base):
    a = 2
    def __init__(self):
        super(Medium2, self).__init__()
        print(Medium2.__mro__)
        # print("Medium2 init")

    def method(self):
        print("Medium2 method")


class Leaf(Medium1, Medium2):
    def __init__(self):
        super(Leaf, self).__init__()
        print(Leaf.__mro__)

    def test_fun(self):
        print("test_fun")
        super(Medium1, self).method()

leaf = Leaf()
leaf.test_fun()

参考链接

https://www.jianshu.com/p/9e947014549f

http://www.ishenping.com/ArtInfo/2952538.html

本文作者:朝圣

本文链接:www.zh-noone.cn/2019/11/类方法

版权声明:本博客所有文章除特别声明外,均采用CC BY-NC-SA 3.0许可协议。转载请注明出处!

Liunx应用故障排查常用命令
0 条评论