Dynamically adding attributes to __init__ method in python - python

Is there any way you can define a function that can add,at some later point in the program, new attributes to an already existing __init__ method? For example, below I created a Class for a Family Tree. Each instance would create a root.
class FamilyTree:
def __init__(self,rootObj):
self.key = rootObj
I want each new root instance to have the ability to have a specific number of children attributes: For example:
self.child1 = 'B'
self.child2 = 'C'
self.child3 = 'D'
Since every new root instance can have various number of children, how can I variably add new attributes to the __init__ method?

A possible way to automate this is as follow:
class FamilyTree:
def __init__(self,rootObj, child_names=None):
self.key = rootObj
if child_names is not None:
# child_names is a list of child attributes values
for i, child in enumerate(child_names):
setattr(self, 'child{0}'.format(i), child)
setattr(self, 'child_nbr', len(child_names))
def add_child(self, *child_names)
for name in child_names:
self.child_nbr += 1
setattr(self, 'child{0}'.format(self.child_nbr), name)
usage:
>>> f=FamilyTree('Family1', ['B', 'C', 'D'])
>>> print(f.child1, f.child2, f.child3)
>>> i = 0
>>> while i < f.child_nbr:
>>> print getattr(f, 'child{0}'.format(i+1))
>>> f.add_child('E')
>>> print(f.child4)
>>> f.add_child('F', 'G')
>>> print(f.child5, f.child6)

I guess what you really (should) want is a list of children:
class FamilyTree:
def __init__(self, rootObj):
self.key = rootObj
self.children = []
def add_children(*new_children)
self.children.extend(new_children)
Now you can use the add_children method to add any number of children to the list at once, or you could simply directly access the children instance member list as well:
tree = FamilyTree("whatever a rootObj is...")
tree.add_children("Alice", "Bob", "Claudia", "Dave")
tree.children.append("Eva")
tree.children += ["Fred", "Gina", "Herbert"]
print(tree.children)
# Output: ["Alice", "Bob", "Claudia", "Dave", "Eva", "Fred", "Gina", "Herbert"]

class FamilyTree:
def __init__(self,rootObj):
self.key = rootObj
child1='B'
child2='C'
child3='D'
objold=FamilyTree('keyold')
FamilyTree.child1=child1
FamilyTree.child2=child2
FamilyTree.child3=child3
objnew=FamilyTree('keynew')
print objnew.key,objnew.child1,objnew.child2,objnew.child3
print objold.key,objold.child1,objold.child2,objold.child3
'''
keynew B C D
keyold B C D
'''

Maybe I understand this wrong, but you don't add attributes to the 'init' method, you add attributes to the instance of the class. 'self' refers to the instance of the class.
To add attributes during runtime to a class, look at Abhra's example.
To add attributes to a specific instance of a class, look here (sorry Abhra, stole your example):
class FamilyTree:
def __init__(self,rootObj):
self.key = rootObj
child1='B'
child2='C'
child3='D'
objold=FamilyTree('keyold')
objold.child1=child1
objold.child2=child2
objold.child3=child3
objnew=FamilyTree('keynew')
print objnew.key, objnew.child1, objnew.child2, objnew.child3
print objold.key, objold.child1, objold.child2, objold.child3
'''
keynew AttributeError: 'FamilyTree' object has no attribute 'child1' (same error for the others)
keyold B C D
'''

Related

Call constructor of cls object in Python

I am trying to call the constructor of a class object in python. I managed to get it to work using the following few lines:
obj = cls.__new__(cls)
n = (List of attribute names)
v = (List of attribute values)
for s in n:
setattr(obj, s, v[s])
I was wondering if there is a way to directly insert the attribute value + name pairs into the constructor, cause the arguments are just ignored if i call the following:
obj = cls.__new__(cls, v)
p.s.: I am using python3
The class looks similar to this:
class InheritingClass(BaseClass):
def __init__(self, basic_attribute, another_attribute=None):
super().__init__(basic_attribute=basic_attribute)
self.another_attribute= another_attribute
class BaseClass:
def __init__(self, basic_attribute=1):
self.basic_attribute= basic_attribute
So nothing special there
I was wondering if there is a way to directly insert the attribute value + name pairs into the constructor
Please don't do that. This would be the anti pattern. Instead, use the __init__ method to set the values. The __new__ method should be the memory space allocation that returns the object instance, obj in your case.
So you should probable better do this inside your __init__:
k = ['a', 'b', 'c']
v = [1, 2, 3]
d = dict(zip(k, v))
class C:
def __init__(self, d):
for _ in d:
setattr(self, _, d[_])
ci=C(d)
print(ci.a) # 1
I used the dict as __init__ parameter, where I used the zip method to create one.
__init__ is the constructor of Python class instead of __new__. Refer Pythons use of new and init for more information.
To add, if you want to store arbitrary attributes to your class, you can use dict.update like so:
class BaseClass:
def __init__(self, basic_attribute=1, **kw):
self.basic_attribute = basic_attribute
self.__dict__.update(**kw)
class InheritingClass(BaseClass):
def __init__(self, basic_attribute, another_attribute=None, **kw):
super().__init__(basic_attribute=basic_attribute, **kw)
self.another_attribute = another_attribute
Then:
ic = InheritingClass('hi', a=1, b=20)
print(ic.a, ic.b) # prints 1, 20
To answer the question "How do you call the constructor on a class object?" you need to look at the comments from Amadan way back on Aug 24, 2016 at 6:41.
The answer:
new_obj = cls()
Here's some example code that illustrates the point:
class C:
#classmethod
def c(cls):
return cls()
c = C.c()
print(c) # displays <__main__.C object at 0x10ef16a90>
class D(C):
pass
d = D.c()
print(d) # displays <__main__.D object at 0x10ef16370>
And so we see that you can instantiate an object from the cls object.
Now if we combine Amadan's comment with prosti's cool code for setting attributes, we get this:
class ObjectFactory:
#classmethod
def new(cls,**kwargs):
return cls(**kwargs)
def __init__( self, **kwargs ):
for _ in kwargs:
setattr( self, _ , kwargs[ _ ] )
class Person(ObjectFactory):
pass
person = Person.new( first = "John", last = "Doe" )
print(person) # <__main__.Person object at 0x10fe49ff0>
print(person.__dict__) # {'first': 'John', 'last': 'Doe'}

Set dictionary key to attribute of class instance

I have a dictionary which stores objects of a class foo. Class foo has an attribute Name. For every instance, I want the Name attribute to be the key to the instance in the dictionary. All instances of the class will be defined inside the dictionary.
class foo:
def __init__(self):
self.Name = None #self.Name should equal "self"
foo_dict = {
'foo1' = foo()
}
#(foo.Name should equal 'foo1')
How can I set the Name attribute to be the key to the instance in the dictionary?
Comment if specifications are needed.
I can't possibly stress enough how BAD this is... Please, please, use this only for educational purposes. It's crumbly, unreliable... BAD If you change anything in your code, it'll stop working. It is dirty. It is possibly non portable... OMG... I think a few kittens were killed when I hit Post Your Answer
import inspect
import re
class Foo(object):
def __init__(self):
r = re.compile(
r"\W+['\"](?P<name>\w+)['\"]\W+%s\W+"
% self.__class__.__name__
)
caller_frame = inspect.currentframe().f_back
code_context = inspect.getframeinfo(caller_frame).code_context
match = r.match(''.join(code_context))
if match:
self.name = match.groupdict()['name']
print "Assigned name: %s" % self.name
else:
raise Exception("This wasn't called as it was supposed to")
if __name__ == "__main__":
foo_dict = {
'foo1': Foo(),
'foo2': Foo(),
}
But it does what you seem to be asking:
borrajax#borrajax:/tmp$ python ./test.py
Assigned name: foo1
Assigned name: foo2
Now, what I would do is:
Option 1:
Pass the name in the initialization.
Possibly the simplest, most maintainable and that leaves the code in a much clearer state (important if someone else reads your code)
class Foo(object):
def __init__(self, name):
self.name = name
print "Assigned name: %s" % self.name
if __name__ == "__main__":
foo_dict = {
'foo1': Foo('foo1'),
'foo2': Foo('foo2'),
}
Option 2:
Create your own dict class and overwrite the __setitem__ method (see also Subclassing Python dictionary to override __setitem__ and How to "perfectly" override a dict?):
class Foo(object):
pass
class MyDict(dict):
def __setitem__(self, key, val):
if not isinstance(val, Foo):
raise TypeError("My dict only accepts %s" % Foo)
val.name = key
print "Assigned name: %s" % val.name
return super(MyDict, self).__setitem__(key, val)
if __name__ == "__main__":
foo_dict = MyDict()
foo_dict['foo1'] = Foo()
foo_dict['foo2'] = Foo()
foo_dict['foo3'] = 1
Prints:
borrajax#borrajax:/tmp$ python ./test.py
Assigned name: foo1
Assigned name: foo2
Traceback (most recent call last):
File "./stack64.py", line 17, in <module>
foo_dict['foo3'] = 1
File "./stack64.py", line 8, in __setitem__
raise TypeError("My dict only accepts %s" % Foo)
TypeError: My dict only accepts <class '__main__.Foo'>
This has the disadvantage of magically adding attributes (the .name) to the instances of Foo when assigned to the dictionary, which can cause name conflicts (if your Foo class already had a .name, this method would change its value). In general, I'd stay away of methods that magically add attributes to instances in the middle of the execution.
Option 3:
Use #Daniel's answer to this question. Clean and understandable for someone else reading your code.
Seems like you need a reference to the instance to do what you want. If you build the dictionary with a comprehension, you can create instance references and use them.
class Foo(object):
def __init__(self, n = None):
self.name = n
d = {f.name:f for f in (Foo(n) for n in 'abcd')}
>>> d
{'a': <__main__.Foo object at 0x03DF9710>, 'c': <__main__.Foo object at 0x03E01250>, 'b': <__main__.Foo object at 0x03DF9A50>, 'd': <__main__.Foo object at 0x03E01290>}
>>>
>>> d = {f.name:f for f in (Foo(n) for n in [1])}
>>> d
{1: <__main__.Foo object at 0x03E01B50>}
>>> foo_dict = {}
>>> foo_dict.update(d)
>>> foo_dict
{1: <__main__.Foo object at 0x03E01B50>}
>>>
I stumbled upon this SO answer the other day. Using that class decorator/descriptor, you could create a class factory that produces Foo objects and keeps track of the current object and a counter for the next object.
class InnerClassDescriptor(object):
'''allows access to the outer class and its attributes
decorator/descriptor
an instance of a nested inner class can access the outer class and its attributes
'''
def __init__(self, cls):
self.cls = cls
def __get__(self, instance, outerclass):
class Wrapper(self.cls):
outer = instance
Wrapper.__name__ = self.cls.__name__
return Wrapper
class FooFactory(object):
next_foo = 0
this_foo = None
#InnerClassDescriptor
class Foo(object):
def __init__(self):
# print 'Foo,__init__, next_foo = ', self.outer.next_foo
self.name = 'Foo' + str(self.outer.next_foo)
self.outer.next_foo += 1
self.outer.this_foo = self
Usage:
ff = FooFactory()
d = {ff.this_foo.name:ff.Foo()}
for k, v in d.items():
print k, v.name
>>>
Foo0 Foo0
>>>
This relies on the dictionary item value being evaluated before the key - which seems to be the case for Python 2.7
Do it the other way round:
class Foo:
def __init__(self, name=None):
self.name = name
foo1 = Foo('foo1')
foo_dict = {
foo1.name: foo1
}

Problems when converting a dictionary to object

I am using a technique discussed here before, to turn a dictionary into an object, so that I can access the elements of the dictionary with the dot (.) notion, as instance variables.
This is what I am doing:
# Initial dictionary
myData = {'apple':'1', 'banana':'2', 'house':'3', 'car':'4', 'hippopotamus':'5'}
# Create the container class
class Struct:
def __init__(self, **entries):
self.__dict__.update(entries)
# Finally create the instance and bind the dictionary to it
k = Struct(**myData)
So now, I can do:
print k.apple
and the result is:
1
This works, however the issues start if I try to add some other methods to the "Struct" class. For example lets say that I am adding a simple method that just creates an variable:
class Struct:
def __init__(self, **entries):
self.__dict__.update(entries)
def testMe(self):
self.myVariable = 67
If I do:
k.testMe()
My dictionary object is broken, "myVariable" is inserted as a key with the value "67". So If I do:
print k.__dict__
I am getting:
{'apple': '1', 'house': '3', 'myVariable': 67, 'car': '4', 'banana': '2', 'hippopotamus': '5'}
Is there a way to fix this? I kind of understand what is happening, but not sure If I need to entirely change my approach and build a class with internal methods to handle the dictionary object or is there a simpler way to fix this problem?
Here is the original link:
Convert Python dict to object?
Thanks.
For your needs, don't store you variables in __dict__. Use your own dictionary instead, and override .__getattr__ (for print k.apple) and __setattr__ (for k.apple=2):
# Initial dictionary
myData = {'apple':'1', 'banana':'2', 'house':'3', 'car':'4', 'hippopotamus':'5'}
# Create the container class
class Struct:
_dict = {}
def __init__(self, **entries):
self._dict = entries
def __getattr__(self, name):
try:
return self._dict[name]
except KeyError:
raise AttributeError(
"'{}' object has no attribute or key '{}'".format(
self.__class__.__name__, name))
def __setattr__(self, name, value):
if name in self._dict:
self._dict[name] = value
else:
self.__dict__[name] = value
def testMe(self):
self.myVariable = 67
def FormattedDump(self):
return str(self._dict)
# Finally create the instance and bind the dictionary to it
k = Struct(**myData)
print k.apple
print k.FormattedDump()
k.testMe()
k.apple = '2'
print k.FormattedDump()
In the alternative, if your FormattedDump() routine is bothering you, you could just fix it:
# Initial dictionary
myData = {'apple':'1', 'banana':'2', 'house':'3', 'car':'4', 'hippopotamus':'5'}
# Create the container class
class Struct:
def __init__(self, **entries):
self.__dict__.update(entries)
self.public_names = entries.keys()
def testMe(self):
self.myVariable = 67
def GetPublicDict(self):
return {key:getattr(self, key) for key in self.public_names}
def FormattedDump(self):
return str(self.GetPublicDict())
# Finally create the instance and bind the dictionary to it
k = Struct(**myData)
print k.apple
print k.FormattedDump()
k.testMe()
k.apple = '2'
print k.FormattedDump()

Exposing dict values via properties

I have this (Py2.7.2):
class MyClass(object):
def __init__(self, dict_values):
self.values = dict_values
self.changed_values = {} #this should track changes done to the values{}
....
I can use it like this:
var = MyClass()
var.values['age'] = 21
var.changed_values['age'] = 21
But I want to use it like this:
var.age = 21
print var.changed_values #prints {'age':21}
I suspect I can use properties to do that, but how?
UPDATE:
I don't know the dict contents at the design time. It will be known at run-time only. And it will likely to be not empty
You can create a class that inherits from a dict and override the needed functions
class D(dict):
def __init__(self):
self.changed_values = {}
self.__initialized = True
def __setitem__(self, key, value):
self.changed_values[key] = value
super(D, self).__setitem__(key, value)
def __getattr__(self, item):
"""Maps values to attributes.
Only called if there *isn't* an attribute with this name
"""
try:
return self.__getitem__(item)
except KeyError:
raise AttributeError(item)
def __setattr__(self, item, value):
"""Maps attributes to values.
Only if we are initialised
"""
if not self.__dict__.has_key('_D__initialized'): # this test allows attributes to be set in the __init__ method
return dict.__setattr__(self, item, value)
elif self.__dict__.has_key(item): # any normal attributes are handled normally
dict.__setattr__(self, item, value)
else:
self.__setitem__(item, value)
a = D()
a['hi'] = 'hello'
print a.hi
print a.changed_values
a.hi = 'wow'
print a.hi
print a.changed_values
a.test = 'test1'
print a.test
print a.changed_values
output
>>hello
>>{'hi': 'hello'}
>>wow
>>{'hi': 'wow'}
>>test1
>>{'hi': 'wow', 'test': 'test1'}
Properties (descriptors, really) will only help if the set of attributes to monitor is bounded. Simply file the new value away in the __set__() method of the descriptor.
If the set of attributes is arbitrary or unbounded then you will need to overrive MyClass.__setattr__() instead.
You can use the property() built-in function.
This is preferred to overriding __getattr__ and __setattr__, as explained here.
class MyClass:
def __init__(self):
self.values = {}
self.changed_values = {}
def set_age( nr ):
self.values['age'] = nr
self.changed_values['age'] = nr
def get_age():
return self.values['age']
age = property(get_age,set_age)

create objects on runtime in python

how do i create object-instances on runtime in python?
say i have 2 classes:
class MyClassA(object):
def __init__(self, prop):
self.prop = prop
self.name = "CLASS A"
def println(self):
print self.name
class MyClassB(object):
def __init__(self, prop):
self.prop = prop
self.name = "CLASS B"
def println(self):
print self.name
and a dict
{('a': MyClassA), ('b': MyClassB)}
how can i create dynamic an instance of one of my two classes, depending of i choose 'a' or 'b'.
kind of this:
somefunc(str):
if 'a': return new MyClassA
if 'b': return new MyClassB
to get "CLASS B" on calling: somefunc('a').println
but in a more elegant and dynamic way (say i add more classes to the dict on runtime)
You might create a dispatcher, which is a dictionary with your keys mapping to classes.
dispatch = {
"a": MyClassA,
"b": MyClassB,
}
instance = dispatch[which_one]() # Notice the second pair of parens here!
You create a class instance by calling the class. Your class dict {('a': MyClassA), ('b': MyClassB)} returns classes; so you need only call the class:
classes['a']()
But I get the sense you want something more specific. Here's a subclass of dict that, when called with a key, looks up the associated item and calls it:
>>> class ClassMap(dict):
... def __call__(self, key, *args, **kwargs):
... return self.__getitem__(key)(*args, **kwargs)
...
>>> c = ClassMap()
>>> c['a'] = A
>>> c['b'] = B
>>> c('a')
<__main__.A object at 0x1004cc7d0>

Categories