2

So there is a static method in base class, and sub classes should use it.

However, I need to know which sub class calls the static method.

The code is like this:

class BaseClass():
    @staticmethod
    def getname():
        #some magic

class SubClassA(BaseClass): 
    pass

class SubClassB(BaseClass):
    pass

SubClassA.getname()   #hope to see 'SubClassA'
SubClassB.getname()   #hope to see 'SubClassB'

Or, is this even possible?

Cauly
  • 370
  • 1
  • 2
  • 12
  • 2
    Why don't use `@classmethod`? Static methods are static, they're not supposed to carry any class-related data. – vaultah Oct 22 '14 at 17:52
  • Possible duplicate of [How to get (sub)class name from a static method in Python?](http://stackoverflow.com/questions/3596641/how-to-get-subclass-name-from-a-static-method-in-python) – Mr_and_Mrs_D Apr 27 '17 at 09:22

2 Answers2

5

not possible with staticmethod. is possible, however, with classmethod

class A(object):
    @classmethod
    def f(cls):
        print cls
acushner
  • 9,595
  • 1
  • 34
  • 34
  • 1
    It *is* possible with `staticmethod` (see my metaclass example in my answer) -- but either just asking for `cls.__name__` or making it a `classmethod` are surely better solutions. – ely Oct 22 '14 at 18:15
4

By definition, a staticmethod is not provided with a reference to either an invoking class or instance -- that's what "static" means in Python.

You can do some hacky things to achieve this with either metaclasses or decorators.

Note that I'm using the example idea of just getting the class's name from your post, however, you can modify this example to work with any kind of function you want to have as a static function. You would just define that function inside getnameable below, like I have defined getname and make use of the some_class argument below (and you'd use a different name than all the "getname" stuff I use here).

With decorators, you could do this:

def getnameable(some_class):

    # Note, this could be any kind of static method that you want, not
    # just for getting the name. And it can use `some_class` in whatever
    # way is needed.
    def getname():
        return some_class.__name__
    some_class.getname = staticmethod(getname)
    return some_class

then this works:

In [334]: @getnameable
class SubClassA(BaseClass):
    pass
   .....: 

In [335]: SubClassA.getname()
Out[335]: 'SubClassA'

but note that if you implemented this in BaseClass directly, then the class name which gets bound would be BaseClass, even in the children classes. So in this case, you'd need to put the decorator on every class you wanted.

Metaclasses offers away around that, by indicating that you want this business of decorating the class to be part of class-creation (not instance creation, mind you).

class GetNameableMeta(type):
    def __new__(cls, name, bases, attrs):
        temp_class = super(GetNameableMeta, cls).__new__(cls, name, bases, attrs)
        return getnameable(temp_class)

class BaseClass(object):
    __metaclass__ = GetNameableMeta

class SubClassA(BaseClass):
    pass

Testing it out:

In [337]: %cpaste
Pasting code; enter '--' alone on the line to stop or use Ctrl-D.
:class GetNameableMeta(type):
:    def __new__(cls, name, bases, attrs):
:        temp_class = super(GetNameableMeta, cls).__new__(cls, name, bases, attrs)
:        return getnameable(temp_class)
:
:class BaseClass(object):
:    __metaclass__ = GetNameableMeta
:
:class SubClassA(BaseClass):
:    pass
:--

In [338]: SubClassA.getname()
Out[338]: 'SubClassA'

In [339]: BaseClass.getname()
Out[339]: 'BaseClass'

Notice how much high-falutin code we needed to write to do this, when we had several reasonable alternatives:

  1. Just ask directly for the __name__ attribute.
  2. Make it a classmethod to begin with.
  3. Use the decorators just where we need this and give up the inheritance part.

I suspect that in "the essence of Python" any of these is better than the machinery I describe above, since it makes for simpler, easier to understand code.

ely
  • 74,674
  • 34
  • 147
  • 228