Semantics of python loops and strings - python

Consider:
args = ['-sdfkj']
print args
for arg in args:
print arg.replace("-", '')
arg = arg.replace("-", '')
print args
This yields:
['-sdfkj']
sdfkj
['-sdfkj']
Where I expected it to be ['sdfkj'].
Is arg in the loop a copy?
It behaves as if it is a copy (or perhaps an immutable thingie, but then I expect an error would be thrown...)
Note: I can get the right behavior with a list comprehension. I am curious as to the cause of the above behavior.

Is arg in the loop a copy?
Yes, it contains a copy of the reference.
When you reassign arg you aren't modifying the original array, nor the string inside it (strings are immutable). You modify only what the local variable arg points to.
Before assignment After assignment
args arg args arg
| | | |
| | | |
(array) / (array) 'sdfkj'
|[0] / |[0]
\ / |
\ / |
'-sdfkj' '-sdfkj'

Since you mention in your question that you know it can be done using list comprehensions, I'm not going to show you that way.
What is happening there is that the reference to each value is copied into the variable arg. Initially they both refer to the same variable. Once arg is reassigned, it refers to the newly created variable, while the reference in the list remains unchanged. It has nothing to do with the fact that strings are immutable in Python. Try it out using mutable types.
A non-list comprehension way would be to modify the list in place:
for i in xrange(len(args)):
args[i]=args[i].replace('-','')
print args

If you want to modify the actual list you have to specifically assign the changes to the list 'args.'
i.e.
for arg in args:
if arg == "-":
args[arg] = args[arg].replace("-", '')

for a list I'd try:
args = [x.replace("-", '') for x in args]

Related

Understanding Python help() 's ... syntax, and the pop function for dictionaries

The ... next to various python "help" method/function descriptions don't seem to be defined anywhere. What does function(...) mean, in the context of the python help output description?
Specifically, how should the documentation for python's pop function be interpreted?
Details
Given that the pop requires an input, it is a little confusing that help({}) doesn't show this in the functions input description (...).
Interpreting the ... as "ditto" doesn't work - for example, "items" cannot take any inputs.
To be clear, this is the functions input definition from help.
pop(...)
The full output of the help({}) function, for get...pop is below. What do the ...'s mean, and, why is there no input defined, for the pop function?
| get(self, key, default=None, /)
| Return the value for key if key is in the dictionary, else default.
|
| items(...)
| D.items() -> a set-like object providing a view on D's items
|
| keys(...)
| D.keys() -> a set-like object providing a view on D's keys
|
| pop(...)
| D.pop(k[,d]) -> v, remove specified key and return the corresponding value.
| If key is not found, d is returned if given, otherwise KeyError is raised
languages = ['Python', 'Java', 'C++', 'Ruby', 'C']
# here is a list
languages.pop(2)
# this will call the pop function for
# languages list and the it removes the element
# at the specified position.
# so after you take a look at languages
languages
Output: ['Python', 'Java', 'Ruby', 'C']
So as for your question. "pop" is a function so its usage is like any other function
thelist.pop(the-element-you-want-to-be-removed)

Passing list as an argument to a function . Can someone explain this behavior?

def test(lister):
lister.append('why am i being shown in output?')
def pass_the_list():
x = []
test(x)
print(x)
pass_the_list()
Output
['why am i being shown in output?']
Process finished with exit code 0
how to pass string as a reference without return?
The list x is passed as a reference not value, therefore changes made in the function will preserve also when the function finishes.
You can pass a string as a refence, but it doesnt matter, because string, unlike list, is immutable. So any change you do to the string will cause creation of a new object.
test_string = 'Test my immutability'
test_string[0]='B'
>> TypeError: 'str' object does not support item assignment
You can modify string like this though (by putting it into something mutable like list)
test_string = 'Test my immutability'
test_list = [test_string]
def reference_test(test_list):
test_list[0]=test_list[0].replace('Test','Fest')
reference_test(test_list)
print(test_list)
>> ['Rest my immutability']

Python: How do I pass a string by reference?

From this link: How do I pass a variable by reference?, we know, Python will copy a string (an immutable type variable) when it is passed to a function as a parameter, but I think it will waste memory if the string is huge. In many cases, we need to use functions to wrap some operations for strings, so I want to know how to do it more effective?
Python does not make copies of objects (this includes strings) passed to functions:
>>> def foo(s):
... return id(s)
...
>>> x = 'blah'
>>> id(x) == foo(x)
True
If you need to "modify" a string in a function, return the new string and assign it back to the original name:
>>> def bar(s):
... return s + '!'
...
>>> x = 'blah'
>>> x = bar(x)
>>> x
'blah!'
Unfortunately, this can be very inefficient when making small changes to large strings because the large string gets copied. The pythonic way of dealing with this is to hold strings in an list and join them together once you have all the pieces.
Python does pass a string by reference. Notice that two strings with the same content are considered identical:
a = 'hello'
b = 'hello'
a is b # True
Since when b is assigned by a value, and the value already exists in memory, it uses the same reference of the string. Notice another fact, that if the string was dynamically created, meaning being created with string operations (i.e concatenation), the new variable will reference a new instance of the same string:
c = 'hello'
d = 'he'
d += 'llo'
c is d # False
That being said, creating a new string will allocate a new string in memory and returning a reference for the new string, but using a currently created string will reuse the same string instance. Therefore, passing a string as a function parameter will pass it by reference, or in other words, will pass the address in memory of the string.
And now to the point you were looking for- if you change the string inside the function, the string outside of the function will remain the same, and that stems from string immutability. Changing a string means allocating a new string in memory.
a = 'a'
b = a # b will hold a reference to string a
a += 'a'
a is b # False
Bottom line:
You cannot really change a string. The same as for maybe every other programming language (but don't quote me).
When you pass the string as an argument, you pass a reference. When you change it's value, you change the variable to point to another place in memory. But when you change a variable's reference, other variables that points to the same address will naturally keep the old value (reference) they held.
Wish the explanation was clear enough
In [7]: strs="abcd"
In [8]: id(strs)
Out[8]: 164698208
In [9]: def func(x):
print id(x)
x=x.lower() #perform some operation on string object, it returns a new object
print id(x)
...:
In [10]: func(strs)
164698208 # same as strs, i.e it actually passes the same object
164679776 # new object is returned if we perform an operation
# That's why they are called immutable
But operations on strings always return a new string object.
def modify_string( t ):
the_string = t[0]
# do stuff
modify_string( ["my very long string"] )
If you want to potentially change the value of something passed in, wrap it in a dict or a list:
This doesn't change s
def x(s):
s += 1
This does change s:
def x(s):
s[0] += 1
This is the only way to "pass by reference".
wrapping the string into a class will make it pass by reference:
class refstr:
"wrap string in object, so it is passed by reference rather than by value"
def __init__(self,s=""):
self.s=s
def __add__(self,s):
self.s+=s
return self
def __str__(self):
return self.s
def fn(s):
s+=" world"
s=refstr("hello")
fn(s) # s gets modified because objects are passed by reference
print(s) #returns 'hello world'
Just pass it in as you would any other parameter. The contents won't get copied, only the reference will.

Python: make a copy of object when equal old object to new

I've created new class based on default str class. I've also changed default methods like __add__, __mul__, __repr__ etc. But I want to change default behaviour when user equal new variable to old one. Look what I have now:
a = stream('new stream')
b = a
b += ' was modified'
a == b
>>> True
print a
>>> stream('new stream was modified')
print b
>>> stream('new stream was modified')
So as you see each time I modify second variable Python also changes original variable. As I understand Python simply sends adress of variable a to variable b. Is it possible to make a copy of variable on creation like in usual str? As I think I need smth like new in C++.
a = 'new string'
b = a
b += ' was modified'
a == b
>>> False
P.S. Creation of the object begins in self.new() method. Creation is made like this:
def __new__(self, string):
return(str.__new__(self, string))
It is more complicated, because it takes care of unicode and QString type, first getting str object from them, but I think it's not neccessary.
I don't believe you can change the behavior of the assignment operator, but there are explicit ways to create a copy of an object rather than just using a reference. For a complex object, take a look at the copy module. For a basic sequence type (like str), the following works assuming you're implementing slice properly:
Code
a = str('abc')
#A slice creates a copy of a sequence object.
#[:] creates a copy of the entire thing.
b = a[:]
#Since b is a full copy of a, this will not modify a
b += ' was modified'
#Check the various values
print('a == b' + str(a == b))
print(a)
print(b)
Output
False
abc
abc was modified

I thought Python passed everything by reference?

Take the following code
#module functions.py
def foo(input, new_val):
input = new_val
#module main.py
input = 5
functions.foo(input, 10)
print input
I thought input would now be 10. Why is this not the case?
Everything is passed by value, but that value is a reference to the original object. If you modify the object, the changes are visible for the caller, but you can't reassign names. Moreover, many objects are immutable (ints, floats, strings, tuples).
Inside foo, you're binding the local name input to a different object (10). In the calling context, the name input still refers to the 5 object.
Assignment in Python does not modify an object in-place. It rebinds a name so that after input = new_val, the local variable input gets a new value.
If you want to modify the "outside" input, you'll have to wrap it inside a mutable object such as a one-element list:
def foo(input, new_val):
input[0] = new_val
foo([input])
Python does not do pass-by-reference exactly the way C++ reference passing works. In this case at least, it's more as if every argument is a pointer in C/C++:
// effectively a no-op!
void foo(object *input, object *new_val)
{
input = new_val;
}
Python is neither call-by-value, or call-by-reference, it is Call By Object.
"Arguments are passed by call-by-sharing, similar to
call-by-value, except that the arguments are objects
and can be changed only if they are mutable."
Well, python functions are neither call-by-reference nor call-by-value, they are call-by-object.
before applying this concept on functions, let's take a look at those code snippets:
1-
listA = [0]
listB = listA
listB.append(1)
print (listA) # [0, 1]
2-
listA = [0]
listB = listA
listB = [2, 3]
print(listA) # [0]
In the first code snippet, listB was able to access the objects stored in listA and modify them BUT IT COULDN'T MODIFY listA in the second code snippet, as they are not the same thing! you passed a container of objects and were able to modify the objects in that container but, you can't modify that container itself.
in your code:
def foo(input, new_val):
input = new_val
input = 5
foo(input, 10)
print (input)
you can't modify the variable 'input' because you don't have it!
Passing by object is a very confusing concept and reminds me with Quantum Physics but, try playing around with the code and you will understand it well.
you can check the following links for more illustrations:
geeks for geeks
ROBERT HEATON blog

Categories

Resources