-
Notifications
You must be signed in to change notification settings - Fork 3
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Python Singleton #103
Comments
重写类中新建实例时的 __new__ 方法# do not print "enter call"
class Singleton(object):
_instance = None
def __new__(cls, *args, **kwargs):
print('enter call')
if not cls._instance:
# 看下面这个 super 后面 __new__ 的参数,依然要带着 cls
# 因为这里要调用元类的 __new__ 函数(静态方法),所以需要当前类作为参数
cls._instance = super().__new__(cls)
return cls._instance
a = Singleton() # print "enter call"
b = Singleton() # print "enter call" again
a is b # True In [7]: object.__new__
Out[7]: <function object.__new__(*args, **kwargs)>
|
重写元类中新建实例时的 __call__ 方法,新建一个单例元类# do not print "enter call"
class SingletonType(type):
_instances = {}
def __call__(cls, *args, **kwargs):
print('enter call')
if cls not in cls._instances:
# 这里的 super 后面的 __call__ 无需使用 cls 作为参数
# 因为是在继承 type 中 __call__ 方法
cls._instances[cls] = super().__call__(*args, **kwargs)
return cls._instances[cls]
# do not print "enter call" either
class Singleton(metaclass=SingletonType):
pass
a = Singleton() # print "enter call"
b = Singleton() # print "enter call" again
a is b # True In [6]: type.__call__
Out[6]: <slot wrapper '__call__' of 'type' objects> 值得注意的是,这里虽然是新建一种所谓的单例类型,但是实质上还是在类实例化的过程中动手脚。 一种更清晰的写法class SingletonType(type):
_instances = {}
def __call__(cls, *args, **kwargs):
print('enter call')
if cls not in SingletonType._instances:
# 这里的 super 后面的 __call__ 无需使用 cls 作为参数
# 因为是在继承 type 中 __call__ 方法
SingletonType._instances[cls] = type.__call__(cls, *args, **kwargs)
return SingletonType._instances[cls]
# do not print "enter call" either
class Singleton(metaclass=SingletonType):
pass
a = Singleton() # print "enter call"
b = Singleton() # print "enter call" again
a is b # True |
还可以使用装饰器控制类的实例化装饰器是一种面向切面编程的设计模式,Python 中有函数装饰器,也有类装饰器。通常函数装饰器便够用了。 from functools import wraps
def singleton(cls):
instances = {}
@wraps(cls)
def get_instance(*args, **kwargs):
print('enter getinstance')
if cls not in instances:
instances[cls] = cls(*args, **kwargs)
return instances[cls]
return get_instance
@singleton
class Singleton(object):
pass
a = Singleton() # print "enter get_instance"
b = Singleton() # print "enter get_instance" again
a is b # True
type(Singleton) # function
print(Singleton) # <function Singleton at xxxxxxxx> 值得注意的是,这里在使用函数装饰器时,虽然使用了 wraps,但最终得到的 Singleton 当然还是一个函数,而且是一个闭包。 |
值得深挖的一个点
在 Python 中,不止函数是 在 Python 中一切皆对象,只要对象实现了 def t():
pass
dir(t)
['__annotations__',
'__call__', # 看见没,函数也有 __call__ 方法
'__class__',
'__closure__', # 这个是用来实现闭包的
'__code__',
'__defaults__',
'__delattr__',
'__dict__',
'__dir__',
'__doc__',
'__eq__',
'__format__',
'__ge__',
'__get__',
'__getattribute__',
'__globals__',
'__gt__',
'__hash__',
'__init__',
'__init_subclass__', # 这是新增加的方法,可以在很多情况下替换 __new__
'__kwdefaults__',
'__le__',
'__lt__',
'__module__',
'__name__',
'__ne__',
'__new__',
'__qualname__', # 这个名字挺有用,很适合用在通用化的日志输出中
'__reduce__',
'__reduce_ex__',
'__repr__',
'__setattr__',
'__sizeof__',
'__str__',
'__subclasshook__'] |
要稍稍懂一点的元类元类中有 new 方法,在创建类时执行,返回类。而 call 方法则是针对调用类生成实例这个过程。 class NewMetaType(type):
def __new__(meta_cls, name, bases, dct):
x = super().__new__(meta_cls, name, bases, dct)
x.attr = 100
return x 在 |
再往深处走参见:理解python的类实例化 - 简书 # 需要看解释器源码。。。。
# 忽略错误检查等健壮性处理,对于常规类的实例化主要有如下过程:
def __call__(obj_type, *args, **kwargs):
obj = obj_type.__new__(*args, **kwargs)
if obj is not None and issubclass(obj, obj_type):
obj.__init__(*args, **kwargs)
return obj
# __new__ 方法为对象分配了内存空间,构建它为一个“空"对象然后 __init__ 方法被调用来初始化它。
# 总的来说:
# Foo(*args, **kwargs)等价于Foo.__call__(*args, **kwargs)
# 而 Foo 是一个 type 的实例,Foo.__call__(*args, **kwargs) 实际调用的是 type.__call__(Foo, *args, **kwargs)
# type.__call__(Foo, *args, **kwargs)调用type.__new__(Foo, *args, **kwargs),然后返回一个对象。
# obj随后通过调用obj.__init__(*args, **kwargs)被初始化。
# obj被返回。
|
彻底而邪恶的单例:源自单类的单例# do not print "enter call"
class SingletonType(type):
_instances = {}
_cls = None
def __call__(cls, *args, **kwargs):
print('enter call')
if cls not in cls._instances:
# 这里的 super 后面的 __call__ 无需使用 cls 作为参数
# 因为是在继承 type 中 __call__ 方法
cls._instances[cls] = super().__call__(*args, **kwargs)
return cls._instances[cls]
def __new__(meta_cls, name, bases, dct):
assert bases == ()
if not meta_cls._cls:
meta_cls._cls = super().__new__(meta_cls, name, bases, dct)
else:
for k, v in dct.items():
setattr(meta_cls._cls, k, v)
return meta_cls._cls
# do not print "enter call" either
class SingletonA(metaclass=SingletonType):
def a():
return 'a'
class SingletonB(metaclass=SingletonType):
def b():
return 'b'
a = SingletonA() # print "enter call"
b = SingletonB() # print "enter call" again
a is b # True
SingletonA is SingletonB # True
SingletonA.b() # b
SingletonB.a() # a |
总结
|
面试经常考察的一个点,值得掌握,借此更熟练的使用 class 的种种特性
最简单易用的:Python 中的 module 是天然的 singleton 模式
The text was updated successfully, but these errors were encountered: