How to update a property for all dataclass objects in a list?
Question:
I have a list of objects of the following type:
@dataclass
class Feature:
name: str
active: bool
and my list is:
features = [Feature("name1",False), Feature("name2",False), Feature("name3",True)]
I want to get back a list with all the features but switch their active
property to True
. I tried to use map()
like this:
active_features=list(map(lambda f: f.active=True,features))
but it gives me an error expected parameter
. How can this be achieved?
Note I thought it was following from the example, but I guess I should have clarified. I want to do this with some short of inline method, without defining a new separate function as suggested from some of the answers, but maybe it cannot be done like this?
Answers:
Reason why your logic is not working?
It gives you error because the lambda
function you’re using is trying to modify the value of f.active
, which is not allowed in a lambda function. lambda
functions are allowed to only for expressions that return a value, rather than statements that perform some actions.
So I think one way to do it like below-
from dataclasses import dataclass
import copy
@dataclass
class Feature:
name: str
active: bool
features = [Feature("name1",False), Feature("name2",False), Feature("name3",True)]
active_features = []
main_features = copy.deepcopy(features)
for f in main_features:
f.active = True
active_features.append(f)
print(active_features)
print(features)
Output:
[Feature(name='name1', active=True), Feature(name='name2', active=True), Feature(name='name3', active=True)]
[Feature(name='name1', active=False), Feature(name='name2', active=False), Feature(name='name3', active=True)]
Change the data in the old list:
from dataclasses import dataclass
from copy import deepcopy
@dataclass
class Feature:
name: str
active: bool
features = [Feature("name1",False), Feature("name2",False), Feature("name3",True)]
for feature in features:
feature.active = True
print(features)
# [Feature(name='name1', active=True), Feature(name='name2', active=True), Feature(name='name3', active=True)]
Or make a new list of modified values:
def func(feature):
feature.active = True
return feature
active_features = list(map(func, deepcopy(features)))
print(active_features)
# [Feature(name='name1', active=True), Feature(name='name2', active=True), Feature(name='name3', active=True)]
# If you try to print the original list, You can see that the element in the original does not change.
print(features)
# [Feature(name='name1', active=False), Feature(name='name2', active=False), Feature(name='name3', active=True)]
In the lambda function you can’t use the assignment operator =
.
If you are happy modifying the instances in place, there’s no need to build a new list. Just use a regular for
loop.
features = [Feature("name1",False), Feature("name2",False), Feature("name3",True)]
for f in features:
f.active = True
If you want a list of new objects, leaving the original objects unchanged, I would still use a regular for
loop:
new_features = []
for f in features:
new_features.append(Feature(f.name, True))
though adding an instance method that can create an active feature from an existing feature would make things cleaner:
@dataclass
class Feature:
name: str
active: bool
def make_new_active(self):
return type(self)(self.name, True)
features = [...]
new_features = [f.make_new_active() for f in features]
(Though note: I am basically reimplementing dataclasses.replace
, which I forgot about. See https://stackoverflow.com/a/74852093/1126841 instead.)
You can call dataclasses’ replace
which will create a copy with the changed attribute:
from dataclasses import dataclass, replace
@dataclass
class Feature:
name: str
active: bool
features = [Feature("used-to-be", False), Feature("maybe", False)]
active_features = [replace(x, active=True) for x in features]
print(active_features) # should be active
print(features) # should be intact
Just create a method on the original class.
@dataclass
class Feature:
name: str
active:bool
def activate(self):
self.active =True
return self
features = [Feature("name1",False), Feature("name2",False), Feature("name3",True)]
active_features=list(map(lambda f: f.activate(),features))
active_features
[Feature(name='name1', active=True), Feature(name='name2', active=True), Feature(name='name3', active=True)]
Well if you want to do this using map
you can not use lambda because lambda can only have expressions not assignments. Also it has to return something as well. Do this instead
from dataclasses import dataclass
@dataclass
class Feature:
name: str
active: bool
def activate_feature(feature: Feature):
feature.active = True
return feature
features = [Feature("name1",False), Feature("name2",False), Feature("name3",True)]
active_features = list(map(activate_feature, features))
print(active_features)
Output
[Feature(name='name1', active=True), Feature(name='name2', active=True), Feature(name='name3', active=True)]
Or if you want to go with lambda as well you can do this as well
from dataclasses import dataclass
@dataclass
class Feature:
name: str
active: bool
def activate(self):
self.active = True
return self
features = [Feature("name1", False), Feature("name2", False), Feature("name3", True)]
active_features = list(map(lambda f: f.activate(), features))
print(active_features)
Output
[Feature(name='name1', active=True), Feature(name='name2', active=True), Feature(name='name3', active=True)]
I have a list of objects of the following type:
@dataclass
class Feature:
name: str
active: bool
and my list is:
features = [Feature("name1",False), Feature("name2",False), Feature("name3",True)]
I want to get back a list with all the features but switch their active
property to True
. I tried to use map()
like this:
active_features=list(map(lambda f: f.active=True,features))
but it gives me an error expected parameter
. How can this be achieved?
Note I thought it was following from the example, but I guess I should have clarified. I want to do this with some short of inline method, without defining a new separate function as suggested from some of the answers, but maybe it cannot be done like this?
Reason why your logic is not working?
It gives you error because the lambda
function you’re using is trying to modify the value of f.active
, which is not allowed in a lambda function. lambda
functions are allowed to only for expressions that return a value, rather than statements that perform some actions.
So I think one way to do it like below-
from dataclasses import dataclass
import copy
@dataclass
class Feature:
name: str
active: bool
features = [Feature("name1",False), Feature("name2",False), Feature("name3",True)]
active_features = []
main_features = copy.deepcopy(features)
for f in main_features:
f.active = True
active_features.append(f)
print(active_features)
print(features)
Output:
[Feature(name='name1', active=True), Feature(name='name2', active=True), Feature(name='name3', active=True)]
[Feature(name='name1', active=False), Feature(name='name2', active=False), Feature(name='name3', active=True)]
Change the data in the old list:
from dataclasses import dataclass
from copy import deepcopy
@dataclass
class Feature:
name: str
active: bool
features = [Feature("name1",False), Feature("name2",False), Feature("name3",True)]
for feature in features:
feature.active = True
print(features)
# [Feature(name='name1', active=True), Feature(name='name2', active=True), Feature(name='name3', active=True)]
Or make a new list of modified values:
def func(feature):
feature.active = True
return feature
active_features = list(map(func, deepcopy(features)))
print(active_features)
# [Feature(name='name1', active=True), Feature(name='name2', active=True), Feature(name='name3', active=True)]
# If you try to print the original list, You can see that the element in the original does not change.
print(features)
# [Feature(name='name1', active=False), Feature(name='name2', active=False), Feature(name='name3', active=True)]
In the lambda function you can’t use the assignment operator =
.
If you are happy modifying the instances in place, there’s no need to build a new list. Just use a regular for
loop.
features = [Feature("name1",False), Feature("name2",False), Feature("name3",True)]
for f in features:
f.active = True
If you want a list of new objects, leaving the original objects unchanged, I would still use a regular for
loop:
new_features = []
for f in features:
new_features.append(Feature(f.name, True))
though adding an instance method that can create an active feature from an existing feature would make things cleaner:
@dataclass
class Feature:
name: str
active: bool
def make_new_active(self):
return type(self)(self.name, True)
features = [...]
new_features = [f.make_new_active() for f in features]
(Though note: I am basically reimplementing dataclasses.replace
, which I forgot about. See https://stackoverflow.com/a/74852093/1126841 instead.)
You can call dataclasses’ replace
which will create a copy with the changed attribute:
from dataclasses import dataclass, replace
@dataclass
class Feature:
name: str
active: bool
features = [Feature("used-to-be", False), Feature("maybe", False)]
active_features = [replace(x, active=True) for x in features]
print(active_features) # should be active
print(features) # should be intact
Just create a method on the original class.
@dataclass
class Feature:
name: str
active:bool
def activate(self):
self.active =True
return self
features = [Feature("name1",False), Feature("name2",False), Feature("name3",True)]
active_features=list(map(lambda f: f.activate(),features))
active_features
[Feature(name='name1', active=True), Feature(name='name2', active=True), Feature(name='name3', active=True)]
Well if you want to do this using map
you can not use lambda because lambda can only have expressions not assignments. Also it has to return something as well. Do this instead
from dataclasses import dataclass
@dataclass
class Feature:
name: str
active: bool
def activate_feature(feature: Feature):
feature.active = True
return feature
features = [Feature("name1",False), Feature("name2",False), Feature("name3",True)]
active_features = list(map(activate_feature, features))
print(active_features)
Output
[Feature(name='name1', active=True), Feature(name='name2', active=True), Feature(name='name3', active=True)]
Or if you want to go with lambda as well you can do this as well
from dataclasses import dataclass
@dataclass
class Feature:
name: str
active: bool
def activate(self):
self.active = True
return self
features = [Feature("name1", False), Feature("name2", False), Feature("name3", True)]
active_features = list(map(lambda f: f.activate(), features))
print(active_features)
Output
[Feature(name='name1', active=True), Feature(name='name2', active=True), Feature(name='name3', active=True)]