3

I'm trying to use a class decorator to achieve a singleton pattern as below:

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 

The singleton pattern seems to be working well, but I'm wondering why @staticmethod is not needed above the function new_ in my code, as I know that cls.__new__ is a static method.

 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 ... 

Update test with python2.7+

The @staticmethod seems to be needed in py2 and not needed in 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 Answers 3

2

__new__ explicitly takes the class instance as its first argument. __new__, as mentioned in other answers, is a special case and a possible reason for it to be staticmethod is to allow the creation of other classes using new:

super(CurrentClass, cls).__new__(otherCls, *args, **kwargs)

The reason why your code works without @staticmethod decorator in Python 3 but doesn't work in Python 2 is because of the difference in how Python 2 and Python 3 allow a class's method access.

There is no unbounded method in Python 3 [ 2 ]. When you try to access a class method on Python 3 you get a function whereas in Python 2 you get unbounded method. You can see this if you do:

# Python 2 >>> A.__new__ <unbound method A.new_> # Python 3 >>> A.__new__ <function __main__.single_class.<locals>.new_(cls, *args, **kwargs)> 

In Python 2, your decorator is equal to single_class.__new__(A) but since __new__ is an unbound method you can't call it with the class itself. You need a class instance but for that, you need to create your class(catch-22) and that's why staticmethod is needed. The error message says the same thing unbound method new_() must be called with A instance as first argument.

Whereas in Python 3 __new__ is treated as a function you can call it with class A itself. So, single_class.__new__(A) will work.


Sign up to request clarification or add additional context in comments.

2 Comments

Thanks for replying first, the function new_ above is a class method so I don't think there are somthiong wrong with @staticmethod . And what confusing me is in py2 , @staticmethod is needed as py3 not
Updated answer.
1

From the docs:

Called to create a new instance of class cls. __new__() is a static method (special-cased so you need not declare it as such) that takes the class of which an instance was requested as its first argument.

(My emphasis.)

You might also want to have a look at this SO answer.

1 Comment

Thansk for your answer first, but I find it's the same doc py2.7 with py2 but in my test with py2, the decorator @staticmethod is needed
1

It's not needed for this special method because that's the official spec (docs.python.org/3/reference/datamodel.html#object.new). Quite simply.

EDIT:

The @staticmethod seems to be needed in py2

It's not:

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'> 

but your example is quite a corner case since you're rebinding this method after the class has been created, and then it stop working in py2 indeed:

>>> 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) 

I assume that in py2, __new__ (if present) is special-cased by the metaclass constructor (type.__new__ / type.__init__) which wraps it in a staticmethod:

>>> Foo.__dict__["__new__"] <staticmethod object at 0x7fe11e15af50> >>> Bar.__dict__["__new__"] <function new at 0x7fe11e12b950> 

There have been a couple changes in the object model between py2 and py3 which probably explain the different behaviour here, one might be able to find the exact info somewhere in the release notes.

3 Comments

Thansk for your replying sir, but when goto py2, the decorator seems to be needed in my trial. So I'm a little confused with that
I mean, with py2, @staticmethod is needed but with py3 it works fine whether there is @staticmethod or not
@jiaJimmy indeed, but only for this corner case where you rebind __new__ after the class has been created (cf my edited answer).

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.