在蟒蛇當一個類定義要麼__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
持有的所有利益綁定方法(即梯度功能) 。