Python类的进阶 Python面向对象编程之类的进阶
一碗周 人气:01、引用的概念
引用 (Reference
)是对象的指针
引用是内存中真实对象的指针,表示为变量名或者内存地址
每个对象存在至少一个引用,id()
函数用于获得引用
在传递参数和赋值时,Python
传递对象的引用,而不是复制对象
示例代码:
list1 = [1, 2, 3, 4] list2 = list1 print(id(list1)) # 2044656837192 print(id(list2)) # 2044656837192 # 因为list1是类的实例化,list2引用的是list1,两个都是引用的最基础的object类,所以两个的结果是一样的
Python
内部机制对引用的处理
- 不可变对象:
immutable
解释器为相同值维护尽量少的内存区域 - 可变对象:
mutable
解释器为每个对象维护不同内存区域
示例代码:
text1 = "一碗周" text2 = text1 text3 = "一碗周" text4 = "一碗" text5 = "周" text6 = text4 + text5 print(id(text1)) # 1616972638288 print(id(text2)) # 1616972638288 print(id(text3)) # 1616972638288 print(id(text4)) # 1616973621272 print(id(text5)) # 1616973578032 print(id(text6)) # 1616974246288
因为text1
和2是引用的一个字符串,所以内存地址是一样的;因为Python
解释器会大可能的节省内存空间,所以当不可变类型的值一样时,Python会将其自动的引用一个地址空间,来达到节省空间的目的,所以text1/2/3
的地址空间是一致的;Python解释器并不会对计算出来的结果来进行地址空间的优化,就算两个的值是一样的,Python
解释器也会为新计算出来的结果来开辟一个新的地址空间
示例代码:
list1 = [] list2 = [] list3 = [] print(id(list1)) # 3204114440776 print(id(list2)) # 3204114440840 print(id(list3)) # 3204115873544
每个可变对象都有自己独立的地址空间,并不复用地址空间
导致引用被+1的情况一般都4种
- 对象被创建
- 对象被引用
- 对象被作为函数或方法的参数
- 对象被作为一个容器中的元素
导致引用-1的情况一把也都4种
- 对象被删除
- 对象的名字呗赋予新的对象
- 对象离开作用域
- 对象所在容器被删除
2、对象的拷贝
拷贝是复制一个对象为新对象,内存空间有”变化“,拷贝分为浅拷贝和深拷贝
- 浅拷贝:仅仅复制最顶层对象的拷贝方式,默认拷贝方式
- 深拷贝:迭代复制所有对象的拷贝方式
示例代码(浅拷贝1)
list1 = ["甜甜", [1, 2, 3]] list2 = list1.copy() # 使用copy方法复制 list3 = list1[:] # 使用切片复制 list4 = list(list1) # 使用生成列表方式复制 for ch in [list1, list2, list3, list4]: for i in ch: print(i, id(i), "\t", end="") # 打印列表的没一项和id print(ch, id(ch)) # 打印每个列表和id ''' ---输出结果--- 一碗周 2905817180184 [1, 2, 3] 2905787490888 ['一碗周', [1, 2, 3]] 2905787490952 一碗周 2905817180184 [1, 2, 3] 2905787490888 ['一碗周', [1, 2, 3]] 2905817092488 一碗周 2905817180184 [1, 2, 3] 2905787490888 ['一碗周', [1, 2, 3]] 2905817137800 一碗周 2905817180184 [1, 2, 3] 2905787490888 ['一碗周', [1, 2, 3]] 2905817771656 '''
浅拷贝只是拷贝的列表这一层的内存空间,里面的元素的内存空间不会被拷贝
示例代码(浅拷贝2)
list1 = ["一碗周", [1, 2, 3]] list2 = list1.copy() # 使用copy方法复制 list3 = list1[:] # 使用切片复制 list4 = list(list1) # 使用生成列表方式复制 list4[1].append(4) print(list1) print(list2) print(list3) print(list4) ''' --输出结果-- ['一碗周', [1, 2, 3, 4]] ['一碗周', [1, 2, 3, 4]] ['一碗周', [1, 2, 3, 4]] ['一碗周', [1, 2, 3, 4]] '''
这里只对list4
进行来数据的修改,但是所有的列表的内容都发生了;这是因为每个列表所引用的内容是一样的,所以修改了1个四个会发生改变
深拷贝要采用copy
库里面的deepcopy()
方法,迭代拷贝对象内层的各层次对象,完全新开辟内存空间建立对象以及对象下层的各种对象元素,深拷贝仅仅针对可变类别,不可变类型不许创建新对象
示例代码
import copy # 导入库 list1 = ["一碗周", [1, 2, 3]] list2 = copy.deepcopy(list1) # 使用copy库的deepcopy方法复制 for ch in [list1, list2]: for i in ch: print(i, id(i), "\t", end="") # 打印列表的没一项和id print(ch, id(ch)) # 打印每个列表和id ''' ---输出结果--- 一碗周 2190823984184 [1, 2, 3] 2190853845832 ['一碗周', [1, 2, 3]] 2190853766728 一碗周 2190823984184 [1, 2, 3] 2190853961544 ['一碗周', [1, 2, 3]] 2190853961480 '''
因为“甜甜”字符串属于不可变类型,所以其地址空间不会发生改变,剩下的地址空间都发生了改变
2.1 实例方法的引用
实例方法也是一种引用,就是对象本身的引用,当方法被引用时,方法(即函数)将产生一个对象:方法对象
2.2 类的特性装饰器
@property
装饰器可以把方法改变成对外可见的”属性“,在类内部表现为方法,在外边表现为属性
示例代码
class TestClass: def __init__(self, name): self.name = name @property # 将方法转换为属性 def age(self): return self.__age @age.setter # 为属性进行赋值操作 def age(self, value): if value < 0 or value > 110: value = 19 self.__age = value tt = TestClass("一碗周") bb = TestClass("一碗粥") tt.age = 18 bb.age = -19 print(tt.age) # 18 print(bb.age) # 19
3、类的名称修饰
名称修饰 (Name Mangling
)是类中名称的转换约定,Python
可以通过名称修饰来完成一些重要功能,在Python
中采用下划线_来进行名称修饰,分为5种情况,
_name
name_
__name
__name__
_
3.1 _单下划线开头的名称修饰
- 单下划线开头属性或者方法为类内部使用的约定,是
PEP8
规定的一种约定 - 只是约定,依然可以通过<对象名>.<属性名>方式访问
- 在功能的上的不同是使用
from XX import
*时不会导入单下划线开头的属性或者方法
示例代码
class TestClass: def __init__(self, name): self._name = name # 约定在内部使用 tt = TestClass("一碗周") print(tt._name) # 一碗周
虽然约定在内部使用,但是依然可以被访问
3.2 _单下划线结尾的名称修饰
单下划线结尾的属性或者方法是避免与保留字或已有命名冲突,这也是PEP8
规定的,这仅仅是一个约定,没有任何对应的功能
3.3 __双下划线开头的名称修饰
双下划线开头属性或者方法将被解释器修改名称,避免命名冲突,这不是一个约定,而是功能性的, _nama
会被修改为_<类名>__name
的形式,来实现私有属性、私有方法;这是一种类的名称修饰,间接的来当做私有属性或者私有方法
3.4 __name__双下划线开头和结尾的名称修饰
双下划线开头和结尾的属性或方法没有任何特殊功能,名字不能被修改,部分名称是保留属性或者保留方法
3.5 单下划线
单下划线仅仅是一个无关紧要的名字吗,没有特殊功能
4、Python的最小空类
作用:
类是一个命名空间,最小空类可以当做命名空间使用
- 最小空类可以辅助存储和使用
- 动态增加属性是
Python
类的一个特点
示例代码:
class TestClass: pass a = TestClass a.text = "一碗周" print(a.text) # 一碗周 # 可以动态增加属性来达到存储信息的目的
加载全部内容