我正在尝试使用类装饰器来实现如下所示的单例模式:
python3.6+
def single_class(cls):
cls._instance = None
origin_new = cls.__new__
# @staticmethod
# why staticmethod decorator is not needed here?
def new_(cls, *args, **kwargs):
if cls._instance:
return cls._instance
cls._instance = cv = origin_new(cls)
return cv
cls.__new__ = new_
return cls
@single_class
class A():
...
a = A()
b = A()
print(a is b ) # True
单例模式似乎运行良好,但是我想知道为什么我的代码中函数@staticmethod
上方不需要new_
,因为我知道cls.__new__
是静态的方法。
class object:
""" The most base type """
...
@staticmethod # known case of __new__
def __new__(cls, *more): # known special case of object.__new__
""" Create and return a new object. See help(type) for accurate signature. """
pass
...
使用
更新测试python2.7+
@staticmethod
中似乎需要py2
,而py3
中则不需要
def single_class(cls):
cls._instance = None
origin_new = cls.__new__
# @staticmethod
# without @staticmethod there will be a TypeError
# and work fine with @staticmethod adding
def new_(cls, *args, **kwargs):
if cls._instance:
return cls._instance
cls._instance = cv = origin_new(cls)
return cv
cls.__new__ = new_
return cls
@single_class
class A(object):
pass
a = A()
b = A()
print(a is b )
# TypeError: unbound method new_() must be called with A instance as the first argument (got type instance instead)
答案 0 :(得分:1)
此特殊方法不需要它,因为这是官方规范(docs.python.org/3/reference/datamodel.html#object。新)。很简单。
编辑:
py2中似乎需要@static方法
不是:
bruno@bruno:~$ python2
Python 2.7.17 (default, Nov 7 2019, 10:07:09)
[GCC 7.4.0] on linux2
Type "help", "copyright", "credits" or "license" for more information.
pythonrc start
pythonrc done
>>> class Foo(object):
... def __new__(cls, *args, **kw):
... print("hello %s" % cls)
... return object.__new__(cls, *args, **kw)
...
>>> f = Foo()
hello <class '__main__.Foo'>
但是您的示例是一个非常极端的例子,因为在创建类后将重新绑定此方法,然后它实际上在py2中不再起作用:
>>> class Bar(object):
... pass
...
>>> def new(cls, *args, **kw):
... print("yadda %s" % cls)
... return object.__new__(cls, *args, **kw)
...
>>> Bar.__new__ = new
>>> Bar()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unbound method new() must be called with Bar instance as first argument (got type instance instead)
我假设在py2中,__new__
(如果存在)是元类构造函数(type.__new__
/ type.__init__
)的特殊情况,该构造函数将其包装在静态方法中:
>>> Foo.__dict__["__new__"]
<staticmethod object at 0x7fe11e15af50>
>>> Bar.__dict__["__new__"]
<function new at 0x7fe11e12b950>
py2和py3之间的对象模型进行了几处更改,这可能解释了此处的不同行为,也许可以在发行说明中找到确切的信息。
答案 1 :(得分:1)
__new__
显式地将类实例作为其第一个参数。正如其他答案中提到的那样,__new__
是一种特殊情况,它成为静态方法的可能原因是允许使用new创建其他类:
super(CurrentClass, cls).__new__(otherCls, *args, **kwargs)
您的代码在Python 3中不使用@staticmethod
装饰器而在Python 2中不工作的原因是因为Python 2和Python 3允许类的方法访问的方式不同。
Python 3 [2]中没有无限方法。当您尝试在Python 3上访问类方法时,您会获得一个函数,而在Python 2中,您将获得无限方法。您可以通过以下方式看到此信息:
# Python 2
>>> A.__new__
<unbound method A.new_>
# Python 3
>>> A.__new__
<function __main__.single_class.<locals>.new_(cls, *args, **kwargs)>
在 Python 2 中,您的装饰器等于single_class.__new__(A)
,但是由于__new__
是未绑定的方法,因此您不能使用类本身来调用它。您需要一个类实例,但是为此,您需要创建您的类(catch-22),这就是为什么需要staticmethod的原因。错误消息说同样的话unbound method new_() must be called with A instance as first argument
。
在 Python 3 中,__new__
被视为一个函数,您可以使用类A本身对其进行调用。因此,single_class.__new__(A)
将起作用。
答案 2 :(得分:0)
来自the docs:
被称为创建类
cls. __new__()
的新实例的是静态方法(特殊情况,因此您不必这样声明),该方法采用了请求实例的类作为第一个论点。
(我的重点。)
您可能还想看看this SO answer。