Prevent member objects referencing the same list - python

This should be simple...
class Object:
x = 0
y = []
a = Object()
b = Object()
a.x = 1
b.x = 2
print a.x, b.x
# output: 1 2
# works as expected
a.y.append(3)
b.y.append(4)
print a.y, b.y
# output: [3, 4] [3, 4]
# same list is referenced how to fix?
# desired output: [3] [4]
As far as I can tell, a.y and b.y reference the same list. How can I get them to be separate? Preferably, without adding an __init__ method.

You're creating the value of y only once, when you define the class. To assign a different list to each instance of y, you do need an init function. Just put self.y = [] in __init__ and it will work as intended.

What's happening here is that you actually have actually redefined x as an instance level attribute, and your class definition had them as both class level attributes.
If you do this, you can see your original x is still at 0.
>>> Object.x
0
As you don't create a new list, it's taking the class attribute. If you were to do this:
>>> a.y = []
>>> b.y = []
>>> a.y.append(1)
>>> b.y.append(2)
>>> print a.y, b.y
[1] [2]
That is what you are expecting. Really though you should be defining your class like this:
class Object(object):
def __init__(self):
self.y = []
self.x = 0
(and don't use Object as a classname!)

The easiest way to setup instance properties instead of class properties is to use __init__
When you reference an instance property (like a.y) the parser tries to return that first but if it isn't found the class property (Object.y) is returned.
In your case only defined a class property which is shared by all instances.

The only way to do that is creating the __init__ method
class Object:
def __init__(self):
self.x = 0
self.y = []
That way upon Object's construction, a new value will be assined to x and a new List will be created for y.
The way you were doing before creates two class/static variables to Object, but only y stays the same because it holds statically only a reference to the true List, reflecting to all instances of Object.
More on class/static variables on this other question:
Static class variables in Python
*Sorry if I used the wrong terms, I'm more of a Java person ;-)

Related

Python - How to print the variable name of an Object

Thanks for reading. Preface, I don't mean how to make a print(objectA) make python output something other than the <__main__.A object at 0x00000273BC36A5C0> via the redefining the __str__ attribute.
I will use the following example to try to explain what I'm doing.
class Point:
'''
Represents a point in 2D space
attributes: x, y
'''
def __init__(self, x=0, y=0):
allowed_types = {int, float}
if type(x) not in allowed_types or type(y) not in allowed_types:
raise TypeError('Coordinates must be numbers.')
else:
self.x = x
self.y = y
def __str__(self):
return f' "the points name" has the points: ({self.x}, {self.y})'
__repr__ = __str__
I would like the "the points name" to be replaced with whatever the variable name assigned to a specific object. So if I instantiated pointA=Point(1,0), I would like to be able to print
pointA has the points: (1,0)
I can't seem to find anything like this online, just people having issues that are solved by redefining __str__. I tried to solve this issue by adding a .name attribute, but that made this very unwieldy (especially since I want to make other object classes that inherit Point()). I'm not entirely sure if this is possible from what I know about variable and object names in python, but after wrestling with it for a couple of days I'd figured I'd reach out for ideas.
Note that an object may be referred to as multiple names.
It is also possible that there is no object name referring to the object.
Below is one approach that achieves your goal. It uses globals(), the dictionary that stores mappings from names to objects inside the global environment. Essentially, the __str__ method searches the object in the global listings (so it can be very slow if there are many objects) and keeps the name if matches.
You could possibly use locals instead to narrow the search scope.
In the example, C is referring to the same object as A. So print(C) tells both A and C are the names.
class Point:
def __init__(self, x=0, y=0):
self.x = x
self.y = y
def __str__(self):
results = []
for name, obj in globals().items():
if obj == self:
results.append(f' "{name}" has the points: ({self.x}, {self.y})')
return "; ".join(results)
A = Point()
B = Point()
print(A)
#"A" has the points: (0, 0)
print(B)
# "B" has the points: (0, 0)
C = A
print(C)
# "A" has the points: (0, 0); "C" has the points: (0, 0)

Python passing object as argument without overwriting

Consider this code:
class MyClass:
def __init__(self, x):
self.x = x
def myfunct(arg):
arg.x += 1
return arg
a = MyClass(0)
print(a.x)
b = myfunct(a)
print(a.x)
print(b.x)
This returns:
0
1
1
I would expect this code to behave in the same way as this one:
def myfunct(arg):
arg += 1
return arg
c = 0
print(c)
d = myfunct(c)
print(c)
print(d)
However the latter returns:
0
0
1
I understand this is due to Python's way of passing arguments by assignment, as explained in this post, or this post.
However, I can't figure out a way to work around the behavior exhibited in the first code, which is unwanted in the project I am working on. How can I pass an object as an argument to a function, return a madified object, and keep the original one untouched?
The simple solution is, just create or pass a copy. To do that you have to possibiltys. Either you create a .copy() method on the class or use the copy module.
A copy method could look like this:
class MyClass:
def __init__(self, x):
self.x = x
def copy(self):
return self.__class__(self.x)
The copy module works like this:
import copy
b = copy.copy(a)
You can use either way to create a function that simply returns a new copy of the argument:
def myfunct(arg):
arg = arg.copy() # Or copy.copy(arg)
arg.x += 1
return arg
Edit: As many other answers say, my approach shown above doesn't work if you have mutable objects in mutable objects (as example an object of your class, that has another object of your class in its args attribute). In that case use the copy.deepcopy function instead:
def myfunct(arg):
arg = copy.deepcopy(arg)
arg.x += 1
return arg
You're explicitly modifying your object. Python supports this behavior by default, but if you want to prevent modification of your object you may want to update the __setattr__ to manage attribute modification.
If you want to prevent the original object from modifying and you want to modify the object sent to the function you can add a __copy__ method to your object to be copyable in a way you like, then pass a copy of your object to the function using copy.copy().
class MyClass:
def __init__(self, x):
self.x = x
# default copy
def __copy__(self):
cls = self.__class__
result = cls.__new__(cls)
result.__dict__.update(self.__dict__)
return result
Demo:
In [21]: a = MyClass(0)
...: print(a.x)
...:
0
# import copy
In [22]: b = myfunct(copy.copy(a))
In [24]: a.x
Out[24]: 0
In [25]: b.x
Out[25]: 1
from copy import deepcopy
def myfunct(arg):
new_arg = deepcopy(arg)
new_arg.x += 1
return new_arg
I would recommend deepcopy over copy since you want to make sure that all references to the original object are cut.

#properties and public attribute

I'm following a tutorial on python 3 and there is a simple example I'm struggling with.
class P:
def __init__(self,x):
self.x = x
#property
def x(self):
return self.__x
#x.setter
def x(self, x):
if x < 0:
self.__x = 0
elif x > 1000:
self.__x = 1000
else:
self.__x = x
Why is the attribute x in __init__ defined as public but is accessed like a private attribute with self.__x in the functions decorated with #property and #x.setter?
This isn't that straightforward because it heavily relies on Pythons descriptor protocol see also Descriptor HOW-TO which refers to property as well. But I will try to explain it in easy terms.
You have a class that has (besides what is inherited by the implicit superclass object and some automatically included stuff) 2 attributes:
>>> P.__dict__
mappingproxy({'__init__': <function __main__.P.__init__>,
'x': <property at 0x2842664cbd8>})
I removed the automatically added attributes for the sake of this discussion. You can always add or replace attributes as much as you want:
>>> P.y = 1000
>>> P.__dict__
mappingproxy({'__init__': <function __main__.P.__init__>,
'x': <property at 0x2842664cbd8>,
'y': 1000})
But when you create an instance the instance will have only one attribute _P__x (the _P is inserted because variables starting with __ and not ending in __ are name-mangled):
>>> p = P(10)
>>> p.__dict__
{'_P__x': 10}
You can also add almost (only almost because the descriptor protocol intercepts certain operations - see below) any attribute for the instance:
>>> p.y = 100
>>> p.__dict__
{'_P__x': 10, 'z': 100}
That's where the descriptor protocol comes into play. If you access an attribute on the instance, it starts by looking if the instance has that attribute. If the instance doesn't have that attribute it will look at the class - but through the descriptor protocol! So when you access self.x this is roughly equivalent to: type(self).x.__get__(self):
>>> p.x
10
>>> type(p).x.__get__(p)
10
Likewise setting the attribute with self.x = 200 will call type(self).x.__set__(self, 200):
>>> p.x = 200
>>> p.x
200
>>> type(p).x.__set__(p, 100)
>>> p.x
100
The #property will intercept though the descriptor protocol any access to x on self. So you can't use the name x to store the actual value on the instance because it would always go into the #property and #x.setter (and also x.deleter but you haven't implemented that one) functions of the class. So you have to use another name to store the variable.
It's typically stored with the same name but one leading underscore (also eases maintainability). It's actually not good practice to use two leading underscores because that makes it hard to subclass your class and modify the x property - without name-mangling the variable name yourself.

python: differences between init method and in-class declare?

is there any difference between these two method?
option1 :
class a(object):
def __init__(self):
self.x = 123
self.y = 345
option2 :
class a(object):
x = 123
y = 345
is there any difference between these two options?
Thanks in advance.
An example of the first method (instance level variables):
instance0 = a()
instance1 = b()
instance0.x = 5
print instance1.x # prints 123
print a.x # undefined variable - x is not defined
An example of the second method (class level variables):
instance0 = a()
instance1 = b()
instance0.x = 5
print instance1.x # prints 5
print a.x # prints 5
The second method, the variables are assigned at the class level meaning changing this value propagates to all instances of that class. You can also access the variables without an instance of the class.
Yes, in the first case each object of class a has its own copy of x and y, in the second case all objects of class a share them.
By the way, if your starting out with Python, use a capital for the first character of your class names, e.g. MyClass. People are used to that and it will help you understand your own programs once they get bigger.

Argument passing by reference to a class in python (รก la C++), to modify it with the class methods

In this case, I want that the program print "X = changed"
class Clase:
def __init__(self,variable):
self.var = variable
def set_var(self):
self.var = 'changed'
X = 'unchanged'
V = Clase(X)
V.set_var()
print "X = ",X
All values are objects and are passed by reference in Python, and assignment changes the reference.
def myfunc(y):
y = 13
x = 42 # x now points at the integer object, 42
myfunc(y) # inside myfunc, y initially points to 42,
# but myfunc changes its y to point to a
# different object, 13
print(x) # prints 42, since changing y inside myfunc
# does not change any other variable
It's important to note here that there are no "simple types" as there are in other languages. In Python, integers are objects. Floats are objects. Bools are objects. And assignment is always changing a pointer to refer to a different object, whatever the type of that object.
Thus, it's not possible to "assign through" a reference and change someone else's variable. You can, however, simulate this by passing a mutable container (e.g. a list or a dictionary) and changing the contents of the container, as others have shown.
This kind of mutation of arguments through pointers is common in C/C++ and is generally used to work around the fact that a function can only have a single return value. Python will happily create tuples for you in the return statement and unpack them to multiple variables on the other side, making it easy to return multiple values, so this isn't an issue. Just return all the values you want to return. Here is a trivial example:
def myfunc(x, y, z):
return x * 2, y + 5, z - 3
On the other side:
a, b, c = myFunc(4, 5, 6)
In practice, then, there is rarely any reason to need to do what you're trying to do in Python.
In python list and dict types are global and are passed around by reference. So if you change the type of your variable X to one of those you will get the desired results.
[EDIT: Added use case that op needed]
class Clase:
def __init__(self,variable):
self.var = variable
def set_var(self):
self.var.test = 'changed'
class ComplicatedClass():
def __init__(self, test):
self.test = test
X = ComplicatedClass('unchanged')
print('Before:', X.test)
V = Clase(X)
V.set_var()
print("After:",X.test)
>>> Before: unchanged
>>> After: changed
strings are immutable so you could not change X in this way
... an alternative might be reassigning X in the global space... this obviously will fail in many many senarios (ie it is not a global)
class Clase:
def __init__(self,variable):
self.var = variable
def set_var(self):
globals()[self.var] = 'changed'
X = 'unchanged'
V = Clase('X')
V.set_var()
print "X = ",X
the other alternative is to use a mutable data type as suggested by Ashwin
or the best option is that this is probably not a good idea and you should likely not do it...

Categories