Static and instance methods in Python [duplicate]
Asked Answered
M

4

10

Can I define a Python method to be both static and instance at the same time? Something like:

class C(object):
    @staticmethod
    def a(self, arg1):
        if self:
            blah
        blah

So that I can call it with both:

C.a(arg1)
C().a(arg1)

The intent is to be able to run two sets of logics. If accessed as an instance method, it would make use of instance variables and do stuff. If access as a static method, it will do without.

Mastigophoran answered 5/5, 2011 at 4:17 Comment(1)
#5813037 is definitely interesting. I was just wondering if there's a less 'hack' way of doing itMastigophoran
C
17
import functools

class static_or_instance(object):
  def __init__(self, func):
    self.func = func

  def __get__(self, instance, owner):
    return functools.partial(self.func, instance)

class C(object):
  @static_or_instance
  def a(self, arg):
    if self is None:
      print "called without self:", arg
    else:
      print "called with self:", arg

C.a(42)
C().a(3)
Cateran answered 5/5, 2011 at 4:34 Comment(1)
You did it. I can't believe you did it!Tibbitts
W
4

formencode has a classinstancemethod decorator, which does what what you want. It requires the method to have 2 arguments (self and cls, one of them could get passed None depending on calling context)

Lifted from formencode/declarative.py

class classinstancemethod(object):
    """
    Acts like a class method when called from a class, like an
    instance method when called by an instance.  The method should
    take two arguments, 'self' and 'cls'; one of these will be None
    depending on how the method was called.
    """

    def __init__(self, func):
        self.func = func

    def __get__(self, obj, type=None):
        return _methodwrapper(self.func, obj=obj, type=type)

class _methodwrapper(object):

    def __init__(self, func, obj, type):
        self.func = func
        self.obj = obj
        self.type = type

    def __call__(self, *args, **kw):
        assert not kw.has_key('self') and not kw.has_key('cls'), (
            "You cannot use 'self' or 'cls' arguments to a "
            "classinstancemethod")
        return self.func(*((self.obj, self.type) + args), **kw)

    def __repr__(self):
        if self.obj is None:
            return ('<bound class method %s.%s>'
                    % (self.type.__name__, self.func.func_name))
        else:
            return ('<bound method %s.%s of %r>'
                    % (self.type.__name__, self.func.func_name, self.obj))

Sample usage

class A(object):
    data = 5

    @classinstancemethod
    def print_(self=None, cls=None):
        ctx = self or cls
        print ctx.data


>>> A.print_()
5
>>> a = A()
>>> a.data = 4
>>> a.print_()
4
Wholism answered 5/5, 2011 at 4:33 Comment(0)
J
2

No. What would self mean inside the method, if you could do that?

Joke answered 5/5, 2011 at 4:20 Comment(0)
D
1

Your code will work if you remove the self parameter to a(). When you call it with C().a(arg1) the instance is ignored.

But you want this method to work as both a static method and a method that receives an instance. You can't have it both ways.

Dogfight answered 5/5, 2011 at 4:21 Comment(2)
But then it would become a normal static method. I would like to be able to run a different set of logic and access self if it is used as an instance methodMastigophoran
You'd be better passing on the variables you want to use from within the method. Then you don't care if they are instance variables or not.Tibbitts

© 2022 - 2024 — McMap. All rights reserved.