my reduce getattr问题的Python解决方案
我曾经使用函数以链式方式调用属性,如“thisattr.thattr.blaattar” 即: 工作非常好,但是现在我有一个新的要求,我的字符串可以调用属性的特定数字,例如:“thisattr.thatattr[2].blaattar” 现在它不起作用了,我得到my reduce getattr问题的Python解决方案,python,attributes,reduce,getattr,Python,Attributes,Reduce,Getattr,我曾经使用函数以链式方式调用属性,如“thisattr.thattr.blaattar” 即: 工作非常好,但是现在我有一个新的要求,我的字符串可以调用属性的特定数字,例如:“thisattr.thatattr[2].blaattar” 现在它不起作用了,我得到xattr对象没有属性“yattr[2]”error 对于这两种情况,哪种解决方案最有效 关于您需要 获取xattr.yattr 获取第二项 对于第二项,获取zattr 如您所见,这涉及两种不同的操作减少不能(优雅地)做到这一点。对于这两
xattr对象没有属性“yattr[2]”
error
对于这两种情况,哪种解决方案最有效
关于您需要
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
因此未经测试。稍后您可能希望调用某个方法,而不是获取属性。快速重新实现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'
您所要求的似乎相当困难,因为您希望将属性选择与方法调用混合在一起(因为索引只是调用的糖分)。通过使用getattr为您提供一个绑定方法,调用函数非常容易,但是您需要将字符串中包含参数的部分转换为实际参数 既然您需要一个eval()来计算参数,为什么不直接计算整个过程呢
def proc(objname, attrstring ) :
return eval( '%s.%s' % (objname,attrstring) )
你的例子是:
proc("myobject", "xattr.yattr[2].zattr")
你可以试试:
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) )
下面是一个处理切片和嵌套列表表示法的小型解析器:
# 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')
我用这个
reduce(lambda i,j:getattr(i,j),'xattr.yattr.zattr'.split('..),myobject)
当我试图获得扩展链\u getattr('foo',foo\u obj)
时,它说属性错误:'foo'对象没有属性'fo'
。Argh,修复了它。之前,它匹配序列(字母或u后跟字母或数字或u)0次或更多次。现在它正确地匹配任何有效的Python标识符(字母或后面跟着任意数量的字母、数字或是)。请考虑编辑您的帖子,以添加更多关于您的代码所做的解释以及它为什么会解决问题的更多解释。一个只包含代码的答案(即使它在工作)通常不会帮助OP理解他们的问题。你为什么还要使用lambda呢?只需将getattr
作为reduce
的第一个参数传递即可。
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) )
# 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')