How to Get a List of Class Attribute - python-3.x

there is a list of instances from the same class, and i want to extract a certain attribute of every instance and build up a new list
class Test:
def __init__(self, x):
self.x = x
l = [Test(1), Test(2), Test(3), Test(4)]
something like that, and i want to get a list which result is [1, 2, 3, 4]

The best way to do it would probably be like this:
class Test:
def __init__(self, x):
self.x = x
l = [Test(1), Test(2), Test(3), Test(4)]
res = [inst.x for inst in l] # [1, 2, 3, 4]
or just do it from the start:
l = [Test(1).x, Test(2).x, Test(3).x, Test(4).x]

Related

Using dataloader to sample with replacement in pytorch

I have a dataset defined in the format:
class MyDataset(Dataset):
def __init__(self, N):
self.N = N
self.x = torch.rand(self.N, 10)
self.y = torch.randint(0, 3, (self.N,))
def __len__(self):
return self.N
def __getitem__(self, idx):
return self.x[idx], self.y[idx]
During the training, I would like to sample batches of m training samples, with replacement; e.g. the first iteration includes data indices [1, 5, 6], second iteration includes data points [12, 3, 5], and so on and so forth. So the total number of iterations is an input, rather than N/m
Is there a way to use dataloader to handle this? If not, is there any other method than something in the form of
for i in range(iter):
x = np.random.choice(range(N), m, replace=True)
to implement this?
You can use a RandomSampler, this is a utility that slides in between the dataset and dataloader:
>>> ds = MyDataset(N)
>>> sampler = RandomSampler(ds, replacement=True, num_samples=M)
Above, sampler will sample a total of M (replacement is necessary of course if num_samples > len(ds)). In your example M = iter*m.
You can then initialize a DataLoader with sampler:
>>> dl = DataLoader(ds, sampler=sampler, batch_size=2)
Here is a possible result with N = 2, M = 2*len(ds) = 4, and batch_size = 2:
>>> for x, y in dl:
... print(x, y)
tensor([[0.5541, 0.3596, 0.5180, 0.1511, 0.3523, 0.4001, 0.6977, 0.1218, 0.2458, 0.8735],
[0.0407, 0.2081, 0.5510, 0.2063, 0.1499, 0.1266, 0.1928, 0.0589, 0.2789, 0.3531]])
tensor([1, 0])
tensor([[0.5541, 0.3596, 0.5180, 0.1511, 0.3523, 0.4001, 0.6977, 0.1218, 0.2458, 0.8735],
[0.0431, 0.0452, 0.3286, 0.5139, 0.4620, 0.4468, 0.3490, 0.4226, 0.3930, 0.2227]])
tensor([1, 0])
tensor([[0.5541, 0.3596, 0.5180, 0.1511, 0.3523, 0.4001, 0.6977, 0.1218, 0.2458, 0.8735],
[0.5541, 0.3596, 0.5180, 0.1511, 0.3523, 0.4001, 0.6977, 0.1218, 0.2458, 0.8735]])
tensor([1, 1])

Empty list in Python class constructor causes error

I'm creating a simple tree where each node has any number of children in Python, and I created a Node class to help me.
Each node holds a reference to its parent node (int), and any children nodes (list).
However, explicitly adding an empty list to the Node constructor's argument gave me strange results, and I'd love an explanation as to why this behaviour changes when the list is explicit or not explicitly put in the constructor arguments:
Implementation #1:
class Node:
def __init__(self, value, parent, children=[]):
self.parent = parent
self.value = value
self.children = children
Implementation #2:
class Node:
def __init__(self, value, parent):
self.parent = parent
self.value = value
self.children = []
To populate the 'nodes' array:
parents = [4,-1,4,1,1]
nodes = [None] * n
for i in range(n):
nodes[i] = Node(i, parents[i])
To store the parent attribute of each node:
tree = Tree()
for i, node in enumerate(nodes):
parent_id = node.parent
if parent_id == -1:
tree.root = nodes[i]
else:
nodes[parent_id].children.append(node.value)
print([(node.value, node.children) for node in nodes])
With Implementation #1 I get:
[(0, [0, 2, 3, 4]), (1, [0, 2, 3, 4]), (2, [0, 2, 3, 4]), (3, [0, 2, 3, 4]), (4, [0, 2, 3, 4])]
but with Implementation #2 I (correctly) get:
[(0, []), (1, [3, 4]), (2, []), (3, []), (4, [0, 2])]
Why the difference? I don't understand why the list is fully populated for each node even with the if and else statements.
All help appreciated, including if you think there are better ways to do this.
Default arguments are bound once when the function is defined, so every object of Node gets the same list object in your first implementation.
Locals are evaluated when the function is run, so self.children=[] assigns a new list in each object.
A better approach if you want to allow an optional children argument would be to
class Node:
def __init__(self, value, parent, children=None):
self.parent = parent
self.value = value
self.children = children or []
This uses None as the default value. The or operator allows us to select children if the argument is truthy, and an empty list if it is falsey.
From the docs.

How do i define a setter for a list with an index or slicing?

With the property and setter decorator I can define getter and setter functions. This is fine for primitives but how do I index a collection or a numpy array?
Setting values seems to work with an index, but the setter function doesn't get called. Otherwise the print function in the minimal example would be executed.
class Data:
def __init__(self):
self._arr = [0, 1, 2]
#property
def arr(self):
return self._arr
#arr.setter
def arr(self, value):
print("new value set") # I want this to be executed
self._arr = value
data = Data()
print(data.arr) # prints [0, 1, 2]
data.arr[2] = 5
print(data.arr) # prints [0, 1, 5]
If you want to do this just for one list of your class instance you can do this in a way by using the __set_item__ and __get_item__ dunder methods of the class:
class Data:
def __init__(self):
self._arr = [0, 1, 2]
#property
def arr(self):
return self._arr
#arr.setter
def arr(self, value):
print("new inner list set")
self._arr = value
def __setitem__(self, key, value):
print("new value set")
self._arr[key] = value
def __getitem__(self, key):
return self._arr[key]
data = Data()
print(data.arr)
data[2] = 5
print(data.arr)
data.arr = [42, 43]
print(data.arr)
Output:
[0, 1, 2]
new value set # by data[2] = 5 using __set_item__
[0, 1, 5]
new inner list set # by data.arr = [42, 43] using #arr.setter
[42, 43]
This would only work for one list member though, because the __set_item__ are working on the class instance itself, not the list that is a member of the class instance.

Multiple Assignment output is not correct

I am trying to write a program where all the odd linked list nodes are pushed to the start of the linked list followed by the odd ones. What I have is below:
def oddEvenList(self, head: ListNode) -> ListNode:
sentinel = node1 = head
node2 = second = head.next
while node1.next:
node1.next, node1 = node1.next.next, node1.next
while node2.next:
node2.next, node2 = node2.next.next, node2.next
node1.next = second
return head
The definition for the linked list class is as below:
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
My input is:
[1, 2, 3, 4, 5]
and my output is:
[1, 3, 5, 2]
What the output should be is:
[1, 3, 5, 2, 4]
Even though I assign second as the head of the second list, I still do not seem to add anything but the very head of the second list to the first list. I am doing the assignment here:
node2 = second = head.next
What am I doing wrong?

python use multiple values in multiple function

How to do something like this in python
def func1():
x = 2
y = 3
return x, y
def funcx():
print(func1().x)
def funcy():
print(func1().y)
So basically return multiple values from a single function then use each returned value in a different function
Python functions can return only one value, but it is easy for that value to contain others. In your example, func1 returns a single tuple, which in turn contains two values.
>>> def func1():
... x = 2
... y = 3
... return x, y
...
>>> func1()
(2, 3)
You can index or unpack this return value just like any other tuple:
>>> func1()[0]
2
>>> func1()[1]
3
>>> a, b = func1()
>>> a
2
You can use indexing also in your desired functions:
def funcx():
print(func1()[0])
def funcy():
print(func1()[1])
If you desire named fields, you can use a dict or namedtuple:
# dict
def func1():
return {'x': 2, 'y': 3}
def funcx():
print(func1()['x'])
# namedtuple
from collections import namedtuple
Point2D = namedtuple('Point2D', ['x', 'y'])
def func1():
return Point2D(x=2, y=3)
def funcx():
print(func1().x)

Resources