Singly Linked List in Python
$begingroup$
A school student here. Hope you are having a nice day.
So, I implemented a Singly Linked List in Python and of course also a Node. I tried to make it have the best Big O time complexity I could. Here it is, please tell me what do you think can be improved, changed, etc.
class LinkedList():
"""
A basic class implementing a Singly Linked List.
LinkedList() - new empty linked list
LinkedList(iterable) - new linked list with items of iterable:
head - iterable[0] tail - iterable[-1]
"""
class Node():
"""A class for a singly linked list node."""
def __init__(self, value):
"""Initialize default values."""
self.value = value
self.link = None # by default a node is linked to nothing
def __init__(self, seq=()):
"""Initialize default values."""
self.size = 0
# While instantiated there are no elements in list
# but None, to which will be linked the first element
self.head = None
node = self.head
for i in seq: # iterate through and copy contents
new_node = self.Node(i)
if node:
node.link = new_node
node = node.link
else:
# runs only once, at the first item,
# when the list is empty(head is None)
self.head = new_node
node = self.head
self.size += 1
def __len__(self):
"""Implement len(self). Return the number of items in list."""
return self.size
def __iter__(self):
"""Implement iter(self)."""
node = self.head
while node:
yield node.value
node = node.link
def __repr__(self):
"""Return repr(self)."""
return self.__str__()
def __str__(self):
"""Define string casting for the list."""
node = self.head
s = ''
while node:
s += str(node.value) + ' => '
node = node.link
return s + 'None'
def __getitem__(self, index):
"""Implement indexing access: a[b]."""
# change index if negative
index = self.size + index if index < 0 else index
if 0 <= index < self.size:
i = 0
node = self.head
while i < index:
node = node.link
i += 1
return node.value
else:
raise IndexError('list index out of range')
def __setitem__(self, index, item):
"""Implement indexed assignment."""
# change index if negative
index = self.size + index if index < 0 else index
if 0 <= index < self.size:
i = 0
node = self.head
while i < index:
node = node.link
i += 1
node.value = item
else:
raise IndexError('list assignment index out of range')
def __delitem__(self, index):
"""Implement indexed deletion."""
# change index if negative
index = self.size + index if index < 0 else index
# check .remove() method for explanation
if 0 < index < self.size:
i = 0
node = self.head
while i < index - 1:
node = node.link
i += 1
node.link = node.link.link
self.size -= 1
elif index == 0:
self.head = self.head.link
self.size -= 1
else:
raise IndexError('list deletion index out of range')
def __contains__(self, item):
"""Implement 'in' access: if item in..."""
i = 0
node = self.head
while i < self.size:
if node.value == item:
return True
node = node.link
i += 1
return False
def insertStart(self, item):
"""Insert an item to the head of the link."""
self.size += 1
new_node = self.Node(item)
if not self.head: # check in the list has a head
self.head = new_node
else:
new_node.link = self.head # link the node to the head
self.head = new_node # make it the head
def insertEnd(self, item):
"""Insert an item at the tail."""
new_node = self.Node(item)
if self.head: # check if list is empty
node = self.head
while node.link: # iterate through the list to get to the tail
node = node.link
node.link = new_node
else:
self.head = new_node # create a head
self.size += 1
def insert(self, index, item):
"""Insert given item before specified index."""
t = type(index)
if t is not int:
raise TypeError(' cannot be interpreted as an integer'.format(t))
else:
# change index if negative
index = self.size + index if index < 0 else index
if index > self.size - 1: # check for special cases
self.insertEnd(item)
elif index <= 0:
self.insertStart(item)
else: # iterate through and insert item
i = 0
node = self.head
while i < index - 1:
node = node.link
i += 1
new_node = self.Node(item)
new_node.link = node.link
node.link = new_node
self.size += 1
def remove(self, value=None):
"""
Remove the first occurence of the value(default head).
Raises a ValueError if the is not present.
Raises an IndexError if the list is empty.
"""
if not self.head:
raise IndexError("remove from an empty list")
else:
if value: # check if value is provided
if self.head.value == value:
self.head = self.head.link
else:
node = self.head
try:
# iterate through the list while checking for
# given value and being one step behind to be
# able to efficiently remove it
while node.link.value != value:
node = node.link
node.link = node.link.link
except AttributeError: # mute the original error
raise ValueError('value not present in list') from None
else:
self.head = self.head.link # value not present. remove head
self.size -= 1
def index(self, item):
"""Return index of first occurence of specified item. -1 if absent."""
i = 0
node = self.head
while i < self.size:
if node.value == item:
return i
node = node.link
i += 1
return -1
def reverse(self):
"""Reverse list in place."""
current_node = self.head
prev_node = None
next_node = None
while current_node:
next_node = current_node.link
current_node.link = prev_node
prev_node, current_node = current_node, next_node
self.head = prev_node
python python-3.x linked-list
$endgroup$
add a comment |
$begingroup$
A school student here. Hope you are having a nice day.
So, I implemented a Singly Linked List in Python and of course also a Node. I tried to make it have the best Big O time complexity I could. Here it is, please tell me what do you think can be improved, changed, etc.
class LinkedList():
"""
A basic class implementing a Singly Linked List.
LinkedList() - new empty linked list
LinkedList(iterable) - new linked list with items of iterable:
head - iterable[0] tail - iterable[-1]
"""
class Node():
"""A class for a singly linked list node."""
def __init__(self, value):
"""Initialize default values."""
self.value = value
self.link = None # by default a node is linked to nothing
def __init__(self, seq=()):
"""Initialize default values."""
self.size = 0
# While instantiated there are no elements in list
# but None, to which will be linked the first element
self.head = None
node = self.head
for i in seq: # iterate through and copy contents
new_node = self.Node(i)
if node:
node.link = new_node
node = node.link
else:
# runs only once, at the first item,
# when the list is empty(head is None)
self.head = new_node
node = self.head
self.size += 1
def __len__(self):
"""Implement len(self). Return the number of items in list."""
return self.size
def __iter__(self):
"""Implement iter(self)."""
node = self.head
while node:
yield node.value
node = node.link
def __repr__(self):
"""Return repr(self)."""
return self.__str__()
def __str__(self):
"""Define string casting for the list."""
node = self.head
s = ''
while node:
s += str(node.value) + ' => '
node = node.link
return s + 'None'
def __getitem__(self, index):
"""Implement indexing access: a[b]."""
# change index if negative
index = self.size + index if index < 0 else index
if 0 <= index < self.size:
i = 0
node = self.head
while i < index:
node = node.link
i += 1
return node.value
else:
raise IndexError('list index out of range')
def __setitem__(self, index, item):
"""Implement indexed assignment."""
# change index if negative
index = self.size + index if index < 0 else index
if 0 <= index < self.size:
i = 0
node = self.head
while i < index:
node = node.link
i += 1
node.value = item
else:
raise IndexError('list assignment index out of range')
def __delitem__(self, index):
"""Implement indexed deletion."""
# change index if negative
index = self.size + index if index < 0 else index
# check .remove() method for explanation
if 0 < index < self.size:
i = 0
node = self.head
while i < index - 1:
node = node.link
i += 1
node.link = node.link.link
self.size -= 1
elif index == 0:
self.head = self.head.link
self.size -= 1
else:
raise IndexError('list deletion index out of range')
def __contains__(self, item):
"""Implement 'in' access: if item in..."""
i = 0
node = self.head
while i < self.size:
if node.value == item:
return True
node = node.link
i += 1
return False
def insertStart(self, item):
"""Insert an item to the head of the link."""
self.size += 1
new_node = self.Node(item)
if not self.head: # check in the list has a head
self.head = new_node
else:
new_node.link = self.head # link the node to the head
self.head = new_node # make it the head
def insertEnd(self, item):
"""Insert an item at the tail."""
new_node = self.Node(item)
if self.head: # check if list is empty
node = self.head
while node.link: # iterate through the list to get to the tail
node = node.link
node.link = new_node
else:
self.head = new_node # create a head
self.size += 1
def insert(self, index, item):
"""Insert given item before specified index."""
t = type(index)
if t is not int:
raise TypeError(' cannot be interpreted as an integer'.format(t))
else:
# change index if negative
index = self.size + index if index < 0 else index
if index > self.size - 1: # check for special cases
self.insertEnd(item)
elif index <= 0:
self.insertStart(item)
else: # iterate through and insert item
i = 0
node = self.head
while i < index - 1:
node = node.link
i += 1
new_node = self.Node(item)
new_node.link = node.link
node.link = new_node
self.size += 1
def remove(self, value=None):
"""
Remove the first occurence of the value(default head).
Raises a ValueError if the is not present.
Raises an IndexError if the list is empty.
"""
if not self.head:
raise IndexError("remove from an empty list")
else:
if value: # check if value is provided
if self.head.value == value:
self.head = self.head.link
else:
node = self.head
try:
# iterate through the list while checking for
# given value and being one step behind to be
# able to efficiently remove it
while node.link.value != value:
node = node.link
node.link = node.link.link
except AttributeError: # mute the original error
raise ValueError('value not present in list') from None
else:
self.head = self.head.link # value not present. remove head
self.size -= 1
def index(self, item):
"""Return index of first occurence of specified item. -1 if absent."""
i = 0
node = self.head
while i < self.size:
if node.value == item:
return i
node = node.link
i += 1
return -1
def reverse(self):
"""Reverse list in place."""
current_node = self.head
prev_node = None
next_node = None
while current_node:
next_node = current_node.link
current_node.link = prev_node
prev_node, current_node = current_node, next_node
self.head = prev_node
python python-3.x linked-list
$endgroup$
add a comment |
$begingroup$
A school student here. Hope you are having a nice day.
So, I implemented a Singly Linked List in Python and of course also a Node. I tried to make it have the best Big O time complexity I could. Here it is, please tell me what do you think can be improved, changed, etc.
class LinkedList():
"""
A basic class implementing a Singly Linked List.
LinkedList() - new empty linked list
LinkedList(iterable) - new linked list with items of iterable:
head - iterable[0] tail - iterable[-1]
"""
class Node():
"""A class for a singly linked list node."""
def __init__(self, value):
"""Initialize default values."""
self.value = value
self.link = None # by default a node is linked to nothing
def __init__(self, seq=()):
"""Initialize default values."""
self.size = 0
# While instantiated there are no elements in list
# but None, to which will be linked the first element
self.head = None
node = self.head
for i in seq: # iterate through and copy contents
new_node = self.Node(i)
if node:
node.link = new_node
node = node.link
else:
# runs only once, at the first item,
# when the list is empty(head is None)
self.head = new_node
node = self.head
self.size += 1
def __len__(self):
"""Implement len(self). Return the number of items in list."""
return self.size
def __iter__(self):
"""Implement iter(self)."""
node = self.head
while node:
yield node.value
node = node.link
def __repr__(self):
"""Return repr(self)."""
return self.__str__()
def __str__(self):
"""Define string casting for the list."""
node = self.head
s = ''
while node:
s += str(node.value) + ' => '
node = node.link
return s + 'None'
def __getitem__(self, index):
"""Implement indexing access: a[b]."""
# change index if negative
index = self.size + index if index < 0 else index
if 0 <= index < self.size:
i = 0
node = self.head
while i < index:
node = node.link
i += 1
return node.value
else:
raise IndexError('list index out of range')
def __setitem__(self, index, item):
"""Implement indexed assignment."""
# change index if negative
index = self.size + index if index < 0 else index
if 0 <= index < self.size:
i = 0
node = self.head
while i < index:
node = node.link
i += 1
node.value = item
else:
raise IndexError('list assignment index out of range')
def __delitem__(self, index):
"""Implement indexed deletion."""
# change index if negative
index = self.size + index if index < 0 else index
# check .remove() method for explanation
if 0 < index < self.size:
i = 0
node = self.head
while i < index - 1:
node = node.link
i += 1
node.link = node.link.link
self.size -= 1
elif index == 0:
self.head = self.head.link
self.size -= 1
else:
raise IndexError('list deletion index out of range')
def __contains__(self, item):
"""Implement 'in' access: if item in..."""
i = 0
node = self.head
while i < self.size:
if node.value == item:
return True
node = node.link
i += 1
return False
def insertStart(self, item):
"""Insert an item to the head of the link."""
self.size += 1
new_node = self.Node(item)
if not self.head: # check in the list has a head
self.head = new_node
else:
new_node.link = self.head # link the node to the head
self.head = new_node # make it the head
def insertEnd(self, item):
"""Insert an item at the tail."""
new_node = self.Node(item)
if self.head: # check if list is empty
node = self.head
while node.link: # iterate through the list to get to the tail
node = node.link
node.link = new_node
else:
self.head = new_node # create a head
self.size += 1
def insert(self, index, item):
"""Insert given item before specified index."""
t = type(index)
if t is not int:
raise TypeError(' cannot be interpreted as an integer'.format(t))
else:
# change index if negative
index = self.size + index if index < 0 else index
if index > self.size - 1: # check for special cases
self.insertEnd(item)
elif index <= 0:
self.insertStart(item)
else: # iterate through and insert item
i = 0
node = self.head
while i < index - 1:
node = node.link
i += 1
new_node = self.Node(item)
new_node.link = node.link
node.link = new_node
self.size += 1
def remove(self, value=None):
"""
Remove the first occurence of the value(default head).
Raises a ValueError if the is not present.
Raises an IndexError if the list is empty.
"""
if not self.head:
raise IndexError("remove from an empty list")
else:
if value: # check if value is provided
if self.head.value == value:
self.head = self.head.link
else:
node = self.head
try:
# iterate through the list while checking for
# given value and being one step behind to be
# able to efficiently remove it
while node.link.value != value:
node = node.link
node.link = node.link.link
except AttributeError: # mute the original error
raise ValueError('value not present in list') from None
else:
self.head = self.head.link # value not present. remove head
self.size -= 1
def index(self, item):
"""Return index of first occurence of specified item. -1 if absent."""
i = 0
node = self.head
while i < self.size:
if node.value == item:
return i
node = node.link
i += 1
return -1
def reverse(self):
"""Reverse list in place."""
current_node = self.head
prev_node = None
next_node = None
while current_node:
next_node = current_node.link
current_node.link = prev_node
prev_node, current_node = current_node, next_node
self.head = prev_node
python python-3.x linked-list
$endgroup$
A school student here. Hope you are having a nice day.
So, I implemented a Singly Linked List in Python and of course also a Node. I tried to make it have the best Big O time complexity I could. Here it is, please tell me what do you think can be improved, changed, etc.
class LinkedList():
"""
A basic class implementing a Singly Linked List.
LinkedList() - new empty linked list
LinkedList(iterable) - new linked list with items of iterable:
head - iterable[0] tail - iterable[-1]
"""
class Node():
"""A class for a singly linked list node."""
def __init__(self, value):
"""Initialize default values."""
self.value = value
self.link = None # by default a node is linked to nothing
def __init__(self, seq=()):
"""Initialize default values."""
self.size = 0
# While instantiated there are no elements in list
# but None, to which will be linked the first element
self.head = None
node = self.head
for i in seq: # iterate through and copy contents
new_node = self.Node(i)
if node:
node.link = new_node
node = node.link
else:
# runs only once, at the first item,
# when the list is empty(head is None)
self.head = new_node
node = self.head
self.size += 1
def __len__(self):
"""Implement len(self). Return the number of items in list."""
return self.size
def __iter__(self):
"""Implement iter(self)."""
node = self.head
while node:
yield node.value
node = node.link
def __repr__(self):
"""Return repr(self)."""
return self.__str__()
def __str__(self):
"""Define string casting for the list."""
node = self.head
s = ''
while node:
s += str(node.value) + ' => '
node = node.link
return s + 'None'
def __getitem__(self, index):
"""Implement indexing access: a[b]."""
# change index if negative
index = self.size + index if index < 0 else index
if 0 <= index < self.size:
i = 0
node = self.head
while i < index:
node = node.link
i += 1
return node.value
else:
raise IndexError('list index out of range')
def __setitem__(self, index, item):
"""Implement indexed assignment."""
# change index if negative
index = self.size + index if index < 0 else index
if 0 <= index < self.size:
i = 0
node = self.head
while i < index:
node = node.link
i += 1
node.value = item
else:
raise IndexError('list assignment index out of range')
def __delitem__(self, index):
"""Implement indexed deletion."""
# change index if negative
index = self.size + index if index < 0 else index
# check .remove() method for explanation
if 0 < index < self.size:
i = 0
node = self.head
while i < index - 1:
node = node.link
i += 1
node.link = node.link.link
self.size -= 1
elif index == 0:
self.head = self.head.link
self.size -= 1
else:
raise IndexError('list deletion index out of range')
def __contains__(self, item):
"""Implement 'in' access: if item in..."""
i = 0
node = self.head
while i < self.size:
if node.value == item:
return True
node = node.link
i += 1
return False
def insertStart(self, item):
"""Insert an item to the head of the link."""
self.size += 1
new_node = self.Node(item)
if not self.head: # check in the list has a head
self.head = new_node
else:
new_node.link = self.head # link the node to the head
self.head = new_node # make it the head
def insertEnd(self, item):
"""Insert an item at the tail."""
new_node = self.Node(item)
if self.head: # check if list is empty
node = self.head
while node.link: # iterate through the list to get to the tail
node = node.link
node.link = new_node
else:
self.head = new_node # create a head
self.size += 1
def insert(self, index, item):
"""Insert given item before specified index."""
t = type(index)
if t is not int:
raise TypeError(' cannot be interpreted as an integer'.format(t))
else:
# change index if negative
index = self.size + index if index < 0 else index
if index > self.size - 1: # check for special cases
self.insertEnd(item)
elif index <= 0:
self.insertStart(item)
else: # iterate through and insert item
i = 0
node = self.head
while i < index - 1:
node = node.link
i += 1
new_node = self.Node(item)
new_node.link = node.link
node.link = new_node
self.size += 1
def remove(self, value=None):
"""
Remove the first occurence of the value(default head).
Raises a ValueError if the is not present.
Raises an IndexError if the list is empty.
"""
if not self.head:
raise IndexError("remove from an empty list")
else:
if value: # check if value is provided
if self.head.value == value:
self.head = self.head.link
else:
node = self.head
try:
# iterate through the list while checking for
# given value and being one step behind to be
# able to efficiently remove it
while node.link.value != value:
node = node.link
node.link = node.link.link
except AttributeError: # mute the original error
raise ValueError('value not present in list') from None
else:
self.head = self.head.link # value not present. remove head
self.size -= 1
def index(self, item):
"""Return index of first occurence of specified item. -1 if absent."""
i = 0
node = self.head
while i < self.size:
if node.value == item:
return i
node = node.link
i += 1
return -1
def reverse(self):
"""Reverse list in place."""
current_node = self.head
prev_node = None
next_node = None
while current_node:
next_node = current_node.link
current_node.link = prev_node
prev_node, current_node = current_node, next_node
self.head = prev_node
python python-3.x linked-list
python python-3.x linked-list
edited Aug 27 '18 at 19:31
Nick
asked Aug 27 '18 at 16:12
NickNick
514
514
add a comment |
add a comment |
1 Answer
1
active
oldest
votes
$begingroup$
Your code is good because you implemented many __methods__
that allow natural use of the class with for
and print
built-ins.
A great way to make it easier to improve the code is to add automatic tests, for example with doctest
.
Let me show you a practical example:
I note that __str__
repeats logic already inside __iter__
, so first thing I write a test to see how it works now:
import doctest
class LinkedList():
def __str__(self):
"""
Define string casting for the list.
>>> str(LinkedList([1, 2, 3]))
'1 => 2 => 3 => None'
"""
# old code
if __name__ == "__main__":
doctest.testmod()
Than I write the new implementation that uses __iter__
through the for
keyword:
def __str__(self):
"""
Define string casting for the list.
>>> str(LinkedList([1, 2, 3]))
'1 => 2 => 3 => None'
"""
return ' => '.join((str(x) for x in self)) + ' => None'
Just executing the code runs the test and I know that the new implementation works the same as the old one at least in this case.
More tests can be added for example for empty list or different data types inside the list but this is the basic idea.
The same can be said for index
, you can reuse the __iter__
logic once again:
def index(self, item):
"""
Return index of first occurence of specified item. -1 if absent.
>>> LinkedList(['a', 'b', 'c', 'd']).index('b')
1
"""
for index, x in enumerate(self):
if x == item:
return index
return -1
In general when you write a collection the __iter__
method is very useful for implementing other methods.
$endgroup$
1
$begingroup$
Thanks for telling me that I could use__iter__
method inside class definition. I didn't know that it could be used inside definition so I went for more manual solutions. I will definitely keep that in mind.
$endgroup$
– Nick
Aug 27 '18 at 19:28
1
$begingroup$
What aboutmap(str, self)
instead of(str(x) for x in self)
?
$endgroup$
– Solomon Ucko
Aug 27 '18 at 22:29
$begingroup$
@Solomon Ucko Yes It is the same, you can use It no problem
$endgroup$
– Caridorc
Aug 28 '18 at 9:29
add a comment |
Your Answer
StackExchange.ifUsing("editor", function ()
return StackExchange.using("mathjaxEditing", function ()
StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix)
StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
);
);
, "mathjax-editing");
StackExchange.ifUsing("editor", function ()
StackExchange.using("externalEditor", function ()
StackExchange.using("snippets", function ()
StackExchange.snippets.init();
);
);
, "code-snippets");
StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "196"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);
else
createEditor();
);
function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: false,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
imageUploader:
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
,
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);
);
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f202592%2fsingly-linked-list-in-python%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
$begingroup$
Your code is good because you implemented many __methods__
that allow natural use of the class with for
and print
built-ins.
A great way to make it easier to improve the code is to add automatic tests, for example with doctest
.
Let me show you a practical example:
I note that __str__
repeats logic already inside __iter__
, so first thing I write a test to see how it works now:
import doctest
class LinkedList():
def __str__(self):
"""
Define string casting for the list.
>>> str(LinkedList([1, 2, 3]))
'1 => 2 => 3 => None'
"""
# old code
if __name__ == "__main__":
doctest.testmod()
Than I write the new implementation that uses __iter__
through the for
keyword:
def __str__(self):
"""
Define string casting for the list.
>>> str(LinkedList([1, 2, 3]))
'1 => 2 => 3 => None'
"""
return ' => '.join((str(x) for x in self)) + ' => None'
Just executing the code runs the test and I know that the new implementation works the same as the old one at least in this case.
More tests can be added for example for empty list or different data types inside the list but this is the basic idea.
The same can be said for index
, you can reuse the __iter__
logic once again:
def index(self, item):
"""
Return index of first occurence of specified item. -1 if absent.
>>> LinkedList(['a', 'b', 'c', 'd']).index('b')
1
"""
for index, x in enumerate(self):
if x == item:
return index
return -1
In general when you write a collection the __iter__
method is very useful for implementing other methods.
$endgroup$
1
$begingroup$
Thanks for telling me that I could use__iter__
method inside class definition. I didn't know that it could be used inside definition so I went for more manual solutions. I will definitely keep that in mind.
$endgroup$
– Nick
Aug 27 '18 at 19:28
1
$begingroup$
What aboutmap(str, self)
instead of(str(x) for x in self)
?
$endgroup$
– Solomon Ucko
Aug 27 '18 at 22:29
$begingroup$
@Solomon Ucko Yes It is the same, you can use It no problem
$endgroup$
– Caridorc
Aug 28 '18 at 9:29
add a comment |
$begingroup$
Your code is good because you implemented many __methods__
that allow natural use of the class with for
and print
built-ins.
A great way to make it easier to improve the code is to add automatic tests, for example with doctest
.
Let me show you a practical example:
I note that __str__
repeats logic already inside __iter__
, so first thing I write a test to see how it works now:
import doctest
class LinkedList():
def __str__(self):
"""
Define string casting for the list.
>>> str(LinkedList([1, 2, 3]))
'1 => 2 => 3 => None'
"""
# old code
if __name__ == "__main__":
doctest.testmod()
Than I write the new implementation that uses __iter__
through the for
keyword:
def __str__(self):
"""
Define string casting for the list.
>>> str(LinkedList([1, 2, 3]))
'1 => 2 => 3 => None'
"""
return ' => '.join((str(x) for x in self)) + ' => None'
Just executing the code runs the test and I know that the new implementation works the same as the old one at least in this case.
More tests can be added for example for empty list or different data types inside the list but this is the basic idea.
The same can be said for index
, you can reuse the __iter__
logic once again:
def index(self, item):
"""
Return index of first occurence of specified item. -1 if absent.
>>> LinkedList(['a', 'b', 'c', 'd']).index('b')
1
"""
for index, x in enumerate(self):
if x == item:
return index
return -1
In general when you write a collection the __iter__
method is very useful for implementing other methods.
$endgroup$
1
$begingroup$
Thanks for telling me that I could use__iter__
method inside class definition. I didn't know that it could be used inside definition so I went for more manual solutions. I will definitely keep that in mind.
$endgroup$
– Nick
Aug 27 '18 at 19:28
1
$begingroup$
What aboutmap(str, self)
instead of(str(x) for x in self)
?
$endgroup$
– Solomon Ucko
Aug 27 '18 at 22:29
$begingroup$
@Solomon Ucko Yes It is the same, you can use It no problem
$endgroup$
– Caridorc
Aug 28 '18 at 9:29
add a comment |
$begingroup$
Your code is good because you implemented many __methods__
that allow natural use of the class with for
and print
built-ins.
A great way to make it easier to improve the code is to add automatic tests, for example with doctest
.
Let me show you a practical example:
I note that __str__
repeats logic already inside __iter__
, so first thing I write a test to see how it works now:
import doctest
class LinkedList():
def __str__(self):
"""
Define string casting for the list.
>>> str(LinkedList([1, 2, 3]))
'1 => 2 => 3 => None'
"""
# old code
if __name__ == "__main__":
doctest.testmod()
Than I write the new implementation that uses __iter__
through the for
keyword:
def __str__(self):
"""
Define string casting for the list.
>>> str(LinkedList([1, 2, 3]))
'1 => 2 => 3 => None'
"""
return ' => '.join((str(x) for x in self)) + ' => None'
Just executing the code runs the test and I know that the new implementation works the same as the old one at least in this case.
More tests can be added for example for empty list or different data types inside the list but this is the basic idea.
The same can be said for index
, you can reuse the __iter__
logic once again:
def index(self, item):
"""
Return index of first occurence of specified item. -1 if absent.
>>> LinkedList(['a', 'b', 'c', 'd']).index('b')
1
"""
for index, x in enumerate(self):
if x == item:
return index
return -1
In general when you write a collection the __iter__
method is very useful for implementing other methods.
$endgroup$
Your code is good because you implemented many __methods__
that allow natural use of the class with for
and print
built-ins.
A great way to make it easier to improve the code is to add automatic tests, for example with doctest
.
Let me show you a practical example:
I note that __str__
repeats logic already inside __iter__
, so first thing I write a test to see how it works now:
import doctest
class LinkedList():
def __str__(self):
"""
Define string casting for the list.
>>> str(LinkedList([1, 2, 3]))
'1 => 2 => 3 => None'
"""
# old code
if __name__ == "__main__":
doctest.testmod()
Than I write the new implementation that uses __iter__
through the for
keyword:
def __str__(self):
"""
Define string casting for the list.
>>> str(LinkedList([1, 2, 3]))
'1 => 2 => 3 => None'
"""
return ' => '.join((str(x) for x in self)) + ' => None'
Just executing the code runs the test and I know that the new implementation works the same as the old one at least in this case.
More tests can be added for example for empty list or different data types inside the list but this is the basic idea.
The same can be said for index
, you can reuse the __iter__
logic once again:
def index(self, item):
"""
Return index of first occurence of specified item. -1 if absent.
>>> LinkedList(['a', 'b', 'c', 'd']).index('b')
1
"""
for index, x in enumerate(self):
if x == item:
return index
return -1
In general when you write a collection the __iter__
method is very useful for implementing other methods.
edited Aug 27 '18 at 19:19
answered Aug 27 '18 at 19:06
CaridorcCaridorc
23k538117
23k538117
1
$begingroup$
Thanks for telling me that I could use__iter__
method inside class definition. I didn't know that it could be used inside definition so I went for more manual solutions. I will definitely keep that in mind.
$endgroup$
– Nick
Aug 27 '18 at 19:28
1
$begingroup$
What aboutmap(str, self)
instead of(str(x) for x in self)
?
$endgroup$
– Solomon Ucko
Aug 27 '18 at 22:29
$begingroup$
@Solomon Ucko Yes It is the same, you can use It no problem
$endgroup$
– Caridorc
Aug 28 '18 at 9:29
add a comment |
1
$begingroup$
Thanks for telling me that I could use__iter__
method inside class definition. I didn't know that it could be used inside definition so I went for more manual solutions. I will definitely keep that in mind.
$endgroup$
– Nick
Aug 27 '18 at 19:28
1
$begingroup$
What aboutmap(str, self)
instead of(str(x) for x in self)
?
$endgroup$
– Solomon Ucko
Aug 27 '18 at 22:29
$begingroup$
@Solomon Ucko Yes It is the same, you can use It no problem
$endgroup$
– Caridorc
Aug 28 '18 at 9:29
1
1
$begingroup$
Thanks for telling me that I could use
__iter__
method inside class definition. I didn't know that it could be used inside definition so I went for more manual solutions. I will definitely keep that in mind.$endgroup$
– Nick
Aug 27 '18 at 19:28
$begingroup$
Thanks for telling me that I could use
__iter__
method inside class definition. I didn't know that it could be used inside definition so I went for more manual solutions. I will definitely keep that in mind.$endgroup$
– Nick
Aug 27 '18 at 19:28
1
1
$begingroup$
What about
map(str, self)
instead of (str(x) for x in self)
?$endgroup$
– Solomon Ucko
Aug 27 '18 at 22:29
$begingroup$
What about
map(str, self)
instead of (str(x) for x in self)
?$endgroup$
– Solomon Ucko
Aug 27 '18 at 22:29
$begingroup$
@Solomon Ucko Yes It is the same, you can use It no problem
$endgroup$
– Caridorc
Aug 28 '18 at 9:29
$begingroup$
@Solomon Ucko Yes It is the same, you can use It no problem
$endgroup$
– Caridorc
Aug 28 '18 at 9:29
add a comment |
Thanks for contributing an answer to Code Review Stack Exchange!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
Use MathJax to format equations. MathJax reference.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f202592%2fsingly-linked-list-in-python%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown