在蟒蛇当一个类定义要么__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
属性的“OPS”之一:
>> FloatNode(1, []).__add__
<bound method __add__ of <__main__.FloatNode object at 0xb6fd61ec>>
你会得到封装了primitive
持有的所有利益绑定方法(即梯度功能) 。