目录
前言:
__init__方法
__new__方法(重点!)
1.__new__方法的调用过程
2.重写__new__方法
3.__new__方法不同返回值的情况
3.单例模式
前言:
上一期初步介绍了__new__()方法,但是实际上这个方法还有非常多的内容值得去讲一讲,学会了这个方法我们就可以去灵活把握控制我们对象的空间分配(上一期链接Python进阶-----面对对象6.0(绑定方法[类方法、静态方法]与内置方法)_Python欧尼酱的博客-CSDN博客),下面一期来看看吧!
__init__方法
这个方法对于大家应该是再熟悉不过了,是的,这是一个初始化方法也叫构造方法,当我们建立了一个新的对象之后,我们要对这个对象进行属性或者方法的初始化,这个方法是伴随着对象的建立而自动执行的。下面看一个示例:
class Person(object):
def __init__(self,name,age):
self.name=name
self.age=age #定义实例化对象的属性
print('person message')
dick=Person('Dick',18)
#输出结果:person message
print(dick.name,dick.age)
#输出结果:Dick 18
从这个例子就很容易看出,__init__方法是自动调用的,会自动输出person message,但是在此之前还有一个方法比__init__方法更加提前调用,那就是给对象分配空间__new__方法
__new__方法(重点!)
1.__new__方法的调用过程
在Python中所有类的父类都是object类,object类也叫做超类,当我们创建了一个实例对象的时候,最先会去分配这个对象的内存空间,然后才可以往里面放入数据,这时候就会通过__new__方法去创建这个空间,然后返回这个对象空间给__init__方法再进行初始化.,也就是说__new__方法会返回这个空间传给__init__方法中的self实例参数,所以__new__方法也是跟__init__方法一样的会自动调用。
__new__
方法是内置的静态类方法,python解释器在得到这个方法返回对象引用之后,将这个引用作为第一个参数传递给init方法所以new方法 比init方法执行要早
通俗的理解:
上面的说法可能会有点难理解,这里我做个比喻:在日常生活中,我们所用的木制家具是对木头进行加工处理而来的,__new__方法也就是一个伐木人,专门负责获取木材,然后把这些木材给__init__方法也就是木匠,木匠可以通过这些木材做出形形色色的家具供我们使用
可能这里就会有人问,我每次定义了一个类的时候没有去定义__new__方法,只是定义了__init__方法,但是内存空间是怎么来的呢?
answer:注意了,所有类都是继承了object类,所以当我们去给一个类写入内容的时候,实际上是对object类进行重写,__init__方法和__new__方法都是object类的方法,当我们去def __init__(self): 时,其实是对object里面的__init__方法进行重写,但是没有去def __new__(cls): 也就是说没有对__new__方法进行重写,那么我们所定义的这个类会保留并且调用这个方法,然后返回这个实例化对象的空间给__init__方法.
示例:
class A(object):
def __init__(self,name):
self.name=name
class B(object):
object.__new__(object) #当我们没有重写__new__方法,默认继承并且调用object类__new__方法
def __init__(self,name):
self.name=name
user=A('dick')
user2=B("hao")
print(user.__sizeof__(),user2.__sizeof__()) #查看占内存
#输出结果:32 32
上面这个示例可以看出,类A和类B的效果是一模一样的,只是我把类B的__new__方法调用写出来了而已,所以实际上__new__方法会自动继承object类且调用的,无需我们去操心
注意:__new__方法是比__init__方法更加提前调用
看个例子:
class User(object):
def __init__(self,name):
self.name=name
print(f'我的名字是{self.name}')
def __new__(cls, *args, **kwargs): #对__new__方法重写
print('这个是__new__方法')
return object.__new__(cls) #返回这个类对象的空间
jb=User('Dick')
#输出结果:
# 这个是__new__方法
# 我的名字是Dick
从输出结果就可以看出,是先输出__new__方法里面的内容,然后再输出__init__方法的内容,所以__new__方法是先执行的
2.重写__new__方法
有时候我们不喜欢父类的__new__方法,所以我们会进行方法的重写,那么对这个方法的重写需要注意哪些东西呢?
我们去重写__new__方法时,是需要一个对象空间引索的返回值,这个返回值一般是使用父类的__new__方法
注意事项:
1.要有返回对这个对象的引用(也就是对象的空间)
2.要注意所返回的对象的类
示例1: 如果没有返回对象的引用会发生什么?
class A(object):
def __new__(cls, *args, **kwargs):
print('重写new方法')
#这里我没有写返回值会怎么样呢?
def __init__(self,name):
print('初始化方法')
self.name=name
user=A('dick')
print(user)
#输出结果:重写new方法
# None
这里我没有返回值,当我创建了一个实例对象的时候是不知道这个分配的空间在哪里的,所以我无法对这个对象进行初始化,所以__init__方法无法执行,那么这个实例对象是没有获取到空间的,也就是说创建了一个空间,但是找不到这个空间在哪里,数据无法存入。
示例2:
class B(object):
def __init__(self,name):
self.name=name
print('初始化方法')
def __new__(cls, *args, **kwargs):
print('这是new方法')
return object.__new__(cls)
user=B('Dick')
print(user.name)
#输出结果:
# 这是new方法
# 初始化方法
# Dick
这的__new__方法有对象引索的返回值,会把当前类的实例化对象传给__init__方法中的self参数,然后进行初始化,所以这个对象可以获取到空间。
3.__new__方法不同返回值的情况
前面我们学习过了类方法,都知道类方法会有一个参数--cls,这个参数意思是表示这个类的本身,同样__new__方法也有一个cls参数,但是这个参数不一定是表示自身类的意思,还可以传入其他的父类。所以当这个参数有不同的传入值时,会发生不同的情况,下面就以代码作为示例一一讲解。
示例1:
class A(object):
def __init__(self,name):
self.name=name
def __new__(cls, *args, **kwargs):
print('这个是A类重写的方法')
res=object.__new__(cls)
return res
class B(A):
def __init__(self,sound):
self.sound=sound
user=B('汪汪')
print(user.sound)
#输出结果:
# 这个是A的类
# 汪汪
这里A重写了object类中的__new__方法,B继承A,但是B没有重写A中的__new__方法,所以B是直接使用A中的__new__方法,传入当前类(cls),然后返回当前类(cls)实例化的空间
示例2:
class A(object):
def __init__(self,name):
self.name=name
def __new__(cls, *args, **kwargs):
print('这个是A类重写的方法')
res=object.__new__(cls)
return res
class B(A):
def __init__(self,sound):
self.sound=sound
def __new__(cls, *args, **kwargs):
print('这个是B类重写的方法')
res=cls.__new__(cls) #这里是返回自身的实例对象空间引索
return res
user=B('汪汪')
print(user.sound)
#结果:报错,进入死循环
注意:重写__new__方法时,不能返回自身类对象的空间引索,否则会进入死循环
示例3:
class A(object):
jk='beauty'
def __init__(self,name):
self.name=name
def __new__(cls, *args, **kwargs):
res=object.__new__(cls)
return res
def fun(self):
print('大家好!')
class B(A):
def __init__(self,sound):
self.sound=sound
def __new__(cls, *args, **kwargs):
res=super().__new__(A) #返回的是类对象A的空间引索
return res
user=B('汪汪') #实际上这里创建是类A的实例化对象
print(user.jk) #输出结果:beauty
user.fun() #输出结果:大家好!
print(user.sound) #报错'A' object has no attribute 'sound'
print(user.name) #报错'A' object has no attribute 'name'
这个例子是返回类A分配的空间引索,但是跟类B类型不同,所以无法进入到类B的__init__初始化,这也就导致了只有空间,但是没有初始化的情况,当我们去创建一个实例对象user的时候,user是一个类A的实例化对象,并不是类B的实例化对象。(这里的实例化对象user不经过类A的__init__初始化和类B__init__初始化)
3.单例模式
单例模式是计算机非常常见的一种对象空间分配模式,比如:当我们在电脑点开设置窗口的时候,当我们再次点开这个设置窗口,计算机会检测这个窗口是否存在,如果存在的话就不会再次弹窗一个窗口,也就是说只有你把这个窗口叉掉后才会生成一个新的窗口,这样可以避免多个窗口的弹窗,同时还可以避免无效的内存占用(每次出现一个窗口是需要内存的),这个就叫做单例模式。
Python中我们每次创建一个实例对象的时候都需要占用一个内存地址,在做项目的时候有些没有用的实例对象总是占用这地址导致程序运行缓慢。在Python中怎么去通过__new__方法来实现单例模式呢?我们往下看。
单例的好处: 节省内存
代码如下:
class Student(object):
_instance=None #定义一个类属性,是可以在类中进行调用的
def __init__(self, name,age):
self.name=name
self.age=age
def __new__(cls, *args, **kwargs):
if cls._instance is None: #如果这个内存为空的话就进行空间分配
print('进行空间分配')
cls._instance=super().__new__(cls)
return cls._instance
Jack=Student('Jack',18)
John=Student('John',19)
Dick=Student('Dick',18)
print(Jack is Dick is John) #输出:True
print(Jack.name,Jack.age) #输出:Dick 18
print(John.name,John.age) #输出:Dick 18
print(Dick.name,Dick.age) #输出:Dick 18
以上就是一个单例模式,这些实例化对象都是占用同一个内存地址,所以内存地址都是相同的,但是每次创建一个实例化对象,这个实例化对象会把是一个对象给覆盖掉,最后空间是属于最后一个实例化对象,故都是输出 Dick 18
好了,以上就是本期的全部内容了,小伙伴们你们学会了怎么去分配对象的空间内存吗?
日常分享一张壁纸 :