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
Related
I have a class within which there is a DataFrame type property. I want to be able to perform arithmetic on the objects using the built-ins while keeping the original objects immutable. Unfortunately, the operations seem to be mutating the original objects as well. Here's an example:
import numpy as np
import pandas as pd
class Container:
def __init__(self):
self.data = pd.DataFrame()
def generate(self):
self.data = pd.DataFrame(np.random.randint(0,100,size=(100, 1)), columns=['A'])
return self
def __add__(self, other):
copy = self
new = Container()
new.data['A'] = copy.data.eval(f"A + {0}".format(other))
return new
one = Container().generate()
two = one + 1
print(one.data == two.data)
I think the problem is the copy = self line, but I can't seem to preserve the original object even using the copy() method.
How do I make sure the original object doesn't change when a new one is created from it?
Surprisingly, while copy = self isn't a copy, your bug doesn't actually have anything to do with that. I don't think you even need a copy there.
Your bug is due to double-formatting a string:
f"A + {0}".format(other)
f"A + {0}" is an f-string. Unlike format, it evaluates the text 0 as a Python expression and substitutes the string representation of the resulting object into the resulting string, producing "A + 0". Calling format on that doesn't do anything, since there's no format placeholder left. You end up calling
copy.data.eval("A + 0")
instead of adding what you wanted to add.
Did you deepcopy?
from copy import deepcopy
dupe=deepcopy(thing)
#now thing and dupe are two separate objects
In Python 2.7 I want to intialize a variables type depending on another variable.
For example I want to do something like:
var_1 = type(var_2)
Is there a simple/fast way to do that?
Just create another instance
var_1 = type(var_2)()
Note that if you're not sure whether the object has a non-default constructor, you cannot rely on the above, but you can use copy or deepcopy (you get a "non-empty" object.
import copy
var_1 = copy.copy(var_2) # or copy.deepcopy
You could use both combined with the latter as a fallback mechanism
Note: deepcopy will ensure that your second object is completely independent from the first (If there are lists of lists, for instance)
a = 1 # a is an int
a_type = type(a) # a_type now contains the int-type
b = '1' # '1' is a string
c = a_type(b) # c is now an int with the value 1
So you can get the type of a variable using type(). You can then store this type in a variable and you can then use that variable just like you would use int(b), str(b), float(b) etc.
Ok so I want to conserve space,and not write out all my variables,and a command.
I want to do something like this
a = 1
b = 1
def add(var):
var += 1
both = a and b
Can I do something like this or is it impossible. Thanks in advance
I think you want to add a variable to both a and b
you can do that with a container object but not a immutable variable. When you want to change immutable variables like numbers or strings under the covers Python destroys the variable and creates another one in its place.
Here is a container example:
a = [1]
b = a
id(a) #gives the location of a in memory - 4352812552
id(b) # gives the same location - 4352812552
#both are the same
a[0] += 1
print(b)
Also a warning: If you create a function to do this you will need to keep the variable names the same as the variables you want to change.
so:
def add(a, position):
a[position] += 1
See this for more detailed explanation https://www.youtube.com/watch?v=_AEJHKGk9ns
The class is given below, this will print 20_20. Now till the line 5 code is same, I don't want the value of T.a to change when i change value of T1.a. How to solve it?
class Test:
def __init__(self, val):
self.a = val
T = Test(10)
T1 = T
T1.a = 20
print T.a + '__' + T1.a
Expected Output Is 10_20 .
In line 6. T1 = T, you are telling python to make a new reference to object T called T1.
i dont want the value of T.a to change when i change value of T1.a
This can't be done the way you set it up since T and T1 point to the same object. You can see this by noting that T is T1 evaluates to True in the python interpreter.
It seems maybe that you want to instantiate 2 Test objects, each with its own a property, so that changing one object won't affect the other.
You could also use the copy method of the copy module:
import copy
T = Test(20)
T1 = copy.copy(T)
T1.a = 40
print T.a
# Output 20
print T1.a
# Output 40
For more info check out the copy docs, https://docs.python.org/2/library/copy.html
The thing you want to do is probably called "deep copying" https://docs.python.org/2/library/copy.html
this allows you to create a copy of an existing object, instead of a reference. That is the closest thing to pass by value, I can think of, when using objects.
By making T1=T you are making both of them same so if you change T1 it will affect T and vice versa . You could use copy,deep copy etc
class Test:
def __init__(self, val):
self.a = val
T = Test (10)
T1 = Test(20)
print str(T.a) + '__' + str(T1.a)
see this link for more on mutuable and non-mutable object assignment and value changing
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.