Duck typing and class methods (or, how to use a method from both a class and an instance?)

后端 未结 3 1303
后悔当初
后悔当初 2021-01-07 05:47

I have some code which I would like to pass instances or classes interchangeably. All I will do in that code is to call a method that I expect both classes and instances to

相关标签:
3条回答
  • 2021-01-07 06:11

    How about something like:

    import inspect
    
    class A(object):
       @staticmethod
       def go(obj):
          if inspect.isclass(obj):
             print 'class'
          else:
             print 'instance'
    
    A.go(int) # class
    A.go(1)   # instance
    A.go(A)   # class
    A.go(A()) # instance
    
    0 讨论(0)
  • 2021-01-07 06:13

    You could create an own method type with a specially crafted __get__() method.

    In this method, you could do something like this:

    class combimethod(object):
        def __init__(self, func):
            self._func = func
        def classmethod(self, func):
            self._classfunc = classmethod(func)
            return self
        def __get__(self, instance, owner):
            if instance is None:
                return self._classfunc.__get__(instance, owner)
            else:
                return self._func.__get__(instance, owner)
    
    class A(object):
        @combimethod
        def go(self):
            print "instance", self
        @go.classmethod
        def go(cls):
            print "class", cls
    
    a=A()
    print "i:",
    a.go()
    print "c:",
    A.go()
    

    NOTE: The above is not very thoroughly tested, but seems to work. Nevertheless, it should be seen as a kind of "solution-near pseudo-code", not as a solution. It should give you an idea how to achieve your goal.

    0 讨论(0)
  • 2021-01-07 06:29

    Consider reusing the classinstancemethod decorator from formencode.

    https://bitbucket.org/formencode/official-formencode/src/06d52c5b33c9/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 'self' not in kw and 'cls' not in kw, (
                "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))
    
    0 讨论(0)
提交回复
热议问题