El mismo método para clase e instancia.

Tengo clase de Books y método select en ella. También hay una instancia de esa clase llamada book . Quiero poder hacer tanto Books.select(where='...') como book.select(where='...') :

 class Books(): def select(obj, where): print(obj, where) book = Books() Books.select(where='asdf') book.select(where='asdf') 

Lo anterior obviamente no funciona, porque select es un método vinculado a la instancia:

 Traceback (most recent call last): File "test.py", line 7, in  Books.select(where='asdf') TypeError: select() takes exactly 2 arguments (1 given) 

Un código de trabajo:

 class Books(): @staticmethod def select(obj, where): print(obj, where) book = Books() Books.select(Books, where='asdf') Books.select(book, where='asdf') 

Y me sale:

 vic@wic:~/projects/snippets$ python3 test.py  asdf  asdf 

Pero tengo que pasar manualmente la clase o su instancia como el primer argumento del método de select , no lo que quiero.

Si hago select un método de clase:

 class Books(): @classmethod def select(obj, where): print(obj, where) book = Books() Books.select(where='asdf') book.select(where='asdf') 

Siempre recibo una clase como primer argumento:

 vic@wic:~/projects/snippets$ python3 test.py  asdf  asdf 

Pero quiero conseguir una instancia en el segundo caso.

Entonces, ¿hay una manera de lograr lo que quiero sin pasar manualmente la clase / instancia como el primer argumento a un método estático?

Podrías usar un descriptor :

 class Select(object): def __get__(self,obj,objtype): x=objtype if obj is None else obj def select(where): print(x,where) return select class Books(object): select=Select() book = Books() Books.select(where='asdf') book.select(where='asdf') 

rendimientos

  asdf <__main__.Books object at 0xb7696dec> asdf 

Solución utilizando un descriptor y un decorador:

 class class_or_instance_method(): def __init__(self, method): self.method = method def __get__(self, obj, objtype): x = obj or objtype def wrapped(*args, **kwargs): return self.method(x, *args, **kwargs) return wrapped class Books(): @class_or_instance_method def select(obj, where): print(obj, where) book = Books() Books.select(where='asdf') book.select(where='asdf') 

Resultado:

  asdf <__main__.Books object at 0x2695890> asdf 

Solo un ejemplo que encontré en http://code.activestate.com/recipes/52304-static-methods-aka-class-methods-in-python/

Se crea una pequeña envoltura:

 class Callable: def __init__(self, anycallable): self.__call__ = anycallable 

Y luego define su clase y una variable de clase dentro de ella.

 class Class2: def static2(name): print "Hi there",name static2 = Callable(static2) # now, a call such as: Class2.static2("Peter") # works just fine, and as-expected