I've created a new class and I'm trying to add to that class dynamically,
I've created a list, that I want to put multiple objects in, then I will iterate over that list in Django (is this the correct way of doing things?)
but I'm getting the below error
TypeError: __init__() takes exactly 9 arguments (1 given)
I know what the error means, I'm just wonder how I go about creating a new instance of my objects and adding to it on the fly easily?
### create User Object
class User:
def __init__(self, Policy, Level, StartDate, EndDate, StartTime, EndTime, Name, Mobile):
self.Policy = Policy
self.Level = Level
self.StartDate = StartDate
self.EndDate = EndDate
self.StartTime = StartTime
self.EndTime = EndTime
self.Name = Name
self.Mobile = Mobile
def __init__(self):
pass
### Get all the Polices ###
lstOnCall = []
for objPolicy in objPolicyData['escalation_policies']:
strPolicyName = objPolicy['name']
if strPolicyName.lower().find('test') == -1:
for objOnCall in objPolicy['on_call']:
objUser = User()
objUser.Policy = strPolicyName
objUser.Level = objOnCall['level']
objUser.StartDate = getDate(objOnCall['start'])
objUser.EndDate = getDate(objOnCall['end'])
objUser.StartTime = getTime(objOnCall['start'])
objUser.EndTime = getTime(objOnCall['end'])
objUser = objOnCall['user']
objUser.Name = objUser['name']
objUser.Mobile = getUserMobile(objUser['id'])
lstOnCall.append(objUser)
print lstOnCall
UPDATE:
adding the below works, i just need to know how to print the items now?
def __init__(self):
pass
the below
for item in lstOnCall:
print item()
returns
print item()
AttributeError: User instance has no __call__ method
You can write a dynamic constructor (def __init__) for your class so:
class User(object):
__attrs = ['Policy', 'Level', 'StartDate', 'EndDate', 'StartTime',
'EndTime', 'Name', 'Mobile']
def __init__(self, **kwargs):
for attr in self.__attrs:
setattr(self, attr, kwargs.get(attr, None))
def __repr__(self):
return ', '.join(
['%s: %r' % (attr, getattr(self, attr)) for attr in self.__attrs])
The variable __attrs stores the variables names. I used double underscore variable, so that it's inaccessible from extend.
user = User()
print(user.__attrs)
Traceback (most recent call last):
print(user.__attrs)
AttributeError: 'User' object has no attribute '__attrs'
Yes, there are other method to access double underscore variable, but no one will do that ;)
The function __repr__ return the string by calling print or str, if the function __str__ doesn't exist.
Now test it
>>> u1 = User(Name='user1')
>>> u2 = User(Name='user2', Policy=1, Level=3)
>>> print(u1)
Policy: None, Level: None, StartDate: None, EndDate: None, StartTime: None, EndTime: None, Name: 'user1', Mobile: None
>>> print(u2)
Policy: 1, Level: 3, StartDate: None, EndDate: None, StartTime: None, EndTime: None, Name: 'user2', Mobile: None
If you use my codes, you can print the items in your case so:
for item in lstOnCall:
print item
Other problem of your code
There aren't the definition Function overloading in Python. You can define multiple function with the same name in python. But it doesn't make any sense. Only the last definition remains in your class/module. The previous definitions will be overwritten. What you are doing with
class User:
def __init__(self, a, b, c):
...
def __init__(self):
pass
is False. It works in Java or C# but not in Python. The function def __init__(self, a, b, c) will be overwritten. Only the function def __init__(self) exists in your class.
You could set all of the parameters to __init__ to be None by default:
def __init__(self, Policy=None, Level=None, etc...):
Convert the positional parameters of your constructor method to named, optional parameters with a useful default value:
class User:
def __init__(self, Policy=Null, Level=1,
StartDate="2016-01-01", EndDate="2016-12-31",
StartTime="00:00", EndTime="23:59",
Name="UNKNOWN", Mobile=""):
self.Policy = Policy
self.Level = Level
self.StartDate = StartDate
self.EndDate = EndDate
self.StartTime = StartTime
self.EndTime = EndTime
self.Name = Name
self.Mobile = Mobile
Try this,
class User:
def __init__(self,*args,**kargs):
if len(kargs)==0 : ''' No param passed '''
self.Policy = 'Some'
self.Level = 0
else:
self.Policy = kargs['Policy']
self.Level = kargs['Level']
[..]
user= User()
user1= User(Policy='Some',Level=13)
Related
I have a function to check whether my list of class objects meet a certain criteria, and if they do, they will be stored in a list.
Here is the function:
def filter_stories(stories, triggerlist):
"""
Takes in a list of NewsStory instances.
Returns: a list of only the stories for which a trigger in triggerlist fires.
"""
print('checkpoint6, triggerlist', triggerlist)
fired_trigger = []
for trig in triggerlist:
print('checkpoint7, trig', trig)
for story in stories:
print('checkpoint8, story', story)
if trig.evaluate(story):
print('checkpoint9, calling evaluate method', trig.evaluate(story))
fired_trigger.append(story)
return fired_trigger
just to illustrate that they are indeed class objects. Here are the results from my terminal:
checkpoint6, triggerlist [<__main__.AndTrigger object at 0x0000026C2655A248>, <__main__.AndTrigger object at 0x0000026C26575D88>]
checkpoint7, trig <__main__.AndTrigger object at 0x0000026C2655A248>
checkpoint8, story <__main__.NewsStory object at 0x0000026C26576188>
but when I tried to call method of the class object in this line if trig.evaluate(story):, I get this error 'str' object has no attribute 'evaluate'...
Here is my class and subclasses definitions if it is helpful.
class NewsStory:
def __init__(self, guid, title, description, link, pubdate):
self.guid = guid
self.title = title
self.description = description
self.link = link
self.pubdate = pubdate
def get_guid(self):
return self.guid
def get_title(self):
return self.title
def get_description(self):
return self.description
def get_link(self):
return self.link
def get_pubdate(self):
return self.pubdate
# ======================
# Triggers
# ======================
class Trigger(object):
def evaluate(self, story):
"""
Returns True if an alert should be generated
for the given news item, or False otherwise.
"""
# DO NOT CHANGE THIS!
raise NotImplementedError
# PHRASE TRIGGERS
class PhraseTrigger(Trigger):
def __init__(self, phrase):
self.phrase = phrase.strip().lower()
def is_phrase_in(self, text):
result = []
phrase_list = self.phrase.split()
phrase = " ".join(phrase_list)
for punct in string.punctuation:
if punct in text:
text = text.replace(punct, " ")
text_list = text.lower().split()
text = " ".join(text_list)
result.append(re.search(fr'\b{phrase}\b', text))
if None in result:
return False
return True
def __str__(self):
print('TitleTrigger!')
class TitleTrigger(PhraseTrigger):
def evaluate(self, story):
return self.is_phrase_in(story.get_title())
class DescriptionTrigger(PhraseTrigger):
def evaluate(self, story):
return self.is_phrase_in(story.get_description())
# TIME TRIGGERS
class TimeTrigger(Trigger):
def __init__(self, date):
try: # if the date supplied is string, strip it into datetime and replace the tz into EST
date_obj = datetime.strptime(date, "%d %b %Y %H:%M:%S")
self.time = datetime.replace(date_obj, tzinfo=pytz.timezone("EST"))
except TypeError: # else if it is already in datetime object, replace the tz
self.time = datetime.replace(date, tzinfo=pytz.timezone("EST"))
class BeforeTrigger(TimeTrigger):
def evaluate(self, story):
story_date = story.get_pubdate()
try: # if the date supplied is string, strip it into datetime and replace the tz into EST
story_date_obj = datetime.strptime(story_date, "%d %b %Y %H:%M:%S")
story_time = datetime.replace(story_date_obj, tzinfo=pytz.timezone("EST"))
except TypeError: # else if it is already in datetime object, replace the tz
story_time = datetime.replace(story_date, tzinfo=pytz.timezone("EST"))
if self.time > story_time:
return True
return False
class AfterTrigger(TimeTrigger):
def evaluate(self, story):
story_date = story.get_pubdate()
try: # if the date supplied is string, strip it into datetime and replace the tz into EST
story_date_obj = datetime.strptime(story_date, "%d %b %Y %H:%M:%S")
story_time = datetime.replace(story_date_obj, tzinfo=pytz.timezone("EST"))
except TypeError: # else if it is already in datetime object, replace the tz
story_time = datetime.replace(story_date, tzinfo=pytz.timezone("EST"))
if self.time < story_time:
return True
return False
# COMPOSITE TRIGGERS
class NotTrigger(Trigger):
def __init__(self, other_trigger):
self.other = other_trigger
def evaluate(self, story):
return not self.other.evaluate(story)
class AndTrigger(Trigger):
def __init__(self, trigger1, trigger2):
self.trigger1 = trigger1
self.trigger2 = trigger2
def evaluate(self, story):
return self.trigger1.evaluate(story) and self.trigger2.evaluate(story)
class OrTrigger(Trigger):
def __init__(self, trigger1, trigger2):
self.trigger1 = trigger1
self.trigger2 = trigger2
def evaluate(self, story):
return self.trigger1.evaluate(story) or self.trigger2.evaluate(story)
Here is the complete error traceback:
checkpoint6, triggerlist [<__main__.AndTrigger object at 0x000001DA9D77A088>, <__main__.AndTrigger object at 0x000001DA9D795C88>]
checkpoint7, trig <__main__.AndTrigger object at 0x000001DA9D77A088>
checkpoint8, story <__main__.NewsStory object at 0x000001DA9D799208>
'str' object has no attribute 'evaluate'
Process finished with exit code -1
I don't understand why it is giving me this error as the print statement shows it is an object and not of type string.. and so, very much clueless on how to fix this..
Sorry for the basic question and thank you for your help. :)
In case this might be of help to anyone who is facing the same error, my problem was when I parsed string as the arguments to my class constructor instead of the intended object type.
This is the class definition:
class PhraseTrigger(Trigger):
def __init__(self, phrase):
self.phrase = phrase.strip().lower()
def is_phrase_in(self, text):
result = []
phrase_list = self.phrase.split()
phrase = " ".join(phrase_list)
for punct in string.punctuation:
if punct in text:
text = text.replace(punct, " ")
text_list = text.lower().split()
text = " ".join(text_list)
result.append(re.search(fr'\b{phrase}\b', text))
if None in result:
return False
return True
class DescriptionTrigger(PhraseTrigger):
def evaluate(self, story):
return self.is_phrase_in(story.get_description())
class AndTrigger(Trigger):
def __init__(self, trigger1, trigger2):
self.trigger1 = trigger1 # should be class object
self.trigger2 = trigger2 # should be class object
def evaluate(self, story):
return self.trigger1.evaluate(story) and self.trigger2.evaluate(story)
Here is the function where I instantiate the classes objects:
lines = ['t2,DESCRIPTION,Hillary Clinton', 't3,DESCRIPTION,Donald Trump', 't5,AND,t2,t3']
func_dict = {'AND': AndTrigger,
'DESCRIPTION': DescriptionTrigger} # to map the keyword to class constructor
trigger_dict = {} # to store all created class objects
for line in lines:
split = line.split(',')
if split[1] != 'AND':
trigger_dict[split[0]] = func_dict[split[1]](split[2])
if split[1] == 'AND':
trigger_dict[split[0]] = func_dict[split[1]](trigger_dict[split[2]], trigger_dict[split[3]])
So, I have a dictionary called func_dict where I store the class constructor as the value to its corresponding key (keyword which I will use to search in lines). I then iterate over lines and instantiate the class objects depending on the second element of each line. If the second element is 'AND', I wanted to create a new object of AndTrigger with class object t2 and t3 as arguments.
trigger1 and trigger2 (see AndTrigger class definition) should be class object but I initially instantiated it like so:
trigger_dict[split[0]] = func_dict[split[1]](split[2], split[3])
here, split[2] is 't2' and split[3] is 't3' from here 't5,AND,t2,t3'. They are of type string.
Instead, I should have parsed the instantiated class object stored in trigger_dict as the argument.
trigger_dict[split[0]] = func_dict[split[1]](trigger_dict[split[2]], trigger_dict[split[3]])
Hope that helped!
I struggle to understand how to assign a value to PersonProfile from a class method when PersonProfile inherits from PersonBase.
class PersonBase:
def __init__(self, contact_no=None, email=None, house=None, category=None, near=None, house_number=None, road=None,
unit=None, level=None, staircase=None, entrance=None, po_box=None, postcode=None, suburb=None,
city_district=None, city=None, island=None, state_district=None, state=None, country_region=None,
country=None, world_region=None):
self.contact_no = contact_no
self.email = email
self.house = house
self.category = category
self.near = near
self.house_number = house_number
self.road = road
self.unit = unit
self.level = level
self.staircase = staircase
self.entrance = entrance
self.po_box = po_box
self.postcode = postcode
self.suburb = suburb
self.city_district = city_district
self.city = city
self.island = island
self.state_district = state_district
self.state = state
self.country_region = country_region
self.country = country
self.world_region = world_region
class PersonProfile(PersonBase):
#classmethod
def from_string(cls, full_address):
"""
Takes raw, unformatted address string and creates a PersonProfile class out of it.
:param full_address: unformatted address coming from a DB
:type full_address: str
:return: PersonProfile class
:rtype: PersonProfile
"""
if full_address == "":
# return empty PersonProfile cls with all its defaults
return cls(PersonProfile)
elif full_address is None:
raise TypeError("Provided object must be of type string")
# extract phone numbers
_contact_no = PersonProfile.find_phone_no(full_address)
if len(_contact_no) != 0:
cls.contact_no = ",".join(_contact_no)
for c in _contact_no:
full_address = full_address.replace(c, '')
return cls()
So when I try to assign a phone number to PersonProfile.contact_no using cls.contact_no = ",".join(_contact_no) it doesn't seem to have any effect. What is a correct way to do so?
Also, at the end of that class method how would I return all values including the one I've overwritten during classmethod execution? I'm using return cls() but doesn't seem to be working either.
I'd like to make it work like so:
p1 = PersonProfile.from_string("(+22) 936107349")
print(p1.contact_no)
--> (+22) 936107349
print(p1.city)
--> None
You want to provide the parsed data to the __init__ method; by calling cls(**key_word_arguments), the dictionary of kwargs returned by the staticmethod parse_full_address is passed the constructor.
I suggest that you write a parser, and validators for the data separate from the class factory, and pass to the factory arguments that have been thoroughly extracted.
class PersonProfile(PersonBase):
#classmethod
def from_string(cls, full_address):
"""
Takes raw, unformatted address string and creates a PersonProfile class out of it.
:param full_address: unformatted address coming from a DB
:type full_address: str
:return: PersonProfile class
:rtype: PersonProfile
"""
kwargs = PersonProfile.parse_full_address(full_address)
return cls(**kwargs)
#staticmethod
def parse_full_address(full_address):
"""parses the full_address and returns the parsed data as a dictionary
currently a stub that returns test data
"""
return {'contact_no': '(+22) 936107349', 'email': 'a#a.com', 'house': 'Ze Mansion', 'city': 'Dummy Data'}
p1 = PersonProfile.from_string("(+22) 936107349")
print(p1.contact_no)
print(p1.city)
I have a class Node with a function defined
class Node(object):
def __init__(self, index, state = None, input = None, directed_neighbours=False):
"""
Parameters
----------
index : int
Node index. Must be unique in the graph.
"""
self._input = input
self.state = state
#self._status = 'active'
self._index = int(index)
self._neighbours = set()
self._port_count = 0
self._ports = []
if directed_neighbours:
self._predecessors = set()
self._successors = self._neighbours
self._directed_neighbours = True
else:
self._successors = self._neighbours
self._predecessors = self._neighbours
self._directed_neighbours = False
#property
def setStatus(self, status):
self._status = status
I have another function
def init(node):
node.setStatus('active')
Now, I have a class
class DistAlgo:
def __init__(self, name, initFunc, states, messages, sendFunc, receiveFunc, stoppingCheck):
self.name = name
#self.inputGraph = inputGraph
self.initFunc = initFunc
self.states = states
self.messages = messages
self.sendFunc = sendFunc
self.receiveFunc = receiveFunc
self.comm_round = 0
self.stoppingCheck = stoppingCheck
def run(self, inputGraph):
for node in inputGraph.nodes:
print('hello', node)
node.state = self.initFunc(node)
<....more code...>
When I create an object of DistAlgo
myalgo = DistAlgo('BMM', init, states, messages, send, receive, stoppingCheck)
and then call its run function:
myalgo.run(problemGraph)
I get an error in the init function above, as:
TypeError: setStatus() missing 1 required positional argument: 'status'
I surely am doing more than one thing wrong I guess, as this is my first Python try. Please point them out!
Properties work a bit differently:
#property
def status(self):
return self._status
#status.setter
def status(self, status):
self._status = status
Now you can set the value with an assignment:
node.status = 'active'
I have an object (Person) that has multiple subobjects (Pet, Residence) as properties. I want to be able to dynamically set the properties of these subobjects like so:
class Person(object):
def __init__(self):
self.pet = Pet()
self.residence = Residence()
class Pet(object):
def __init__(self,name='Fido',species='Dog'):
self.name = name
self.species = species
class Residence(object):
def __init__(self,type='House',sqft=None):
self.type = type
self.sqft=sqft
if __name__=='__main__':
p=Person()
setattr(p,'pet.name','Sparky')
setattr(p,'residence.type','Apartment')
print p.__dict__
Currently I get the wrong output: {'pet': <__main__.Pet object at 0x10c5ec050>, 'residence': <__main__.Residence object at 0x10c5ec0d0>, 'pet.name': 'Sparky', 'residence.type': 'Apartment'}
As you can see, instead of setting the name attribute on the Pet subobject of the Person, a new attribute pet.name is created on the Person.
I cannot specify person.pet to setattr() because different sub-objects will be set by the same method, which parses some text and fills in the object attributes if/when a relevant key is found.
Is there a easy/builtin way to accomplish this?
Or perhaps I need to write a recursive function to parse the string and call getattr() multiple times until the necessary subobject is found and then call setattr() on that found subobject?
You could use functools.reduce:
import functools
def rsetattr(obj, attr, val):
pre, _, post = attr.rpartition('.')
return setattr(rgetattr(obj, pre) if pre else obj, post, val)
# using wonder's beautiful simplification: https://stackoverflow.com/questions/31174295/getattr-and-setattr-on-nested-objects/31174427?noredirect=1#comment86638618_31174427
def rgetattr(obj, attr, *args):
def _getattr(obj, attr):
return getattr(obj, attr, *args)
return functools.reduce(_getattr, [obj] + attr.split('.'))
rgetattr and rsetattr are drop-in replacements for getattr and setattr,
which can also handle dotted attr strings.
import functools
class Person(object):
def __init__(self):
self.pet = Pet()
self.residence = Residence()
class Pet(object):
def __init__(self,name='Fido',species='Dog'):
self.name = name
self.species = species
class Residence(object):
def __init__(self,type='House',sqft=None):
self.type = type
self.sqft=sqft
def rsetattr(obj, attr, val):
pre, _, post = attr.rpartition('.')
return setattr(rgetattr(obj, pre) if pre else obj, post, val)
def rgetattr(obj, attr, *args):
def _getattr(obj, attr):
return getattr(obj, attr, *args)
return functools.reduce(_getattr, [obj] + attr.split('.'))
if __name__=='__main__':
p = Person()
print(rgetattr(p, 'pet.favorite.color', 'calico'))
# 'calico'
try:
# Without a default argument, `rgetattr`, like `getattr`, raises
# AttributeError when the dotted attribute is missing
print(rgetattr(p, 'pet.favorite.color'))
except AttributeError as err:
print(err)
# 'Pet' object has no attribute 'favorite'
rsetattr(p, 'pet.name', 'Sparky')
rsetattr(p, 'residence.type', 'Apartment')
print(p.__dict__)
print(p.pet.name)
# Sparky
print(p.residence.type)
# Apartment
For an out of the box solution, you can use operator.attrgetter:
from operator import attrgetter
attrgetter(dotted_path)(obj)
For one parent and one child:
if __name__=='__main__':
p = Person()
parent, child = 'pet.name'.split('.')
setattr(getattr(p, parent), child, 'Sparky')
parent, child = 'residence.type'.split('.')
setattr(getattr(p, parent), child, 'Sparky')
print p.__dict__
This is simpler than the other answers for this particular use case.
unutbu's answer (https://stackoverflow.com/a/31174427/2683842) has a "bug". After getattr() fails and is replaced by default, it continues calling getattr on default.
Example: rgetattr(object(), "nothing.imag", 1) should equal 1 in my opinion, but it returns 0:
getattr(object(), 'nothing', 1) == 1.
getattr(1, 'imag', 1) == 0 (since 1 is real and has no complex component).
Solution
I modified rgetattr to return default at the first missing attribute:
import functools
DELIMITER = "."
def rgetattr(obj, path: str, *default):
"""
:param obj: Object
:param path: 'attr1.attr2.etc'
:param default: Optional default value, at any point in the path
:return: obj.attr1.attr2.etc
"""
attrs = path.split(DELIMITER)
try:
return functools.reduce(getattr, attrs, obj)
except AttributeError:
if default:
return default[0]
raise
This should be a
def getNestedAttr(obj,nestedParam):
next = obj
for p in nestedParam.split('.'):
next = getattr(next,p)
return next
class Issue : pass
issue = Issue()
issue.status = Issue()
issue.status.name = "Hello"
getattr(issue,'status.name')
'''
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Issue' object has no attribute 'status.name'
'''
getNestedAttr(issue,'status.name')
#'Hello'
simple solution
I made a simple version based on ubntu's answer called magicattr that also works on attrs, lists, and dicts by parsing and walking the ast.
For example, with this class:
class Person:
settings = {
'autosave': True,
'style': {
'height': 30,
'width': 200
},
'themes': ['light', 'dark']
}
def __init__(self, name, age, friends):
self.name = name
self.age = age
self.friends = friends
bob = Person(name="Bob", age=31, friends=[])
jill = Person(name="Jill", age=29, friends=[bob])
jack = Person(name="Jack", age=28, friends=[bob, jill])
You can do this
# Nothing new
assert magicattr.get(bob, 'age') == 31
# Lists
assert magicattr.get(jill, 'friends[0].name') == 'Bob'
assert magicattr.get(jack, 'friends[-1].age') == 29
# Dict lookups
assert magicattr.get(jack, 'settings["style"]["width"]') == 200
# Combination of lookups
assert magicattr.get(jack, 'settings["themes"][-2]') == 'light'
assert magicattr.get(jack, 'friends[-1].settings["themes"][1]') == 'dark'
# Setattr
magicattr.set(bob, 'settings["style"]["width"]', 400)
assert magicattr.get(bob, 'settings["style"]["width"]') == 400
# Nested objects
magicattr.set(bob, 'friends', [jack, jill])
assert magicattr.get(jack, 'friends[0].friends[0]') == jack
magicattr.set(jill, 'friends[0].age', 32)
assert bob.age == 32
It also won't let you/someone call functions or assign a value since it doesn't use eval or allow Assign/Call nodes.
with pytest.raises(ValueError) as e:
magicattr.get(bob, 'friends = [1,1]')
# Nice try, function calls are not allowed
with pytest.raises(ValueError):
magicattr.get(bob, 'friends.pop(0)')
And a easy to understand three-liner based on jimbo1qaz's answer, reduced to the very limit:
def rgetattr(obj, path, default):
try:
return functools.reduce(getattr, path.split(), obj)
except AttributeError:
return default
Usage:
>>> class O(object):
... pass
... o = O()
... o.first = O()
... o.first.second = O()
... o.first.second.third = 42
... rgetattr(o, 'first second third', None)
42
Just keep in mind that "space" is not a typical delimiter for this use case.
Thanks for the accepted answer above. It was helpful.
In case anyone wants to extend the use for hasattr use the code below:
def rhasattr(obj, attr):
_nested_attrs = attr.split(".")
_curr_obj = obj
for _a in _nested_attrs[:-1]:
if hasattr(_curr_obj, _a):
_curr_obj = getattr(_curr_obj, _a)
else:
return False
return hasattr(_curr_obj, _nested_attrs[-1])
Ok so while typing the question I had an idea of how to do this and it seems to work fine. Here is what I came up with:
def set_attribute(obj, path_string, new_value):
parts = path_string.split('.')
final_attribute_index = len(parts)-1
current_attribute = obj
i = 0
for part in parts:
new_attr = getattr(current_attribute, part, None)
if current_attribute is None:
print 'Error %s not found in %s' % (part, current_attribute)
break
if i == final_attribute_index:
setattr(current_attribute, part, new_value)
current_attribute = new_attr
i+=1
def get_attribute(obj, path_string):
parts = path_string.split('.')
final_attribute_index = len(parts)-1
current_attribute = obj
i = 0
for part in parts:
new_attr = getattr(current_attribute, part, None)
if current_attribute is None:
print 'Error %s not found in %s' % (part, current_attribute)
return None
if i == final_attribute_index:
return getattr(current_attribute, part)
current_attribute = new_attr
i += 1
I guess this solves my question, but I am still curious if there is a better way to do this?
I feel like this has to be something pretty common in OOP and python, so I'm surprised gatattr and setattr do not support this natively.
Here's something similar to ChaimG's answer, but it works with an arbitrary number of cases. However, it only supports get attributes, not setting them.
requested_attr = 'pet.name'
parent = Person()
sub_names = requested_attr.split('.')
sub = None
for sub_name in sub_names:
try:
sub = parent.__getattribute__(sub_name)
parent = sub
except AttributeError:
raise Exception("The panel doesn't have an attribute that matches your request!")
pets_name = sub
I just love recursive functions
def rgetattr(obj,attr):
_this_func = rgetattr
sp = attr.split('.',1)
if len(sp)==1:
l,r = sp[0],''
else:
l,r = sp
obj = getattr(obj,l)
if r:
obj = _this_func(obj,r)
return obj
I know this post is pretty old but below code might help some one.
def getNestedObjectValue(obj={}, attr=""):
splittedFields = attr.split(".")
nestedValue = ""
previousValue = ""
for field in splittedFields:
previousValue = nestedValue
nestedValue = (
obj.get(field) if previousValue == "" else previousValue.get(field)
)
return nestedValue
print(
getNestedObjectValue(
obj={
"name": "ADASDASD",
"properties": {"somefield": {"value": "zxczxcxczxcxzc"}},
},
attr="properties.somefield.value",
)
)
Output
PS C:\myprograms\samples> python .\sample.py
zxczxcxczxcxzc
Here is my class:
class ManagementReview(object):
"""Class describing ManagementReview Object.
"""
# Class attributes
id = 0
Title = 'New Management Review Object'
fiscal_year = ''
region = ''
review_date = ''
date_completed = ''
prepared_by = ''
__goals = [] # List of <ManagementReviewGoals>.
__objectives = [] # List of <ManagementReviewObjetives>.
__actions = [] # List of <ManagementReviewActions>.
__deliverables = [] # List of <ManagementReviewDeliverable>.
__issues = [] # List of <ManagementReviewIssue>.
__created = ''
__created_by = ''
__modified = ''
__modified_by = ''
def __init__(self,Title='',id=0,fiscal_year='',region='',review_date='',
date_completed='',prepared_by='',created='',created_by='',
modified='',modified_by=''):
"""Instantiate object.
"""
if id:
self.setId(id)
if Title:
self.setTitle(Title)
if fiscal_year:
self.setFiscal_year(fiscal_year)
if region:
self.setRegion(region)
if review_date:
self.setReview_date(review_date)
if date_completed:
# XXX TODO: validation to see if date_completed pattern matches ISO-8601
self.setDate_completed(date_completed)
if prepared_by:
self.setPrepared_by(prepared_by)
if created:
# XXX TODO: validation to see if date_completed pattern matches ISO-8601
self.setCreated(created)
else:
self.setCreated(self.getNow())
if created_by:
self.setCreated_by(created_by)
self.__modified = self.getNow()
if modified_by:
self.__modified_by = modified_by
def __str__(self):
return "<ManagementReview '%s (%s)'>" % (self.Title,self.id)
def __setattr__(self, name, value): # Override built-in setter
# set the value like usual and then update the modified attribute too
object.__setattr__(self, name, value) # Built-in
self.__dict__['__modified'] = datetime.now().isoformat()
def getActions(self):
return self.__actions
def addAction(self,mra):
self.__actions.append(mra)
def removeAction(self,id):
pass # XXX TODO
I have this test:
from datetime import datetime
import random
import unittest
from ManagementReview import ManagementReview, ManagementReviewAction
# Default Values for ManagementReviewAction Object Type
DUMMY_ID = 1
DUMMY_ACTION = 'Action 1'
DUMMY_OWNER = 'Owner 1'
DUMMY_TITLE = 'Test MR'
DUMMY_FISCAL_YEAR = '2011'
DUMMY_REGION = 'WO'
DUMMY_REVIEW_DATE = '2009-01-18T10:50:21.766169',
DUMMY_DATE_COMPLETED = '2008-07-18T10:50:21.766169'
DUMMY_PREPARED_BY = 'test user'
DUMMY_CREATED = '2002-07-18T10:50:21.766169'
DUMMY_CREATED_BY = 'test user 2'
DUMMY_MODIFIED = datetime.now().isoformat()
DUMMY_MODIFIED_BY = 'test user 3'
class TestManagementReviewSetAction(unittest.TestCase):
def setUp(self):
self.mr = ManagementReview(DUMMY_TITLE,DUMMY_ID,fiscal_year=DUMMY_FISCAL_YEAR,region=DUMMY_REGION,
review_date=DUMMY_REVIEW_DATE,date_completed=DUMMY_DATE_COMPLETED,
prepared_by=DUMMY_PREPARED_BY,created=DUMMY_CREATED,
created_by=DUMMY_CREATED_BY,modified=DUMMY_MODIFIED,
modified_by=DUMMY_MODIFIED_BY)
def tearDown(self):
self.mr = None
def test_add_action(self):
for i in range(1,11):
mra = ManagementReviewAction(i,'action '+str(i),'owner '+str(i))
self.mr.addAction(mra)
self.assertEqual(len(self.mr.getActions()),10)
def test_remove_action(self):
print len(self.mr.getActions())
for i in range(1,11):
mra = ManagementReviewAction(i,'action '+str(i),'owner '+str(i))
self.mr.addAction(mra)
self.mr.removeAction(3)
self.assertEqual(len(self.mr.getActions()),9)
if __name__ == '__main__':
unittest.main()
The first test passes. That is, self.mr.getActions() has 10 actions.
However, when I run the 2nd test, test_remove_action, the value for len(self.mr.getActions()) is 10. At this point, though, it should be 0.
Why is this?
Thanks
see if you are keeping track of actions in a class attribute of ManagementReview as opposed to an instance attribute
A class attribute will be something like
class Spam(object):
actions = []
and an instance attribute will look something like
class Spam(object):
def __init__(self):
self.actions = []
What happens is that actions = [] is executed when the class is created and all instances of the class share the same list.
EDIT:
In light of your update, I can see that this is definitely what is going on