Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Static and instance methods in Python [duplicate]

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.

like image 643
xster Avatar asked May 05 '11 04:05

xster


4 Answers

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)
like image 93
Fred Nurk Avatar answered Oct 09 '22 19:10

Fred Nurk


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

like image 43
Greg Hewgill Avatar answered Oct 09 '22 18:10

Greg Hewgill


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.

like image 31
David Heffernan Avatar answered Oct 09 '22 19:10

David Heffernan


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
like image 20
Imran Avatar answered Oct 09 '22 19:10

Imran