Python 在循环中动态添加方法时,作用域已捕获

Python 在循环中动态添加方法时,作用域已捕获,python,pandas,Python,Pandas,我有一个API来分析我的运动数据(我从scrape的网站上下载) 我的主类是pandas.DataFrame的一个子类,它基本上是一个表格数据的容器。它支持按列名索引,返回列值数组 我想根据数据中存在的“健身活动”类型添加一些便利属性。例如,我想添加一个属性“running”: @property def running(self): return self[self['type'] == 'running'] 它将返回数据帧中所有在“type”列中具有“running”的行 我尝试对

我有一个API来分析我的运动数据(我从scrape的网站上下载)

我的主类是pandas.DataFrame的一个子类,它基本上是一个表格数据的容器。它支持按列名索引,返回列值数组

我想根据数据中存在的“健身活动”类型添加一些便利属性。例如,我想添加一个属性“running”:

@property
def running(self):
    return self[self['type'] == 'running']
它将返回数据帧中所有在“type”列中具有“running”的行

我尝试对数据中存在的所有类型动态执行此操作。下面是我天真地做的:

class Activities(pandas.DataFrame):
    def __init__(self,data):
        pandas.DataFrame.__init__(self,data)
        # The set of unique types in the 'type' column:
        types = set(self['type'])
        for type in types:
            method = property(lambda self: self[self['type'] == type])
            setattr(self.__class__,type,method)
结果是,所有这些属性都返回了同一类型活动(“行走”)的数据表

发生的事情是,当访问属性时,会调用lambda,它们会在定义名称“type”的范围内查找。他们发现它绑定到字符串“walking”,因为这是for循环的最后一次迭代。for循环的每个迭代都没有自己的名称空间,因此所有lambda只看到最后一次迭代,而不是实际定义时“type”所具有的值

有人能想出一个办法来解决这个问题吗?我能想到两种,但它们似乎并不特别理想:

  • 定义
    \uuuu getattr\uuuu
    以检查属性是否为活动类型并返回相应的行

  • 使用递归函数调用而不是for循环,这样每个递归级别都有自己的名称空间


  • 这两个对我的口味来说都有点太聪明了,而且
    pandas.DataFrame
    已经有了一个
    \uu getattr\uuuu
    ,如果我也做了一个,我必须谨慎地与之交互。递归可以工作,但感觉非常错误,因为类型集没有任何内在的树状结构。它是平面的,在代码中应该是平面的

    修改
    lambda
    以将值拉入新范围

    method = property(lambda self=self, type=type: self[self['type'] == type])
    

    老实说,如果可以避免的话,我建议不要创建DataFrame的子类。根据我的经验,Java古老的格言“重组合轻继承”更可取

    太好了,这正是我需要的!但是,如果这个对象有多个实例,lambda(self)的第一个参数可能不应该有默认设置。这是因为,正如我刚刚发现的,您必须向类中添加属性,而不是实例(在我的问题中编辑)。因此,我们不希望属性指向特定实例,否则对象将返回彼此的数据!我几乎总是同意你的看法,但在这种情况下,我想要的对象在各个方面都是一个
    DataFrame
    ,唯一的区别是添加了这些方法。因此,如果我改用组合,我将真正地包装每个
    DataFrame
    方法。我猜,当子类化时,您通常只需要父类功能的一个子集,在这种情况下,我真的需要全部功能。