Iterating through list elements and apply to class - python

I'm looking to establish a list, (list_=[a,b,c]), iterate over the list and apply it to a class (class_). So something like this:
for letter in list_:
x = class_.letter
Is this possible?

Attributes are typically accessed from a class using a dot notation, ex: my_class.attribute_1. This is useful when accessing attributes are hard coded.
But as you point out, this is not useful when needing to dynamically access attributes, as in the case of the list above. The solution in these cases is to access attributes using the getattr() method which takes input of your class and the attribute name. Ex: x = 'attribute_1, getattr(my_class, x) would return the same results as my_class.attribute_1.
💡 It is worth pointing out that attributes can be set in a similar way: my_class.foo = 1 is equivalent to setattr(my_class, 'foo', 1).
Here is a solution where attributes are accessed using a list (from an example class):
class MyClass:
def __init__(self):
self.a = 1
self.b = 2
self.c = 3
# Instantiate your class
my_class = MyClass()
# Create a list of letters
list_1 = ['a', 'b', 'c']
# Get attributes
for letter in list_1:
x = getattr(my_class, letter)
print(f'{letter=}, {x=}')

Related

python classes: class attributes don't update when accessed through a list

I am running into some odd behavior with a python class, and I was hoping someone might be able to explain the cause and fix. I am using python 3.8.5. The problem is that when I have a list of class attributes and I try and update them, that they don't seem to get updated. Here is a simple example.
class testclass:
def __init__(self) -> None:
self.a = 2
self.b = 3
self.c = 4
def update(self):
for s in [self.a, self.b, self.c]:
s = 10
def response(self):
self.update()
print([self.a, self.b, self.c])
if __name__ == '__main__':
myclass = testclass()
myclass.response()
The output that I get is [2, 3, 4], when it should be [10,10,10] after the update() method is applied, right? Is there a better way to do something like this?
When you iterate over the list [self.a, self.b, self.c], each s is effectively a copy of that element.
This can be boiled down more simply to get rid of the class and self stuff:
>>> f = [1, 2, 3]
>>> for s in f:
... # s is implicitly assigned s=1, s=2, s=3
... s = 10
...
>>> f
[1, 2, 3]
Is there a better way to do something like this?
Depends what you really want to do here, since this example seems somewhat contrived. In general try to avoid modifying a list while iterating over it.
Let's assume your goal is to set each self.XXX to an arbitrary int. Here's a Pythonic way you can do that:
self.a = self.b = self.c = 10
Make sure to only use this trick with immutable data, such as the int 10. If you were to chain-assign to a list, for instance, future assignments would also mutate the other attributes besides the one you're acting on.

Set class attributes with keyword arguments

This popular question addresses setting instance attributes with keyword arguments. However, I'd like to construct a class whose instances all have the same attributes based on some dictionary. How can this be achieved?
Here's my attempt. It seems I haven't quite understood something about class definitions.
d = {'x': 1, 'y': 2}
# Here's what I'd like to do
class A:
__dict__ = d
# Answer from the linked question that works
class B:
def __init__(self, **kwargs):
self.__dict__.update(kwargs)
a = A()
b = B(**d)
# print(A.x) # Has no attribute x
# print(a.x) # Has no attribute x
print(b.x)
This is curious, because both a.__dict__ and b.__dict__ return the same thing.
The type function can take arguments that allow it to create a new class dynamically:
B = type('B', (), d)
b = B()
print(b.x, b.y)
Output:
1 2
The first argument is the name of the generated class. The second is a tuple containing its base classes. In particular, the following two snippets are (roughly) equivalent:
class D(A, B, C):
pass
and:
D = type('D', (A, B, C), {})
The last argument is a dict mapping names to attributes (both methods and values).

Update attributes and return a new copy of the same class

class Main(object):
def __init__(self, config):
selt.attributes = config
def return_new_copy(self, additional_attributes):
addtional_attributes.update(self.attributes)
return Main(additional_attributes)
I want to update the instance attributes and return a new instance of the same class. I guess I am trying to find out if the above code is Pythonic or if it's a dirty approach. I can't use classmethod for several reasons not mentioned here. Is there another recommended approach.
Your return_new_copy modifies the parameter passed in which is probably undesirable. It also overrides in the wrong direction (giving precedence to self.attributes)
I'd write it as follows:
def return_new_copy(self, additional_attributes):
# python<3.5 if there are only string keys:
# attributes = dict(self.attributes, **additional_attributes)
# python<3.5 if there are non-string keys:
# attributes = self.attributes.copy()
# attributes.update(additional_attributes)
# python3.5+
attributes = {**self.attributes, **additional_attributes}
return type(self)(attributes)
A few subtleties:
- I make sure to copy both the input attributes and the self attributes
- I merge the additional attributes on top of the self attributes
If you're looking for something to do this automatically, you might want to check out namedtuple
For example:
>>> C = collections.namedtuple('C', ('a', 'b'))
>>> x = C(1, 2)
>>> x
C(a=1, b=2)
>>> y = x._replace(b=3)
>>> y
C(a=1, b=3)
>>> x
C(a=1, b=2)

Make an array class using dictionaries only

I am a student who is new to python. I am trying to define an array class below which uses a dictionary as its only member variable. I am assuming that Python implements dictionaries as the only structured type (i.e., there is no array, list, tuple, etc.).
I am facing difficulty in coding such a program.
This is my code:
class array(object):
def __init__(self):
self.dic={}
def __init__(self,diction):
self.dic = {}
for x in diction:
self.dic.append(x)
def __setitem__(self,key,value):
self.dic[key]=value
def __getitem__(self,key):
if key not in self.dic.keys():
raise KeyError
return self.dic[key]
I want the program to work this way:
a = array('a','b','c') #------output------
print(a) # ['a', 'b', 'c']
print(a[1]) # b
a[1] = 'bee'
print(a) # ['a', 'bee', 'c']
a[3] = 'day'
print(a) # ['a', 'bee', 'c', 'day']
print(a[6]) # IndexError exception
Any suggestions, advice. :)
There are quite a few issues with your class definition:
array is already a data structure: better to rename using the proper Python class-naming conventions (MyClass).
You cannot overload function definitions: better to use an unpacking operator (*) to extract all (if any) arguments.
You cannot append to a dictionary: you need to assign to a key.
Your call to print will display a generic class name, since you don't specify a __str__ magic method. Since a dict is unordered, I did some funny business here to make it display as sorted, though I'm sure there's a better way.
No need to raise a KeyError in __getitem__, since this will be raised anyway.
Finally, I corrected your spacing.
Note that I've only implemented the methods necessary to make your test cases work.
class MyArray(object):
def __init__(self, *diction):
self.dic = {}
for i, x in enumerate(diction):
self.dic[i] = x
def __setitem__(self, key, value):
self.dic[key] = value
def __getitem__(self, key):
return self.dic[key]
def __str__(self):
return str([self.dic[i] for i in sorted(self.dic.keys())])

map a method over list of different objects

I would like to map a method over a list of objects instantiating different classes. All the objects inherit from the same base class and define a method of the desired name.
To make it clearer consider the following code:
class A:
def __init__(self, x):
self.x = x
def foo (self):
return self.x
class B(A):
def foo (self):
return self.x+1
class C(A):
def foo (self):
return self.x-1
Now consider a list of objects instantiating the classes B and C. I would like to do something like that:
result = []
for obj in [B(1), C(1)]:
result.append(obj.foo())
How would you proceed to map the method foo on each element of the list? Is it at all possible? The best I could come up with is something like that:
map(A.foo, [B(1), C(1)])
but clearly it doesn't return my desired result. How can I specify the method related to the object?
I hope I made myself clear.
NB: I work primarily with Python2.7, but I would equally be interested in solutions valid for "newer" versions.
Map(A.foo, [B(1), C(1)]) is basically doing A.foo(B(1)) and A.foo(C(1)) which isn't what you are looking for.
Using your classes from above, I would just do:
In: objs = [B(1), C(1)]
In: [x.foo() for x in objs]
Out: [2, 0]
Amjith has a pure map implementation if you'd prefer that.
>>> map(lambda x: x.foo(), [B(1), C(1)])
>>> [2, 0]
The lambda function will take each object in the list and call foo() on that object. Thus the resulting list will have the results returned by the corresponding object's foo().
For most practical purposes, I'd recommend #AlG's list comprehension, but you can do this with map as well:
>>> import operator
>>> map(operator.methodcaller("foo"), [B(1), C(1)])
[2, 0]

Categories