Python method is available for both instance / uninstalled class

I have a class that retrieves details and populates the class with information if it has already been created idusing the method details. If it has not been created yet, I want to use the argument passed in instead details, as idwell as return a new instance of the object. Something like the following:

f = Foo()
f.id = '123'
f.details()

but also allow:

f = Foo.details(id='123')

Can the same method be used detailsfor this? Or do I need to create two separate methods and make one a @classmethod? Can they have the same name if I declare one as @classmethodand the other not?

+4
source share
2 answers

, ; , , :

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

    def __get__(self, instance, cls=None):
        if instance is None:
            return classmethod(self.func).__get__(None, cls)
        return self.func.__get__(instance, cls)

classmethod(), , .

:

class Foo(object):
    @class_or_instance_method
    def details(cls_or_self, id=None):
        if isinstance(cls_or_self, type):
            # called on a class
        else:
            # called on an instance

, -, , .

:

>>> class Foo(object):
...     @class_or_instance_method
...     def details(cls_or_self, id=None):
...         if isinstance(cls_or_self, type):
...             return 'Class method with id {}'.format(id)
...         else:
...             return 'Instance method with id {}'.format(cls_or_self.id)
... 
>>> Foo.details(42)
'Class method with id 42'
>>> f = Foo()
>>> f.id = 42
>>> f.details()
'Instance method with id 42'
+7

, , - :

class overriding_instance_method(object):
    """
    can be used as a decorator: see example below in __main__
    """
    def __init__(self, class_method_func, instance_method_func=None):
        self.class_method_func = class_method_func
        self.instance_method_func = instance_method_func

    def __call__(self, instance_method_func):
        return type(self)(self.class_method_func, 
                          instance_method_func=instance_method_func)

    def __get__(self, instance, cls=None):
        if instance is None:
            return classmethod(self.class_method_func).__get__(None, cls)
        return self.instance_method_func.__get__(instance, cls)

:

class OverridingClassMethodTest(object):

    def print_me(cls):
        print 'class: {}'.format(cls)

    @overriding_instance_method(print_me)
    def print_me(self):
        print 'instance: {}'.format(self)

OverridingClassMethodTest.print_me()
OverridingClassMethodTest().print_me()
0

All Articles