Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do you know in advance if a method (or function) will alter the variable when called?

Tags:

python

methods

I am new to Python from R. I have recently spent a lot of time reading up on how everything in Python is an object, objects can call methods on themselves, methods are functions within a class, yada yada yada.

Here's what I don't understand. Take the following simple code:

mylist = [3, 1, 7]

If I want to know how many times the number 7 occurs, I can do:

mylist.count(7)

That, of course, returns 1. And if I want to save the count number to another variable:

seven_counts = mylist.count(7)

So far, so good. Other than the syntax, the behavior is similar to R. However, let's say I am thinking about adding a number to my list:

mylist.append(9)

Wait a minute, that method actually changed the variable itself! (i.e., "mylist" has been altered and now includes the number 9 as the fourth digit in the list.) Assigning the code to a new variable (like I did with seven_counts) produces garbage:

newlist = mylist.append(9)

I find the inconsistency in this behavior a bit odd, and frankly undesirable. (Let's say I wanted to see what the result of the append looked like first and then have the option to decide whether or not I want to assign it to a new variable.)

My question is simple:

Is there a way to know in advance if calling a particular method will actually alter your variable (object)?

like image 746
Miles Coltrane Avatar asked Aug 22 '18 20:08

Miles Coltrane


People also ask

How do you know if a variable is a certain class?

Using isinstance() function, we can test whether an object/variable is an instance of the specified type or class such as int or list. In the case of inheritance, we can checks if the specified class is the parent class of an object. For example, isinstance(x, int) to check if x is an instance of a class int .

How do you use a variable from one function to another in Java?

You can't. Variables defined inside a method are local to that method. If you want to share variables between methods, then you'll need to specify them as member variables of the class. Alternatively, you can pass them from one method to another as arguments (this isn't always applicable).


2 Answers

Aside from reading the documentation (which for some methods will include type annotations specifying the return value) or playing with the method in the interactive interpreter (including using help() to check the docstring for a type annotation), no, you can't know up front just by looking at the method.

That said, the behavior you're seeing is intentional. Python methods either return a new modified copy of the object or modify the object in place; at least among built-ins, they never do both (some methods mutate the object and return a non-None value, but it's never the object just mutated; the pop method of dict and list is an example of this case).

This either/or behavior is intentional; if they didn't obey this rule, you'd have had an even more confusing and hard to identify problem, namely, determining whether append mutated the value it was called on, or returned a new object. You definitely got back a list, but is it a new list or the same list? If it mutated the value it was called on, then

newlist = mylist.append(9)

is a little strange; newlist and mylist would be aliases to the same list (so why have both names?). You might not even notice for a while; you'd continue using newlist, thinking it was independent of mylist, only to look at mylist and discover it was all messed up. By having all such "modify in place" methods return None (or at least, not the original object), the error is discovered more quickly/easily; if you try and use newlist, mistakenly believing it to be a list, you'll immediately get TypeErrors or AttributeErrors.

Basically, the only way to know in advance is to read the documentation. For methods whose name indicates a modifying operation, you can check the return value and often get an idea as to whether they're mutating. It helps to know what types are mutable in the first place; list, dict, set and bytearray are all mutable, and the methods they have that their immutable counterparts (aside from dict, which has no immutable counterpart) lack tend to mutate the object in place.

The default tends to be to mutate the object in place simply because that's more efficient; if you have a 100,000 element list, a default behavior for append that made a new 100,001 element list and returned it would be extremely inefficient (and there would be no obvious way to avoid it). For immutable types (e.g. str, tuple, frozenset) this is unavoidable, and you can use those types if you want a guarantee that the object is never mutate in place, but it comes at a cost of unnecessary creation and destruction of objects that will slow down your code in most cases.

like image 71
ShadowRanger Avatar answered Oct 14 '22 22:10

ShadowRanger


Just checkout the doc:

>>> list.count.__doc__
'L.count(value) -> integer -- return number of occurrences of value'
>>> list.append.__doc__
'L.append(object) -> None -- append object to end'

There isn't really an easy way to tell, but:

immutable object --> no way of changing through method calls

So, for example, tuple has no methods which affect the tuple as it is unchangeable so methods can only return new instances.

And if you "wanted to see what the result of the append looked like first and then have the option to decide whether or not I want to assign it to a new variable" then you can concatenate the list with a new list with one element.

i.e.

>>> l = [1,2,3]
>>> k = l + [4]
>>> l
[1, 2, 3]
>>> k
[1, 2, 3, 4]
like image 29
Joe Iddon Avatar answered Oct 14 '22 22:10

Joe Iddon