为什么需要def\uu get\uuuuuu:return类型。方法类型?

2024-07-03 07:16:40 发布

您现在位置: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)

有人举过这样的例子吗/为什么我需要这个?你知道吗


Tags: ofselfobjgetreturnifobjectdef
2条回答

您发布的代码用作描述符。 这有以下效果:如果一个类有一个描述符的对象,那么一个实例就有一个与类中的对象同名的属性。你知道吗

如果设置该属性,则调用描述符的__set__(self, instance, value)命令。你知道吗

如果删除它,则调用描述符时使用__delete__(self, instance)函数。你知道吗

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

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

因此,在这种情况下,接收具有底层primitive的属性的数据会导致py2的types.MethodType(self, instance)或py3的types.MethodType(self, instance, owner),其中self是原语,instance是检索属性的对象,owner是持有primitive对象的类。(如前所述)

我希望我能帮上忙

代号lambda

在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描述符对象时,都是在类实例对象上创建绑定方法。你知道吗

这里的“descriptor”特性只有在用作类属性时才真正被使用,通过primitivedocstring读取它提到的类包装函数作为装饰器。你知道吗

在下面的几行中,你可以看到它作为一个装饰者在起作用:

@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正在调用所有 两个“ops”列表中的字符串。同样的setattr调用primitive是 调用getattr来检索相同的内置方法 来自类型float的名称,将其作为初始func参数传入。现在,无论何时访问这些操作,它们都是绑定方法。你知道吗

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

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

您将得到一个绑定方法,它封装了primitive所拥有的所有优点(即梯度函数)。你知道吗

相关问题 更多 >