getattr and setattr on nested subobjects / chained properties? - python

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

Related

Python: Create a single object for a set of arguments to the constructor [duplicate]

I defined a class named Experiment for the results of some lab experiments I am conducting. The idea was to create a sort of database: if I add an experiment, this will be pickled to a db before at exit and reloaded (and added to the class registry) at startup.
My class definition is:
class IterRegistry(type):
def __iter__(cls):
return iter(cls._registry)
class Experiment(metaclass=IterRegistry):
_registry = []
counter = 0
def __init__(self, name, pathprotocol, protocol_struct, pathresult, wallA, wallB, wallC):
hashdat = fn.hashfile(pathresult)
hashpro = fn.hashfile(pathprotocol)
chk = fn.checkhash(hashdat)
if chk:
raise RuntimeError("The same experiment has already been added")
self._registry.append(self)
self.name = name
[...]
While fn.checkhash is a function that checks the hashes of the files containing the results:
def checkhash(hashdat):
for exp in cl.Experiment:
if exp.hashdat == hashdat:
return exp
return False
So that if I add a previously added experiment, this won't be overwritten.
Is it possible to somehow return the existing instance if already existant instead of raising an error? (I know in __init__ block it is not possible)
You can use __new__ if you want to customize the creation instead of just initializing in newly created object:
class Experiment(metaclass=IterRegistry):
_registry = []
counter = 0
def __new__(cls, name, pathprotocol, protocol_struct, pathresult, wallA, wallB, wallC):
hashdat = fn.hashfile(pathresult)
hashpro = fn.hashfile(pathprotocol)
chk = fn.checkhash(hashdat)
if chk: # already added, just return previous instance
return chk
self = object.__new__(cls) # create a new uninitialized instance
self._registry.append(self) # register and initialize it
self.name = name
[...]
return self # return the new registered instance
Try to do it this way (very simplified example):
class A:
registry = {}
def __init__(self, x):
self.x = x
#classmethod
def create_item(cls, x):
try:
return cls.registry[x]
except KeyError:
new_item = cls(x)
cls.registry[x] = new_item
return new_item
A.create_item(1)
A.create_item(2)
A.create_item(2) # doesn't add new item, but returns already existing one
After four years of the question, I got here and Serge Ballesta's answer helped me. I created this example with an easier syntax.
If base is None, it will always return the first object created.
class MyClass:
instances = []
def __new__(cls, base=None):
if len(MyClass.instances) == 0:
self = object.__new__(cls)
MyClass.instances.append(self)
if base is None:
return MyClass.instances[0]
else:
self = object.__new__(cls)
MyClass.instances.append(self)
# self.__init__(base)
return self
def __init__(self, base=None):
print("Received base = %s " % str(base))
print("Number of instances = %d" % len(self.instances))
self.base = base
R1 = MyClass("apple")
R2 = MyClass()
R3 = MyClass("banana")
R4 = MyClass()
R5 = MyClass("apple")
print(id(R1), R1.base)
print(id(R2), R2.base)
print(id(R3), R3.base)
print(id(R4), R4.base)
print(id(R5), R5.base)
print("R2 == R4 ? %s" % (R2 == R4))
print("R1 == R5 ? %s" % (R1 == R5))
It gives us the result
Received base = apple
Number of instances = 2
Received base = None
Number of instances = 2
Received base = banana
Number of instances = 3
Received base = None
Number of instances = 3
Received base = apple
Number of instances = 4
2167043940208 apple
2167043940256 None
2167043939968 banana
2167043940256 None
2167043939872 apple
R2 == R4 ? True
R1 == R5 ? False
Is nice to know that __init__ will be always called before the return of the __new__, even if you don't call it (in commented part) or you return an object that already exists.

Can't loop through dict_keys in #property method in python 3?

So I have the following code:
#property
def mod_list(self) -> List[Modifier]:
mods = []
print(len(self.statuses)) #Prints 0??? Update method prints the actual number when called??? Also means it *is* getting called properly when it's getting accessed
for status in self.statuses: # I've tried calling the keys() method on the dict but that doesn't work either
print("hello") #Doesn't print, indicating that it isn't looping
mods.extend(status.mods) # Note: statuses dict uses StatusEffect objects as keys, with values being the number of turns left before that status is removed; StatusEffects all possess a 'mods' property that is initialized to '[]' and can only be made up of modifiers
return mods
I don't understand why it can't access the keys of the dict? Even if I remove the decorator and call it instead of accessing it?
Especially when this method works properly?
def update(self):
deletion = []
print(len(self.statuses)) #Prints actual number of keys????
for name in self.statuses.keys():
print(name.name, self.statuses[name]) #Prints normally whenever update is called???
if hasattr(name, "turn_effect"):
name.turn_effect(self.entity)
self.statuses[name] -= 1
if self.statuses[name] < 1:
deletion.append(name)
...
for status in deletion:
del self.statuses[status]
Why isn't it working properly? And how do I fix it?
Edit: I managed to recreate the issue below, I think it might have to do with 'deepcopy' in the spawn method since I couldn't recreate the issue from scratch until I implemented and used the spawn method.
from __future__ import annotations
from typing import Dict, List
from copy import copy, deepcopy
class Entity:
def __init__(self, name:str, **kwargs:Component):
self.name = name
self.components:Dict[str, Component] = {}
for name, component in kwargs.items():
self.add_component(name, component)
def add_component(self, name:str, component:Component):
self.components[name] = component
component.entity = self
def update(self):
for comp in self.components.values():
comp.update()
def spawn(self):
return deepcopy(self)
class Component:
__entity: Entity
#property
def entity(self) -> Entity:
return self.__entity
#entity.setter
def entity(self, entity:Entity):
if hasattr(self, "__entity") and self.__entity is not None:
self.entity.remove_component(self)
self.__entity = entity
def update(self):
"""Placeholder method for component update methods"""
class StatusList(Component):
entity: Entity
def __init__(self) -> None:
self.statuses:Dict[StatusEffect, int] = {}
def add_status(self, status:StatusEffect, turns:int=1):
self.statuses[status] = turns
def update(self):
deletion = []
print(len(self.statuses.keys()))
for name in self.statuses.keys():
print(name.name, self.statuses[name])
if hasattr(name, "turn_effect"):
name.turn_effect(self.entity)
self.statuses[name] -= 1
if self.statuses[name] < 1:
deletion.append(name)
for status in deletion:
del self.statuses[status]
#property
def mod_list(self) -> List[Modifier]:
mods = []
print(len(self.statuses))
for status in self.statuses:
print("hello")
mods.extend(status.mods)
return mods
class StatusEffect:
name:str
turn_effect: function
mods:List[Modifier] = []
def apply(self, entity:Entity, turns:int=1):
if "status_list" in entity.components.keys():
entity.components["status_list"].add_status(self.copy(), turns)
def copy(self): #I specifically defined this method in the original code in case I need to modify it in the future
return copy(self)
class StatList(Component):
entity: Entity
stat_record: List[Stat] = []
def __init__(self, **stats:Stat) -> None:
for name, stat in stats.items():
stat.stat_list = self
stat.name = name
self.stat_record.append(stat)
def get_stat(self, name:str) -> Optional[Stat]:
for stat in self.stat_record:
if name == stat.name:
return stat
def get_stat_name(self, stat:Stat) -> Optional[str]:
if stat in record:
return stat.name
class Stat:
name:str
base_value:int
def __init__(self, base:int=0):
self.base_value = base
#property
def entity(self) -> Entity:
return self.stat_list.entity
#property
def current_value(self) -> int:
value = self.base_value
for mod in self.get_modifiers():
value += mod.value
return int(value)
def get_modifiers(self):
for component in self.entity.components.values():
if hasattr(component, "mod_list"):
for mod in component.mod_list:
if mod.stat == self.name:
yield mod
class Modifier:
stat: str
value: Union[int, float]
def __init__(self, stat:str, value:Union[int, float]):
self.stat = stat
self.value = value
rage = StatusEffect()
rage.name = "Rage"
rage.turn_effect = lambda entity : print(f"{entity.name} is enraged")
rage.mods = [
Modifier("atk", 5)
]
player = Entity(
name="Player",
stat_list=StatList(atk=Stat(5)),
status_list=StatusList()
).spawn()
rage.apply(player, 10)
while True:
player.update()
player.components["stat_list"].get_stat("atk").current_value
input()
Unfortunately, using copy() in the spawn method would result in entities created that way sharing status effects, stats, etc., which really defeats the purpose of spawning new entities
Edit 2: Modified spawn method to use copy and to copy all components, have to add guard clauses now but it works.

What to replace xml.dom.minidom with to get something that can pickle efficiently?

I have an application that deals with ~1-2 megabyte XML files. Doesn't sound like much, but I've run into a performance problem nonetheless.
Since I've some compute bound tasks that I'd like to speed up I've tried using multiprocessing.imap to do that - which requires pickling this XML data. Pickling the datastructures containing references into this DOM turns out to be slower than those compute bound processes, and the culprit seems to be recursions - I had to set the recursion limit to 10'000 in order to get pickle to work in the first place :-S.
Anyways, my question is:
If I wanted to attack this problem from the referential performance angle, what should I replace minidom with? Criterias are both pickling performance but also ease of transition.
To give you an idea of what kind of methods are needed, I have pasted a wrapper class (written sometimes earlier in order to speed up getElementsByTagName calls). It would be acceptable to replace all minidom nodes with nodes that adhere to the same interface as this class, i.e. I don't need all the methods from minidom. Getting rid of the parentNode method would also be acceptable (and probably a good idea in order to improve pickling performance).
And yes, if I'd be designing this nowadays I wouldn't go for XML node references in the first place, but it would be a lot of work to rip all of this out now, so I hope this can be patched instead.
Should I just write the damn thing myself using python built-ins or the collections library?
class ImmutableDOMNode(object):
def __init__(self, node):
self.node = node
self.cachedElementsByTagName = {}
#property
def nodeType(self):
return self.node.nodeType
#property
def tagName(self):
return self.node.tagName
#property
def ownerDocument(self):
return self.node.ownerDocument
#property
def nodeName(self):
return self.node.nodeName
#property
def nodeValue(self):
return self.node.nodeValue
#property
def attributes(self):
return self.node.attributes
#property
def parentNode(self):
return ImmutableDOMNode(self.node.parentNode)
#property
def firstChild(self):
return ImmutableDOMNode(self.node.firstChild)
#property
def childNodes(self):
return [ImmutableDOMNode(node) for node in self.node.childNodes]
def getElementsByTagName(self, name):
result = self.cachedElementsByTagName.get(name)
if result != None:
return result
uncachedResult = self.node.getElementsByTagName(name)
cachedResult = [ImmutableDOMNode(node) for node in uncachedResult]
self.cachedElementsByTagName[name] = cachedResult
return cachedResult
def getAttribute(self, qName):
return self.node.getAttribute(qName)
def toxml(self, encoding=None):
return self.node.toxml(encoding)
def toprettyxml(self, indent="", newl="", encoding=None):
return self.node.toprettyxml(indent, newl, encoding)
def appendChild(self, node):
raise Exception("cannot append child to immutable node")
def removeChild(self, node):
raise Exception("cannot remove child from immutable node")
def cloneNode(self, deep):
raise Exception("clone node not implemented")
def createElement(self, tagName):
raise Exception("cannot create element for immutable node")
def createTextNode(self, tagName):
raise Exception("cannot create text node for immutable node")
def createAttribute(self, qName):
raise Exception("cannot create attribute for immutable node")
So I decided to just make my own DOM implementation that meets my requirements, I've pasted it below in case it helps someone. It depends on lru_cache from memoization library for python 2.7 and #Raymond Hettinger's immutable dict from Immutable dictionary, only use as a key for another dictionary. However, these dependencies are easy to remove if you don't mind less safety/performance.
class CycleFreeDOMNode(object):
def __init__(self, minidomNode=None):
if minidomNode is None:
return
if not isinstance(minidomNode, xml.dom.minidom.Node):
raise ValueError("%s needs to be instantiated with a minidom.Node" %(
type(self).__name__
))
if minidomNode.nodeValue and minidomNode.childNodes:
raise ValueError(
"both nodeValue and childNodes in same node are not supported"
)
self._tagName = minidomNode.tagName \
if hasattr(minidomNode, "tagName") else None
self._nodeType = minidomNode.nodeType
self._nodeName = minidomNode.nodeName
self._nodeValue = minidomNode.nodeValue
self._attributes = dict(
item
for item in minidomNode.attributes.items()
) if minidomNode.attributes else {}
self._childNodes = tuple(
CycleFreeDOMNode(cn)
for cn in minidomNode.childNodes
)
childNodesByTagName = defaultdict(list)
for cn in self._childNodes:
childNodesByTagName[cn.tagName].append(cn)
self._childNodesByTagName = ImmutableDict(childNodesByTagName)
#property
def nodeType(self):
return self._nodeType
#property
def tagName(self):
return self._tagName
#property
def nodeName(self):
return self._nodeName
#property
def nodeValue(self):
return self._nodeValue
#property
def attributes(self):
return self._attributes
#property
def firstChild(self):
return self._childNodes[0] if self._childNodes else None
#property
def childNodes(self):
return self._childNodes
#lru_cache(maxsize = 100)
def getElementsByTagName(self, name):
result = self._childNodesByTagName.get(name, [])
for cn in self.childNodes:
result += cn.getElementsByTagName(name)
return result
def cloneNode(self, deep=False):
clone = CycleFreeDOMNode()
clone._tagName = self._tagName
clone._nodeType = self._nodeType
clone._nodeName = self._nodeName
clone._nodeValue = self._nodeValue
clone._attributes = copy.copy(self._attributes)
if deep:
clone._childNodes = tuple(
cn.cloneNode(deep)
for cn in self.childNodes
)
childNodesByTagName = defaultdict(list)
for cn in clone._childNodes:
childNodesByTagName[cn.tagName].append(cn)
clone._childNodesByTagName = ImmutableDict(childNodesByTagName)
else:
clone._childNodes = tuple(cn for cn in self.childNodes)
clone._childNodesByTagName = self._childNodesByTagName
return clone
def toxml(self):
def makeXMLForContent():
return self.nodeValue or "".join([
cn.toxml() for cn in self.childNodes
])
if not self.tagName:
return makeXMLForContent()
return "<%s%s>%s</%s>" %(
self.tagName,
" " + ", ".join([
"%s=\"%s\"" %(k,v)
for k,v in self.attributes.items()
]) if any(self.attributes) else "",
makeXMLForContent(),
self.tagName
)
def getAttribute(self, name):
return self._attributes.get(name, "")
def setAttribute(self, name, value):
self._attributes[name] = value

Adding to a class dynamically

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)

Ordering enum value in python

I would like to be able to arrange the ordering of Enum. Has somebody suggestions how this can be solved?
The following Enum meta class is using:
class EnumMeta(type):
def __new__(typ, name, bases, attrs):
cls_attrs = {}
cls_choices = []
for attr_name, value in attrs.items():
cls_attrs[attr_name] = attr_name.lower()
if not attr_name.startswith("__"):
cls_choices.append((attr_name.lower(), value))
def choices(cls):
return cls_choices
def values(cls, value=None):
if value is None:
return {choice[0]: unicode(choice[1]) for choice in cls.choices()}
elif isinstance(value, list):
return {choice[0]: unicode(choice[1]) for choice in cls.choices() if choice[0] in value}
else:
return unicode(dict(cls.choices()).get(value))
def keys(cls, nil=False):
items = [item[0] for item in cls.choices()]
if nil:
items.append('')
return items
def combined_length(cls):
return len(",".join(cls.values().keys()))
def max_length(cls):
return max(map(len, cls.values().keys()))
cls_attrs['choices'] = classmethod(choices)
cls_attrs['values'] = classmethod(values)
cls_attrs['keys'] = classmethod(keys)
cls_attrs['combined_length'] = classmethod(combined_length)
cls_attrs['max_length'] = classmethod(max_length)
return type(name, bases, cls_attrs)
An example of an Enum is as follow:
class SideHemType:
__ordering__ = ['double', 'single']
__metaclass__ = EnumMeta
Single = "Single side hem for opaque fabrics"
Double = "Double side hem for transparent fabrics"
class TestEnumOrdering:
print SideHemType.keys()
print SideHemType.values()
By printing the Enum SideHemType first Double is printed and then Single. But I would like first Single and then Double.
If you are using Python3.4 you can use the new enum.Enum type, which remembers the order the enum members are declared in.
If you are using an earlier Python, you should use the enum34 package available from PyPI, which supports Pythons back to 2.4.
The enum34 package, if used in Python3, also remembers the order of member declarations. If used in Python 2 it supports an extra _order_ attribute:
from enum import Enum
class SideHemType(Enum):
_order_ = 'Single Double' # only needed in Python 2
Single = "Single side hem for opaque fabrics"
Double = "Double side hem for transparent fabrics"
#classmethod
def combined_length(cls):
return len(",".join(mbr.name for mbr in cls))
#classmethod
def max_length(cls):
return max(map(len, (mbr.name for mbr in cls)))
print list(SideHemType) # [SideHemType.Single, SideHemType.Double]
print SideHemType.Double.value # "Double side hem for transparent fabrics"
Use IntEnum from the enum package and use the integer values to specify the order that you want:
class Shape(IntEnum):
CIRCLE = 1
SQUARE = 2
Shape.CIRCLE < Shape.SQUARE
Prints True.
How about this
class MyEnum(enum.Enum):
first_item = 'bla bla'
whatever = 'blubb'
another_one = 'blobb'
def __lt__(self, other: 'MyEnum'):
if self == other:
return False
# the following works because the order of elements in the definition is preserved
for elem in MyEnum:
if self == elem:
return True
elif other == elem:
return False
raise RuntimeError('Bug: we should never arrive here') # I just like being pedantic
Your Enum loses the ordering in 3 places. First the attributes on the class body are stored in a dictionary, then you copy the items into another dictionary. Finally your values() returns a 3rd dictionary. A dictionary does not save ordering, and it is impossible to get the ordering of the attributes within the class body.
With this system the easiest is to have a variable
__ordering__ = [ 'single', 'double' ]
And make the values() return a list of tuples (like dict.items()).
class EnumMeta(type):
def __new__(typ, name, bases, attrs):
cls_attrs = {}
cls_choices = {}
for attr_name, value in attrs.items():
cls_attrs[attr_name] = attr_name.lower()
if not attr_name.startswith("__"):
cls_choices[attr_name.lower()] = value
ordering = attrs.get('__ordering__')
if ordering == None:
ordering = sorted(cls_choices.keys())
def choices(cls):
return dict(cls_choices)
def values(cls, value=None):
if value is None:
return [ (k, cls_choices[k] ) for k in ordering ]
elif not isinstance(value, basestring):
return [ (k, cls_choices[k] ) for k in value ]
else:
return unicode(cls_choices.get(value))
def keys(cls, nil=False):
items = list(ordering)
if nil:
items.append('')
return items
def combined_length(cls):
return len(",".join(cls.values().keys()))
def max_length(cls):
return max(map(len, cls.values().keys()))
cls_attrs['choices'] = classmethod(choices)
cls_attrs['values'] = classmethod(values)
cls_attrs['keys'] = classmethod(keys)
cls_attrs['combined_length'] = classmethod(combined_length)
cls_attrs['max_length'] = classmethod(max_length)
return type(name, bases, cls_attrs)
class SideHemType:
__ordering__ = ['double', 'single']
__metaclass__ = EnumMeta
Single = "Single side hem for opaque fabrics"
Double = "Double side hem for transparent fabrics"
print SideHemType.keys()
print SideHemType.values()

Categories

Resources