2010-07-30 31 views
5

我用reduce and getattr函數鏈中的方式像「thisattr.thatattr.blaattar」 IE調用屬性:Python的解決方案,我減少GETATTR問題

reduce(getattr, 'xattr.yattr.zattr'.split('.'), myobject) 

工作完全正常,但現在我有一個新的要求,我的琴絃可以撥打電話進行這樣的屬性的具體數字:「thisattr.thatattr [2] .blaattar」

reduce(getattr, 'xattr.yattr[2].zattr'.split('.'), myobject) 

現在它不工作,我得到xattr object has no attribute 'yattr[2]'錯誤。

什麼將是一個elegent解決方案,這適用於任何方式?

問候

回答

0

您需要

  1. 獲取xattr.yattr
  2. 獲取的是
  3. 第二項的第二個項目,獲得zattr

正如你可以看到,這涉及到兩個不同的操作。 reduce不能這樣做(優雅地)。爲兩者工作的解決方案必須解析字符串以檢測需要索引訪問的位置。一個簡單而脆弱的(即行爲會未定義如果喂BS)的解決辦法是這樣的:

def extended_chain_getattr(names, obj): 
    import re 
    result = obj   
    for name in names.split('.'): 
     name_match = re.match(r'([a-zA-Z_][a-zA-Z0-9_]*)(\[\d\])?', name) 
     assert name_match is not None 
     result = getattr(result, name_match.group(1)) 
     if len(name_match.groups()) == 2: 
      index = int(name_match.group(2)) 
      result = result[index] 
    return result 

關閉我的頭頂,因此未經測試。

+0

似乎並沒有工作。 – kennytm 2010-07-30 15:32:39

+0

如何獲得更多信息? – delnan 2010-07-30 16:28:20

+0

當我嘗試獲得'extended_chain_getattr('foo',Foo_obj)'時,它說'AttributeError:'Foo'對象沒有屬性'fo''。 – kennytm 2010-07-30 18:36:41

1

然後,您可能希望調用某種方法而不是獲取屬性。很快重新實施Python方法的一部分將成爲一場噩夢。即使getattr/getitem支持的當前需求也不能作爲單線程來解決。

相反,你可以只使用Python本身來解釋蟒蛇,

# Create some object for testing 
>>> class A(object): 
...  b = None 
... 
>>> a = A() 
>>> a.b = A() 
>>> a.b.b = A() 
>>> a.b.b.b = [A(), A(), A(), A()] 
>>> a.b.b.b[1].b 
>>> a.b.b.b[1].b = "Some result" 
>>> 
>>> ctx = {'obj':a, 'val':None} 
>>> exec("val = obj.{0}".format('b.b.b[1].b')) in ctx 
>>> ctx['val'] 
'Some result' 
0

你所要求的,只要你想混合使用的方法調用屬性選擇(因爲指數是一個公正的糖似乎相當困難呼叫)。通過使用getattr爲您提供綁定方法,調用函數非常容易,但您需要將包含參數的字符串部分轉換爲實際參數。

鑑於您將需要一個eval()來計算參數,爲什麼不只是評估整個事情呢?

def proc(objname, attrstring) : 
    return eval('%s.%s' % (objname,attrstring)) 

你舉的例子則是:

proc("myobject", "xattr.yattr[2].zattr") 
1

你可以嘗試:

import re 
extended_split = re.compile(r'''\[\d+\]|[^\[.]+''').findall 

def extended_getattr(obj, comp): 
    if comp[0] == '[': 
     return obj[int(comp[1:-1])] 
    else: 
     return getattr(obj, comp) 

reduce(extended_getattr, extended_split('xattr.yattr[2].zattr'), myobject) 

注意,它假定內[…]的東西是一個非負十進制數。


如果你對性能的關注,但仍快於我的測試eval

內容的 z.py
~:491$ python -m timeit -s 'from z import f1, f3, f, rs' 'f3(rs, "f")' # eval 
100 loops, best of 3: 5.62 msec per loop 

~:492$ python -m timeit -s 'from z import f1, f3, f, rs' 'f1(rs, f)'  # my method 
100 loops, best of 3: 4.69 msec per loop 

import re 
import random 
from functools import reduce 

extended_split = re.compile(r'''\[\d+\]|[^\[.]+''').findall 

def extended_getattr(obj, comp): 
    if comp[0] == '[': 
     return obj[int(comp[1:-1])] 
    else: 
     return getattr(obj, comp) 

class Foo(object): 
    def __init__(self): 
     self.foo = self 

    def __getitem__(self, i): 
     return self 

def construct_random_string(): 
    yield 'foo' 
    for i in range(2000): 
     if random.randrange(2): 
      yield '.foo' 
     else: 
      yield '[0]' 


random.seed(0) # to ensure fair comparison 
rs = ''.join(construct_random_string()) 

f = Foo() 

def f1(names, obj): 
    return reduce(extended_getattr, extended_split(names), obj) 

def f3(attrstring, objname) : 
    return eval('%s.%s' % (objname, attrstring)) 
0

這是一個很小的解析器來處理切片和嵌套列表符號:

# define class that we can just add attributes to 
class Bag(object): pass 

z = Bag() 
z.xattr = Bag() 
z.xattr.yattr = [Bag(), Bag(), Bag()] 
z.xattr.yattr[2].zattr = 100 
z.xattr.yattr[1] = [0,1,2,3,4,5] 

from pyparsing import * 

LBRACK,RBRACK = map(Suppress,'[]') 
ident = Word(alphas+"_", alphanums+"_") 
integer = Word(nums+'-',nums).setParseAction(lambda t:int(t[0])) 
NONE = Literal("None").setParseAction(replaceWith(None)) 
indexref = LBRACK + Group(delimitedList((Optional(integer|NONE,None)), delim=':')) + RBRACK 
compoundAttr = delimitedList(Group(ident("name") + ZeroOrMore(indexref)("index")), delim='.') 

def lookup(ob, attr): 
    try: 
     attrParts = compoundAttr.parseString(attr) 
    except ParseException: 
     raise AttributeError("could not resolve compound attribute '%s'" % attr) 

    # remaining code will raise AttributeError or IndexError as appropriate 

    ret = ob 
    for a in attrParts: 
     ret = getattr(ret, a.name) 
     if a.index: 
      for i in a.index: 
       if len(i) == 1: 
        ret = ret[i[0]] 
       else: 
        ret = ret[slice(*i.asList())] 
    return ret 


print len(lookup(z, 'xattr.yattr')) 
print len(lookup(z, 'xattr.yattr[1:3]')) 
print len(lookup(z, 'xattr.yattr[None:3]')) 
print lookup(z, 'xattr.yattr[1][None:4]') 
print sum(lookup(z, 'xattr.yattr[1][:4]')) 
print lookup(z, 'xattr.yattr[2].zattr') 
0

我用這個

reduce(lambda i, j: getattr(i, j), 'xattr.yattr.zattr'.split('.'), myobject)

+0

請考慮編輯您的帖子以添加更多關於您的代碼的解釋以及爲什麼它可以解決問題。一個主要包含代碼的答案(即使它正在工作)通常不會幫助OP瞭解他們的問題。 – SuperBiasedMan 2015-09-18 09:18:50

+0

爲什麼你甚至使用lambda?你可以傳遞'getattr'作爲'reduce'的第一個參數。 – 2015-12-27 22:15:27