我曾经使用reduce and getattr函数以链式方式调用属性,例如“thisattr.thatattr.blaattar” IE:
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]'
错误。
对此有哪些优势解决方案,哪种方式都适用?
此致
答案 0 :(得分:1)
之后你可能希望调用一些方法而不是获取属性。快速重新实现部分python方法将成为一场噩梦。甚至当前对getattr / getitem支持的要求也不能解决为单行。
相反,您可以使用python本身来解释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'
答案 1 :(得分: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
更快:
~: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
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) )
答案 2 :(得分:0)
你需要
如您所见,这涉及两种不同的操作。 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
离开我的头顶,因此未经测试。
答案 3 :(得分:0)
您要求的内容似乎非常困难,因为您希望将属性选择与方法调用混合(因为索引只是调用的糖)。通过使用getattr为您提供绑定方法,调用函数很容易,但是您需要将包含参数的字符串部分转换为实际参数。
鉴于你无论如何都需要一个eval()来计算参数,为什么不只是评估整个事物呢?
def proc(objname, attrstring ) :
return eval( '%s.%s' % (objname,attrstring) )
那么你的榜样是:
proc("myobject", "xattr.yattr[2].zattr")
答案 4 :(得分: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')
答案 5 :(得分:0)
我用这个
reduce(lambda i, j: getattr(i, j), 'xattr.yattr.zattr'.split('.'), myobject)