[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行
问题
- code[12] 类属性方法可以直接在类里面使用,而不是 类名.属性 这样使用
这是因为在类内部使用变量会在当前类(不考虑继承的情况)作用域下寻找变量,如果找到则使用,否则抛出异常
- 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()