面向对象 类和对象的命名空间&&实例化过程

1. 通过示例解析实例化过程

我们可以通过以下示例来说明实例化过程中类命名空间和对象命名空间的开辟、函数的声明、指针的指向、self的赋值等等

class A:
	静态变量 = '值'
	def __init__(self,属性):
		self.属性 = 属性

	def show(self):
		print('展示一下所有属性')

a = A('参数') # 实例化对象a

上述代码,定义了一个A类,并实例化了一个对象a

那么它后台的代码执行逻辑以及内存空间的开辟过程是怎样的呢?
在这里插入图片描述
如上图,即从定义类A到实例化一个对象a到完整过程
该过程可看出,会分别开辟类到命名空间和对象到命名空间,且在对象命名空间中只存了实例变量

那么对象该如何调用类中定义到方法呢?
其实,在对象的命名空间里,还存在这一样东西,即对象指针(指针也是一个变量,只不过指针里直接存内存地址);而在a的对象命名空间里存的对象指针存有A类的内存地址,即上图绿框的内存地址
在这里插入图片描述
回过头来我们说a对象如何调用类中定义的show方法:
a是一个对象,当调用show这个方法时,先从自己的空间里找,若找不到则再通过指针到类的空间里找,此时便完成了show方法的调用
同理,对象a也可以直接通过a.静态变量的方法来获取静态变量的值

那么对象指针是怎么放到a对象命名空间里的呢?又是什么时候放进去的呢?
其实,在实例化开辟新的对象命名空间的时候,就先把这个对象指针写进去了,因为a是实例化的A类,所以这个对象指针直接指向了A类(的内存地址),接下来才会把对象空间传给self然后进行之后的步骤

为什么不把方法直接存到对象空间中呢?
因为一个类可能有很多方法,比如一个人可能有吃饭、喝水、拉屎、撒尿、睡觉…等很多方法,如果都存到各个对象中,会造成空间的浪费,因此最合理的方法就是把这些一样的方法统一存在类空间,而对象则通过指针来调用方法或静态变量

综上可知:
1)对象可以引用类的命名空间中的内容,包括方法和静态变量
2)类的命名空间中存有:
  a)静态变量
  b)方法
3)对象的命名空间中
  a)对象指针
  b)实例变量/对象的属性

2. 例子补充

例1

用对象修改局部变量,只要用到赋值,相当于在自己的空间中新建了内容

class Person:
	sum = 0
	def __init__(self):
		self.salary = 1000
		
# 一家人
father = Person()
mother = Person()

mother.age = 18
print(mother.age)	# 18

print(mother.sum)	# 0

mother.sum = 1000
print(mother.sum)	# 1000

print(Person.sum)	# 0

上述虽然定义了mother.sum=1000,但是Person.sum并没有发生变化
此处补充一个知识点:
用对象修改局部变量,只要用到赋值,相当于在自己的空间中新建了内容

那么如何将爸爸妈妈的工资加到一家人的总和(sum)中呢?

class Person:
	sum = 0
	def __init__(self):
		self.salary = 1000

father = Person()
mother = Person()

mother.age = 18

Person.sum += mother.salary

print(Person.sum)	# 1000

Person.sum += father.salary

print(Person.sum)	# 2000
print(mother.sum)	# 2000
print(father.sum)	# 2000

由上述代码执行结果可知,此时爸爸妈妈的工资分别加到了全家的总和中,都是2000元了

因此,再次强调:
用对象修改局部变量,只要用到赋值,相当于在自己的空间中新建了内容

例2

简单实现一个实例化的计数器,能够获取当前实例了多少对象

class Person:
	cnt = 0
	def __init__(self):
		Person.cnt+=1

tom = Person()
jerry = Person()
lilei = Person()

print(Person.cnt)	# 3

由上述代码可知,因为init方法中使用类变量直接进行自加,所以,最后类变量会变成3

例3

class A:
	num = 0
	def __init__(self):
		self.num += 1

a1 = A()
a2 = A()
a3 = A()

print(A.num)	# 0
print(a1.num,a2.num,a3.num)	# 1 1 1

由上述代码结果看出,通过self(即对象)去修改类变量,只是修改了他们自身的值,而未改变类变量本身的值;类变量仍为0,充分印证了:
用对象修改局部变量,只要用到赋值,相当于在自己的空间中新建了内容

例4

list 列表类型的 追加赋值修改列表元素 的对比

1)追加

class A:
	lst = []
	def __init__(self):
		self.lst.append(1)

a1 = A()
a2 = A()
a3 = A()

print(A.lst)	# [1, 1, 1]
print(a1.lst,a2.lst,a3.lst)	# [1, 1, 1] [1, 1, 1] [1, 1, 1]

由上述打印结果可知,A.lst发生了变化;
我们之前说过用对象修改局部变量,只要用到赋值,相当于在自己的空间中新建了内容,那么为什么A的lst列表发生了变化呢?
因为lst在A类空间中,我们实例化a1,a2,a3时,对象空间没有lst,那么继续网上找到类空间的lst,进行append操作;append操作并非赋值操作,只是在lst列表里追加了1,而非对lst列表的重新赋值,所以lst被改变

看下一个例子对比理解一下:

2)赋值

class A:
	lst = []
	def __init__(self):
		self.lst = [1]

a1 = A()
a2 = A()
a3 = A()

print(A.lst)	# []
print(a1.lst,a2.lst,a3.lst)	# [1] [1] [1]

由上述打印结果可知,当append改为赋值之后,便印证了用对象修改局部变量,只要用到赋值,相当于在自己的空间中新建了内容,并未修改类变量的值

3)修改列表中的元素

class A:
	lst = [0]
	def __init__(self):
		self.lst[0] += 1

a1 = A()
print(A.lst)	# [1]
a2 = A()
print(A.lst)	# [2]
a3 = A()

print(A.lst)	# [3]
print(a1.lst,a2.lst,a3.lst)	# [3] [3] [3]

上述代码,每次自增,都是对lst列表中第0个元素进行赋值,而不是对lst本身进行赋值,所以才会有最终的结果

总结/注:
1)用对象修改局部变量,只要用到赋值,相当于在自己的空间中新建了内容
2)在操作静态变量的时候:如果是查看,用类或者对象都可以;如果是修改,仅用类名进行修改
3)不要在对象的空间中创建一个和类变量同名的实例变量

扩展

到底什么是面向对象?
我们定义一个字符串、列表、字典类型的数据/对象时:

string = 'bacdefg'
lst = [1,2,3,4,5]
d = {'name':'tom'}

string.split('c')
lst.append('b')

其实,我们定义的这些具体数据,都是一个具体的对象,而他们的数据类型则是我们所说的
如上述lst变量,即我们实例化了一个列表的对象;这个对象也拥有列表类中定义的append方法

因此:
1)在python中,一切皆对象
2)内置的类:不需要我们定义就可以使用;如 int、bool、str、list、tuple、dict、set
3)lst = [1,2,3] 等于 lst = list([1,2,3]) ,前者是后者的简便写法

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值