2017-10-19 5 views
4

Ich habe eine benutzerdefinierte __dir__ Implementierung in meiner Basisklasse, die eine Liste aller benutzerdefinierten __slots__ Attribute zurückgeben soll. Dies funktioniert im Allgemeinen, aber es scheint eine sort auf das Ergebnis zu tun, bevor es zurückkehrt, obwohl ich nicht programmiert habe, um dies zu tun (ich brauche die Attribute in der Reihenfolge genau die Reihenfolge, die sie zugewiesen sind).Benutzerdefiniert __dir __() gibt eine alphabetisch sortierte Liste von Attributen zurück

Ein Beispiel:

class A: 
    __slots__ = ['b', 'a'] 

    def __dir__(self): 
     slot_attrs = [] 
     for parent_class in reversed(type(self).__mro__[:-1]): 
      for attr in parent_class.__slots__: 
       slot_attrs.append(attr) 
     for attr in self.__slots__: 
      slot_attrs.append(attr) 
     return slot_attrs 


class B(A): 
    __slots__ = ['c', 'd'] 
    pass 


class C(B): 
    __slots__ = [] 
    pass 


class D: 
    __slots__ = ['b', 'a'] 

    def slots(self): 
     slot_attrs = [] 
     for parent_class in reversed(type(self).__mro__[:-1]): 
      for attr in parent_class.__slots__: 
       slot_attrs.append(attr) 
     for attr in self.__slots__: 
      slot_attrs.append(attr) 
     return slot_attrs 


class E(D): 
    __slots__ = ['c', 'd'] 
    pass 


class F(E): 
    pass 

Die Ausgabe für slots() und __dir__() sollte, IMO, identisch.

sondern geschieht dies:

>>>c = C() 
>>>f = F() 

>>>print(dir(c)) 
['a', 'b', 'c', 'd'] 
>>>print(f.slots()) 
['b', 'a', 'c', 'd', 'c', 'd', 'c', 'd'] 

Ich kann Art von verstehen, dass es die Ausgabe alphabetisch sortiert, wenn dir() mit - das ist documented in the docs. Es sieht jedoch aus wie ein Fehler - oder zumindest ein unerwartetes Verhalten für mich -, dass es die Ausgabe sortiert, obwohl ich eine benutzerdefinierte __dir__ Methode definiert habe.

Die zweite Ausgabe wirft mich einfach komplett aus meinem Spiel. Es schlägt vor, dass dir auch eine Art von Filter verwendet, möglicherweise eine set doppelte Ausgaben zu vermeiden, da der Code identisch ist, aber Aufruf slots() gibt doppelte Werte zurück.

Ich weder A) verstehe, warum es das in erster Linie tut, noch B) was auf der Erde tut dir tut nach allem.

Irgendwelche Hinweise hier?

bearbeiten:
Der zweite Fall ist solved- __mro__ enthält die Klasse des Anrufers sowie alle Klassen aus erbt - also die Klasse zweimal enthalten ist. d.h .:

>>>F.__mro__ 
(<class '__main__.F'>, <class '__main__.E'>, <class '__main__.D'>, <class 'object'>) 

bearbeiten 2:
Die Handlung verdichtet. Die Frage in den Kommentaren verwiesen Schuppen ein kleines bisschen mehr Licht auf die Quelle dieses Verhalten:

>>Couldn't __dir__ also be allowed to return a tuple? 
no, because tuples are not sortable, and i don't want to 
over complicate the c-side code of PyObject_Dir. 
having __dir__ returning only a list is equivalent to 
__repr__ returning only strings. 

Dies geht aus dem C-Quellcode stammt, etwas zu sein, aus der Zeit vor __dir__ umgesetzt wurde.

bearbeiten 3:
Ich habe eine issue on python's bug tracker geöffnet. Mal sehen, was der Konsens ist. Allerdings erwarte ich, dass dies auf den Backburner (wenn überhaupt) gesetzt wird, da dir(), afaik, in erster Linie für die Inspektion in IDLE und dergleichen ausgelegt ist.

+0

werden alle nicht eindeutigen Elemente entfernt werden, da Sie keinen Zugriff auf Methoden mit dem gleichen Namen in Python haben. 'dir' ruft nur Ihre benutzerdefinierte Methode auf, post-verarbeitet sie jedoch, um sie mit der Dokumentation zu kompilieren. –

+0

Eigentlich entfernt ID sie nicht - ich habe es gerade getestet, es hält sie auch.Ich habe gerade '__slots__' zweimal von' F' hinzugefügt. – nlsdfnbch

+0

Es scheint immer noch wie unerwartetes Verhalten. Wenn 'dir()' '__dir __()' aufruft, dann würde ich erwarten, dass es genau so läuft, wie ich es sage, und nicht hinter den Kulissen noch Voodoo machen. – nlsdfnbch

Antwort

1

Gemäß der issue opened on the Python bug tracker:

https://docs.python.org/3/library/functions.html#dir also states that "The resulting list is sorted alphabetically." The section has an example where __dir__ returns an unsorted list but dir() returns a sorted list: 

      class Shape: 

...  def __dir__(self): 
...   return ['area', 'perimeter', 'location'] 

      s = Shape() 
      dir(s) 

['area', 'location', 'perimeter'] 

Since the primary purpose of dir() is convenient use for humans, sorting makes perfectly sense. If you need tight control over order of values, you should make your object iterable instead or provide another method. 

Several dunder methods perform some sort of post-processing or post-check: 

      class Example: 

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

      bool(Example()) 

Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
TypeError: __bool__ should return bool, returned int 

      class MyInt(int): 

...  pass 
... 

      type(MyInt(1)) 

<class '__main__.MyInt'> 

      class Example: 

...  def __int__(self): 
...   return MyInt(1) 
... 

      int(Example()) 

1 

      type(int(Example())) 

<class 'int'> 
Verwandte Themen