2010-07-30 16 views
5

tôi sử dụng để sử dụng chức năng reduce and getattr để gọi thuộc tính trong một cách chuỗi như "thisattr.thatattr.blaattar" IE:Pythonic giải pháp cho vấn đề của tôi giảm getattr

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

trình hoàn toàn tốt đẹp, tuy nhiên bây giờ tôi có một yêu cầu mới, các chuỗi của tôi có thể gọi cho một số cụ thể của một thuộc tính như vậy: "thisattr.thatattr [2] .blaattar"

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

Bây giờ nó không hoạt động, tôi nhận được xattr object has no attribute 'yattr[2]' lỗi.

Điều gì sẽ là một giải pháp sang trọng cho điều này, hoạt động theo một trong hai cách?

Trân

Trả lời

0

Bạn sẽ cần phải

  1. Nhận xattr.yattr
  2. Lấy mục thứ hai đó
  3. Trong mục thứ hai, có zattr

Như bạn có thể thấy, điều này liên quan đến hai hoạt động khác nhau. reduce không thể làm điều đó (thanh lịch). Một giải pháp làm việc cho cả hai sẽ phải phân tích chuỗi để phát hiện nơi truy cập được lập chỉ mục là cần thiết. Giải pháp đơn giản nhưng dễ vỡ (tức là hoạt động không xác định nếu được cung cấp BS) sẽ trông giống như:

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 

Tắt đầu, do đó chưa được kiểm tra.

+0

Dường như không hoạt động. – kennytm

+0

Làm thế nào về một chút thông tin? – delnan

+0

Khi tôi cố gắng để có được 'extend_chain_getattr ('foo', Foo_obj)', nó nói 'AttributeError: 'Foo' đối tượng không có thuộc tính 'fo''. – kennytm

1

Và sau đó bạn có thể muốn gọi một số phương thức thay vì nhận thuộc tính. Việc thực hiện lại các phần của cách tiếp cận python một cách nhanh chóng sẽ trở thành một cơn ác mộng. Ngay cả yêu cầu hiện tại của hỗ trợ getattr/getitem cũng không thể được giải quyết như một lớp lót.

Thay vào đó, bạn chỉ có thể sử dụng python bản thân để giải thích trăn,

# 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

gì bạn đang yêu cầu có vẻ khá khó khăn khi bạn muốn kết hợp lựa chọn thuộc tính với các cuộc gọi phương pháp (như chỉ số là chỉ đường cho một gọi điện). Chức năng gọi là dễ dàng, đủ để làm bằng cách sử dụng getattr để cung cấp cho bạn một phương pháp ràng buộc, nhưng sau đó bạn cần phải chuyển đổi một phần của chuỗi có chứa các đối số vào các đối số thực tế.

Cho rằng bạn sẽ cần một eval() để tính toán các đối số, tại sao không chỉ đánh giá toàn bộ?

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

dụ của bạn là sau đó:

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

Bạn có thể thử:

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) 

Lưu ý rằng nó giả những thứ bên trong […] là một số thập phân không âm.


Trong trường hợp bạn lo ngại về hiệu suất, nó vẫn nhanh hơn là hơn eval trong thử nghiệm của tôi:

~: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 

Nội dung z.py:

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

Đây là một phân tích cú pháp nhỏ để xử lý lát và ký hiệu danh sách lồng nhau:

# 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

Tôi sử dụng số này

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

+0

Vui lòng xem xét chỉnh sửa bài đăng của bạn để thêm giải thích thêm về mã của bạn và tại sao nó sẽ giải quyết vấn đề. Một câu trả lời mà hầu hết chỉ chứa mã (ngay cả khi nó hoạt động) thường không giúp OP hiểu được vấn đề của họ. – SuperBiasedMan

+0

Tại sao bạn thậm chí sử dụng lambda? Bạn chỉ có thể truyền 'getattr' làm đối số đầu tiên của' reduce'. –