2017-02-27 2 views
0

Le code ci-dessous provient de la base de code de SCons. Nous travaillons sur le portage du code de sorte qu'il fonctionnera avec Python 2.7.x et 3.x.Comment créer une classe List like qui permet d'appeler des méthodes d'objets contenus sur des tranches qui fonctionnent à la fois avec py2 et py3

Le code ci-dessous fonctionne très bien sous 2.7.x python, mais lorsqu'il est exécuté en python 3.5 échoue comme suit:

python3.5 ~/tmp/blah123.py Traceback (most recent call last):
File "/home/bdbaddog/tmp/blah123.py", line 73, in print("stuff:%s"%nl[0:2].bar) AttributeError: 'list' object has no attribute 'bar'

Ce code est un peu à cœur la fonctionnalité de SCons. Toute aide serait la bienvenue. (Voir le code original ici: src/engine/SCons/Util.py)

from __future__ import print_function 


try: 
    from UserList import UserList 
except ImportError as e: 
    from collections import UserList 


class NodeList(UserList): 
    """This class is almost exactly like a regular list of Nodes 
    (actually it can hold any object), with one important difference. 
    If you try to get an attribute from this list, it will return that 
    attribute from every item in the list. For example: 

    >>> someList = NodeList([ ' foo ', ' bar ' ]) 
    >>> someList.strip() 
    [ 'foo', 'bar' ] 
    """ 
    def __nonzero__(self): 
     return len(self.data) != 0 

    def __bool__(self): 
     return self.__nonzero__() 

    def __str__(self): 
     return ' '.join(map(str, self.data)) 

    def __iter__(self): 
     return iter(self.data) 

    def __call__(self, *args, **kwargs): 
     result = [x(*args, **kwargs) for x in self.data] 
     return self.__class__(result) 

    def __getattr__(self, name): 
     result = [getattr(x, name) for x in self.data] 
     return self.__class__(result) 

# def __getitem__(self, index): 
#  return self.__class__(self.data[index]) 
#  return self.data[index] 

    def __getitem__(self, index): 
     """ 
     This comes for free on py2, 
     but py3 slices of NodeList are returning a list 
     breaking slicing nodelist and refering to 
     properties and methods on contained object 
     """ 
#  return self.__class__(self.data[index]) 

     if isinstance(index, slice): 
      # Expand the slice object using range() 
      # to a maximum of eight items. 
      return [self[x] for x in 
        range(*index.indices(8))] 
     else: 
      # Return one item of the tart 
      return self.data[index] 


class TestClass(object): 
    def __init__(self, name, child=None): 
     self.child = child 
     self.bar = name 

t1 = TestClass('t1', TestClass('t1child')) 
t2 = TestClass('t2', TestClass('t2child')) 
t3 = TestClass('t3') 

nl = NodeList([t1, t2, t3]) 
print("stuff:%s"%nl[0:2].bar) 
print("another:%s"%nl[1:].bar) 


assert nl.bar == [ 't1', 't2', 't3' ], nl.bar 
assert nl[0:2].child.bar == [ 't1child', 't2child' ], \ 
     nl[0:2].child.bar 

for f in nl: 
    print("->%s"%f.bar) 

Répondre

3

Votre __getitem__ appelée avec un slice devrait probablement retourner une nouvelle instance de la même classe à nouveau. Par exemple:

def __getitem__(self, index): 
    if isinstance(index, slice): 
     return self.__class__(self[x] for x in 
           range(*index.indices(len(self.data))) 
    else: 
     return self.data[index] 

ensuite vos impressions de cas de test:

stuff:t1 t2 
->t1 
->t2 
->t3 
+0

Merci! Cela couvrait presque tous les tests de la suite de tests complète. Un test unitaire échoue toujours. Si je peux suivre et simplifier, je vais ajouter à l'exemple de code ci-dessus. – bdbaddog

+0

Ajout du test mis à jour ci-dessus voir "autre". Fix est en fait de supprimer le maximum de 8 dans les index.indices (8) ci-dessus. Et remplacez par index.indices (len (self.data)). Si vous modifiez ci-dessus, je marquerai comme réponse. – bdbaddog

+0

@bdbaddog Super! Je suis content que ça marche :) – MSeifert