用__new__方法实现的单例模式,比如下面的MyClass类,会对类的初始化有影响吗?会对类的实例方法、类方法、静态方法有影响吗?下面会说下我对这些概念的理解,如有错误,欢迎交流指出,在此表示感谢。
__new__()是在新式类中新出现的方法,在Python2.7以前的版本在定义类时,都要显示的继承object才能使用。
object将__new__()方法定义为类的静态方法,即使没有被加上静态方法装饰器。并且至少需要传递一个位置参数cls,cls表示需要实例化的类,此参数在实例化时由Python解释器自动提供。
__new__方法接受的参数虽然和__init__一样,但__init__是在类实例创建之后调用,而__new__()方法是在类准备将自身实例化时调用, __new__方法正是创建这个类实例的方法。
先看下object类中对__new__()方法的定义:
class object:
@staticmethod # known case of __new__
def __new__(cls, *more): # known special case of object.__new__
""" T.__new__(S, ...) -> a new object with type S, a subtype of T """
pass
object将__new__()方法定义为静态方法。下面类中对__new__()方法的实现:
class juli(object):
def __init__(self):
print("__init__() called...")
def __new__(cls, *args, **kwargs):
print('__new__()')
return object.__new__(cls, *args,**kwargs) ##注意这两个参数
if __name__=='__main__':
a=juli()
#output:
#__new__()
#__init__() called...
发现实例化对象的时候,调用__init__()初始化之前,先调用了__new__()方法
__new__()必须要有返回值,返回实例化出来的实例,需要注意的是,可以return父类__new__()出来的实例,也可以直接将object的__new__()出来的实例返回。
__init__()有一个参数self,该self参数就是__new__()返回的实例,__init__()在__new__()的基础上可以完成一些其它初始化的动作,__init__()不需要返回值。
若__new__()没有正确返回当前类cls的实例,那__init__()将不会被调用,即使是父类的实例也不行。
我们可以将类比作制造商,__new__()方法就是前期的原材料购买环节,__init__()方法就是在有原材料的基础上,加工,初始化商品环节。先看一段代码:
class doubleFloat(float):
def __new__(cls, *args, **kwargs):
return float.__new__(cls, *args,**kwargs)
def __init__(self, *args):
print('=======')
a = doubleFloat()
print(a)
b = doubleFloat(1.9)
print(b)
举个实例来说明它的用途,比如说要定义一个Person类,在实例化一个对象时对初始化参数进行检查,如果合法就创建实例,如果不合法就不创建实例返回。
class Person(object):
def __new__(cls, name, age):
if 0 < age < 150:
return object.__new__(cls)
# return super(Person, cls).__new__(cls)
else:
return None
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self):
return '{0}({1})'.format(self.__class__.__name__, self.__dict__)
print(Person('Tom', 10))
print(Person('Mike', 200))
#Person({'name': 'Tom', 'age': 10})
#None
通过上面的例子,总结: 在Python中__new__方法与__init__方法类似,但是如果两个都存在那么__new__先执行。
接下来总结一下__new__与__init__的异同点:
1,两个功能相似,但是如果都存在__new__先执行;
2,__new__方法必须要返回一个实例化的对象;
3,__init__方法没有返回值;
4,__new__有一个参数cls,__init__有一个参数self即为__new__返回的实例对象。
日常编写Python代码的过程中,特别是Python新手,经常会遇到这样的错误:
TypeError: object() takes no parameters
对于上面这个错误,很容易迷惑我们,因为这个错误信息没有很明确的指出,到底是哪段代码除了问题。那这个错误是怎么产生的呢?
在python中,方法是一个属性,也就是说,当我们调用一个方法时,python需要所属方法名对应的属性,比如说:
o.m()
python会现在对象o中搜索m属性,如果对象o有m属性(判断对象o有没有m属性,可以用hasattr函数调用它.)
然而,python的方法是定义在一个class里的,而不是object里。也就是说如果m是o的方法,那就不可能是它的属性。正常情况下,python会先搜索对象的属性,如果没有,再去搜索类的属性,如果属性存在,则可以调用。(这地方可能大家会被类和对象两个概念搞混,不太准确的来说,类就是class,对象就是实例,具体大家可以查看文章笨办法学Python)
在python中,大多数的类都继承自object,在Python3中,如果你没有指定继承object,解释器会自动给你加上,而Python如果你没有指定,则为old-style class。大家在平时编写类时,建议大家都最好加上继承object,这样一个是代码兼容性号,一个是比较优雅。
这个错误是我在创建对象实例时报的错误,例如:
class Foo(object):
pass
如果我这样:
f = Foo()
就不会有任何问题,但是如果我这样:
f = Foo(10)
然后我就会得到上面的错误,这究竟是为什么?
这是因为Python在创建对象时,分为两个阶段:第一个阶段,对象是通过调用__new__方法来创建的,这个方法的细节我们基本上不用关心。__new__方法并不会立即返回一个对象实例,__new__方法之后,会调用__init__方法来给对象增加新的属性。对于上面的对象o,调用的就是
o.__init__()
Python首先查找o的__init__方法,但是没找到,然后查找父类的__init__方法,假设父类是上面的Foo,如果__init__方法依然不存在,所以最后会找到object的__init__属性。object的__init__是存在的,并且是个方法,然后调用这个方法,传入相应的参数,但是object.__init__方法没有参数,然后我们就得到下面的错误。
TypeError: object() takes no parameters
整个流程下来,最让人迷惑的地方是,Python没有这样报错:
“object.__init__()” takes no parameters
于是我们没法定为这个问题出在哪。
总结下来,在实现一个python的类时,最后写上__init__方法,这样就可以避免这样的迷惑性的错误。
下面说下单例模式,单例模式是确保一个类只有一个实例,并且这个实例是自己创造的,在系统中用到的都是这个实例。单例模式是设计模式的一种,关于设计模式和单例模式更具体的内容,可以查看相关的书本。
通过重载__new__实现单例(引例)
class SingleTon(object):
_instance = {}
def __new__(cls, *args, **kwargs):
if cls not in cls._instance:
cls._instance[cls] = super(SingleTon, cls).__new__(cls, *args, **kwargs)
print(cls._instance)
return cls._instance[cls]
class MyClass(SingleTon):
class_val = 22
该类中的__new__()方法的使用,就是再进行初始化之前,检查缓存中是否存在该对象,如果存在则将缓存存放对象直接返回,如果不存在,则将对象放至缓存中,供下次使用。
在Python中,__new__是用来创造一个类的实例的,而__init__是用来初始化这个实例的。既然__new__用来创造实例,也就需要最后返回相应类的实例,那么如果返回的是其他类的实例,结果如何呢?见下面的代码。以下代码运行后,首先打印出NoReturn __new__然后打印出other instance,最后通过type(t)可以看到t的类型是
# output:NoReturn __new__
# other instance
#
class Other(object):
val = 111
def __init__(self):
print('other instance')
class NoReturn(object):
def __new__(cls, *args, **kwargs):
print('NoReturn __new__')
return Other()
def __init__(self, a):
print(a)
print('NoReturn __init__')
t = NoReturn(66)
print(type(t))
# 完善方法
# output:NoReturn __new__
# 66
# NoReturn __init__
#
class Other(object):
val = 111
def __init__(self):
print('other instance')
class NoReturn(object):
def __new__(cls, *args, **kwargs):
print('NoReturn __new__')
return super(NoReturn, cls).__new__(cls)
def __init__(self, a):
print(a)
print('NoReturn __init__')
t = NoReturn(66)
print(type(t))
# 进一步完善方法
class SingleTon(object):
_instance = {}
def __new__(cls, *args, **kwargs):
if cls not in cls._instance:
cls._instance[cls] = cls._instance[cls] = super(SingleTon, cls).__new__(cls)
print(cls._instance)
return cls._instance[cls]
class MyClass(SingleTon):
class_val = 22
def __init__(self, val):
self.val = val
def obj_fun(self):
print(self.val, 'obj_fun')
@staticmethod
def static_fun():
print('staticmethod')
@classmethod
def class_fun(cls):
print(cls.class_val, 'classmethod')
if __name__ == '__main__':
a = MyClass(1)
b = MyClass(2)
print(a is b) # True
print(id(a), id(b)) # 4367665424 4367665424
# 类型验证
print(type(a)) #
print(type(b)) #
# 实例方法
a.obj_fun() # 2 obj_fun
b.obj_fun() # 2 obj_fun
# 类方法
MyClass.class_fun() # 22 classmethod
a.class_fun() # 22 classmethod
b.class_fun() # 22 classmethod
# 静态方法
MyClass.static_fun() # staticmethod
a.static_fun() # staticmethod
b.static_fun() # staticmethod
# 类变量
a.class_val = 33
print(MyClass.class_val) # 22
print(a.class_val) # 33
print(b.class_val) # 33
# 实例变量
print(b.val) # 2
print(a.val) # 2
__new__方法来实现单例模式最终版
class SingleTon(object):
_instance = {}
def __new__(cls, *args, **kwargs):
if cls not in cls._instance:
cls._instance[cls] = super(SingleTon, cls).__new__(cls)
# cls._instance[cls] = super(SingleTon, cls).__new__(cls, *args, **kwargs)
# print cls._instance
return cls._instance[cls]
class MyClass(SingleTon):
class_val = 22
def __init__(self, val):
self.val = val
def obj_fun(self):
print(self.val, 'obj_fun')
@staticmethod
def static_fun():
print('staticmethod')
@classmethod
def class_fun(cls):
print(cls.class_val, 'classmethod')
if __name__ == '__main__':
a = MyClass(11)
b = MyClass(22)
print(a is b) # True
print(id(a), id(b)) # 4367665424 4367665424
# 类型验证
print(type(a)) #
print(type(b)) #
最后来说用__new__方法实现的单例模式,会对实例方法,类方法,静态方法,实例变量和类变量有影响吗?答案是对相应的方法是没有影响的,但是如果用不同的变量都初始化了这个实例,在后面的变量中修改实例变量和类变量的话,前面的也会相应修改,而这也正好符合单例,无论多少个变量指向了这个实例,他们指向的是同一个。在__new__中产生完实例后,每次初始化实例对象,都是产生的同一个实例,而这个实例中相关的方法、变量还是和普通的实例一样使用。
from functools import wraps
def single_ton(cls):
_instance = {}
@wraps(cls)
def single(*args, **kwargs):
if cls not in _instance:
_instance[cls] = cls(*args, **kwargs)
return _instance[cls]
return single
@single_ton
class SingleTon(object):
val = 123
def __init__(self, a):
self.a = a
if __name__ == '__main__':
s = SingleTon(1)
t = SingleTon(2)
print(s is t)
print(s.a, t.a)
print(s.val, t.val)