Instantiating a class with an arbitrary number of attributes - python

This question is specifically in the context of Python classes here, but could be more generalized. I am creating a class m that will be initialized with four variables, which I would then like to assign to the instantiation of the class. Right now, this looks like this:
class mm(object):
def __init__(self, a, b, c, r):
self.a = a
self.b = b
self.c = c
self.r = r
# etc.
However, I would like to be able to assign the variables to self in one line, something like the pseudocode:
def __init__(self, a, b, c, r):
self.varname = var for var in [a, b, c, r]
It would be even better if this could be generalized to an arbitrary number of variables, so that you could get something like:
def __init__(self, **kwargs):
assign(self, varname, value) for varname, value in kwargs.iteritems()
However, as far as I know, it's not really possible to loop through statements like that. Does anyone know a way to do this?

You can use setattr:
class mm(object):
def __init__(self, **kwargs):
for varname, value in kwargs.iteritems():
setattr(self, varname, value)

You can also use the instance's namespace dictionary for a mass assignment:
class mm(object):
def __init__(self, **kwargs):
self.__dict__.update(kwargs)

Related

Passing kwargs in a class inheritance chain

I have the following setup:
class A:
def __init__(self, **kwargs):
# Some variables initialized
for k, v in kwargs.items():
setattr(self, k, v)
class B(A):
def __init__(self, **kwargs):
A.__init__(self, **kwargs)
self._b = {}
for k, v in kwargs.items():
setattr(self, k, v)
#property
def b(self):
return self._b
#b.setter
def b(self, value):
self._b.update(value)
class C(B):
def __init__(self, **kwargs):
B.__init__(self, **kwargs)
# Some variables initialized
for k, v in kwargs.items():
setattr(self, k, v)
When I now create a new instance of C I get the following error:
AttributeError: 'C' object has no attribute '_b'
Now this makes sense since B._b hasn't been initialized when A.__init__(self, **kwargs) is being called. I can resolve this issue simply by re-ordering the B's initialization like so,
class B(A):
def __init__(self, **kwargs):
self._b = {}
A.__init__(self, **kwargs)
for k, v in kwargs.items():
setattr(self, k, v)
I'd like to understand if there is a recommended/best practice approach when I need to pass kwargs from child to parent classes during initialization? It seems to me like the following things would work,
Re-order the initialization like I have above
Assign kwargs in each child class then pop them and pass the remaining kwargs along to the parent initialization
Something better
Hoping to get some approaches for 3.
The issue you have is with these loops:
for k, v in kwargs.items():
setattr(self, k, v)
You have one in each class, and that means that every one of the classes is setting all the keyword arguments as attributes on self.
When that loop runs in A, it fails because B has a property that needs initializing before it can work.
As you noted in the question, a quick fix would be to make sure that B sets up its dictionary before it runs A.__init__:
class B(A):
def __init__(self, **kwargs):
_b = {} # set this up first
A.__init__(self, **kwargs) # before calling the superclass
for k, v in kwargs.items():
setattr(self, k, v)
But there's probably a better approach that would let you avoid the redundant loops. I'd suggest explicitly naming the keyword arguments you expect in each class. That way b will only be seen by the B class, not by A, nor C (except as part of kwargs).
class A:
def __init__(self, *, a): # a is keyword-only arg, no kwargs accepted here
self.a = a
class B(A):
def __init__(self, *, b, **kwargs):
super().__init__(**kwargs) # doesn't mess with b!
self._b = {}
self.b = b
#property
def b(self):
...
class C(B):
def __init__(self, *, c, **kwargs):
super().__init__(**kwargs)
self.c = c
Now you can call C(a="foo", b={1: 2}, c="bar") and each class will only pay attention to the attribute it cares about.

Defining logic in class constructor to modify instance variable based on other constructor arguments in python

i have a python class in which i have few arguments sent to constructor as below.
class Test(object):
def __init__(self, a, b):
self.a = a
if b<10:
self.a = a*2
I know that, constructors are just meant to initialize variable's and there should be no logic inside a constructor. But, if not this way, how can i set value of "a" variable based a logic with "b" variable. I tried to use property. following is my code
class Test(object):
def __init__(self, a, b):
self.a = a
self.b = b
#property
def a(self):
self._a
#a.setter
def a(self, value):
if self.b < 10:
self._a = value*2
else:
self._a = value
But, problem is that, setter is not called when initializing with a constructor. So, how can i solve this problem of modifying the default setting of few variable inside a constructor

Are properties set in the specified order?

Suppose I have a class with 3 instance attributes, 'a', 'b' and 'c', which are initialized each with property setters. Now, my property 'b' assignment should use the value of the instance variable 'a'. So for 'b' to be initialized, 'a' has to be initialized beforehand.
Following the code below, does python set the instance 'a' first, then goes to instance 'b', and then finnally to 'c', or may the initialisation occur in any random order, which might destroy the possibility to sucessfully initialise the variables?
class Foo(object):
def __init__(self):
self.a = None
self.b = None
self.c = None
#property
def a(self):
return self._a
#a.setter
def a(self, value):
self._a = value
#property
def b(self):
return self._b
#b.setter
def b(self, value):
value = self.a
self._b = value
#property
def c(self):
return self._c
#c.setter
def c(self, value):
value = self.b
self._c = value
I am asking this question in a simplified version because I am having difficulties in a real case. In that case, I used logs to view the execution of the execution of the initialisation, and it appears to me that it starts by executing the last property ('c' in this case), instead of the desired first, 'a'.
__init__ is like any other method in Python; the statements in it are executed in the order given, so in your example code, a is set before b, which is set before c, always.
The Python language spec in general provides stronger ordering guarantees than languages like C/C++ (e.g. a, b, c = d, e, f guarantees that d is read first, then e, then f, and a is set first, then b, then c).
It does not matter if they are properties, plain attributes, or whatever; assignment might do funky things, but those things occur in the order the statements occur.

Can I get common parameters from parent constructor?

I would like to know if I can use the parent constructor for passing parameters which will be needed for every subclass. So for instance:
Class A():
def __init__(a,b):
...do some stuff...
Class B(A):
def __init__(c,d):
...do some stuff needing a and b...
Class C(A):
def __init__(e,f,g):
...do some stuff needing a and b...
Basically there are some parameters each of my subclasses is going to want and some others which are specific. I don't want to have to add a,b to the definition of every subclass of A. Is there some way I can do this in python?
What I'd like to see is the ability to call:
b=B(a=1,b=2,c=3,d=4)
without having to include a and b in the subclass definition.
Many thanks!
# Python 3, but the idea is the same in 2
class A:
def __init__(self, a, b):
# ...
class B(A):
def __init__(self, c, d, *args, **kwargs):
super().__init__(*args, **kwargs)
class C(A):
def __init__(self, e, f, g, *args, **kwargs):
super().__init__(*args, **kwargs)

Python - How to use underlying object's constructor as class method?

Please forgive the bad title - I had a hard time trying to think of a concise way to explain this.
I have a Python class that will have some underlying objects of other classes. I want to be able to create these underlying objects via a method of the original object. Let me try to explain better with an example:
class Foo:
def __init__(self):
self.bars = []
def Bar(self, a, b, c):
self.bars.append(Bar(a, b, c))
class Bar:
def __init__(self, a, b, c):
self.a = a
self.b = b
self.c = c
I would use the above as such:
f = Foo()
f.Bar(1, 2, 3)
So this works how I want but is kind of crappy with respect to maintenance. Is there a nice "Pythonic" way to do this that would make maintaining this easy? For instance, let's say I changed the constructor of Bar to:
__init__(self, a, b, c, d):
would there be a way to define all of this so I don't have to update the argument list in 3 places?
Sure, no problem: Just pass *args and **kwargs on to Bar:
class Foo:
def __init__(self):
self.bars = []
def append_bar(self, *args, **kwargs):
self.bars.append(Bar(*args, **kwargs))
class Bar:
def __init__(self, a, b, c, d):
self.a = a
self.b = b
self.c = c
self.d = d
f=Foo()
f.append_bar(1,2,3,4)
PS. I changed the name of the method to append_bar because the usual convention in Python is to use lowercase names for methods, and I think methods whose names are verbs help describe what the method does.

Categories

Resources