python类加载分析-类属性-实例方法-类方法-静态方法对比

一、 概述
本文对python3的类加载过程进行分析,属于python最核心原理,有下面内容
1、 类对象
2、 实例对象
3、 self变量名称问题
4、 类属性、实例变量、局部变量
5、 类方法
6、 实例方法
7、 类方法与实例方法相互调用
8、 静态方法
9、 继承时三类方法的影响
二、 pytyon类对象
1、 pytyon是脚本语言
pyton的代码在编译时,无论是函数,还是类,都生成了相应的对象,无论这个类是否实例化,都生成了类对象
2、 pytyon类对象是一个静态对象
一旦生成,就不再变化
3、 init构造函数不运行
类对象生成时,init构造函数没有运行
所以调用类方法时,init构造方法的实例属性不存在,不可以调用init方法中self.xxx实例属性
4、 python类对象作用于类方法
@classmethon,类方法的第1个参数是类对象

5、 类属性是类对象的静态变量
即写在类下面,不在任何一个方法中的变量
这类变量可以通过类对象访问,也可以通过类访问,例如
def fun2(cls123):
cls123.n+=2
Test1.n+=2 #与上一行代码作用相同
因为此时,id(cls123)==id(Test1)

6、 类对象与类方法测试示例见后面类方法例

三、 python实例对象
1、 python语言又是面向对象编程
可以实例化,每次实例化时,生成一个实例对象,与类对象没有关系
下面生成3个不同的实例对象
t1=Test1()
t2=Test1()
t3=Test1()
2、 类属性存在二义性
写在类下面的所有变量,既是类对象的静态变量,同时又是每个实例动态变量,有2个不同的内存地址,二者的值在第一次改变前相同,有相同的内存地址,改变后,内存地址不同
见 def fun13(self)示例分析
3、 实例对象时,init构造方法运行
不同于java对象,python没有构造函数重载,可以写多个init,编绎时无语法错误,只有最后一个init方法有效
4、 实例成员访问
任何实例成员,只能通过self.xxx,或self.funxxx(…)访问
任何实例方法中,任何代码块中,无局部变量
5、 实例变量
实例变量self.xxx,可以存在于任何方法中,init构造方法中,任何其他实例方法中,及任何控制语句代码块中,只要这个变量self.xxx运行到了,则这个变量在以后任何地方均有效。

四、 self变量名称问题
1、 每个实例方法的第1个参数是self
2、 可以是任何其他名称
见 def fun12(self123)
self123与self完全相同

五、 类中的变量
1、 类属性有二义性
作用于实例对象的实例方法时,是当前实例动态变量
作用于类对象的类方法时,是静态变量
2、 实例全局变量
self.xxx可存在任何方法中,控制语句代码块中
3、 类方法中的全局变量
与实例变量作用域相同
fun22(self)是类方法,这里用self,只是一个自定义代号,也可以是cls、cls123
self.n2在fun14中有效。
4、 局部变量
实例方法中,不能有局部变量
类方法、静态方法中,可以有局部变量
局部变量仅在当前方法中有效
示例见 def fun22(self)

六、 类方法分析
1、 方法上面有@classmethod
2、 类方法的第1个参数是类对象
可以用类对象调用类方法
可以实例对象调用类方法
还可以从实例方法调用类方法
无论上面三种情况的哪一种, 类方法的第1个参数,永远是类对象,是一个静态对象

3、 类方法可以用类对象直接调用
例如,Test1.fun22() #Test1是一个类对象,编绎到Test1时,生成Test1类对象

4、 类方法也可以通过任何一个实例对象调用
无论是哪一个实例对象访问类方法,类方法的第1个参数永远是类对象,与实例对象无关
5、 类方法地址变化
某一个时刻点,访问类方法时,有相同的方法地址,包括定时N长时间,访问类方法的地址,依然没有变化

再次选中代码运行时,同一个类对象,类方法的地址则发生改变
见示例fun2(cls123)
6、 类方法访问其他类方法
例如def fun22(self)中
self.fun23() #OK
不是self.fun23(self) #语法错误
7、 类方法可访问实例方法
fun22(self)中,self.fun14(self)
fun2(cls123)中,cls123.fun12(cls123)

8、 示例如下


# -*- coding: utf-8 -*-
"""
2018-05-07 14:03:34
作者: 刘明
"""

class Test1():
      n=10
      def __init__(self,n2):
            self.n2=n2
      def fun11(self):
            self.n+=1
            print('self.n=%d,self.n2=%d,self.n3=%d,self.n5=%d'%(self.n,self.n2,self.n3,self.n5))
            print('实-fun1,id(self)=%d,id(self.fun11)=%d,id(self.n)=%d'%(id(self),id(self.fun11),id(self.n)))
            print('self.n=%d,self.n2=%d,self.n3=%d'%(self.n,self.n2,self.n3))
#下面检查实例动态变量可以写在任何实例方法中,不一定要写在__init__中,
      def fun12(self123): #self变量只是一个占位参数,可以是任何变量名称
            self123.n3=3
            #n4=2  #语法错误,实例方法中,不能有局部变量
            if self123.n3==3:
            #  n4=2  #语法错误,实例方法中,不能有局部变量
               self123.n5=5
               pass
            self123.fun11()
            #下面方法检测类变量有2义性,既是静态变量,同时又是实例对的动态变量,在第一次赋值前,两个变量的地址相同,在赋值后,两个变量的地址就发生了改变
      def fun13(self):
            print('1--fun13,id(self.n)=%d,id(Test1.n)=%d'%(id(self.n),id(Test1.n)))
            self.n+=3
            Test1.n+=5
            print('2--fun13,id(self.n)=%d,id(Test1.n)=%d'%(id(self.n),id(Test1.n)))
      def fun14(self2):
            self2.n3=3
            self2.n4=4
            #self2.n2=2 #如果此方法设想为类方法,则self2.n2不是存在的,如果是实例方法,则此变量存在
            #self.n2=2
            #print('n5=%d'%(n5))
            print('self.n=%d,self.n2=%d'%(self2.n,self2.n2))
            #self2.fun11(self2) #本方法只能供类方法调用
            #self2.fun11() #本方法只能供实例方法调用

      @classmethod
      def fun2(cls123):
            cls123.n+=2
            #cls123.fun13()  #语法错误
            #cls123.fun13(cls123) #OK
            cls123.fun14(cls123) #OK
            print('fun2,id(cls123.fun2)=%d,id(cls123)=%d,id(Test1)=%d,id(cls123.n)=%d,id(Test1.n)=%d'%(id(cls123.fun2),id(cls123),id(Test1),id(cls123.n),id(Test1.n)))
            print('fun2,cls123.n=%d,Test1.n=%d'%(cls123.n,Test1.n)) #cls123.n与Test1.n是同一个变量
      @classmethod
      def fun22(self):
            self.n+=2
            if True:
                  n5=2  #局部变量可以存在于类方法中,此变量仅存在于fun22中,在fun14中无效
                  print('1--self.n=%d,n5=%d'%(self.n,n5)) #OK

            self.n2=3 #此变量在fun14中可见
            print('2--self.n=%d,n5=%d'%(self.n,n5))  #OK
            self.fun23() #类方法中访问其他类方法
            self.fun14(self)  #类方法中访问实例方法
      @classmethod
      def fun23(self2):
            print('fun23...')  
            print('self.n=%d,self.n2=%d'%(self2.n,self2.n2))

      @staticmethod
      def fun3():
            n1=2 #局部变量可以存在于静态方法中
            Test1.n+=2
            print('fun3,id(Test1.fun3)=%d,id(Test1.n)=%d,id(n1)=%d'%(id(Test1.fun3),id(Test1.n),id(n1)))
            print('n1=%d,Test1.n=%d'%(n1,Test1.n))

'''
类方法测试
可以用类对象调用类方法
也可以用实例对象调用类方法,但对象地址依然是类方法地址
类方法可以

'''

#测试类与类类方法
#通过类对象访问类方法,类方法访问类方法,类方法访问实例方法
Test1.fun22()
'''
#输出如下
1--self.n=12,n5=2
2--self.n=12,n5=2
fun23...
self.n=12,self.n2=3
self.n=12,self.n2=3

'''

#通过实例对象访问类方法,类方法的对象永远是类对象,无论是哪一个实例调用,每一次访问类方法是不同地址
i1=id(Test1)
i2=id(Test1.n)
print('id(Test1)=%d,id(Test1.n)=%d'%(i1,i2))

'''
输出如下
id(Test1)=2595967872712,id(Test1.n)=1563127616

'''
t1=Test1(1)
t2=Test1(1)
t3=Test1(1)
print('id(t1)=%d,id(t2)=%d,id(t3)=%d'%(id(t1),id(t2),id(t3)))
'''
输出如下
id(t1)=2595986789600,id(t2)=2595986790496,id(t3)=2595986791112

'''

t1.fun2()
t2.fun2()
t3.fun2()

'''
输出如下
self.n=14,self.n2=3
fun2,id(cls123.fun2)=2595961726344,id(cls123)=2595967872712,id(Test1)=2595967872712,id(cls123.n)=1563127680,id(Test1.n)=1563127680
fun2,cls123.n=14,Test1.n=14
self.n=16,self.n2=3
fun2,id(cls123.fun2)=2595961726344,id(cls123)=2595967872712,id(Test1)=2595967872712,id(cls123.n)=1563127744,id(Test1.n)=1563127744
fun2,cls123.n=16,Test1.n=16
self.n=18,self.n2=3
fun2,id(cls123.fun2)=2595961726344,id(cls123)=2595967872712,id(Test1)=2595967872712,id(cls123.n)=1563127808,id(Test1.n)=1563127808
fun2,cls123.n=18,Test1.n=18


'''
#再次执行上面语句
Test1.fun2()
t1.fun2()
t2.fun2()
t3.fun2()
'''
输出如下,id(cls123.fun2)会发生改变,cls123.n与Test1.n是同一个变量
Test1.fun2()
t1.fun2()
t2.fun2()
t3.fun2()
self.n=68,self.n2=3
fun2,id(cls123.fun2)=2595956193928,id(cls123)=2595967872712,id(Test1)=2595967872712,id(cls123.n)=1563129408,id(Test1.n)=1563129408
fun2,cls123.n=68,Test1.n=68
self.n=70,self.n2=3
fun2,id(cls123.fun2)=2595956193928,id(cls123)=2595967872712,id(Test1)=2595967872712,id(cls123.n)=1563129472,id(Test1.n)=1563129472
fun2,cls123.n=70,Test1.n=70
self.n=72,self.n2=3
fun2,id(cls123.fun2)=2595956193928,id(cls123)=2595967872712,id(Test1)=2595967872712,id(cls123.n)=1563129536,id(Test1.n)=1563129536
fun2,cls123.n=72,Test1.n=72
self.n=74,self.n2=3
fun2,id(cls123.fun2)=2595956193928,id(cls123)=2595967872712,id(Test1)=2595967872712,id(cls123.n)=1563129600,id(Test1.n)=1563129600
fun2,cls123.n=74,Test1.n=74

'''


七、 实例方法分析
1、 实例方法与java动态方法基本相同
2、 所有实例方法的第1个参数是实例名称
实例名称缺省为self
也可以是其他任何名称self123等

3、 实例成员访问只能通过实例对象访问
self.xxx,不可以直接xxx
self.funxxx(),不可以直接funxxx()

4、 实例方法中实例变量作用域与位置
实例变量可以存在于任何地方,包括
1) 类属性
2) 构造方法init
3) 任何实例方法中
4) 任何控制语句代码块中
实例方法1定义的实例变量,也可以在方法2中使用
5、 类属性有二义性
通过实例对象访问类属性是实例变量
如果作为实例变量,则初始值为静态变量的当前值
通过类名称访问类属性是静态变量

6、 实例方法中无局部变量
不允许在实例方法中使用局部变量
7、 实例方法地址变化
某一个实例的实例方法,在某一个运行时刻点,多次运行,是同一个地址
当再次选中代码运行,同一个实例的实例方法地址发了改变
这一点上,与类方法相同
8、 示例如下


'''
实例方法测试
self变量可以是任何名称,只是一个占位参数,写在所有实例方法中的第1个参数位置,包括__init__构造方法
实例方法调用所有变量,或其他实例方法时,一定要加前缀self.xxx,例如self.n2=xxx,self.fun11()
实例方法中没有局部变量,只能是全局变量self.xxx,可以定义在任何方法中,只
实例方法的内部,或外部相互调用时,省略参数self,例如
#下面测试类属性可以写在构造方法中,也可以写在任何实例方法中

'''    
t1=Test1(1)
#t1.fun11() #如如果不先运行fun12,就运行fun11,则fun11找到不self.n3、self.n5
t1.fun12()
t1.fun11()
t1.fun11()
t1.fun11()
'''
上面运行输出如下,说明实例变量可以写在任何地方,可以将类属性当成动态实例变量
实-fun1,id(self)=2595987016896,id(self.fun11)=2595956193928,id(self.n)=1563127584
self.n=11,self.n2=1,self.n3=3,self.n5=5
实-fun1,id(self)=2595987016896,id(self.fun11)=2595956193928,id(self.n)=1563127616
self.n=12,self.n2=1,self.n3=3,self.n5=5
实-fun1,id(self)=2595987016896,id(self.fun11)=2595956193928,id(self.n)=1563127648
self.n=13,self.n2=1,self.n3=3,self.n5=5
实-fun1,id(self)=2595987016896,id(self.fun11)=2595956193928,id(self.n)=1563127680
self.n=14,self.n2=1,self.n3=3,self.n5=5


'''
t1.fun12()
t1.fun11()
t1.fun11()
t1.fun11()
'''
再次选中上面代4行代码运行,实例的地址无变化,实例的方法地址改变了
实-fun1,id(self)=2595987016896,id(self.fun11)=2595959165192,id(self.n)=1563127712
self.n=15,self.n2=1,self.n3=3,self.n5=5
实-fun1,id(self)=2595987016896,id(self.fun11)=2595959165192,id(self.n)=1563127744
self.n=16,self.n2=1,self.n3=3,self.n5=5
实-fun1,id(self)=2595987016896,id(self.fun11)=2595959165192,id(self.n)=1563127776
self.n=17,self.n2=1,self.n3=3,self.n5=5
实-fun1,id(self)=2595987016896,id(self.fun11)=2595959165192,id(self.n)=1563127808
self.n=18,self.n2=1,self.n3=3,self.n5=5


'''

#类属性的二义性分析
t1=Test1(2)
t1.fun13()
t1.fun13()
t1.fun13()
'''
输出如下
1--fun13,id(self.n)=1563127552,id(Test1.n)=1563127552
2--fun13,self.n=13,Test1.n=15
1--fun13,id(self.n)=1563127648,id(Test1.n)=1563127712
2--fun13,self.n=16,Test1.n=20
1--fun13,id(self.n)=1563127744,id(Test1.n)=1563127872
2--fun13,self.n=19,Test1.n=25

'''


t2=Test1(2)
t2.fun13()
t2.fun13()
t2.fun13()
t2.fun13()
'''
输出如下
1--fun13,id(self.n)=1563128032,id(Test1.n)=1563128032
2--fun13,self.n=28,Test1.n=30
1--fun13,id(self.n)=1563128128,id(Test1.n)=1563128192
2--fun13,self.n=31,Test1.n=35
1--fun13,id(self.n)=1563128224,id(Test1.n)=1563128352
2--fun13,self.n=34,Test1.n=40
1--fun13,id(self.n)=1563128320,id(Test1.n)=1563128512
2--fun13,self.n=37,Test1.n=45



'''

八、 静态方法分析
1、 与java的静态方法相同
2、 方法上面@staticmethod
3、 静态方法属于类对象
即通过类名称直接访问静态方法
也可以通过任何实例对象访问静态方法,依然属性类对象
4、 静态方法可以使用局部变量
5、 静态方法通过类名称访问类属性
例如Test1.n
6、 静态方法的地址
无论第几次选中代码运行,静态方法的地址保持不变
这一点是与类方法最大的区别,同一个类对象,每一次选中运行,类方法的运行地址都不相同。
7、 示例如下


#测试静态方法
Test1.fun3()
Test1.fun3()
Test1.fun3()
t1=Test1(1)
t2=Test1(2)
t3=Test1(3)
t1.fun3()
t2.fun3()
t3.fun3()
'''
输出如下
fun3,id(Test1.fun3)=2595987227648,id(Test1.n)=1563127616,id(n1)=1563127296
n1=2,Test1.n=12
fun3,id(Test1.fun3)=2595987227648,id(Test1.n)=1563127680,id(n1)=1563127296
n1=2,Test1.n=14
fun3,id(Test1.fun3)=2595987227648,id(Test1.n)=1563127744,id(n1)=1563127296
n1=2,Test1.n=16
fun3,id(Test1.fun3)=2595987227648,id(Test1.n)=1563127808,id(n1)=1563127296
n1=2,Test1.n=18
fun3,id(Test1.fun3)=2595987227648,id(Test1.n)=1563127872,id(n1)=1563127296
n1=2,Test1.n=20
fun3,id(Test1.fun3)=2595987227648,id(Test1.n)=1563127936,id(n1)=1563127296
n1=2,Test1.n=22


'''


九、 实例方法与类方法的相互调用
1、 实例方法可以类方法
此时将类方法的调用对象依然是类对象,不是实例对象,可以将类方法设想为静态方法
fun11中,self.fun2()
这一点上,与java相同
2、 类方法调用实例方法
此时的实例方法,不再是动态实例方法,而是转换为类方法了
fun22中,cls123.fun12(cls123)
这一点上,与java不相同
3、 示例分析如下



#类方法与实例方法交叉访问

class Test12():
      n=10
      def __init__(self,n2):
            self.n2=n2
            #实例方法调用类方法
      def fun11(self):
            self.n+=1
            print('1-fun1,id(self)=%d,id(self.fun11)=%d,id(self.n)=%d'%(id(self),id(self.fun11),id(self.n)))
            print('2-fun1,self.n=%d,self.n2=%d'%(self.n,self.n2))
            self.fun2()
            #类方法调用实例方法
      def fun12(self): 
            self.n3=3
            self.n+=1
            print('1-fun12,id(self)=%d,id(self.fun12)=%d,id(self.n)=%d,id(self.n3)=%d'%(id(self),id(self.fun12),id(self.n),id(self.n3)))
            print('2-fun12,self.n=%d,self.n3=%d'%(self.n,self.n3))


     #让实例方法调用类方法,fun1 call fun2
      @classmethod
      def fun2(cls123):
            cls123.n+=2
            print('1-fun2,id(cls123)=%d,id(cls123.fun2)=%d,id(cls123.n)=%d'%(id(cls123),id(cls123.fun2),id(cls123.n)))
            print('1-fun2,cls123.n=%d'%(cls123.n)) #cls123.n与Test1.n是同一个变量

     #类方法调用实例方法
      @classmethod
      def fun22(cls123):
            cls123.n+=2
            print('1-fun22,id(cls123)=%d,id(cls123.fun2)=%d,id(cls123.n)=%d'%(id(cls123),id(cls123.fun2),id(cls123.n)))
            cls123.fun12(cls123)
            print('1-fun22,cls123.n=%d'%(cls123.n))



#实例方法调用类方法
i1=id(Test12)
t1=Test12(2)
print('id(Test12)=%d,id(t1)=%d'%(id(Test12),id(t1)))
i2=id(t1)
t1.fun11()  #实例方法fun11调用fun2类方法
'''
输出如下
id(Test12)=2595967902920,id(t1)=2595986963536
1-fun1,id(self)=2595986963536,id(self.fun11)=2595956277704,id(self.n)=1563127584
2-fun1,self.n=11,self.n2=2
1-fun2,id(cls123)=2595967902920,id(cls123.fun2)=2595956277704,id(cls123.n)=1563127616
1-fun2,cls123.n=12

'''

#类方法调用实例方法
Test12.fun22()
'''
输出如下
1-fun22,id(cls123)=2595967902920,id(cls123.fun2)=2595985336776,id(cls123.n)=1563127680
1-fun12,id(self)=2595967902920,id(self.fun12)=2595987050976,id(self.n)=1563127712,id(self.n3)=1563127328
2-fun12,self.n=15,self.n3=3
1-fun22,cls123.n=15
'''

十、 继承中的三类方法
1、 继承时的实例方法
子类继承父类所有实例方法
2、 继承时的类方法
子类继承父类所有类方法
3、 继承时的静态方法
子类继承父类的所有静态方法
这一点上不同于java
4、 示例如下



class Test2(Test1):
      @classmethod
      def fun4(cs):
            print('fun4,id(cs)=%d'%(id(cs)))
            #fun22(cs)  #error
            cs.fun22()

i2=id(Test2)         
t1=Test2(2)
#子类可以继承实例方法
t1.fun12()
t1.fun11()
t1.fun11()
'''
输出如下
实-fun1,id(self)=1788094466928,id(self.fun11)=1788064812232,id(self.n)=1569025824
self.n=11,self.n2=2,self.n3=3,self.n5=5
实-fun1,id(self)=1788094466928,id(self.fun11)=1788064812232,id(self.n)=1569025856
self.n=12,self.n2=2,self.n3=3,self.n5=5
实-fun1,id(self)=1788094466928,id(self.fun11)=1788064812232,id(self.n)=1569025888
self.n=13,self.n2=2,self.n3=3,self.n5=5
'''
#子类可以继承类方法
print('id(Test2)=%d,id(t1)=%d'%(id(Test2),id(t1)))
t1.fun22()
t1.fun4()
Test2.fun4()
'''
输出如下
id(Test2)=1788044155384,id(t1)=1788094466928
1--self.n=12,n5=2
2--self.n=12,n5=2
fun23...
self.n=12,self.n2=3
self.n=12,self.n2=3
fun4,id(cs)=1788044155384
1--self.n=14,n5=2
2--self.n=14,n5=2
fun23...
self.n=14,self.n2=3
self.n=14,self.n2=3
fun4,id(cs)=1788044155384
1--self.n=16,n5=2
2--self.n=16,n5=2
fun23...
self.n=16,self.n2=3
self.n=16,self.n2=3

'''
#子类可以继承静态方法
t1.fun3()     
Test2.fun3() 
'''
输出如下
fun3,id(Test1.fun3)=1788094436760,id(Test1.n)=1569025856,id(n1)=1569025536
n1=2,Test1.n=12
fun3,id(Test1.fun3)=1788094436760,id(Test1.n)=1569025920,id(n1)=1569025536
n1=2,Test1.n=14

'''

created by 刘明

www.isscollege.com

你可能感兴趣的:(python,语言特点)