Behaviour of map function in Python - python

I can not understand how and why the first time it is printed a reference of the object(which object is referring to?) and the second time when I use two variables, these variables get the result of the function instead of a reference.
>>> a = map(int,[1,2])
>>> a
<map object at 0x7f0b1142fa90>
>>> b,c = a
>>> b
1
>>> c
2

In Python 3, map (and other primitive combinators) return an iterator object rather than a list (as they did before.) At the first attempt, you printed that iterator object per se, while the second time you matched it against a sequence, thus forcing and extracting elements. Consider:
>>> a = map(int,[1,2])
>>> a
<map object at 0x7ff6ddbfe748>
>>> list(a)
[1, 2]

Related

Python "reversed" function return object with 'list' function doesn't work well

When i use 'reversed' function return object with 'list' function to make list, it remove their element.
>>> a = [1,2,3,4,5]
>>> b = reversed(a)
>>> b
<list_reverseiterator object at 0x000001D9059A6438>
>>> c = list(b)
>>> c
[5, 4, 3, 2, 1]
>>> c
[5, 4, 3, 2, 1]
>>> list(b)
[]
Second 'list(b)' remover their data, so that i cannot use it data again.
Why this issue happened?
reversed returns a reverse iterator (can only be read once), not a reversed sequence (can be used repeatedly). If you want to make a repeatably usable reversed version of an arbitrary sequence, either immediately convert to list (so you don't see the intermediate iterator):
>>> a = [1,2,3,4,5]
>>> b = list(reversed(a))
or use the reversing slice (which also preserves the type of the original sequence):
>>> a = [1,2,3,4,5]
>>> b = a[::-1]
As you can see in your first output, reversed() returns an iterator, not a new list. That's why you need to call list(b) to get an actual list.
Most iterators can only be used once.*
Every time you fetch a value from an iterator, it updates its internal state to remember the current position, so that the next fetch can get the next value, and so on. Once you get to the end, there's nothing else to fetch.
When you use list(b), the list() function goes into a loop, fetching all the values from the b iterator. When it's done, the iterator is at the end of its data. If you try to use it again, there's nothing left to fetch. There's no way to reset it back to the beginning.
*An exception is when you use a file object as an iterator. Instead of the iterator maintaining its own state, it uses the state of the file object, and just performs a readline() call from the current file position. So you can use file.seek(0) to reset the iterator to the beginning of the file.

Why do python lists act like this when using the = operator [duplicate]

This question already has answers here:
Variable assignment and modification (in python) [duplicate]
(6 answers)
Closed 4 years ago.
How come the following code:
a = [1,2,3]
b = a
b[0] = 3
print(a)
will print list b after it has been altered?[3,2,3].
Also why is this true but that the following code:
a = [1,2,3]
b = a
b = [0,0,0]
print(a,b)
prints [1, 2, 3] [0, 0, 0]?? This seems inconsistent. If the first code is true, then shouldn't the second code print [0,0,0][0,0,0]? Can someone please provide an explanation for this?
In python there are two types of data... mutable and immutable. Numbers, strings, boolean, tuples, and other simple types are immutable. Dicts, lists, sets, objects, classes, and other complex types are mutable.
When you say:
a = [1,2,3]
b = a
You've created a single mutable list in memory, assigned a to point to it, and then assigned b to point to it. It's the same thing in memory.
Therefore when you mutate it (modify it):
b[0] = 3
It is a modification (mutation) of the index [0] of the value which b points to at that same memory location.
However, when you replace it:
b = [0,0,0]
It is creating a new mutable list in memory and assigning b to point at it.
Check out the id() function. It will tell you the "address" of any variable. You can see which names are pointing to the same memory location with id(varname).
Bonus: Every value in python is passed by reference... meaning that when you assign it to a variable it simply causes that variable to point to that value where it was in memory. Having immutable types allows python to "reuse" the same memory location for common immutable types.
Consider some common values when the interpreter starts up:
>>> import sys
>>> sys.getrefcount('abc')
68
>>> sys.getrefcount(100)
110
>>> sys.getrefcount(2)
6471
However, a value that is definitely not present would return 2. This has to do with the fact that a couple of references to that value were in-use during the call to sys.getrefcount
>>> sys.getrefcount('nope not me. I am definitely not here already.')
2
Notice that an empty tuple has a lot of references:
>>> sys.getrefcount(tuple())
34571
But an empty list has no extra references:
>>> sys.getrefcount(list())
1
Why is this? Because tuple is immutable so it is fine to share that value across any number of variables. However, lists are mutable so they MUST NOT be shared across arbitrary variables or changes to one would affect the others.
Incidentally, this is also why you must NEVER use mutable types as default argument values to functions. Consider this innocent little function:
>>> def foo(value=[]):
... value.append(1)
... print(value)
...
...
When you call it you might expect to get [1] printed...
>>> foo()
[1]
However, when you call it again, you prob. won't expect to get [1,1] out... ???
>>> foo()
[1, 1]
And on and on...
>>> foo()
[1, 1, 1]
>>> foo()
[1, 1, 1, 1]
WHY IS THIS? Because default arguments to functions are evaluated once during function definition, and not at function run time. That way if you use a mutable value as a default argument value, then you will be stuck with that one value, mutating in unexpected ways as the function is called multiple times.
The proper way to do it is this:
>>> def foo(value=None):
... if value is None:
... value = []
... value.append(1)
... print(value)
...
...
>>>
>>> foo()
[1]
>>> foo()
[1]
>>> foo()
[1]

Python: those variable dont point to the same values. Why?

I thought that if you assign a variable to another list, it's not copied, but it points to the same location. That's why deepcopy() is for. This is not true with Python 2.7: it's copied.
>>> a=[1,2,3]
>>> b=a
>>> b=b[1:]+b[:1]
>>> b
[2, 3, 1]
>>> a
[1, 2, 3]
>>>
>>> a=(1,2,3)
>>> b=a
>>> b=b[1:]+b[:1]
>>> a
(1, 2, 3)
>>> b
(2, 3, 1)
>>>
What am I missing?
This line changes what b points to:
b=b[1:]+b[:1]
List or tuple addition creates a new list or tuple, and the assignment operator makes b refer to that new list while leaving a referring to the original list or tuple.
Slicing a list or tuple also creates a new object, so that line creates three new objects - one for each slice, and then one for the sum. b = a + b would be a simpler example to demonstrate that addition creates a new object.
You will sometimes see c = b[:] as a way to shallow copy a list, making use of the fact that slicing creates a new object.
When you do b=b[1:]+b[:1] you first create a new object of two b slices and then assign b to reference that object. The same is for both list and tuple cases

Difference between list() and [:]

If we have a list s, is there any difference between calling list(s) versus s[:]? It seems to me like they both create new list objects with the exact elements of s.
In both cases, they should create a (shallow) copy of the list.
Note that there is one corner case (which is hardly worth mentioning) where it might be different...
list = tuple # Don't ever do this!
list_copy = list(some_list) # Oops, actually it's a tuple ...
actually_list_copy = some_list[:]
With that said, nobody in their right mind should ever shadow the builtin list like that.
My advice, use whichever you feel is easier to read and works nicely in the current context.
list(...) makes it explicit that the output is a list and will make a list out of any iterable.
something[:] is a common idiom for "give me a shallow copy of this sequence, I don't really care what kind of sequence it is ...", but it doesn't work on arbitrary iterables.
list() is better - it's more readable. Other than that there is no difference.
The short answer is use list(). In google type python [:] then type python list.
If s is a list then there is no difference, but will s always be a list? Or could it be a sequence or a generator?
In [1]: nums = 1, 2, 3
In [2]: nums
Out[2]: (1, 2, 3)
In [3]: nums[:]
Out[3]: (1, 2, 3)
In [4]: list(nums)
Out[4]: [1, 2, 3]
In [7]: strings = (str(x) for x in nums)
In [8]: strings
Out[8]: <generator object <genexpr> at 0x7f77be460550>
In [9]: strings[:]
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-9-358af12435ff> in <module>()
----> 1 strings[:]
TypeError: 'generator' object has no attribute '__getitem__'
In [10]: list(strings)
Out[10]: ['1', '2', '3']
I didn't realize, but as ventsyv mentioned, s[:] and list(s) both create a copy of s.
Note you can check if an object is the same using is and id() can be used to get the object's memory address to actually see if they are the same or not.
>>> s = [1,2,3]
>>> listed_s = list(s)
>>> id(s)
44056328
>>> id(listed_s) # different
44101840
>>> listed_s is s
False
>>> bracket_s = s[:]
>>> bracket_s is s
False
>>> id(bracket_s)
44123760
>>> z = s # points to the same object in memory
>>> z is s
True
>>> id(z)
44056328
>>>
id(...)
id(object) -> integer
Return the identity of an object. This is guaranteed to be unique among
simultaneously existing objects. (Hint: it's the object's memory address.)

How to pass a list element as reference?

I am passing a single element of a list to a function. I want to modify that element, and therefore, the list itself.
def ModList(element):
element = 'TWO'
l = list();
l.append('one')
l.append('two')
l.append('three')
print l
ModList(l[1])
print l
But this method does not modify the list. It's like the element is passed by value. The output is:
['one','two','three']
['one','two','three']
I want that the second element of the list after the function call to be 'TWO':
['one','TWO','three']
Is this possible?
The explanations already here are correct. However, since I have wanted to abuse python in a similar fashion, I will submit this method as a workaround.
Calling a specific element from a list directly returns a copy of the value at that element in the list. Even copying a sublist of a list returns a new reference to an array containing copies of the values. Consider this example:
>>> a = [1, 2, 3, 4]
>>> b = a[2]
>>> b
3
>>> c = a[2:3]
>>> c
[3]
>>> b=5
>>> c[0]=6
>>> a
[1, 2, 3, 4]
Neither b, a value only copy, nor c, a sublist copied from a, is able to change values in a. There is no link, despite their common origin.
However, numpy arrays use a "raw-er" memory allocation and allow views of data to be returned. A view allows data to be represented in a different way while maintaining the association with the original data. A working example is therefore
>>> import numpy as np
>>> a = np.array([1, 2, 3, 4])
>>> a
array([1, 2, 3, 4])
>>> b = a[2]
>>> b
3
>>> b=5
>>> a
array([1, 2, 3, 4])
>>> c = a[2:3]
>>> c
array([3])
>>> c[0]=6
>>> a
array([1, 2, 6, 4])
>>>
While extracting a single element still copies by value only, maintaining an array view of element 2 is referenced to the original element 2 of a (although it is now element 0 of c), and the change made to c's value changes a as well.
Numpy ndarrays have many different types, including a generic object type. This means that you can maintain this "by-reference" behavior for almost any type of data, not only numerical values.
Python doesn't do pass by reference. Just do it explicitly:
l[1] = ModList(l[1])
Also, since this only changes one element, I'd suggest that ModList is a confusing name.
Python is a pass by value language hence you can't change the value by assignment in the function ModList. What you could do instead though is pass the list and index into ModList and then modify the element that way
def ModList(theList, theIndex) :
theList[theIndex] = 'TWO'
ModList(l, 1)
In many cases you can also consider to let the function both modify and return the modified list. This makes the caller code more readable:
def ModList(theList, theIndex) :
theList[theIndex] = 'TWO'
return theList
l = ModList(l, 1)

Categories

Resources