引言

在前面的章节中,我们讲解了python使用函数指针设置测试状态。在那里,我们只需要通过将定义的函数名赋值给变量,然后在调用就可以。但是还有一个问题,就是我们需要手动填写被调用的状态的维度,这个在有些时候会出现问题。不管是在训练或者在复现的时候,都会因为马虎忘记改写。

而且传统的python字典不支持自引用查找,就是设置字典里的键,其值与字典中其他的键值有关。因此今天来讲解下python自引用字典的代码。

自引用字典

首先给出完整代码:

class ParamDict(dict):
    def __getitem__(self, key):
        val = dict.__getitem__(self, key)
        return callable(val) and val(self) or val
P=ParamDict({
    'a':1,
    'b':lambda self:self['a']
})

以此代码为例进行讲解。在这个类中,分别用到了__getitem__内置方法以及lambda方法以及callable函数。

__getitem__

这个类内置函数实现的功能是,当使用类[key]这样的语句后,就会自动查找类中定义的__getitem__方法,并返回这个方法的return 值。

不论数据结构是否是字典,只要在类中定义了这样的函数,就可以返回。

lambda

lambda这个函数应该是比较常见的了,他实际起到了函数的作用。其用法是:

a = lambda:x:x+1

针对上面的代码,当我们执行这样的语句时:

a = lambda x:x+1
print(a(2))

得到的结果是:

可以看到,它并没有像传统的函数定义那样使用def,就可以实现函数调用的效果。

callable

这个函数的作用是,判断一个对象是否是可以调用的,如果是,返回True;不是,返回False。

注意像a = 1这样的就不是一个可以调用的对象。

对于类来说,其需要在其中实现__call__方法,才可以被认为是可以调用的对象。而__call__方法的作用是,使类具有像函数一样的使用形式,如:

class a():
    def __init__(self,data):
        a = data
    def __call__(self, b):
        print(b)
yes = a(4)
yes(3)

这里这样写是为了提醒大家,虽然__init__也可传参,但是其传参数的位置却和__call__方法需要的地方不一样。__init__需要在实例一个类的时候传入参数,而__call__需要在使用的时候传入。

解析

class ParamDict(dict):
    def __getitem__(self, key):
        val = dict.__getitem__(self, key)
        return callable(val) and val(self) or val
P=ParamDict({
    'a':1,
    'b':lambda self:self['a']
})
P['b']

在此回到代码,我们看到,这里从函数的执行顺来看,需要执行两次__getitem__。

第一次:P['b']

这时候val是一个lambda对象,从callable()也可以看到是True

第二次:self['a']

第二次再执行,是因为第一次返回的True的同时执行了val(self)。

而这时候再次执行__getitem__时,得到的val是一个具体的值,因为从父类继承的dict方法中可以检索到这个值(这里源代码隐藏了,看不到具体的代码,但是从分析也可以i知道其意义)。

在这时正好callable为false,可以返回真正的需要的值了。

妙啊~~~

我们通过print对上面的分析进行检验

class ParamDict(dict):
    def __getitem__(self, key):
        val = dict.__getitem__(self, key)
        print(val)
        print(callable(val))
        print(val)
        return callable(val) and val(self) or val
P=ParamDict({
    'a':1,
    'b':lambda self:self['a']
})
print(P['b'])

当我们把val(self)去掉后

class ParamDict(dict):
    def __getitem__(self, key):
        val = dict.__getitem__(self, key)
        print(val)
        print(callable(val))
        print(val)
        return callable(val)
P=ParamDict({
    'a':1,
    'b':lambda self:self['a']
})
print(P['b'])

可以看到,只调用了一次。

May the force be with you!