Jacobian
Jacobian

Reputation: 10882

Static overloaded method in Python class. Missing argument issue

I have a class db defined like so:

class db:

    def __init_(self, idx, host, port, db_name, user, password):
        self.idx = idx
        ...

    def Connect(self):
        conn = db_engine.connect(host=self.host, ...)

    @staticmethod
    def Connect(idx):
        if idx == 1:
            d = db(1, "localhost", 80, ...)
            conn = d.Connect()
        return conn

So, as you can see, my class has two methods. Most importantly it has a static method Connect which internally tries to call a non-static method Connect. However, when I try to use my program, I get an error message:

d.Connect()

TypeError: Connect() missing 1 required positional argument ...

It seems like the static method tries to call itself, however I want to make it call another non-static method. So, what am I doing wrong?

Upvotes: 0

Views: 268

Answers (1)

Martijn Pieters
Martijn Pieters

Reputation: 1125258

You redefined the Count method. Python doesn't support overloading; you cannot define both a regular method and a static method. You only have the staticmethod version because it was defined last, it replaced the non-static version altogether.

If you need a method that alters its behaviour based on wether or not it is bound (static vs. a regular method) you have two options; either test the type of the self argument (it'll be an integer or an instance of the db class), or you'll have to build a custom descriptor object that can alter how your method is called based on the context; on an instance or on the class.

Testing the type of self looks like this:

class db:
    def __init_(self, idx, host, port, db_name, user, password):
        self.idx = idx
        # ...

    def Connect(self_or_idx):
        if isinstance(self_or_idx, db):
            self = self_or_idx
            conn = db_engine.connect(host=self.host, ...)
            # ...
        else:
            idx = self_or_idx
            if idx == 1:
                d = db(1, "localhost", 80, ...)
                conn = d.Connect()
            return conn

If using a custom descriptor, you could build one that supports two different functions like the property object does:

class static_or_instance_method(object):
    def __init__(self, instancemethod=None, staticmethod=None):
        self.method = instancemethod
        self.static = staticmethod

    def staticmethod(self, staticmethod):
        return type(self)(self.method, staticmethod)

    def instancemethod(self, instancemethod):
        return type(self)(instancemethod, self.static)

    def __get__(self, instance, cls=None):
        if instance is None:
            return self.static
        return self.method.__get__(instance, cls)

then use it like this:

class db:
    def __init_(self, idx, host, port, db_name, user, password):
        self.idx = idx
        # ...

    @static_or_instance_method
    def Connect(self):
        conn = db_engine.connect(host=self.host, ...)
        # ...

    @Connect.staticmethod
    def Connect(idx):
        if idx == 1:
            d = db(1, "localhost", 80, ...)
            conn = d.Connect()
        return conn

Quick demo of the descriptor:

>>> class Foo:
...     @static_or_instance_method
...     def bar(self):
...         return 'Bound method of {}'.format(self)
...     @bar.staticmethod
...     def bar(arg):
...         return 'Static method, receiving argument {}'.format(arg)
... 
>>> Foo().bar()
'Bound method of <__main__.Foo object at 0x10b6cf588>'
>>> Foo.bar('spam')
'Static method, receiving argument spam'

Upvotes: 3

Related Questions