为什么不需要staticmethod装饰器?

时间:2020-01-14 07:49:57

标签: python python-3.x

我正在尝试使用类装饰器来实现如下所示的单例模式:

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)

3 个答案:

答案 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