Python passing object as argument without overwriting - python

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.

Related

Custom object does not properly work as dictionary key even after overwriting __hash__() and __eq__()

NOTE: I am aware of this exact same question here and here. However, I have tried the solutions proposed by the answers there and they do not work for me (see sample code below).
A B object has a list of A. A is composed by a tuple of only two integers and an integer.
I am trying to use B objects as keys in a dictionary. However, even after implementing my own __eq__() and __hash__() methods, the length of my dictionary increases even after adding the same object to it.
See code below:
class A:
def __init__(self, my_tuple, my_integer):
self.my_tuple = my_tuple
self.my_integer = my_integer
def __eq__(self, other):
return self.my_tuple == other.my_tuple and self.my_integer == other.my_integer
class B:
def __init__(self):
self.list_of_A = []
def add(self, my_tuple, my_integer):
new_A = A(my_tuple, my_integer)
self.list_of_A.append(new_A)
def __hash__(self):
return hash(repr(self))
def __eq__(self, other):
for i in range(len(self.list_of_A)):
if self.list_of_A[i] != other.list_of_A[i]:
return False
return True
b_1 = B()
b_1.add((1,2), 3)
b_2 = B()
b_2.add((1,2), 3)
my_dict = {}
my_dict[b_1] = 'value'
print(len(my_dict))
my_dict[b_2] = 'value_2'
print(len(my_dict))
The output I am getting is
12
And the expected output is
11
Because I am adding the same object (i.e.:same properties values).
The hashes aren't equal because the repr()s aren't equal. Consider the following example I just did on my python console using your code:
>>> x = B()
>>> y = B()
>>> repr(x)
'<__main__.B object at 0x7f7b3a20c358>'
>>> repr(y)
'<__main__.B object at 0x7f7b3aa197b8>'
Obviously, x and y will have different hashes.
All you need to do, then, is overwrite __repr__() so that it outputs a deterministic value based on the contents of the object, rather than its memory address, and you should be good to go. In your case, that may look something like this:
class A:
...
def __repr__(self):
return f"A(my_tuple:{self.my_tuple}, my_integer:{self.my_integer})"
class B:
...
def __repr__(self):
return f"B(list_of_a:{self.list_of_a})"

Behavioural difference between decorated function and method in Python

I use the following workaround for "Pythonic static variables":
def static_vars(**kwargs):
"""decorator for funciotns that sets static variables"""
def decorate(func):
for k, v in kwargs.items():
setattr(func, k, v)
return func
return decorate
#static_vars(var=1)
def global_foo():
_ = global_foo
print _.var
_.var += 1
global_foo() # >>> 1
global_foo() # >>> 2
It works just as supposed to. But when I move such a decorated function inside a class I get a strange change:
class A(object):
#static_vars(var=1)
def foo(self):
bound = self.foo
unbound = A.foo
print bound.var # OK, print 1 at first call
bound.var += 1 # AttributeError: 'instancemethod' object has no attribute 'var'
def check(self):
bound = self.foo
unbound = A.foo
print 'var' in dir(bound)
print 'var' in dir(unbound)
print bound.var is unbound.var # it doesn't make much sense but anyway
a = A()
a.check() # >>> True
# >>> True
# >>> True
a.foo() # ERROR
I can not see what causes such behaviour. It seems to me that it has something to do with python descriptors protocol, all that bound vs unbound method stuff. Somehow the foo.var attribute is accessible but is not writable.
Any help is appreciated.
P.S. I understand that static function variables are essentially class variables and this decorator is unnecessary in the second case but the question is more for understanding the Python under the hood than to get any working solution.
a.foo doesn't return the actual function you defined; it returns a bound method of it, which wraps up the function and has self assigned toa.
https://docs.python.org/3/howto/descriptor.html#functions-and-methods
That guide is out of date a little, though, since unbound methods just return the function in Python 3.
So, to access the attributes on the function, you need to go through A.foo (or a.foo.__func__)instead of a.foo. And this will only work in Python 3. In Python 2, I think A.foo.__func__ will work.

Prevent member objects referencing the same list

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 ;-)

Returning an lvalue from a function in python

[Sorry, I'm new in Python. Although it seems to be a very basic question, I did my share of due diligence before asking this audience, trying to avoid really stupid questions].
I'm trying to figure out the correct idiom for returning an l-value from a function. Assume I've a container of 64 objects, and I want to be able to return a reference to these objects.
class ChessBoard:
def __init__(self):
self.squares = [None for x in range(64)]
square( row, col ):
return self.squares(row*8+col) <---- I'd like this to be l-value
Then, from outside the class I want to:
board = ChessBoard()
board.square(0,0) = Piece( Shapes.ROOK, Colors.White ) <-- I'm getting an error here
board.square(0,1) = Piece( Shapes.BISHOP, Colors.White )
... etc.
So, I would like the function 'at' to return a lvalue (Something like a reference in C++), but I can't find anything resembling a reference or a pointer in the language. If I stored a list in each square containing one Piece, it is possible I could do something like: board.square(0,0)[0] = Piece - but it seems crazy (or maybe not - as I said, I'm new to the language).
How would you approach this data structure?
In Python, everything is a reference. The only problem is that None is immutable, so you can't use the returned reference to change the value.
You also can't override the assignment operator, so you won't get this particular kind of behaviour. However, a good and very flexible solution would be to override the __setitem__ and __getitem__ methods to implement the subscription operator ([]) for the class:
class ChessBoard(object):
def __init__(self):
self.squares = [None] * 64
def __setitem__(self, key, value):
row, col = key
self.squares[row*8 + col] = value
def __getitem__(self, key):
row, col = key
return self.squares[row*8 + col]
Usage:
>>> c = ChessBoard()
>>> c[1,2] = 5
>>> c[1,2]
5
You can try something like this, at the cost of having to put bogus [:] indexers around:
class Board:
def __init__(self):
self.squares=[None for x in range(64)]
def square(self, row, col):
squares=self.squares
class Prox:
def __getitem__(self, i):
return squares[row*8+col]
def __setitem__(self, i, v):
squares[row*8+col]=v
return Prox()
Then you can do
b=Board()
b.square(2,3)[:]=Piece('Knight')
if b.square(x,y)[:] == Piece('King') ...
And so on. It doesn't actually matter what you put in the []s, it just has to be something.
(Got the idea from the Proxies Perl6 uses to do this)
As Niklas points out, you can't return an l-value.
However, in addition to overriding subscription, you can also use properties (an application of descriptors: http://docs.python.org/howto/descriptor.html) to create an object attribute, which when read from, or assigned to, runs code.
(Not answering your question in the title, but your "How would you approach this data structure?" question:) A more pythonic solution for your data structure would be using a list of lists:
# define a function that generates an empty chess board
make_chess_board = lambda : [[None for x in xrange(8)] for y in xrange(8)]
# grab an instance
b = make_chess_board()
# play the game!
b[0][0] = Piece(Shapes.ROOK, Colors.White)
b[0][1] = Piece(Shapes.BISHOP, Colors.White)
# Or use tuples:
b[0][0] = (Shapes.ROOK, Colors.White)
b[0][1] = (Shapes.BISHOP, Colors.White)

Avoiding unncessary use of lambda for calls to object methods

Consider the following code, which simply calls a method on each member of a list:
class Demo:
def make_change(self):
pass
foo = [Demo(), Demo(), Demo()]
map(lambda x: x.make_change(), foo)
Is there a way to accomplish this without the long-winded lambda syntax? For example, in Scala, something similar to map(_.make_change(), foo) works. Does Python have an equivalent?
It's not very pythonic to use map just for side-effects
so why not
for item in foo:
item.make_change()
This will run faster than using map
you can put it on one line if you insist, but I wouldn't
for item in foo:item.make_change()
operator.methodcaller('make_change')
I'm with gnibbler on the pythonicity. Apart from that, this is also possible:
map(Demo.make_change, foo)
It has problems, though:
class Demo(object):
def __init__(self):
self.x = 1
self.y = 2
def make_change(self):
self.x = 5
class SubDemo(Demo):
def make_change(self):
self.y = 7
d = Demo()
s = SubDemo()
map(Demo.make_change, [d, s])
assert d.x == 5 and s.y == 7 # oops!

Categories