检查对象是否内置(未绑定)函数或方法(绑定)

时间:2019-07-15 11:39:41

标签: python python-3.x

我正在写一个包装getter / setter类函数或方法的类。因为我希望能够包装自己的类以及外部类,所以包装器类应充当(类似于属性的)类函数装饰器,并且也应以常规方式(即直接创建实例)工作。

换句话说,我希望包装器尽可能地灵活。 包装器类的非常简化的定义如下。

(我在macOS上使用的是Python 3.6.8)

class Wrapper(object):
    def __init__(self, func):
        self._f = func  # func is a simple getter function with no arguments

    def __get__(self, obj, owner):
        if obj is None:
            return self
        return self.value(obj)

    def value(self, obj=None):
        if requires_obj(self._f):
            return self._f(obj)
        else:
            return self._f()

此定义使得可以通过三种不同的方式使用包装器。

第一个选项:类函数装饰器

class Foo(object):
    def __init__(self, bar):
        self._b = bar

    @Wrapper
    def bar(self):
        return self._b

foo = Foo(3)

value = foo.bar                 # property-like
value = Foo.bar.value(obj=foo)  # obj argument required

第二个选项:具有未绑定功能的直接实例化

from module import Foo # external class, don't want to mess up with decorators

foo = Foo(3)
wrapper = Wrapper(Foo.bar)  # instantiation with unbound function
value = wrapper.value(obj=foo)  # obj argument required

第三个选项:使用方法直接实例化

from module import Foo # external class, don't want to mess up with decorators

foo = Foo(3)
wrapper = Wrapper(foo.bar)  # instantiation with method
value = wrapper.value()  # obj argument not required

但是,由于调用签名不同,我需要知道是包装一个未绑定的函数还是方法(未绑定的函数需要一个实例作为第一个参数)。换句话说,我想知道如何实现上面的requires_obj功能

对于纯Python类,我可以使用标准库中的types模块很容易地弄清楚这一点(请参见下文)。但是,对于内置和外部类(例如C / C ++包装器),这种方法似乎不起作用...

正如我所提到的,对于Python类,以下工作正常。

def requires_obj(func):
    if isinstance(func, types.MethodType):
        return False
    elif isinstance(func, types.FunctionType):
        return True
    raise TypeError('no idea...')

对于内置函数/方法,我希望以下代码可以工作。

def requires_obj(func):
    if isinstance(func, types.MethodType):
        return False
    elif isinstance(func, types.FunctionType):
        return True
    elif isinstance(func, types.BuiltinMethodType):
        return False
    elif isinstance(func, types.BuiltinFunctionType):
        return True
    raise TypeError('no idea...')

但是,不是。

>>> requires_obj(str.upper)  # expected: True
TypeError: no idea...
>>> requires_obj("Hello".upper)  # expected: False
False
>>> from PyQt5.QtGui import QColor  # PyQt5 is a C++ wrapper
>>> requires_obj(QColor.rgb)  # expected: True
False
>>> requires_obj(QColor("red").rgb)  # expected: False
False

我缺少什么?如何确定包装函数是未绑定还是已绑定,即需要实例作为第一个参数?

解决方案

我想出了一种可行的方法(至少在我测试过的情况下如此)。

def requires_obj(func):
    # builtin types, e.g. str, dict, ...
    if hasattr(func, '__objclass__'):
        return True
    # external (builtin) types, e.g. C++ wrapper
    elif hasattr(func, '__self__') and func.__self__ is None:
        return True
    # pure Python types
    elif isinstance(func, types.FunctionType):
        return True
    return False
>>> requires_obj(dict.get)
True
>>> requires_obj({'name': 'value'}.get)
False
>>> requires_obj(str.upper)
True
>>> requires_obj("Hello".upper)
False
>>> requires_obj(QColor.rgb)
True
>>> requires_obj(QColor('red').rgb)
False

它仍然认为必须有一种更简单的方法来实现requires_obj函数,并且我不明白为什么使用types模块的方法不起作用,但是至少该实现可用于我。

0 个答案:

没有答案