为什么需要def __get__:return types.MethodType?

时间:2016-04-23 14:27:27

标签: python

我试图理解这个block of code

class primitive(object):
    ...
    def __get__(self, obj, objtype):
        if sys.version_info >= (3,):
            def __get__(self, obj, objtype):
                return types.MethodType(self, obj)
        else:
            def __get__(self, obj, objtype):
                return types.MethodType(self, obj, objtype)

有没有人会有这样的例子?为什么我需要这个?

2 个答案:

答案 0 :(得分:2)

在python中,只要类定义了__get____set____delete__,就会说它是描述符类。这些给出了类属性“绑定”行为。这基本上意味着每当您使用通常的点符号通过类访问该对象作为属性时,它将根据正在进行的调用类型运行那些定义的方法。您发布的代码仅定义__get__,这使其成为非数据描述符。

这里有另一个被覆盖的dunder方法,它发挥作用,__call__这使你的类成为一个可调用的对象:

Class CallableClass(object):

    def __init__(self, fun):
        self.fun = fun

    def __call__(self, *args):
        return self.fun(*args)

>>> cc = CallableClass(lambda *args: return sum(args))
>>> cc(1, 2, 3)
6
>>> cc(0)
0

正如您所看到的,您可以像其他任何可调用的函数一样在实例上进行调用(例如函数)。我正在讨论这个,因为描述符类返回types.MethodType(self, obj)types.MethodType(self, obj, objtype),具体取决于您使用的是哪个python版本。

MethodType绑定其第一个参数,该参数必须可调用到其第二个参数,该参数是一个类实例。基本上,每次访问primitive描述符对象时,都会在类实例对象上创建绑定方法。

这里的“描述符”功能只有在被用作类属性时才被使用,通过primitive文档字符串读取它提到该类将函数包装为装饰器。

有些行可以看作它作为装饰者:

@primitive
def merge_tapes(x, y): return x
merge_tapes.defgrad(lambda ans, x, y : lambda g : g)
merge_tapes.defgrad(lambda ans, x, y : lambda g : g, argnum=1)

但是在这里用作描述符类:

differentiable_ops = ['__add__', '__sub__', '__mul__', '__pow__', '__mod__',
                      '__neg__', '__radd__', '__rsub__', '__rmul__', '__rpow__',
                      '__rmod__', DIV, RDIV]

nondifferentiable_ops = ['__eq__', '__ne__', '__gt__', '__ge__', '__lt__', '__le__',]
for float_op in differentiable_ops + nondifferentiable_ops:
    setattr(FloatNode, float_op, primitive(getattr(float, float_op)))

此处您可以看到班级FloatNode正在调用setattr 来自两个“操作”列表的字符串。同样的setattr电话primitive是 调用getattr来检索相同的内置方法 类型为float的名称,将其作为初始func参数传递。现在,无论何时访问任何这些操作,它们都是绑定方法。

因此,如果您调用其中一个被设置为FloatNode属性的“操作”:

>> FloatNode(1, []).__add__
<bound method __add__ of <__main__.FloatNode object at 0xb6fd61ec>>

您将获得一个绑定方法,该方法封装了primitive所拥有的所有好处(即渐变函数)。

答案 1 :(得分:0)

您发布的代码用作描述符。 这具有以下效果:如果类具有描述符的对象,则实例具有与该类中的对象具有相同名称的属性。

如果设置该属性,则调用描述符的__delete__(self, instance)命令。

如果删除它,则在调用描述符时使用__get__(self, instance, owner)函数。

如果您尝试接收存储在该属性中的数据,则会调用描述符的primitive方法。 (owner是包含描述符对象的类)

self参数是描述符本身(就像在python中的任何其他对象一样),而instance参数是包含被修改的属性的对象。

因此,在这种情况下,接收具有基础types.MethodType(self, instance)的属性的数据会导致py2的types.MethodType(self, instance, owner)或py3的self,其中instance是基元, owner是检索属性的对象,primitive是保存 init:function(editor){ editor.ui.addButton('myplug',{ label:'my plug', command:'myplug', toolbar:'mytoolbar' 对象的类。 (如前所述)

我希望我能提供帮助,

CodenameLambda