Does anyone know how the built in dictionary type for python is implemented? My understanding is that it is some sort of hash table, but I haven’t been able to find any sort of definitive answer.
下图是Python哈希表的逻辑表示。在下图中,0, 1, ..., i, ...左侧是哈希表中插槽的索引(它们仅用于说明目的,与表显然没有一起存储!)。
# Logical model of Python Hash table-+-----------------+0|<hash|key|value>|-+-----------------+1|...|-+-----------------+.|...|-+-----------------+
i|...|-+-----------------+.|...|-+-----------------+
n|...|-+-----------------+
Here is everything about Python dicts that I was able to put together (probably more than anyone would like to know; but the answer is comprehensive).
Python dictionaries are implemented as hash tables.
Hash tables must allow for hash collisions i.e. even if two distinct keys have the same hash value, the table’s implementation must have a strategy to insert and retrieve the key and value pairs unambiguously.
Python dict uses open addressing to resolve hash collisions (explained below) (see dictobject.c:296-297).
Python hash table is just a contiguous block of memory (sort of like an array, so you can do an O(1) lookup by index).
Each slot in the table can store one and only one entry. This is important.
Each entry in the table is actually a combination of the three values: < hash, key, value >. This is implemented as a C struct (see dictobject.h:51-56).
The figure below is a logical representation of a Python hash table. In the figure below, 0, 1, ..., i, ... on the left are indices of the slots in the hash table (they are just for illustrative purposes and are not stored along with the table obviously!).
When a new dict is initialized it starts with 8 slots. (see dictobject.h:49)
When adding entries to the table, we start with some slot, i, that is based on the hash of the key. CPython initially uses i = hash(key) & mask (where mask = PyDictMINSIZE - 1, but that’s not really important). Just note that the initial slot, i, that is checked depends on the hash of the key.
If that slot is empty, the entry is added to the slot (by entry, I mean, <hash|key|value>). But what if that slot is occupied!? Most likely because another entry has the same hash (hash collision!)
If the slot is occupied, CPython (and even PyPy) compares the hash AND the key (by compare I mean == comparison not the is comparison) of the entry in the slot against the hash and key of the current entry to be inserted (dictobject.c:337,344-345) respectively. If both match, then it thinks the entry already exists, gives up and moves on to the next entry to be inserted. If either hash or the key don’t match, it starts probing.
Probing just means it searches the slots by slot to find an empty slot. Technically we could just go one by one, i+1, i+2, ... and use the first available one (that’s linear probing). But for reasons explained beautifully in the comments (see dictobject.c:33-126), CPython uses random probing. In random probing, the next slot is picked in a pseudo random order. The entry is added to the first empty slot. For this discussion, the actual algorithm used to pick the next slot is not really important (see dictobject.c:33-126 for the algorithm for probing). What is important is that the slots are probed until first empty slot is found.
The same thing happens for lookups, just starts with the initial slot i (where i depends on the hash of the key). If the hash and the key both don’t match the entry in the slot, it starts probing, until it finds a slot with a match. If all slots are exhausted, it reports a fail.
BTW, the dict will be resized if it is two-thirds full. This avoids slowing down lookups. (see dictobject.h:64-65)
NOTE: I did the research on Python Dict implementation in response to my own question about how multiple entries in a dict can have same hash values. I posted a slightly edited version of the response here because all the research is very relevant for this question as well.
How are Python’s Built In Dictionaries Implemented?
Here’s the short course:
They are hash tables. (See below for the specifics of Python’s implementation.)
A new layout and algorithm, as of Python 3.6, makes them
ordered by key insertion, and
take up less space,
at virtually no cost in performance.
Another optimization saves space when dicts share keys (in special cases).
The ordered aspect is unofficial as of Python 3.6 (to give other implementations a chance to keep up), but official in Python 3.7.
Python’s Dictionaries are Hash Tables
For a long time, it worked exactly like this. Python would preallocate 8 empty rows and use the hash to determine where to stick the key-value pair. For example, if the hash for the key ended in 001, it would stick it in the 1 (i.e. 2nd) index (like the example below.)
<hash> <key> <value>
null null null
...010001 ffeb678c 633241c4 # addresses of the keys and values
null null null
... ... ...
Each row takes up 24 bytes on a 64 bit architecture, 12 on a 32 bit. (Note that the column headers are just labels for our purposes here – they don’t actually exist in memory.)
If the hash ended the same as a preexisting key’s hash, this is a collision, and then it would stick the key-value pair in a different location.
After 5 key-values are stored, when adding another key-value pair, the probability of hash collisions is too large, so the dictionary is doubled in size. In a 64 bit process, before the resize, we have 72 bytes empty, and after, we are wasting 240 bytes due to the 10 empty rows.
This takes a lot of space, but the lookup time is fairly constant. The key comparison algorithm is to compute the hash, go to the expected location, compare the key’s id – if they’re the same object, they’re equal. If not then compare the hash values, if they are not the same, they’re not equal. Else, then we finally compare keys for equality, and if they are equal, return the value. The final comparison for equality can be quite slow, but the earlier checks usually shortcut the final comparison, making the lookups very quick.
Collisions slow things down, and an attacker could theoretically use hash collisions to perform a denial of service attack, so we randomized the initialization of the hash function such that it computes different hashes for each new Python process.
The wasted space described above has led us to modify the implementation of dictionaries, with an exciting new feature that dictionaries are now ordered by insertion.
The New Compact Hash Tables
We start, instead, by preallocating an array for the index of the insertion.
Since our first key-value pair goes in the second slot, we index like this:
[null, 0, null, null, null, null, null, null]
And our table just gets populated by insertion order:
So when we do a lookup for a key, we use the hash to check the position we expect (in this case, we go straight to index 1 of the array), then go to that index in the hash-table (e.g. index 0), check that the keys are equal (using the same algorithm described earlier), and if so, return the value.
We retain constant lookup time, with minor speed losses in some cases and gains in others, with the upsides that we save quite a lot of space over the pre-existing implementation and we retain insertion order. The only space wasted are the null bytes in the index array.
Raymond Hettinger introduced this on python-dev in December of 2012. It finally got into CPython in Python 3.6. Ordering by insertion was considered an implementation detail for 3.6 to allow other implementations of Python a chance to catch up.
Shared Keys
Another optimization to save space is an implementation that shares keys. Thus, instead of having redundant dictionaries that take up all of that space, we have dictionaries that reuse the shared keys and keys’ hashes. You can think of it like this:
For a 64 bit machine, this could save up to 16 bytes per key per extra dictionary.
Shared Keys for Custom Objects & Alternatives
These shared-key dicts are intended to be used for custom objects’ __dict__. To get this behavior, I believe you need to finish populating your __dict__ before you instantiate your next object (see PEP 412). This means you should assign all your attributes in the __init__ or __new__, else you might not get your space savings.
However, if you know all of your attributes at the time your __init__ is executed, you could also provide __slots__ for your object, and guarantee that __dict__ is not created at all (if not available in parents), or even allow __dict__ but guarantee that your foreseen attributes are stored in slots anyways. For more on __slots__, see my answer here.
NB!Open addressing, a.k.a closed hashing should, as noted in Wikipedia, not be confused with its opposite open hashing!
Open addressing means that the dict uses array slots, and when an object’s primary position is taken in the dict, the object’s spot is sought at a different index in the same array, using a “perturbation” scheme, where the object’s hash value plays part.
It is rather hackish, since it relies on converting the values into a single string representation for comparison, but it works as expected for numbers including negative ones (although you will need to format your string appropriately with zero paddings if you are using numbers)
‘key’ is used to sort by an arbitrary value and ‘itemgetter’ sets that value to each item’s ‘name’ attribute.
回答 5
a =[{'name':'Homer','age':39},...]# This changes the list a
a.sort(key=lambda k : k['name'])# This returns a new list (a is not modified)
sorted(a, key=lambda k : k['name'])
a = [{'name':'Homer', 'age':39}, ...]
# This changes the list a
a.sort(key=lambda k : k['name'])
# This returns a new list (a is not modified)
sorted(a, key=lambda k : k['name'])
You could use a custom comparison function, or you could pass in a function that calculates a custom sort key. That’s usually more efficient as the key is only calculated once per item, while the comparison function would be called many more times.
In computer science, the Schwartzian transform is a Perl programming
idiom used to improve the efficiency of sorting a list of items. This
idiom is appropriate for comparison-based sorting when the ordering is
actually based on the ordering of a certain property (the key) of the
elements, where computing that property is an intensive operation that
should be performed a minimal number of times. The Schwartzian
Transform is notable in that it does not use named temporary arrays.
def sort_key_func(item):""" helper function used to sort list of dicts
:param item: dict
:return: sorted list of tuples (k, v)
"""
pairs =[]for k, v in item.items():
pairs.append((k, v))return sorted(pairs)
sorted(A, key=sort_key_func)
Here is the alternative general solution – it sorts elements of dict by keys and values.
The advantage of it – no need to specify keys, and it would still work if some keys are missing in some of dictionaries.
def sort_key_func(item):
""" helper function used to sort list of dicts
:param item: dict
:return: sorted list of tuples (k, v)
"""
pairs = []
for k, v in item.items():
pairs.append((k, v))
return sorted(pairs)
sorted(A, key=sort_key_func)
If you need the original list, call the sorted() function passing it the list and the key function, then assign the returned sorted list to a new variable:
import random
import operator
# create a list of 100 dicts with random 8-letter names and random ages from 0 to 100.
l =[{'name':''.join(random.choices(string.ascii_lowercase, k=8)),'age': random.randint(0,100)}for i in range(100)]# Test the performance with a lambda function sorting on name%timeit sorted(l, key=lambda x: x['name'])13µs ±388 ns per loop (mean ± std. dev. of 7 runs,100000 loops each)# Test the performance with itemgetter sorting on name%timeit sorted(l, key=operator.itemgetter('name'))10.7µs ±38.1 ns per loop (mean ± std. dev. of 7 runs,100000 loops each)# Check that each technique produces same sort order
sorted(l, key=lambda x: x['name'])== sorted(l, key=operator.itemgetter('name'))True
If performance is a concern, I would use operator.itemgetter instead of lambda as built-in functions perform faster than hand-crafted functions. The itemgetter function seems to perform approximately 20% faster than lambda based on my testing.
Likewise, the builtin functions run faster than hand-built equivalents. For example, map(operator.add, v1, v2) is faster than map(lambda x,y: x+y, v1, v2).
Here is a comparison of sorting speed using lambda vs itemgetter.
import random
import operator
# create a list of 100 dicts with random 8-letter names and random ages from 0 to 100.
l = [{'name': ''.join(random.choices(string.ascii_lowercase, k=8)), 'age': random.randint(0, 100)} for i in range(100)]
# Test the performance with a lambda function sorting on name
%timeit sorted(l, key=lambda x: x['name'])
13 µs ± 388 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
# Test the performance with itemgetter sorting on name
%timeit sorted(l, key=operator.itemgetter('name'))
10.7 µs ± 38.1 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
# Check that each technique produces same sort order
sorted(l, key=lambda x: x['name']) == sorted(l, key=operator.itemgetter('name'))
True
Both techniques sort the list in the same order (verified by execution of the final statement in the code block) but one is a little faster.
回答 17
您可以使用以下代码
sorted_dct = sorted(dct_name.items(), key =lambda x : x[1])
To delete a key regardless of whether it is in the dictionary, use the two-argument form of dict.pop():
my_dict.pop('key', None)
This will return my_dict[key] if key exists in the dictionary, and None otherwise. If the second parameter is not specified (ie. my_dict.pop('key')) and key does not exist, a KeyError is raised.
To delete a key that is guaranteed to exist, you can also use
del my_dict['key']
This will raise a KeyError if the key is not in the dictionary.
Specifically to answer “is there a one line way of doing this?”
if 'key' in my_dict: del my_dict['key']
…well, you asked ;-)
You should consider, though, that this way of deleting an object from a dict is not atomic—it is possible that 'key' may be in my_dict during the if statement, but may be deleted before del is executed, in which case del will fail with a KeyError. Given this, it would be safest to either use dict.pop or something along the lines of
It took me some time to figure out what exactly my_dict.pop("key", None) is doing. So I’ll add this as an answer to save others Googling time:
pop(key[, default])
If key is in the dictionary, remove it and return its value, else
return default. If default is not given and key is not in the
dictionary, a KeyError is raised.
>>>import timeit
>>> setup ="d = {i: i for i in range(100000)}">>> timeit.timeit("del d[3]", setup=setup, number=1)1.79e-06>>> timeit.timeit("d.pop(3)", setup=setup, number=1)2.09e-06>>> timeit.timeit("d2 = {key: val for key, val in d.items() if key != 3}", setup=setup, number=1)0.00786
但是,当密钥不存在时,它会if key in my_dict: del my_dict[key]比稍快一点my_dict.pop(key, None)。两者都至少比快三倍del的try/ except语句:
del my_dict[key] is slightly faster than my_dict.pop(key) for removing a key from a dictionary when the key exists
>>> import timeit
>>> setup = "d = {i: i for i in range(100000)}"
>>> timeit.timeit("del d[3]", setup=setup, number=1)
1.79e-06
>>> timeit.timeit("d.pop(3)", setup=setup, number=1)
2.09e-06
>>> timeit.timeit("d2 = {key: val for key, val in d.items() if key != 3}", setup=setup, number=1)
0.00786
But when the key doesn’t exist if key in my_dict: del my_dict[key] is slightly faster than my_dict.pop(key, None). Both are at least three times faster than del in a try/except statement:
But one should keep in mind that, in this process actually it won’t delete any key from the dictionary rather than making specific key excluded from that dictionary. In addition, I observed that it returned a dictionary which was not ordered the same as myDict.
myDict = {'one': 100, 'two': 200, 'three': 300, 'four': 400, 'five': 500}
{key:value for key, value in myDict.items() if key != 'one'}
If we run it in the shell, it’ll execute something like {'five': 500, 'four': 400, 'three': 300, 'two': 200} – notice that it’s not the same ordered as myDict. Again if we try to print myDict, then we can see all keys including which we excluded from the dictionary by this approach. However, we can make a new dictionary by assigning the following statement into a variable:
var = {key:value for key, value in myDict.items() if key != 'one'}
Now if we try to print it, then it’ll follow the parent order:
test_dict ={"sai":22,"kiran":21,"vinod":21,"sangam":21}# Printing dictionary before removal print("dictionary before performing remove is : "+ str(test_dict))# Using items() + dict comprehension to remove a dict. pair # removes vinod
new_dict ={key:val for key, val in test_dict.items()if key !='vinod'}# Printing dictionary after removal print("dictionary after remove is : "+ str(new_dict))
输出:
dictionary before performing remove is:{'sai':22,'kiran':21,'vinod':21,'sangam':21}
dictionary after remove is:{'sai':22,'kiran':21,'sangam':21}
Another way is by Using items() + dict comprehension
items() coupled with dict comprehension can also help us achieve task of key-value pair deletion but, it has drawback of not being an inplace dict technique. Actually a new dict if created except for the key we don’t wish to include.
test_dict = {"sai" : 22, "kiran" : 21, "vinod" : 21, "sangam" : 21}
# Printing dictionary before removal
print ("dictionary before performing remove is : " + str(test_dict))
# Using items() + dict comprehension to remove a dict. pair
# removes vinod
new_dict = {key:val for key, val in test_dict.items() if key != 'vinod'}
# Printing dictionary after removal
print ("dictionary after remove is : " + str(new_dict))
Output:
dictionary before performing remove is : {'sai': 22, 'kiran': 21, 'vinod': 21, 'sangam': 21}
dictionary after remove is : {'sai': 22, 'kiran': 21, 'sangam': 21}
>>> li =['a','b','mpilgrim','z','example']>>> li
['a','b','mpilgrim','z','example']>>> li.append("new")>>> li
['a','b','mpilgrim','z','example','new']>>> li.append(["new",2])>>> li
['a','b','mpilgrim','z','example','new',['new',2]]>>> li.insert(2,"new")>>> li
['a','b','new','mpilgrim','z','example','new',['new',2]]>>> li.extend(["two","elements"])>>> li
['a','b','new','mpilgrim','z','example','new',['new',2],'two','elements']
def append_one(a_list, element):
a_list.append(element)def extend_one(a_list, element):"""creating a new list is semantically the most direct
way to create an iterable to give to extend"""
a_list.extend([element])import timeit
What is the difference between the list methods append and extend?
append adds its argument as a single element to the end of a list. The length of the list itself will increase by one.
extend iterates over its argument adding each element to the list, extending the list. The length of the list will increase by however many elements were in the iterable argument.
append
The list.append method appends an object to the end of the list.
my_list.append(object)
Whatever the object is, whether a number, a string, another list, or something else, it gets added onto the end of my_list as a single entry on the list.
So keep in mind that a list is an object. If you append another list onto a list, the first list will be a single object at the end of the list (which may not be what you want):
>>> another_list = [1, 2, 3]
>>> my_list.append(another_list)
>>> my_list
['foo', 'bar', 'baz', [1, 2, 3]]
#^^^^^^^^^--- single item at the end of the list.
extend
The list.extend method extends a list by appending elements from an iterable:
my_list.extend(iterable)
So with extend, each element of the iterable gets appended onto the list. For example:
Keep in mind that a string is an iterable, so if you extend a list with a string, you’ll append each character as you iterate over the string (which may not be what you want):
Both + and += operators are defined for list. They are semantically similar to extend.
my_list + another_list creates a third list in memory, so you can return the result of it, but it requires that the second iterable be a list.
my_list += another_list modifies the list in-place (it is the in-place operator, and lists are mutable objects, as we’ve seen) so it does not create a new list. It also works like extend, in that the second iterable can be any kind of iterable.
Don’t get confused – my_list = my_list + another_list is not equivalent to += – it gives you a brand new list assigned to my_list.
Iterating through the multiple calls to append adds to the complexity, making it equivalent to that of extend, and since extend’s iteration is implemented in C, it will always be faster if you intend to append successive items from an iterable onto a list.
Performance
You may wonder what is more performant, since append can be used to achieve the same outcome as extend. The following functions do the same thing:
def append(alist, iterable):
for item in iterable:
alist.append(item)
def extend(alist, iterable):
alist.extend(iterable)
Perfect answer, I just miss the timing of comparing adding only one element
Do the semantically correct thing. If you want to append all elements in an iterable, use extend. If you’re just adding one element, use append.
Ok, so let’s create an experiment to see how this works out in time:
def append_one(a_list, element):
a_list.append(element)
def extend_one(a_list, element):
"""creating a new list is semantically the most direct
way to create an iterable to give to extend"""
a_list.extend([element])
import timeit
And we see that going out of our way to create an iterable just to use extend is a (minor) waste of time:
We learn from this that there’s nothing gained from using extend when we have only one element to append.
Also, these timings are not that important. I am just showing them to make the point that, in Python, doing the semantically correct thing is doing things the Right Way™.
It’s conceivable that you might test timings on two comparable operations and get an ambiguous or inverse result. Just focus on doing the semantically correct thing.
Conclusion
We see that extend is semantically clearer, and that it can run much faster than append, when you intend to append each element in an iterable to a list.
If you only have a single element (not in an iterable) to add to the list, use append.
回答 3
append追加一个元素。extend追加元素列表。
请注意,如果您传递要追加的列表,它仍会添加一个元素:
>>> a =[1,2,3]>>> a.append([4,5,6])>>> a
[1,2,3,[4,5,6]]
With append you can append a single element that will extend the list:
>>> a = [1,2]
>>> a.append(3)
>>> a
[1,2,3]
If you want to extend more than one element you should use extend, because you can only append one elment or one list of element:
>>> a.append([4,5])
>>> a
>>> [1,2,3,[4,5]]
So that you get a nested list
Instead with extend, you can extend a single element like this
>>> a = [1,2]
>>> a.extend([3])
>>> a
[1,2,3]
Or, differently, from append, extend more elements in one time without nesting the list into the original one (that’s the reason of the name extend)
>>> a.extend([4,5,6])
>>> a
[1,2,3,4,5,6]
Adding one element with both methods
Both append and extend can add one element to the end of the list, though append is simpler.
append 1 element
>>> x = [1,2]
>>> x.append(3)
>>> x
[1,2,3]
extend one element
>>> x = [1,2]
>>> x.extend([3])
>>> x
[1,2,3]
Adding more elements… with different results
If you use append for more than one element, you have to pass a list of elements as arguments and you will obtain a NESTED list!
>>> x = [1,2]
>>> x.append([3,4])
>>> x
[1,2,[3,4]]
With extend, instead, you pass a list as an argument, but you will obtain a list with the new element that is not nested in the old one.
>>> z = [1,2]
>>> z.extend([3,4])
>>> z
[1,2,3,4]
So, with more elements, you will use extend to get a list with more items.
However, appending a list will not add more elements to the list, but one element that is a nested list as you can clearly see in the output of the code.
The append() method adds a single item to the end of the list.
x = [1, 2, 3]
x.append([4, 5])
x.append('abc')
print(x)
# gives you
[1, 2, 3, [4, 5], 'abc']
The extend() method takes one argument, a list, and appends each of the items of the argument to the original list. (Lists are implemented as classes. “Creating” a list is really instantiating a class. As such, a list has methods that operate on it.)
x = [1, 2, 3]
x.extend([4, 5])
x.extend('abc')
print(x)
# gives you
[1, 2, 3, 4, 5, 'a', 'b', 'c']
Similarly += for in place behavior, but with slight differences from append & extend. One of the biggest differences of += from append and extend is when it is used in function scopes, see this blog post.
回答 8
append(object) -通过将对象添加到列表来更新列表。
x =[20]# List passed to the append(object) method is treated as a single object.
x.append([21,22,23])# Hence the resultant list length will be 2print(x)-->[20,[21,22,23]]
extend(list) -本质上是串联两个列表。
x =[20]# The parameter passed to extend(list) method is treated as a list.# Eventually it is two lists being concatenated.
x.extend([21,22,23])# Here the resultant list's length is 4print(x)[20,21,22,23]
append(object) – Updates the list by adding an object to the list.
x = [20]
# List passed to the append(object) method is treated as a single object.
x.append([21, 22, 23])
# Hence the resultant list length will be 2
print(x)
--> [20, [21, 22, 23]]
extend(list) – Essentially concatenates two lists.
x = [20]
# The parameter passed to extend(list) method is treated as a list.
# Eventually it is two lists being concatenated.
x.extend([21, 22, 23])
# Here the resultant list's length is 4
print(x)
[20, 21, 22, 23]
An interesting point that has been hinted, but not explained, is that extend is faster than append. For any loop that has append inside should be considered to be replaced by list.extend(processed_elements).
Bear in mind that apprending new elements might result in the realloaction of the whole list to a better location in memory. If this is done several times because we are appending 1 element at a time, overall performance suffers. In this sense, list.extend is analogous to “”.join(stringlist).
Append adds the entire data at once. The whole data will be added to the newly created index. On the other hand, extend, as it name suggests, extends the current array.
lis =[1,2,3]# 'extend' is equivalent to this
lis = lis + list(iterable)# 'append' simply appends its argument as the last element to the list# as long as the argument is a valid Python object
list.append(object)
An English dictionary defines the words append and extend as:
append: add (something) to the end of a written document. extend: make larger. Enlarge or expand
With that knowledge, now let’s understand
1) The difference between append and extend
append:
Appends any Python object as-is to the end of the list (i.e. as a
the last element in the list).
The resulting list may be nested and contain heterogeneous elements (i.e. list, string, tuple, dictionary, set, etc.)
extend:
Accepts any iterable as its argument and makes the list larger.
The resulting list is always one-dimensional list (i.e. no nesting) and it may contain heterogeneous elements in it (e.g. characters, integers, float) as a result of applying list(iterable).
2) Similarity between append and extend
Both take exactly one argument.
Both modify the list in-place.
As a result, both returns None.
Example
lis = [1, 2, 3]
# 'extend' is equivalent to this
lis = lis + list(iterable)
# 'append' simply appends its argument as the last element to the list
# as long as the argument is a valid Python object
list.append(object)
I hope I can make a useful supplement to this question. If your list stores a specific type object, for example Info, here is a situation that extend method is not suitable: In a for loop and and generating an Info object every time and using extend to store it into your list, it will fail. The exception is like below:
TypeError: ‘Info’ object is not iterable
But if you use the append method, the result is OK. Because every time using the extend method, it will always treat it as a list or any other collection type, iterate it, and place it after the previous list. A specific object can not be iterated, obviously.
def append_o(a_list, element):
a_list.append(element)print('append:', end =' ')for item in a_list:print(item, end =',')print()def extend_o(a_list, element):
a_list.extend(element)print('extend:', end =' ')for item in a_list:print(item, end =',')print()
append_o(['ab'],'cd')
extend_o(['ab'],'cd')
append_o(['ab'],['cd','ef'])
extend_o(['ab'],['cd','ef'])
append_o(['ab'],['cd'])
extend_o(['ab'],['cd'])
append “extends” the list (in place) by only one item, the single object passed (as argument).
extend “extends” the list (in place) by as many items as the object passed (as argument) contains.
This may be slightly confusing for str objects.
If you pass a string as argument:
append will add a single string item at the end but
extend will add as many “single” ‘str’ items as the length of that string.
If you pass a list of strings as argument:
append will still add a single ‘list’ item at the end and
extend will add as many ‘list’ items as the length of the passed list.
def append_o(a_list, element):
a_list.append(element)
print('append:', end = ' ')
for item in a_list:
print(item, end = ',')
print()
def extend_o(a_list, element):
a_list.extend(element)
print('extend:', end = ' ')
for item in a_list:
print(item, end = ',')
print()
append_o(['ab'],'cd')
extend_o(['ab'],'cd')
append_o(['ab'],['cd', 'ef'])
extend_o(['ab'],['cd', 'ef'])
append_o(['ab'],['cd'])
extend_o(['ab'],['cd'])
Append and extend are one of the extensibility mechanisms in python.
Append: Adds an element to the end of the list.
my_list = [1,2,3,4]
To add a new element to the list, we can use append method in the following way.
my_list.append(5)
The default location that the new element will be added is always in the (length+1) position.
Insert: The insert method was used to overcome the limitations of append. With insert, we can explicitly define the exact position we want our new element to be inserted at.
Method descriptor of insert(index, object). It takes two arguments, first being the index we want to insert our element and second the element itself.
Extend: This is very useful when we want to join two or more lists into a single list. Without extend, if we want to join two lists, the resulting object will contain a list of lists.
a = [1,2]
b = [3]
a.append(b)
print (a)
[1,2,[3]]
If we try to access the element at pos 2, we get a list ([3]), instead of the element. To join two lists, we’ll have to use append.
a = [1,2]
b = [3]
a.extend(b)
print (a)
[1,2,3]
To join multiple lists
a = [1]
b = [2]
c = [3]
a.extend(b+c)
print (a)
[1,2,3]