Python学习笔记(十四):类特殊成员
类的特殊属性和特殊方法
- Python学习笔记(十四):类特殊成员
- 一.类特殊成员
- 二.__new__()
- 三.__repr__()
- 四.__del__()
- Python的垃圾回收机制
- 五.__dir__()
- 六.__dict__属性
一.类特殊成员
以双下划线 "__" 开头和结尾命名的成员(属性和方法),都被称为类的特殊成员(特殊属性和特殊方法)
Python类中的特殊成员,不能在类的外部直接调用,可以用类中的普通方法调用和修改,可以对类的特殊方法进行重写,实现特殊的功能
二.new()
__new__() 是负责创建类实例的静态方法
无需使用 staticmethod 装饰器修饰,优先 __init__() 初始化方法被调用
覆写__new__()将会使用合适的参数调用其父类的super().__new__(),并在返回之前修改实例
__new__(*args, **kwargs)
Create and return a new object. See help(type) for accurate signature.
class demoClass:
instances_created = 0
def __new__(cls,*args,**kwargs):
print("__new__():",cls,args,kwargs)
instance = super().__new__(cls)
instance.number = cls.instances_created
cls.instances_created += 1
return instance
def __init__(self,attribute):
print("__init__():",self,attribute)
self.attribute = attribute
test1 = demoClass("abc")
test2 = demoClass("xyz")
print(test1.number,test1.instances_created)
print(test2.number,test2.instances_created)
__new__(): <class '__main__.demoClass'> ('abc',) {}
__init__(): <__main__.demoClass object at 0x02519690> abc
__new__(): <class '__main__.demoClass'> ('xyz',) {}
__init__(): <__main__.demoClass object at 0x025196F0> xyz
0 2
1 2
__new__()通常会返回该类的一个实例,有时也可能会返回其他类的实例,如果发生了这种情况,会跳过对__init__()方法的调用
class nonZero(int):
def __new__(cls,value):
return super().__new__(cls,value) if value != 0 else None
def __init__(self,skipped_value):
print("__init__()") #__new__方法返回None会跳过此方法
super().__init__()
print(type(nonZero(-10)))
print(type(nonZero(0)))
__init__()
<class '__main__.nonZero'>
<class 'NoneType'>
如果__new__方法返回None,则__init__方法不会被执行,并且返回值只能调用父类中的__new__方法
__new__方法的第一个参数是这个类,其余的参数在调用成功后全部传递给__init__方法初始化
class A:
pass
class B(A):
def __new__(cls):
print("__new__方法被执行")
return super().__new__(cls)
def __init__(self):
print("__init__方法被执行")
b = B()
__new__方法是传入类(cls),__init__方法传入类的实例化对象(self)
__new__方法返回的值是一个实例化对象,如果__new__方法返回None,则__init__方法不会被执行,并且返回值只能调用父类中的__new__方法,而不能调用毫无关系的类的__new__方法
在__init__() 不够用的时候使用 __new__()
__new__()不限于返回同一个类的实例,很容易被滥用,需谨慎使用
Python中大量使用 __new__() 方法且合理的,是 MetaClass 元类
关于MetaClass 元类见之前笔记
Python学习笔记(十二):类和对象
三.repr()
直接输出类的实例化对象
class test:
pass
TEST = test()
print(TEST)
<__main__.test object at 0x02919670>
默认情况下,得到的信息只有“类名+object at+内存地址”,对了解该实例化对象帮助不大
当输出某个实例化对象时,调用的就是该对象的__repr__()方法,输出的是该方法的返回值
所以print(TEST) 等同于print(TEST.__repr__()),输出的内存地址可能不同
Python中的每个类都包含__repr__() 方法
因为object 类包含 __reper__() 方法,而Python中所有的类都直接或间接继承自 object 类
默认情况下,__repr__() 会返回和调用者有关的 “类名+object at+内存地址”信息,可以通过在类中重写这个方法,实现输出想要的信息
class test:
def __init__(self):
self.name = "youchanwill"
self.add = "you.com"
def __repr__(self):
return "test[name="+ self.name +",add=" + self.add +"]"
TEST = test()
print(TEST)
test[name=youchanwill,add=you.com]
__repr__() 方法默认情况下会返回当前对象的“类名+object at+内存地址”,如果对该方法进行重写,可以为其制作自定义的描述信息
四.del()
__del__() 方法用来销毁实例化对象
之前创建的类实例化对象后续不再使用,最好在适当位置手动将其销毁,释放其占用的内存空间(垃圾回收,简称GC)
大多数情况下,不需要手动进行垃圾回收,Python有自动的垃圾回收机制,能自动将不需要使用的实例对象进行销毁
手动和自动都会调用 __del__() 方法
class test:
def __init__(self):
print("调用__init__() 方法构造对象")
def __del__(self):
print("调用__del__() 销毁对象,释放其空间")
TEST = test()
del TEST
调用__init__() 方法构造对象
调用__del__() 销毁对象,释放其空间
Python的垃圾回收机制
Python采用自动引用计数(ARC)的方式实现垃圾回收机制
1.每个Python对象都会配置一个计数器,初始实例对象的计数器值都为 0,如果有变量引用该实例对象,其计数器的值会加 1,依次类推
2.每当一个变量取消对该实例对象的引用,计数器会减 1
3.如果一个对象的的计数器值为 0,表明没有变量引用该对象,说明程序不再需要,自动调用 __del__() 方法将其回收
class test:
def __init__(self):
print("调用__init__() 方法构造对象")
def __del__(self):
print("调用__del__() 销毁对象,释放其空间")
TEST = test()
test1 = TEST #添加一个引用TEST对象的实例对象
del TEST
print("****")
调用__init__() 方法构造对象
****
1.构建TEST实例对象时先用test()调用该类中的 __init__()方法构造出一个该类的对象(计数器为 0)
2.用TEST这个变量作为所建实例对象的引用(计数器值+1)
3.又有一个test1变量引用TEST(相当于引用 CLanguage()),此时计数器再+1
4.调用del TEST语句,只会导致计数器减 1 ,因为计数器值不为 0,因此 不会执行 __del__() 方法
class test:
def __init__(self):
print("调用__init__() 方法构造对象")
def __del__(self):
print("调用__del__() 销毁对象,释放其空间")
TEST = test()
test1 = TEST #添加一个引用TEST对象的实例对象
del TEST
print("****")
del test1
print("----")
调用__init__() 方法构造对象
****
调用__del__() 销毁对象,释放其空间
----
执行 del test1 语句时,计数器变为 0,对于计数器为 0 的实例对象,自动将其视为垃圾进行回收
如果重写子类的 __del__() 方法(父类为非 object 的类),必须显式调用父类的 __del__() 方法,这样才能保证回收子类对象时,其占用的资源能被彻底释放
class test:
def __del__(self):
print("调用父类__del__() 方法")
class test1(test):
def __del__(self):
print("调用子类__del__() 方法")
test2 = test1()
del test2
调用子类__del__() 方法
调用父类的 __del__()方法,保证回收子类对象时,其占用的资源能被彻底释放
test3 = test()
del test3
调用子类__del__() 方法
调用父类__del__() 方法
五.dir()
dir() 函数,返回一个包含有所有属性名和方法名的有序列表
通过 dir() 函数,不仅仅输出本类中新添加的属性名和方法,还会输出从父类继承得到的属性名和方法名
dir() 函数的内部实现,是在调用参数对象 __dir__() 方法的基础上,对该方法返回的属性名和方法名做了排序
class test:
def __init__ (self,):
self.name = "youchanwill"
self.add = "you.com"
def say():
pass
TEST = test()
print(dir(TEST))
class test:
def __init__ (self,):
self.name = "youchanwill"
self.add = "you.com"
def say():
pass
TEST = test()
print(TEST.__dir__())
使用 __dir__() 方法和 dir() 函数输出的数据相同,顺序不同
六.__dict__属性
在Python类的内部,类属性还是实例属性,都以字典的形式进行存储,其中属性名作为key,值作为该key对应的value
__dict__ 属性可以用类名或者类的实例对象来调用
用类名直接调用 __dict__,会输出该由类中所有类属性组成的字典
用类的实例对象调用 __dict__,会输出由类中所有实例属性组成的字典
class test:
a = 10
b = 16
def __init__ (self,):
self.name = "youchanwill"
self.add = "you.com"
print(test.__dict__) #通过类名调用__dict__
{'a': 10, '__dict__': <attribute '__dict__' of 'test' objects>, '__module__': '__main__', '__init__': <function test.__init__ at 0x02A97270>, '__weakref__': <attribute '__weakref__' of 'test' objects>, 'b': 16, '__doc__': None}
TEST = test() #通过类的实例对象调用 __dict__
print(TEST.__dict__)
{'name': 'youchanwill', 'add': 'you.com'}
具有继承关系的父类和子类,父类有自己的 __dict__,子类也有自己的 __dict__,不会包含父类的 __dict__
由类的实例对象调用 __dict__ 属性获取的字典,可以使用字典的方式对其中实例属性的值进行修改
class test:
a = 10
b = 16
def __init__ (self):
self.name = "youchanwill"
self.add = "you.com"
TEST = test()
print(TEST.__dict__) #通过类实例对象调用 __dict__
TEST.__dict__['name'] = "chanwill"
print(TEST.name)
print(TEST.__dict__)
{'name': 'youchanwill', 'add': 'you.com'}
chanwill
{'name': 'chanwill', 'add': 'you.com'}
无法通过类似的方式修改类变量的值