no reference to Module.parameters() after using more than once - python

I have a class that inherits from torch.nn.Module,
now when I do this code:
d = net.parameters()
print(len(list(d)))
print(len(list(d)))
print(len(list(d)))
the output is:
10
0
0
So I have reference to the net.parameters() only once, whys that?
Then it apparently disappear..
I got this error while trying to make my own Optimizer, so I pass this net.parameters() as a parameter to my new class, and apparently I couldn't use it because of that odd situation.

This is working as expected. Module.parameters() returns an iterator, more specifically, a Python generator. One thing about them is that you cannot rewind a generator. So, in the first list(d) call, you are actually "consuming" all the generator. Then, if you try to do that again, it will be empty.
If you're wondering, the .parameters() implementation can be seen here, and it is very simple:
def parameters(self, recurse=True):
for name, param in self.named_parameters(recurse=recurse):
yield param
Perhaps it is easier to wrap your mind around it with this toy example:
def g():
for x in [0, 1, 2, 3, 4]:
yield x
d = g()
print(list(d)) # prints: [0, 1, 2, 3, 4]
print(list(d)) # prints: []

Related

Stop Python Functions Overwriting Inputs

I have a simple function that is supposed to run down the diagonal of an array and turn all the values to 0.
def diagonal_zeros(dataset):
zero = dataset[:]
length = len(zero)
for i in range(length):
zero[i, i] = 0
return zero
When I run this function on an array, it outputs the new, correct 'zero' array, but it also goes back and overwrites the original 'dataset.' I had thought that the line zero = dataset[:] would have prevented this.
I do not, however, get the same behavior with this function:
def seperate_conditions(dataset, first, last):
dataset = dataset[first:last, :]
return dataset
Which leaves the first dataset unchanged. I've been reading StackOverflow answers to related questions, but I cannot for the life of me figure this out. I'm working on a scientific analysis pipeline so I really want to be able to refer back to the matrices at every step.
Thanks
Arguments in python are passed by assignment (thanks to #juanpa.arrivillaga for the correction) and not by value. This means that generally the function does not recieve a copy of the argument, but a "pointer" to the argument itself. If you alter the object referenced by the argument in the function, you are modifying the same object outside. Here's a page with some more information.
A possibility is to use the copy module inside your function, to create a copy of the dataset.
As an example, for your code:
import copy
myDataset = [[1,2,3],[2,3,4],[3,4,5]]
def diagonal_zeros(dataset):
zero = copy.deepcopy(dataset)
length = len(zero)
for i in range(length):
zero[i][i] = 0
return zero
result = diagonal_zeros(myDataset)
print(result) #[[0, 2, 3], [2, 0, 4], [3, 4, 0]]
print(myDataset) #[[1, 2, 3], [2, 3, 4], [3, 4, 5]]
This article helped me a lot with this concept.

The value of an empty list in function parameter, example here [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
“Least Astonishment” in Python: The Mutable Default Argument
def f(a, L=[]):
L.append(a)
return L
print(f(1, [1, 2]))
print(f(1))
print(f(2))
print(f(3))
I wonder why the other f(1), f(2), f(3) has not append to the first f(1, [1,2]).
I guess the result should be :
[1, 2, 1]
[1, 2, 1, 1]
[1, 2, 1, 1, 2]
[1, 2, 1, 1, 2, 3]
But the result is not this. I do not know why.
There are two different issues (better to called concepts) fused into one problem statement.
The first one is related to the SO Question as pointed by agh. The thread gives a detailed explanation and it would make no sense in explaining it again except for the sake of this thread I can just take the privilege to say that functions are first class objects and the parameters and their default values are bounded during declaration. So the parameters acts much like static parameters of a function (if something can be made possible in other languages which do not support First Class Function Objects.
The Second issue is to what List Object the parameter L is bound to. When you are passing a parameter, the List parameter passed is what L is bound to. When called without any parameters its more like bonding with a different list (the one mentioned as the default parameter) which off-course would be different from what was passed in the first call. To make the case more prominent, just change your function as follow and run the samples.
>>> def f(a, L=[]):
L.append(a)
print id(L)
return L
>>> print(f(1, [1, 2]))
56512064
[1, 2, 1]
>>> print(f(1))
51251080
[1]
>>> print(f(2))
51251080
[1, 2]
>>> print(f(3))
51251080
[1, 2, 3]
>>>
As you can see, the first call prints a different id of the parameter L contrasting to the subsequent calls. So if the Lists are different so would be the behavior and where the value is getting appended. Hopefully now it should make sense
Why you wait that's results if you call function where initialize empty list if you dont pass second argument?
For those results that you want you should use closure or global var.

Best practice for setting the default value of a parameter that's supposed to be a list in Python?

I have a Python function that takes a list as a parameter. If I set the parameter's default value to an empty list like this:
def func(items=[]):
print items
Pylint would tell me "Dangerous default value [] as argument". So I was wondering what is the best practice here?
Use None as a default value:
def func(items=None):
if items is None:
items = []
print items
The problem with a mutable default argument is that it will be shared between all invocations of the function -- see the "important warning" in the relevant section of the Python tutorial.
I just encountered this for the first time, and my immediate thought is "well, I don't want to mutate the list anyway, so what I really want is to default to an immutable list so Python will give me an error if I accidentally mutate it." An immutable list is just a tuple. So:
def func(items=()):
print items
Sure, if you pass it to something that really does want a list (eg isinstance(items, list)), then this'll get you in trouble. But that's a code smell anyway.
For mutable object as a default parameter in function- and method-declarations the problem is, that the evaluation and creation takes place at exactly the same moment. The python-parser reads the function-head and evaluates it at the same moment.
Most beginers asume that a new object is created at every call, but that's not correct! ONE object (in your example a list) is created at the moment of DECLARATION and not on demand when you are calling the method.
For imutable objects that's not a problem, because even if all calls share the same object, it's imutable and therefore it's properties remain the same.
As a convention you use the None object for defaults to indicate the use of a default initialization, which now can take place in the function-body, which naturally is evaluated at call-time.
In addition and also to better understand what python is, here my little themed snippet:
from functools import wraps
def defaultFactories(func):
'wraps function to use factories instead of values for defaults in call'
defaults = func.func_defaults
#wraps(func)
def wrapped(*args,**kwargs):
func.func_defaults = tuple(default() for default in defaults)
return func(*args,**kwargs)
return wrapped
def f1(n,b = []):
b.append(n)
if n == 1: return b
else: return f1(n-1) + b
#defaultFactories
def f2(n,b = list):
b.append(n)
if n == 1: return b
else: return f2(n-1) + b
>>> f1(6)
[6, 5, 4, 3, 2, 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, 3, 2, 1]
>>> f2(6)
[1, 2, 3, 4, 5, 6]

Very weird Python variable scope behaviour

I'm having a problem with Python 2.7 that is driving me insane.
I'm passing an array to some functions and altough that variable is suposed to be local, in the end the value of the variable inside main is changed.
I'm a bit new to Python, but this goes against any common sense I got.
Any ideas of what I'm doing wrong?
def mutate(chromo):
# chooses random genes and mutates them randomly to 0 or 1
for gene in chromo:
for codon in gene:
for base in range(2):
codon[randint(0, len(codon)-1)] = randint(0, 1)
return chromo
def mate(chromo1, chromo2):
return mutate([choice(pair) for pair in zip(chromo1, chromo2)])
if __name__ == '__main__':
# top 3 is a multidimensional array with 3 levels (in here I put just 2 for simplicity)
top3 = [[1, 0], [0, 0], [1, 1]]
offspring = []
for item in top3:
offspring.append(mate(top3[0], item))
# after this, top3 is diferent from before the for cycle
UPDATE
Because Python passes by reference, I must make a real copy fo the arrays before using them, so the mate functions must be changed to:
import copy
def mate(chromo1, chromo2):
return mutate([choice(pair) for pair in zip(copy.deepcopy(chromo1), copy.deepcopy(chromo2))])
The problem you are having is stemming from the fact that arrays and dictionaries in python are passed by reference. This means that instead of a fresh copy being created by the def and used locally you are getting a pointer to your array in memory...
x = [1,2,3,4]
def mystery(someArray):
someArray.append(4)
print someArray
mystery(x)
[1, 2, 3, 4, 4]
print x
[1, 2, 3, 4, 4]
You manipulate chromo, which you pass by reference. Therefore the changes are destructive... the return is therefore kind of moot as well (codon is in gene and gene is in chromo). You'll need to make a (deep) copy of your chromos, I think.
try changing
offspring.append(mate(top3[0], item)) to
offspring.append(mate(top3[0][:], item[:]))
or use the list() function

Passing a list while retaining the original

So I'm teaching myself Python, and I'm having an issue with lists. I want to pass my function a list and pop items off it while retaining the original list. How do I make python "instance" the passed list rather that passing a pointer to the original one?
Example:
def burninate(b):
c = []
for i in range(3):
c.append(b.pop())
return c
a = range(6)
d = burninate(a)
print a, d
Output: [0, 1, 2] [5, 4, 3]
Desired output: [0, 1, 2, 3, 4, 5] [5, 4, 3]
Thanks!
As other answers have suggested, you can provide your function with a copy of the list.
As an alternative, your function could take a copy of the argument:
def burninate(b):
c = []
b = list(b)
for i in range(3):
c.append(b.pop())
return c
Basically, you need to be clear in your mind (and in your documentation) whether your function will change its arguments. In my opinion, functions that return computed values should not change their arguments, and functions that change their arguments should not return anything. See python's [].sort(), [].extend(), {}.update(), etc. for examples. Obviously there are exceptions (like .pop()).
Also, depending on your particular case, you could rewrite the function to avoid using pop() or other functions that modify the argument. e.g.
def burninante(b):
return b[:-4:-1] # return the last three elements in reverse order
You can call burninate() with a copy of the list like this:
d = burninate(a[:])
or,
d = burninate(list(a))
The other alternative is to make a copy of the list in your method:
def burninate(b):
c=[]
b=b[:]
for i in range(3):
c.append(b.pop())
return c
>>> a = range(6)
>>> b = burninate(a)
>>> print a, b
>>> [0, 1, 2, 3, 4, 5] [5, 4, 3]
A slightly more readable way to do the same thing is:
d = burninate(list(a))
Here, the list() constructor creates a new list based on a.
A more general solution would be to import copy, and use copy.copy() on the parameter.
Other versions:
def burninate(b):
c = []
for i in range(1, 4):
c.append(b[-i])
return c
def burninate(b):
c = b[-4:-1]
c.reverse()
return c
And someday you will love list comprehensions:
def burninate(b):
return [b[-i] for i in range(1,4)]
You can use copy.deepcopy()
burninate = lambda x: x[:-4:-1]

Categories