Is there a way to prevent side effects in python?

Is there a way to prevent side effects in python? For example, the following function has a side effect, is there any keyword or any other way for a python complaint to write about it?

def func_with_side_affect(a): a.append('foo') 
+11
python functional-programming side-effects
source share
7 answers

Python is really not set up to provide side effect prevention. As some others have said, you can try deepcopy data or use immutable types, but they still have angular cases that are hard to catch, and this is just a ton of effort than it costs.

Using a functional style in Python usually involves a programmer simply creating functional functions. In other words, when you write a function, you write it in such a way that it does not mutate the arguments.

If you call someone else, you must be sure that the data you are transmitting cannot either be mutated, or you need to maintain a safe, untouched copy of the data yourself, so that you stay away from this unreliable function.

+10
source share

Sorry to be very late to the party. You can use the effect library to isolate side effects in your python code. As others have said in Python, you should explicitly write functional style code, but this library really encourages it.

+3
source share

No, but with your example, you can use immutable types and pass a tuple as an argument to a . Side effects cannot affect immutable types, for example, you cannot add to a tuple, you can only create another tuple, expanding this one.

UPD: However, your function can modify the objects referenced by your immutable object (as indicated in the comments), write to files, and perform some other I / O.

+2
source share

The only way to enforce this requirement would be to rewrite the specification of the function in deepcopy any arguments before they are passed to the original function, you could use the function decorator .

Thus, the function does not have a way to actually modify the originally passed arguments. This, however, has a “side effect” of significant slowdown, since deep bone surgery is quite expensive in terms of memory usage (and garbage collection) as well as CPU consumption.

I would rather recommend that you test your code correctly to ensure that no random changes occur, or use a language that uses full semantics one at a time (or has only immutable variables).

As another workaround, you can make your passed objects basically immutable by adding this to your classes:

  """An immutable class with a single attribute 'value'.""" def __setattr__(self, *args): raise TypeError("can't modify immutable instance") __delattr__ = __setattr__ def __init__(self, value): # we can no longer use self.value = value to store the instance data # so we must explicitly call the superclass super(Immutable, self).__setattr__('value', value) 

(Code copied from Wikipedia article on immutable object )

+1
source share

Since any Python code can execute IO, any Python code can launch intercontinental ballistic missiles (and I would think that launching an ICBM would be a pretty catastrophic side effect for most purposes).

The only way to avoid side effects is not to use Python code in the first place, but rather the data, that is, you will end up creating a domain-specific language that prohibits side effects, and a Python interpreter that runs programs in that language.

+1
source share

First you need to make a copy of the list. Something like that:

 def func_without_side_affect(a): b = a[:] b.append('foo') return b 

This shorter version may work for you too:

 def func_without_side_affect(a): return a[:] + ['foo'] 

If you have nested lists or other similar things, you probably want to look at copy.deepcopy to make a copy instead of the Slice operator [:] .

+1
source share

It would be very difficult to do for the general case, but for some practical cases you could do something like this:

 def call_function_checking_for_modification(f, *args, **kwargs): myargs = [deepcopy(x) for x in args] mykwargs = dict((x, deepcopy(kwargs[x])) for x in kwargs) retval = f(*args, **kwargs) for arg, myarg in izip(args, myargs): if arg != myarg: raise ValueError, 'Argument was modified during function call!' for kwkey in kwargs: if kwargs[kwkey] != mykwargs[kwkey]: raise ValueError, 'Argument was modified during function call!' return retval 

But obviously there are a few problems with this. For trivial things (i.e., all input data are simple types), then this is not very useful in any case - they are likely to be immutable, and in any case they are easier (well, relatively) to detect than complex types.

However, for complex types, deepcopy will be expensive, and there is no guarantee that the == operator will work correctly. (and a simple copy is not good enough ... imagine a list in which one item changes value ... a simple copy will just save the link, and therefore the original value with the change too).

In general, however, this is not so useful, because if you are already worried about side effects when calling these functions, you can simply protect them more intelligently (by storing your own copy if necessary, checking the destination function, etc.) and if this is your function, you are worried about the occurrence of side effects, you check it to make sure.

Something like the above can be wrapped in a decorator; with expensive parts created by a global variable ( if _debug == True: something like that), maybe this can be useful in projects where many people are editing the same code, though, I think ...

Edit: this only works in environments where a more "severe" form of "side effects" is expected. In many programming languages, you can make the side effects available much more explicit - for example, in C ++, all at a cost if you don't explicitly have a pointer or link, and even then you can declare incoming links as const so that it can’t be changed There "side effects" can cause errors during compilation. (Of course, there is a way to get something anyway).

The above means that any changed values ​​are in the return value / tuple. If you are in python 3 (I don't know yet), I think you can specify the decoration in the function declaration itself to indicate the attributes of the function arguments, including whether they are allowed to change, and include a function in this function to allow some arguments are explicitly mutable.

Notice that I think you could probably do something like this:

 class ImmutableObject(object): def __init__(self, inobj): self._inited = False self._inobj = inobj self._inited = True def __repr__(self): return self._inobj.__repr__() def __str__(self): return self._inobj.__str__() def __getitem__(self, key): return ImmutableObject(self._inobj.__getitem__(key)) def __iter__(self): return self.__iter__() def __setitem__(self, key, value): raise AttributeError, 'Object is read-only' def __getattr__(self, key): x = getattr(self._inobj, key) if callable(x): return x else: return ImmutableObject(x) def __setattr__(self, attr, value): if attr not in ['_inobj', '_inited'] and self._inited == True: raise AttributeError, 'Object is read-only' object.__setattr__(self, attr, value) 

(Probably not a full implementation, not a lot of testing, but the beginning). It works as follows:

 a = [1,2,3] b = [a,3,4,5] print c [[1, 2, 3], 3, 4, 5] c[0][1:] = [7,8] AttributeError: Object is read-only 

This will allow you to protect a specific object from modification if you do not trust the downstream function, while still being relatively light. However, it requires an explicit shell object. Perhaps you could build a decorator to do this semi-automatically, albeit for all arguments. Be sure to skip those that are called.

0
source share

All Articles