2013-04-16 73 views
4

hasattr(obj, attribute)用於檢查對象是否具有指定的屬性,但是如果給定屬性,是否有辦法知道它定義在哪裏(全部)?Python中的hasattr的反轉

假設我的代碼以字符串的形式獲取屬性(或類方法)的名稱,我想調用classname.attribute但我沒有類名。這在我腦海

一種解決方案是該

def finder(attr): 
    for obj in globals(): 
     try: 
      if globals()[obj].__dict__[attr]: 
       return(globals()[obj]) 
     except: 
      ... 

用法:

​​

我敢肯定,這是不是最好的(OE甚至正確的方法)來做到這一點。有人可以提供一個更好的方法。

+0

看來,遍歷'全局()項目()'會更容易些,但還是不漂亮......。 – mgilson

+4

這似乎是一個可怕的情況。 – delnan

+10

爲什麼你會需要這樣做?對於您正在查找的函數,它們是否始終是在同一個模塊中定義的類的類方法? –

回答

4

它總是「可能的」。再好的是另一個歷史。

一個快速而骯髒的方法是對所有類進行線性迭代,並檢查是否有定義你擁有的屬性。當然,這是有衝突的,它會產生具有這種命名屬性的第一類。如果在一個以上的存在,它是由你來決定你想要的:

def finder(attr): 
    for cls in object.__subclasses__(): 
     if hasattr(cls, attr): 
       return cls 
    raise ValueError 

而是在「全局」搜索這個搜索的「對象」的所有子類 - 因而被發現別班」 t需要位於finder函數所在的模塊的命名空間中。

如果你的方法在你正在搜索的一組類中是唯一的,但是,也許你可以組裝一個所有方法的映射,並用它來調用它們。

讓我們假設所有的類名爲「基地」一類inehrit:

mapper = {attr_name:getattr(cls, attr_name) for cls in base.__subclasses__() for attr_name, obj in cls.__dict__.items() 
      if isinstance(obj, classmethod) } 

你叫他們mapper['attrname']()

這樣可以避免在每次方法調用的線性搜索,因此會好得多。

- 編輯 -

__subclassess__只要找到一個類,而不是繼承樹的直接子類 - 因此它不會在「現實生活」有用的 - 也許是在specifc情況下,OP掌握在其中。
如果需要通過繼承樹查找事物,則還需要遍歷每個子類。

至於舊式的類:當然這不會起作用 - 這是他們在新代碼中被默認破壞的動機之一。

至於非類屬性:無論如何它們只能被發現檢查實例 - 所以需要考慮另一種方法 - 這裏似乎不是O.P.的關注點。

+0

這是一個非常不體面的情況。謝謝jsbueno。 –

+1

由於重複(又名「衝突」)是可能的,我認爲最好是返回所有具有該屬性的類的列表 - 儘管這會使它更慢,因爲它一旦發現它就不能退出比賽。 – martineau

+3

這是一個聰明的解決方案,但您還需要遍歷找到的類的子類。它不會找到沒有從對象繼承的舊式Python 2.x類,雖然它會找到類方法,但它不會找到不屬於類架構一部分的數據屬性。 – tdelaney

2

這可能幫助:

import gc 


def checker(checkee, maxdepth = 3): 

    def onlyDict(ls): 
     return filter(lambda x: isinstance(x, dict), ls) 

    collection = [] 
    toBeInspected = {} 
    tBI = toBeInspected 
    gc.collect() 
    for dic in onlyDict(gc.get_referrers(checkee)): 
     for item, value in dic.iteritems(): 
      if value is checkee: 
       collection.append(item) 
      elif item != "checker": 
       tBI[item] = value 

    def _auxChecker(checkee, path, collection, checked, current, depth): 
     if current in checked: return 
     checked.append(current) 
     gc.collect() 
     for dic in onlyDict(gc.get_referents(current)): 
      for item, value in dic.iteritems(): 
       currentPath = path + "." + item 
       if value is checkee: 
        collection.append(currentPath) 
       else: 
        try: 
         _auxChecker(checkee, currentPath, collection, 
            checked, value, depth + 1) 
         if depth < maxdepth else None 
        except TypeError: 
         continue 
    checked = [] 
    for item, value in tBI.iteritems(): 
     _auxChecker(checkee, item, collection, checked, value, 1) 
    return collection 

使用方法:

referrer = [] 

class Foo:   
     pass   

noo = Foo()  
bar = noo   
import xml   
import libxml2  
import sys   
import os   
op = os.path  
xml.foo = bar  
foobar = noo  

for x in checker(foobar, 5): 
    try: 
     y= eval(x) 
     referrer.append(x) 
    except: 
     continue 

del x, y 

PS:在checkee的屬性將不會進一步檢查,對於給checkee本身的遞歸或嵌套引用。

+0

如果OP wnats dto llok所有的類都在當前模塊的名字空間中(「checker」被定義的模塊),那麼它就可以工作。我不認爲這是做這件事的最好方法。 – jsbueno

+0

同意。答案已更新。 –

1

這應該在所有情況下工作,但仍然需要大量的測試:

import inspect 
import sys 


def finder(attr, classes=None): 
    result = [] 
    if classes is None: 
     # get all accessible classes 
     classes = [obj for name, obj in inspect.getmembers(
      sys.modules[__name__])] 
    for a_class in classes: 
     if inspect.isclass(a_class): 
      if hasattr(a_class, attr): 
       result.append(a_class) 
      else: 
       # we check for instance attributes 
       if hasattr(a_class(), attr): 
        result.append(a_class) 
      try: 
       result += finder(attr, a_class.__subclasses__()) 
      except: 
       # old style classes (that don't inherit from object) do not 
       # have __subclasses; not the best solution though 
       pass 
    return list(set(result)) # workaround duplicates 


def main(attr): 
    print finder(attr) 
    return 0 


if __name__ == "__main__": 
    sys.exit(main("some_attr")) 
+0

謝謝sooo。我會試一下 –