patternpythonMinor
Doubly-linked list implementation in Python with unit-tests
Viewed 0 times
implementationwithtestsdoublypythonlistlinkedunit
Problem
I am soliciting some feedback on my doubly-linked list implementation. In particular, I am looking for your take on my unit-tests and their coverage of the code itself. That is, I developed this class using the test-driven development approach and am wondering how it turned out. In addition, I would welcome any suggestions on making the code more readable and/or efficient (eliminate redundancies, etc).
```
class DLinkedList(object):
_size = 0
class Node(object):
def __init__(self, _value, _previous, _next):
self.value = _value
self.next = _next
self.previous = _previous
def __eq__(self, other):
return (self.value == other.value) and (self.next == other.next) and (self.previous == other.previous)
def __init__(self):
self.head = None
self.tail = None
self._size = 0
def __len__(self):
if self.head:
return self._size
else:
return 0
def __str__(self):
current = self.head
to_print = []
while current:
to_print.append(current.value)
current = current.next
return str(to_print)
def insert(self, _value, _position=0, _previous = None, _next = None):
if _value is None:
raise ValueError('Cannot add None item to a list')
if _position < 0:
raise ValueError('Cannot add to negative position in the list')
if _position == 0:
if self.head:
new_node = self.Node(_value, None, self.head)
old_node = self.head
self.head = new_node
old_node.previous = new_node
self._size = self._size + 1
else:
new_node = self.Node(_value, None, None)
self.tail = new_node
self.head = new_node
self._size = self._size + 1
return new_node
else:
curr
```
class DLinkedList(object):
_size = 0
class Node(object):
def __init__(self, _value, _previous, _next):
self.value = _value
self.next = _next
self.previous = _previous
def __eq__(self, other):
return (self.value == other.value) and (self.next == other.next) and (self.previous == other.previous)
def __init__(self):
self.head = None
self.tail = None
self._size = 0
def __len__(self):
if self.head:
return self._size
else:
return 0
def __str__(self):
current = self.head
to_print = []
while current:
to_print.append(current.value)
current = current.next
return str(to_print)
def insert(self, _value, _position=0, _previous = None, _next = None):
if _value is None:
raise ValueError('Cannot add None item to a list')
if _position < 0:
raise ValueError('Cannot add to negative position in the list')
if _position == 0:
if self.head:
new_node = self.Node(_value, None, self.head)
old_node = self.head
self.head = new_node
old_node.previous = new_node
self._size = self._size + 1
else:
new_node = self.Node(_value, None, None)
self.tail = new_node
self.head = new_node
self._size = self._size + 1
return new_node
else:
curr
Solution
Here are some high-level "round 1" notes to make the code cleaner and more Pythonic:
-
I don't see much point in nesting the
This would also lead to using
-
there is not need to define
-
do not put underscore prefix in front of method arguments names - this kind of notation is reserved for "private" attributes (not enforced by the language though)
-
I would also remove the
-
you can also use the
-
you can omit the parenthesis around the if conditions. For instance, you may replace:
with:
Some other thoughts:
-
what if we improve the linked list "pretty-printing" and, instead of calling
- since you are implementing a "doubly linked list", name the class
DoublyLinkedListinstead of a more questionableDLinkedList
-
I don't see much point in nesting the
Node class inside the linked list, define it separately (and, you know, "Flat is better than nested"):class Node(object):
# ...
class DoublyLinkedList(object):
# ...This would also lead to using
Node in place of self.Node (and you would also need to use node variable name in place of Node in the insert() method) -
there is not need to define
_size on the class level - leave it defined in the class constructor:class DoublyLinkedList(object):
_size = 0 # <-- REMOVE THIS LINE-
do not put underscore prefix in front of method arguments names - this kind of notation is reserved for "private" attributes (not enforced by the language though)
-
I would also remove the
_ from the _size variable - I don't see why would you "hide" it from your linked list's public API-
you can also use the
size += 1 and size -= 1 shortcuts instead of size = size + 1 and size = size - 1-
you can omit the parenthesis around the if conditions. For instance, you may replace:
def __eq__(self, other):
return (self.value == other.value) and (self.next == other.next) and (self.previous == other.previous)with:
def __eq__(self, other):
return self.value == other.value and \
self.next == other.next and \
self.previous == other.previousSome other thoughts:
- check out
attrspackage that is quite handy and helps to avoid a lot of classes/properties/attributes boilerplate
- ideally, the tests should give a clear idea on how the code under test is used, give a good sense of the available API and it's usage. The tests you currently have are not quite readable, try to make the test methods smaller aiming to "one assertion per test method" rule (which itself is extreme, but good to keep in mind), making more descriptive test method names
-
what if we improve the linked list "pretty-printing" and, instead of calling
str() on a list, we'll use ` separator. Something like:
def __str__(self):
current = self.head
to_print = []
while current:
to_print.append(current.value)
current = current.next
return "(HEAD) {items} (TAIL)".format(items=" ".join(map(str, to_print)))
would result into this printed linked list version:
In [1]: dll = DoublyLinkedList()
In [2]: dll.insert(1, 0)
...: dll.insert(3, 0)
...: dll.insert(5, 0)
In [3]: print(dll)
(HEAD) 5 3 1 (TAIL)
-
I think there is a separation of concerns issue. It feels like the Node instead of the linked list itself should decide where the value passed to it's constructor is an allowed one. Consider moving the value "is None" check to the Node` class.Code Snippets
class Node(object):
# ...
class DoublyLinkedList(object):
# ...class DoublyLinkedList(object):
_size = 0 # <-- REMOVE THIS LINEdef __eq__(self, other):
return (self.value == other.value) and (self.next == other.next) and (self.previous == other.previous)def __eq__(self, other):
return self.value == other.value and \
self.next == other.next and \
self.previous == other.previousdef __str__(self):
current = self.head
to_print = []
while current:
to_print.append(current.value)
current = current.next
return "(HEAD) {items} (TAIL)".format(items=" <--> ".join(map(str, to_print)))Context
StackExchange Code Review Q#158047, answer score: 2
Revisions (0)
No revisions yet.