日期:2020年1月25日
作者:Commas
注释:学习就是为了忘记,对Python3类命名空间与作用域来一个小结。
如果您想了解更多有关Python的知识,那么请点《我的Python浅谈系列目录》
在正式讲解本章主题的之前,我们先要对类和类对象进行一个更进一步的了解,首先我们探讨一下类的本质。
一、探索类的本质
程序在解释类的时候,实际上会开辟一块内存空间,将类的相关信息保存到其中,我们不妨先定义一个Chinese类,如下所示:
class Chinese:
"""中国人的类"""
language = "Mandarin"
def __init__(self, name, sex="male"):
self.name = name
self.sex = sex
def say_hello(self):
"""中国式打招呼"""
print("{}:吃了饭没呀?".format(self.name))
接下来我们用id()
查询类的十进制内存地址,如果有,那么就说明程序解释到类的时候,会开辟一块内存空间,如下所示:
print("{}的十进制内存地址为:{}".format(Chinese, id(Chinese)))
print("{}的十六进制内存地址为:{}".format(Chinese, hex(id(Chinese))))
# 控制台输出:
<class '__main__.Chinese'>的十进制内存地址为:2980050429240
<class '__main__.Chinese'>的十六进制内存地址为:0x2b5d8d8e538
既然有开辟内存地址,那么我们看看内存空间中,到底保存了点什么信息,
我们先来用用常用的一个内置函数dir()
查看一下,如下所示:
print(dir(Chinese))
# 控制台输出:
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'language', 'say_hello']
在茫茫词海中,我看到了一个__ dict __
,和字典的dictionary有点像,那么这里面储存了什么信息,如下所示:
print(type(Chinese.__dict__))
# 控制台输出:
{
'__module__': '__main__',
'__doc__': '中国人的类',
'language': 'Mandarin',
'__init__': < function Chinese.__init__ at 0x0000023A1B1198C8 > ,
'say_hello': < function Chinese.say_hello at 0x0000023A1B119950 > ,
'__dict__': < attribute '__dict__' of 'Chinese' objects > ,
'__weakref__': < attribute '__weakref__' of 'Chinese' objects >
}
控制台输出看起来很眼熟啊,这不就是我刚刚定义的Chinese类么?分析如下表:
属性或方法 | 类型 | 说明 |
---|---|---|
__ doc __ | 内置属性 | Chinese类的说明介绍,中国人的类。 |
__ init __ | 内置方法 | 类实例化对象的时候,自动调用的第一个方法,动态属性在其方法中定义。 |
language | 用户定义的静态属性 | Chinese类中静态属性language的值为Mandarin(普通话) |
say_hello | 用户定义的方法 | 用户定义的公有方法,中国式打招呼的方法。 |
看起来很像字典类型(可变类型),实际上是不可变映射类型的“字典”,类型为“mappingproxy”,只允许访问,不允许修改,如下所示:
print(type(Chinese.__dict__))
print(Chinese.__dict__.get("language")) # 访问类的静态属性language的值
print(Chinese.__dict__["language"]) # 访问类的静态属性language的值
Chinese.__dict__["language"] = "Cantonese" # 尝试修改类的静态language值,将“普通话”改为“广东话”,但是不可以,并抛出TypeError异常。
# 控制台输出:
<class 'mappingproxy'>
Mandarin
Mandarin
Traceback (most recent call last):
File "D:/test.py", line 4, in <module>
Chinese.__dict__["language"] = "Cantonese"
TypeError: 'mappingproxy' object does not support item assignment
二、探索类对象(实例)的本质
当类对象被实例化出来的时候,就会在内存空间开辟一个空间,我们用内置函数dir()查看一下对象里面有些什么,如下:
# ①实例化
xu = Chinese("老许")
# ②对象的内存地址
print(xu)
print("{}的十六进制内存地址为:{}".format(xu, hex(id(Chinese))))
# ③用内置函数dir()查询对象
print(dir(xu))
# 控制台输出:
<__main__.Chinese object at 0x000002246ED324A8>
<__main__.Chinese object at 0x000002246ED324A8>的十六进制内存地址为:0x2246cff4bf8
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'language', 'name', 'say_hello', 'sex']
实例xu也有属性__ dict __
,那么我们看一下这个值的类型以及值,如下所示:
print(type(xu.__dict__))
print(xu.__dict__)
# 控制台输出;
<class 'dict'>
{'name': '老许', 'sex': 'male'}
xu对象和Chinese类的__ dict __不一样,如下表所示:
xu对象 | Chinese类 | |
---|---|---|
__ dict __ 类型 | 可变数据类型的字典 | 不可变映射类型的“字典” |
__ dict __ | 仅对象属性(动态属性) | 类的静态属性以及方法 |
三、类与类对象的关系浅谈
如上图所示,类与类对象之间的几个知识点如下:
- 对象只保存动态属性(对象的属性),并不储存类中的静态属性和方法;
- 对象存在一种指向类的关系,共享类中的资源(静态属性和方法),同时可以节省内存;
- 类不存在指向对象的关系,所以在类中,无法访问被自己实例化的对象;
- 对象命名空间遵循
“就近原则”
,即在对象访问变量时,若动态变量有,则访问本对象中的动态变量,否则访问实例化该对象的类的静态属性,若两者皆无,则抛出异常;
版权声明:本文为博主原创文章,如需转载,请给出:
原文链接:https://blog.csdn.net/qq_35844043/article/details/104083837