Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/278.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
my reduce getattr问题的Python解决方案_Python_Attributes_Reduce_Getattr - Fatal编程技术网

my reduce getattr问题的Python解决方案

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 如您所见,这涉及两种不同的操作减少不能(优雅地)做到这一点。对于这两

我曾经使用函数以链式方式调用属性,如“thisattr.thattr.blaattar” 即:

工作非常好,但是现在我有一个新的要求,我的字符串可以调用属性的特定数字,例如:“thisattr.thatattr[2].blaattar”

现在它不起作用了,我得到
xattr对象没有属性“yattr[2]”
error

对于这两种情况,哪种解决方案最有效

关于

您需要

  • 获取xattr.yattr
  • 获取第二项
  • 对于第二项,获取zattr
  • 如您所见,这涉及两种不同的操作<代码>减少不能(优雅地)做到这一点。对于这两者都有效的解决方案必须解析字符串,以检测需要索引访问的位置。简单但脆弱的解决方案(即,如果fed 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
    

    因此未经测试。

    稍后您可能希望调用某个方法,而不是获取属性。快速重新实现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')