我有一個用於分析我的運動數據(我刮runkeeper的網站)的API。在循環中動態添加方法時的範圍gotcha
我的主類是pandas.DataFrame
的子類,它基本上是表格數據的容器。它支持按列名進行索引,並返回列值的數組。
我想添加一些基於數據中存在的「健身活動」類型的便利性。因此,例如,我想添加屬性「跑步」:
@property
def running(self):
return self[self['type'] == 'running']
這將返回已在「類型」列「跑步」的DataFrame
的所有行。
我試圖爲數據中存在的所有類型動態地執行此操作。這是我天真地做:
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'在實際定義時的值。
任何人都可以解決這個問題嗎?我能想到兩個,但他們似乎並不特別理想:
定義
__getattr__
檢查屬性是活動類型,並返回相應的行。使用遞歸函數調用而不是for循環,以便每個遞歸級別都有自己的名稱空間。
這兩者都是我的口味有點太巧了,pandas.DataFrame
已經有一個__getattr__
,我不得不小心地用,如果我做一個太互動。遞歸會起作用,但是感覺非常錯誤,因爲這組類型沒有任何內在樹狀結構。它很平坦,應該在代碼中看起來平坦!
完美,這正是我需要的!但是,如果這個對象有多個實例,lambda(self)的第一個參數可能不應該有默認集合。這是因爲,正如我剛剛發現的那樣,您必須爲類添加屬性而不是實例(在我的問題中編輯)。所以我們不希望該屬性指向特定的實例,否則這些對象將會返回對方的數據! –