Reputation: 2161
I'm writing an implementation of doubly linked lists. In order to traverse the list, I'm using something like:
class Node:
""" A node in our linked list """
def __init__(self, value: Any, next: Union['Node', None] =None,
previous: Union['Node', None] =None) -> None:
self.value = value
self.next = next
self.previous = previous
...
def __next__(self, direction: int =1) -> Union['Node', None]:
if direction == 1:
return self.get_next()
else:
return self.get_previous()
...
where get_next
and get_previous
are just getters of self.next
and self.previous
.
However, PyCharm yells at me for trying to call next
as
next(some_node, direction=-1)
. What's the proper way to do this?
Upvotes: 2
Views: 2887
Reputation: 8510
__next__
is part of the iterator protocol and should be used as described in said protocol, doing otherwise only make problems with the rest python.
In your case just rename the function to simple next
and use as some_node.next(-1)
, though I would change the direction
argument to a boolean, as that is how you use it, and its name too. Like this for example
class None:
...
def next(self, forward:bool=True) -> Union['Node', None]:
if forward:
return self.get_next()
else:
return self.get_previous()
and use as some_node.next()
, some_node.next(False)
or even some_node.next(0)
(using 0 instead of False for the same effect)
Upvotes: 2
Reputation: 152587
Besides __iter__
there is also __reversed__
. Both are required to return iterators
. The __next__
method should be implemented on iterators
(not on node-classes). Note that all magic methods (when called by a function like next
instead of directly invoked) need to implement the expected arguments not more - not less.
For example a doubly linked list could just implement __iter__
and __reversed__
and rely on next
and previous
attribute of the Node:
class Node(object):
def __init__(self, val, nxt, prv):
self.val = val
self.nxt = nxt
self.prv = prv
class DoublyLinkedList(object):
def __init__(self, base=None, last=None):
self.base = base
self.last = last
def prepend(self, val):
new = Node(val, self.base, None)
if self.base is None:
self.base = new
self.last = new
else:
self.base.prv = new
self.base = new
def append(self, val):
new = Node(val, None, self.last)
if self.last is None:
self.base = new
self.last = new
else:
self.last.nxt = new
self.last = new
def __iter__(self):
current = self.base
while current is not None:
yield current
current = current.nxt
def __reversed__(self):
current = self.last
while current is not None:
yield current
current = current.prv
For example:
dl = DoublyLinkedList()
dl.prepend(10)
dl.prepend(20)
dl.prepend(30)
for i in dl:
print(i.val)
gives:
30
20
10
similar for reversed
:
for i in reversed(dl):
print(i.val)
# prints:
10
20
30
Upvotes: 4
Reputation: 177530
The extra argument to next
is a default value, and __next__
doesn't take any extra arguments. Python doesn't have any sort of two-way iterators. If your interface is not exactly the same as for i in obj:
, then you should write your own.
Upvotes: 1