2012-09-29 10 views
8

Possible Duplicate:
Subclassing Python tuple with multiple __init__ argumentsJak zainicjować instancję podklasy krotki w Pythonie?

Chcę, aby zdefiniować klasę, która dziedziczy z tuple i chcę, aby móc instancję za pomocą składni nie obsługiwanego przez tuple. Dla prostego przykładu, powiedzmy, chcę zdefiniować klasę MyTuple, która dziedziczy po tuple i którą mogę utworzyć, przekazując dwie wartości, x i y, aby utworzyć (moją) krotkę (x, y). Próbowałem następujący kod:

class MyTuple(tuple): 
    def __init__(self, x, y): 
     print("debug message") 
     super().__init__((x, y)) 

Ale kiedy próbowałem na przykład MyTuple(2, 3) mam błąd: TypeError: tuple() takes at most 1 argument (2 given). Wygląda na to, że moja funkcja __init__ nie została wywołana (w oparciu o błąd, który otrzymałem i na fakt, że moja "wiadomość debugowania" nie została wydrukowana).

Co jest właściwym sposobem na zrobienie tego?

Używam Python 3.2.

+3

Możesz rzucić okiem na ['collections.namedtuple()' '] (http://docs.python.org/dev/library/collections.html#collections.namedtuple). –

+2

Proszę sprawdzić [to oryginalne pytanie SO] (http://stackoverflow.com/questions/1565374/subclassing-python-tuple-with-multiple-init-arguments), aby uzyskać dokładną odpowiedź na (1) dlaczego musisz użyć '. __new__' zamiast '__init__' (2) jakie inne kroki musisz wykonać. –

Odpowiedz

12
class MyTuple(tuple): 
    def __new__(cls, x, y): 
     return tuple.__new__(cls, (x, y)) 

x = MyTuple(2,3) 
print(x) 
# (2, 3) 

Jedną z trudności przy użyciu super jest to, że nie masz kontroli, która metoda klas o tej samej nazwie będzie nazywany dalej. Zatem wszystkie metody klas muszą mieć ten sam podpis połączenia - co najmniej taką samą liczbę elementów. Ponieważ zmieniasz liczbę argumentów wysłanych na __new__, nie możesz użyć super.


Albo jak sugeruje Lattyware, można zdefiniować namedtuple,

import collections 
MyTuple = collections.namedtuple('MyTuple', 'x y') 

p = MyTuple(2,3) 
print(p) 
# MyTuple(x=2, y=3) 
print(p.x) 
# 2 
+5

Można użyć '* tuple' (lub' * args') zamiast 'x, y', a następnie uogólniłoby się do dowolnego rozmiaru. –

1

innym podejściem byłoby hermetyzacji krotki zamiast dziedziczenie z nim:

>>> class MyTuple(object): 
    count = lambda self, *args: self._tuple.count(*args) 
    index = lambda self, *args: self._tuple.index(*args) 
    __repr__ = lambda self: self._tuple.__repr__() 
    # wrap other methods you need, or define them yourself, 
    # or simply forward all unknown method lookups to _tuple 
    def __init__(self, x, y): 
     self._tuple = x,y 


>>> x = MyTuple(2,3) 
>>> x 
(2, 3) 
>>> x.index(3) 
1 

Jak praktyczny jest , zależy od tego, ile funkcji i modyfikacji potrzebujesz i czy musisz mieć isinstance(MyTuple(2, 3), tuple).

+0

Interesujące. Zajęło mi trochę czasu, aby zrozumieć, co wygląda na zadania terenowe i lambda. Jakiś szczególny powód, dla którego nie używasz normalnej konstrukcji "def"? A także, w jaki sposób "przekazujesz wszystkie nieznane odnośniki do' _tuple' "? – Tom

+0

bez szczególnego powodu dla lambdas, po prostu lubię "wiązać" takie funkcje. 'count = lambda: ..." mówi mi "count ZOSTAŁ ZADOWOLONY, ABY BYĆ liczbą _tuple". – ch3ka

+2

Możesz przekierować nieznane metody wyszukiwania za pomocą 'def __getattr __ (self, attr): \t \t return self._tuple .__ getattribute __ (attr)' – ch3ka