staticmethod not callable? (trying to make a Singleton metaclass..)

Gabriel Genellina gagsl-py2 at yahoo.com.ar
Sun Nov 29 12:58:14 EST 2009


En Sun, 29 Nov 2009 10:25:21 -0300, inhahe <inhahe at gmail.com> escribió:

> I'm trying to come up with a system for singletons, where I don't have to
> modify anything for an individual class except to define __metaclass__  
> or,
> if possible, to inherit another class.
>
> I want it to raise an error if making a duplicate instance of a class is
> attempted, rather than to return the same object,

(I won't comment on the usefulness of such approach...)

> class Singleton(type):
>   def __new__(meta, classname, bases, classDict):
>     @staticmethod
>     def nonewinst(*args, **kwargs):
>       raise ValueError("Can't make duplicate instance of singleton " +
> classname)
>     @staticmethod
>     def newoldnew(obj):
>       return obj
>     oldnew = classDict.get("__new__", newoldnew)
>     @staticmethod
>     def newnew(obj, *args, **kwargs):
>       o = oldnew(obj, *args, **kwargs)
>       obj.__new__ = nonewinst
>       return o
>     classDict["__new__"] = newnew
>     return type.__new__(meta, classname, bases, classDict)

__new__ is a classmethod, not a staticmethod.

> a little bit of experimentation revealed that apparently even functions
> defined within a method become class methods,

????????

> so i tried making them all
> static methods.

????????
Why do you insist on static methods?

> however, python is strange to me when it comes to methods
> and the self parameter.  i mean i understand a function being free of the
> class so that the 'self' parameter doesn't mean anything in particular
> unless you explicitly pass it an instance.  and i understand the method
> being bound to an object so that when it's called its self parameter is
> automatically sent the instance.  but python seems to have this  
> in-between
> mode where sometimes a function isn't particularly bound to an instance  
> but
> if you try to pass the wrong kind of instance to 'self' it'll complain,
> which gets annoying, and i don't understand how its binding works.

Do you mean this error?
Traceback (most recent call last):
   File "<stdin>", line 1, in <module>
TypeError: unbound method foo() must be called with X instan
ce as first argument (got Y instance instead)

This kind of check was removed in Python 3; ClassName.method_name yields a  
plain function, not an unbound method as in 2.x

> but
> anyway, the problem i'm currently having with the code might not even be
> related to that..because here's the error I'm getting:
>
>>>> from funcs import Singleton
>>>> class A:
> ...   __metaclass__ = Singleton
> ...
>>>> b = A()
> Traceback (most recent call last):
>   File "<stdin>", line 1, in <module>
>   File "c:\python25\funcs.py", line 68, in newnew
>     o = oldnew(obj, *args, **kwargs)
> TypeError: 'staticmethod' object is not callable

That's true: instances of the staticmethod type are not callable.

> i'm not that experienced with metaclasses, but it seems to me that
> something's obviously going fubar here because at that point oldnew  
> should
> be the function newoldnew (since class A doesn't have a __new__ defined)
> which is clearly defined right there with a staticmethod decorator, and
> first of all, functions *should* be callable, and secondly, why is my  
> object
> a 'staticmethod' object just because I used a decorator on it?  it would
> seem it should be a function, so i tested it like this:
>
>>>> class A:
> ...   @staticmethod
> ...   def b(): pass
> ...
>>>> type(A.b)
> <type 'function'>
>
> in that case, using @staticmethod returns a function.  i have nfi why  
> it's
> different in my Singleton class.
>
> oh, and thirdly, staticmethod is a decorator and decorators are  
> callables so
> even given that for some mysterious reason it's a staticmethod object i
> don't know why it's not callable.  so that's pretty confusing.  can  
> anyone
> clear this up for me?  thanks..

staticmethod is a type:

	py> staticmethod
	<type 'staticmethod'>

Used as a decorator, it's like this:

	def b(): pass
	b = staticmethod(b)

so b is an staticmethod instance. You can confirm this looking into the  
class:

	py> A.__dict__['b']
	<staticmethod object at 0x00BBCF70>
	py> A.b
	<function b at 0x00BE7970>

A staticmethod instance is a descriptor; its __get__ method is invoked to  
resolve A.b (or getattr(A, "b")). If you retrieve it directly, you get the  
staticmethod object which is not callable:

	py> A.__dict__['b']()
	Traceback (most recent call last):
	  File "<stdin>", line 1, in <module>
	TypeError: 'staticmethod' object is not callable

Going back to your original goal, your code at the top is really a mess. I  
would not even use a metaclass. If you want to avoid creating more than  
one instance, just record the fact that you created it in the class  
constructor, __new__:

class HardSingleton(object):
     "Only one instance of its subclasses may be created ever"
     _created = False
     def __new__(cls, *args, **kw):
         if cls._created:
             raise ValueError("Can't make duplicate instance of singleton  
%s" % cls.__name__)
         result = super(HardSingleton, cls).__new__(cls, *args, **kw)
         cls._created = True
         return result

class A(HardSingleton):
     def __init__(self):
         print "A"

s1 = A()
s2 = A()

-- 
Gabriel Genellina




More information about the Python-list mailing list