user422005
user422005

Reputation: 2031

Python classmethod and(?) instancemethod

I have written a Python class for parsing a specialized text format.

class Parser(object):

   def __init__(self):
       # Initialize parser instance


   def parseFile(self , filename):
       pass

   def modifyParser(self , *args , **kwargs):
       pass 

   #Classmethod has same name as instance method - this does not work.
   @classmethod 
   def parseFile(cls , filename)
       parser = Parser( )
       return parser.parseFile( filename )

As indicated the parser can be modified with the modifyParser method - but in most cases I will just use the Parser instance as it comes from the Parser.__init__(). I would like to be able to do this:

# Parse file using 'custom' parser:
parser = Parser( )
parser.modifyParser( ... )
result = parser.parseFile("file.input")


# Parse using the default parser - do not explicitly instantiate an object:
result = Parser.parseFile("file.input")

This requires that the parseFile( ) method can be called both as an instance method - with a self - and as a classmethod. Is this possible? Bad form?

Upvotes: 3

Views: 156

Answers (2)

Nikita
Nikita

Reputation: 6331

You'll have to use two separate names. In python due to it's dynamic nature there's no operator overloading as in C++, when one function has same name with different arguments.

When you say def in your script, you tell Python "set the following object(function object) to this name". So in your code you just redefine the name to reference classmethod and your instance method function object is lost.

Solution: use different names for instace method and class method.

Upvotes: 2

Andrea Corbellini
Andrea Corbellini

Reputation: 17761

If I were you, I'd offer two distinct functions:

  1. mymodule.Parser().parseFile() (instance method), and
  2. mymodule.parseFile() (module-level function that uses the default instance).

This is what happens for example with the standard json module, where you have json.JSONDecoder().decode() and json.loads(). Offering two distinct functions makes the code less ambiguous, more explicit and more predictable (in my opinion).

However, yes: what you want to do is possible. You have to implement your own descriptor using __get__. Here's an example:

from functools import partial

class class_and_instance_method(object):

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

    def __get__(self, obj, type=None):
        first_arg = obj if obj is not None else type
        return partial(self.func, first_arg)

class Parser(object):

    @class_and_instance_method
    def parseFile(self):
        if isinstance(self, type):
            print('using default parser')
        else:
            print('using the current instance')

>>> Parser.parseFile()
using default parser

>>> p = Parser()
>>> p.parseFile()
using the current instance

Upvotes: 6

Related Questions