简单总结
python3统一了类与类型的概念. 类即类型.
obj.属性 -- 即属性引用
★ 类的两大操作
1. "属性引用"
类的定义里的属性 = 数据属性 + 函数属性
1> 在类的定义阶段("注意!不是类实例化的时候哦")就会执行类里面的代码,开辟一个namescope
2> namescope(类)中的属性是共享给所有的类实例化对象的 【共有属性】
2. "类实例化"
调用类对象/类的实例化 会开辟一个个空的namescope.
类实例化对象开辟一个空的scope后,紧接着就会触发绑定方法__init__的执行,初始化实例化对象
会将类的实例化obj作为绑定方法的第一个形参(约定俗称是self)的值传入
注意: __init__里面不可有return
★ 类的实例化对象可进行"属性引用"!!
1. 引用数据属性
1> 类中的数据属性
2> 独有的(优先级最高) namescope(类实例化)里面存放的就是实例化对象的【独有属性】
2. 引用方法属性
即类中的函数属性 "方法是‘从属于’对象的函数"
Ps:方法不是类实例所特有的,比如列表对象也具有append、insert等方法 【类就是类型】
d = {'x': 1} 本质上是 d = dict({'x':1})
namescope(类)、namescope(实例化对象)都可通过 "obj.__dict__"查看namescope里面的属性
代码块(模块、函数、类)执行代码时,以及类的实例化的时候,会开辟namespace.
特别注意的是,函数里函数体的代码在调用时执行!!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
from types import MethodType, FunctionType
a = 5
def f():
pass
# -- 注意哈!!有个空字典的结果返回,并不意味着开辟了一个空的命名空间,函数在调用的时候才会开辟命名空间
print(f.__dict__) # {}
# -- 类A在定义阶段就会执行里面的代码,开辟命名空间.(不是在实例化的时候开辟哦!!)
class A:
a = 3
# -- 此处namespace(类A)里已经对a名称进行了绑定 {a:3}
print(a) # 3
# -- 类A还在定义的过程中,全局命名空间里还没有A
# -- 准确点说,这里涉及到【类中变量引用的规则】,官方解释:
# 1> 未绑定的局部变量将在全局命名空间中查找
# 2> 在类代码块中定义的名称的作用域/namespace作用范围,不会扩展到方法的代码块中
# 包括列表推导式和生成器表达式 Ps:可以通过 obj.属性 -- 即属性引用的方式来进行访问
# print(A.a) # NameError:name 'A' is not defined
def __init__(self, name):
self.name = 'dc'
def fun1(self):
pass
@classmethod
def fun2(self):
pass
@staticmethod
def foo():
# -- 这里涉及到类中变量引用的规则
print(a) # 5
# -- 类的实例化会先开辟一个命名空间,然后通过__init__进行初始化存放该实例化对象独有的数据属性
x = A('dc')
print(x) # <__main__.A object at 0x7fbcebe8dca0>
print(A) # <class '__main__.A'>
"""
一般单独def的变量是函数对象.
被@classmethod装饰的函数属性,不管是类还是实例化对象进行属性引用,引用的都是PyMethodObject方法对象.
第一个参数是self的函数属性,被实例化对象进行属性引用,引用的是PyMethodObject方法对象.
除此之外,进行的属性引用(eg:@staticmethod),引用的都是PyFunctionObject函数对象.
函数定义: PyCodeObject + def == PyFunctionObject"函数对象"
类定义:
有"self"特征的PyFunctionObject 将与类实例化对象进行绑定 进化为 PyMethodObject"方法对象"
有"@classmethod"特征的PyFunctionObject 将与类对象进行绑定 进化为 PyMethodObject"方法对象"
PyMethodObject和PyFunctionObject在调用时进化成PyFrameObject,开辟一块命名空间,执行函数体代码.
"""
# -- 通过`from types import MethodType, FunctionType`来判断
# 类实例化对象引用的方法属性是函数对象还是方法对象 前者是<function ...> 后者是<bound method ...>
train_list = [
f, # <function f at 0x7fbcebe8a9d0>是函数对象!
A.fun1, # <function A.fun1 at 0x7fbcebe8aa60>是函数对象!
x.fun1, # <bound method A.fun1 of <__main__.A object at 0x7fbcebe8dca0>>是方法对象!
A.fun2, # <bound method A.fun2 of <class '__main__.A'>>是方法对象!
x.fun2, # <bound method A.fun2 of <class '__main__.A'>>是方法对象!
A.foo, # <function A.foo at 0x7fbcebe8aaf0>是函数对象!
x.foo, # <function A.foo at 0x7fbcebe8aaf0>是函数对象!
]
for item in train_list:
if isinstance(item, FunctionType):
print(f"{item}是函数对象!")
else:
print(f"{item}是方法对象!")
A.foo()
print(x.fun1.__dict__) # {}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
class A:
def f1(self):
pass
def f2(self):
# 不加self报错!
# 也许你会想,执行`m()` namescope(f2)里面没有的话,就去namescope(A)里面找???
# 注意哦,【类中变量引用的规则】
# m() # NameError:name 'm' is not defined
self.m()
@staticmethod # -- 静态方法,意味着不会自动绑定类实例化对象
def m():
pass
a = A()
b = A()
a.f1()
b.f1()
a.f2()
"""
分析如下:
"""
1> a.f1()的本质是 A.f1(a)
a.f1()、b.f1()进行方法的绑定,相当于A进行了两次函数调用,函数调用会开辟新的命名空间
所以这里a.f1()、b.f1()开辟的命名空间是不一样的.
2> a.f1 is b.f1 的结果为False
虽然a.f1和b.f1在这个过程中引用的都是namescope(类A)中同一个PyFunctionObject f1
但f1分别与a和b进行绑定后,进化出来的PyMethodObject是不同的
换个说法:
Pyhon是动态语言.对象和类的成员并不一定会一致
通过a.f1这样获得的对象,并不是真正的A.f1,对与不同的类实例来说也不是同一个对象.
这是因为a.f1返回的是一个将实例a绑定到了self上的新创建的一个临时的bound method.
(a.f1是生成了一个捆绑A.f1和a的临时对象)
当调用a.f1(*args)的时候其实是调用了A.f1(a, *args),
是一个把实例本身当作第一个参数也就是self传进去的封装了一层的函数.
3> a.m is b.m 的结果为True
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38