Extracting specific fields from python object - python

This is a sample python object that I am working with.
class DataObj(object):
def __init__(self, cvid, cvname, address, get_info):
self.cvid = cvid
self.cvname = cvname
self.address = address
self.prof = PROF("Honda", "Jason Jones")
class PROF(object):
def __init__(self, organization, manager_name):
self.organization = organization
self.manager_name = manager_name
self.project_list = [Proj("asd", "asd"), Proj("asdsd", "asdsd")]
class Proj(object):
def __init__(self, projectname, projecttype):
self.projectname = projectname
self.projecttype = projecttype
I need to write a function that takes a list of fields and extract all the fields as key value pair from the DataObj. The trick is it should also look for attributes of object composed inside DataObj class. for example if list of fields is ["cvid", "organization", "projectname"], it should return something like this in the following format
{'cvid' : 'value', 'organization' : 'Honda', Proj :[{'projectname' : 'asd'}, {'projectname' : 'asdsd'}]
Where should I write this function, so my code is more modular? I was thinking about writing it inside DataObj but I wouldn't know what are the attributes of object composed inside DataObj. How to achieve what I am trying to do in more object oriented way?

All I did was simply add __iter__ which basically says hey, you can iterate over me, if you cast the object to an iterabale container type.
class Proj(object):
def __init__(self, projectname, projecttype):
self.projectname = projectname
self.projecttype = projecttype
def __iter__(self):
yield ("projectname", self.projectname)
class PROF(object):
def __init__(self, organization, manager_name):
self.organization = organization
self.manager_name = manager_name
self.project_list = [Proj("asd", "asd"), Proj("asdsd", "asdsd")]
def __iter__(self):
for proj in self.project_list:
yield (dict(proj))
class DataObj(object):
def __init__(self, cvid, cvname, address):
self.cvid = cvid
self.cvname = cvname
self.address = address
self.prof = PROF("Honda", "Jason Jones")
def __iter__(self):
yield ('cvid', self.cvid)
yield ('organization', self.prof.organization)
yield ("Proj", list(self.prof))
do = DataObj("1", "heinst", "A Street, Somewhere, USA")
print dict(do)

Between __getattr__ and operator.attrgetter, you could make this work fairly easily:
class DataObj(object):
def __init__(self, cvid, cvname, address, get_info):
self.cvid = cvid
self.cvname = cvname
self.address = address
self.prof = PROF("Honda", "Jason Jones")
def __getattr__(self, name):
# Called when an attribute is accessed which is not found on DataObj
# You can limit the loop to avoid checking some attributes, or
# remove the loop if only self.prof should be checked
for member in (self.cvid, self.cvname, self.address, self.prof):
try:
return getattr(member, name)
except AttributeError:
pass
raise AttributeError(name)
# If only self.prof should be checked, the function could simplify to:
# return getattr(self.prof, name)
Then you can make a simple utility function that runs against a DataObj to get an arbitrary set of key value pairs from it:
from operator import attrgetter
def extractdata(dataobj, *names):
return dict(zip(names, attrgetter(*names)(dataobj)))
Or as a member of DataObj, just name the first param self to match convention:
def extractdata(self, *names):
return dict(zip(names, attrgetter(*names)(self)))
__getattr__ allows delegation of attribute lookup to contained objects, and attrgetter allows you to retrieve a set of arbitrary attributes in a simple way.

Related

Adding properties dynamically to a Python class that point to items in a dictionary

I am trying to attach properties dynamically to a class (Registry) for the sake of easy access to values in a dict. I am using defaultdict to define the dictionary, with the default value as an empty list.
But because of the way I am accessing the list values in the dictionary while defining the property, I end up with all properties pointing to the same list object.
Gist: https://gist.github.com/subhashb/adb75a3a05a611c3d9193da695d46dd4
from collections import defaultdict
from enum import Enum
class ElementTypes(Enum):
PHONE = "PHONE"
CAR = "CAR"
class Registry:
def __new__(cls, *args, **kwargs):
cls.setup_properties()
instance = super(Registry, cls).__new__(cls, *args, **kwargs)
return instance
def __init__(self):
self._elements = {}
def register(self, element_type, item):
if element_type.value not in self._elements:
self._elements[element_type.value] = []
self._elements[element_type.value].append(item)
def get(self, element_type):
return self._elements[element_type.value]
#classmethod
def setup_properties(cls):
for item in ElementTypes:
prop_name = item.value
prop = property(lambda self: getattr(self, "get")(item))
setattr(Registry, prop_name.lower(), prop)
registry = Registry()
registry.register(ElementTypes.PHONE, "phone1")
registry.register(ElementTypes.PHONE, "phone2")
registry.register(ElementTypes.CAR, "car1")
registry.register(ElementTypes.CAR, "car2")
assert dict(registry._elements) == {
"CAR": ["car1", "car2"],
"PHONE": ["phone1", "phone2"],
}
assert hasattr(registry, "phone")
assert hasattr(registry, "car")
assert registry.car == ["car1", "car2"]
assert registry.phone == ["phone1", "phone2"] # This fails
How do I define the code withing the property to be truly dynamic and get access to the individual list values in the dict?
First, don't setup properties in __new__, that gets called for every Registry object created! Instead, just assign the properties outside the class definition.
Secondly, this trips a lot of people up, but if you use a lambda inside a for-loop and you want to use the item variable, you need to make sure that you add an argument called item with the default value of item, otherwise all the properties will refer to the last item of the loop.
class Registry:
def __init__(self):
self._elements = defaultdict(list)
def register(self, element_type, item):
self._elements[element_type.value].append(item)
def get(self, element_type):
return self._elements[element_type.value]
for item in ElementTypes:
prop_name = item.value
prop = property(lambda self, item=item: self.get(item))
setattr(Registry, prop_name.lower(), prop)

Python object inside other objects are not isolated

I was expecting the object items inside other python objects are isolated.
However, the following code shows my expected result is wrong. It seems python uses a central item_list for all Group items. How can I fix this?
class Item:
name = ''
def __init__(self, name):
self.name = name
class Group:
item_list = []
def __init__(self, item):
self.item_list.append(item)
g2 = Group(Item('bb1'))
g2.item_list.append(Item('bb2'))
group_list = []
group_list.append(Group(Item('aaa')))
group_list.append(g2)
group_list.append(Group(Item('ccc')))
print('len = ', len(group_list[-1].item_list))
print('bb2 = ', group_list[1].item_list[1].name)
Result
len = 4
bb2 = bb1
Version
python3 --version
Python 3.5.2
Well, first of all we should make a difference between class attributes and instance attributes. A class attribute (like item_list) belongs to the class itself (in this case "Group"), so it will be accessible by calling Group.item_list. On the other hand, you can define a item_list for every instance of Group by defining self.item_list = [] inside the Group class constructor (__init__).
The Group.item_list array will be unique for the whole class, and thus will be suitable to store things that you want to share across instances (such as g2).
The self.item_list array (that will be different for each instance of Group) will hold values exclusively for the instance itself, so each Group will have its own item_list.
I think you are aiming for the second approach (instance variables) so you should move the definition of item_list = [] inside the class constructor.
The result should look like this:
class Item:
def __init__(self, name):
self.name = name
class Group:
def __init__(self, item):
self.item_list = []
self.item_list.append(item)
item1 = Item("itemName")
group1 = Group(item1)
# This should print an array containing the *item1* instance
print(group1.item_list)
print(group1.item_list[0] == item1)
Variables that are declared outside of the __init__ method (in this case item_list) are shared between all instances of a class (called class variables), which is why your expected result is wrong.
On the other hand, variables inside the __init__ only belong to the given instance of that class.
Your using class variables, which are similar C++ static variables inside classes (i.e. that variable is shared by ALL class instances). You need to put it inside the __init__ (constructor) to make it so each class creates its own version:
class Item:
def __init__(self, name):
self.name = name
class Group:
def __init__(self, item):
self.item_list = []
self.item_list.append(item)
# Though typically you would also have a function like this:
def add_item(self, item):
self.item_list.append(item)
g2 = Group(Item('bb1'))
g2.item_list.append(Item('bb2'))
group_list = []
group_list.append(Group(Item('aaa')))
group_list.append(g2)
group_list.append(Group(Item('ccc')))
print('len = ', len(group_list[-1].item_list))
print('bb2 = ', group_list[1].item_list[1].name)
Instance vs class attributes is covered in other answers. I want to add that you can avoid having shared instance variables by using an immutable type (e.g. tuple) instead of a mutable type (e.g. list) for class attributes. Like that they won't be shared among instances while still allowing you to define class attributes.
class Item:
name = ''
def __init__(self, name):
self.name = name
class Group:
item_list = tuple()
def __init__(self, item):
self.item_list += (item,)

How is the pythonic way to create a reference to a own python class instance and use it?

I would like to store the instance of a class in a container like a list. Other classes/methods should access this instance.
Below is a code snipped which defines a data point.
class dataPoint(object):
def __init__(self, name, typeUUID, value = None):
self.name = name
self.typeUUID = typeUUID
self.value = value
I like to define a method which gives me the reference (no copy constructor, etc.) to this object. Maybe like this:
def getRef(self):
return ???
These references I like to use in different list. The reference I like to use to set properties/call functions of the data point. Below is some pseudocode:
# define a conatiner with datapoints
myContainer = [dataPoint("temperature","double",273.15), dataPoint("power","double",230), dataPoint("errorcode","uint32",666)]
# define interfaces which refers to the datapoints
interface1 = [ref("temperature"), ref("power")]
interface2 = [ref("errorcode"), ]
interface3 = [ref("temperature"), ref("power"), ref("errorcode")]
# set temperature to 300K
ref("temperature") = 300.0
# interfaces
print (interface1[ref("temperature")]) --> 300K
print (interface3[ref("temperature")]) --> 300K
How to do this in Python and how to do this pythonic?
You could put the "instance-container" in the class itself:
class DataPoint:
instances = {}
def __init__(self, name, typeUUID, value=None):
self.name = name
self.typeUUID = typeUUID
self.value = value
self.instances[name] = self
#classmethod
def get(cls, name):
return cls.instances[name]
Then you can use it like this:
>>> d1 = DataPoint("foo", "12345")
>>> d2 = DataPoint("bar", "67890")
>>> DataPoint.get("foo")
<DataPoint object at 0x.........>

set optional/required property according to condition

I have this:
def required(props):
props.fget.required = True
return props
class SomeClass():
#required
#property
def func(self):
return self.conname("conname")
But I want this: when self.name attribute of SomeClass equale "value" , then make the property optional, when non-equal leave required.
You could handle this in the initializer like so:
class SomeClass(object): # inherit from object if you are using python 2.7
def __init__(self, name, optional_required_property=None):
self.name = name
if name == "value" and optional_required_property is None:
raise AttributeError("optional_required_property cannot be None.")
else:
self.optional_required_property = optional_required_property
test_someclass = SomeClass("test") # will work
test_someclass = SomeClass("value") # will fail
Inspired by Stack Overflow Question: How to implement a required property in Python

python descriptors sharing values across classes

A python descriptor that I'm working with is sharing its value across all instances of its owner class. How can I make each instance's descriptor contain its own internal values?
class Desc(object):
def __init__(self, initval=None,name='val'):
self.val = initval
self.name = name
def __get__(self,obj,objtype):
return self.val
def __set__(self,obj,val):
self.val = val
def __delete__(self,obj):
pass
class MyClass(object):
desc = Desc(10,'varx')
if __name__ == "__main__":
c = MyClass()
c.desc = 'max'
d = MyClass()
d.desc = 'sally'
print(c.desc)
print(d.desc)
The output is this, the last call set the value for both objects:
localhost $ python descriptor_testing.py
sally
sally
There is only one descriptor object, stored on the class object, so self is always the same. If you want to store data per-object and access it through the descriptor, you either have to store the data on each object (probably the better idea) or in some data-structure keyed by each object (an idea I don't like as much).
I would save data on the instance object:
class Desc(object):
default_value = 10
def __init__(self, name):
self.name = name
def __get__(self,obj,objtype):
return obj.__dict__.get(self.name, self.default_value)
# alternatively the following; but won't work with shadowing:
#return getattr(obj, self.name, self.default_value)
def __set__(self,obj,val):
obj.__dict__[self.name] = val
# alternatively the following; but won't work with shadowing:
#setattr(obj, self.name, val)
def __delete__(self,obj):
pass
class MyClass(object):
desc = Desc('varx')
In this case, the data will be stored in the obj's 'varx' entry in its __dict__. Because of how data descriptor lookup works though, you can "shadow" the storage location with the descriptor:
class MyClass(object):
varx = Desc('varx')
In this case, when you do the lookup:
MyClass().varx
The descriptor object gets called and can do its lookup, but when the lookup goes like this:
MyClass().__dict__['varx']
The value is returned directly. Thus the descriptor is able to store its data in a 'hidden' place, so to speak.

Categories