simple class as data container: Howto? - python

I want a class with static attributes that can be stored using one or more get methods from outside and the stored values can be retrieved using one or more get methods
class contract_data:
contract_header = dict()
contract_item = dict()
contract_schedule = dict()
# #staticmethod
def put_header(line:list,findex:dict):
contract_header[line[findex['VBELN_VA']]] = {'KNKLI':[line[findex['KNKLI']]],
'VTWEG':[line[findex['VTWEG']]],
'SPART':[line[findex['SPART']]],
'VKBUR':[line[findex['VKBUR']]],
'VKGRP':[line[findex['VKGRP']]],
'BSTKD':[line[findex['BSTKD']]]
}
def get_header(keyval:str)->dict:
return contract_header[keyval]
# #staticmethod
def put_item(line: list, findex: dict):
return
#staticmethod
def put_schedule(line: list, findex: dict):
return
I expected that calling contract_data.put_header(line,findex) I could store values in contract_data attribute contract_header. But it fails with runtime error
in put_header:
contract_header[line[findex['VBELN_VA']]] = {'KNKLI':[line[findex['KNKLI']]],
NameError: name 'contract_header' is not defined. Did you mean: 'contract_data'?
I played around with #staticmethod and .self or self. with no success.
I expect the class attributes, the dictionaries can be used within the class but not outside.

Your dicts are not global variables; they're class attributes, and as such need to be accessed from the class. That means your static methods need to be defined as class methods.
class contract_data:
contract_header = dict()
contract_item = dict()
contract_schedule = dict()
#classmethod
def put_header(cls, line: list, findex: dict):
cls.contract_header[line[findex['VBELN_VA']]] = {
k: [line[findex[k]]]
for k in ['KNKLI', 'VTWEB', 'SPART', 'VKBUR', 'VKGRP', 'BSTKD']}
#classmethod
def get_header(cls, keyval: str)->dict:
return cls.contract_header[keyval]
...

Related

How to return class variable as a dict?

I am trying to write a method in my class and trying to return as dict.
class Test(object):
location = 'Dhaka'
#classmethod
def get_items(cls):
items = dict()
for item in cls:
items[item.name] = item.value
return items
class NewTest(Test):
lat = 4444
and I am trying to get the result following this:
print(NewTest.get_items())
{'location': 'Dhaka', 'lat': 4444}
But returning error like that type can't be iterated.
You can look through the contents of applying dir() to the class to get all its attributes, but need some way to filter out things with special names while looking for class variables. A fairly fast way to do that would be with regular expression pattern matching to recognize them. You'll also need filter out callable values like the get_items() classmethod.
Doing both is illustrated below.
import re
DUNDER = re.compile(r'^__[^\d\W]\w*__\Z', re.UNICODE) # Special method names.
class Test(object):
location = 'Dhaka'
#classmethod
def get_items(cls):
is_special = DUNDER.match # Local var to speed access.
items = {}
for name in dir(cls):
if not is_special(name):
value = getattr(cls, name)
if not callable(value):
items[name] = value
return items
class NewTest(Test):
lat = 4444
print(NewTest.get_items()) # -> {'lat': 4444, 'location': 'Dhaka'}
The class can't be iterated; its __dict__ attribute can, though.
#classmethod
def get_items(cls):
items = dict()
# Loop defined enums
for item in cls.__dict__:
if item == "__module__":
continue
items[item] = cls.__dict__[item]
# return in tuple
return items
You'll want to do some filtering on the keys of the dict, as the class may have additional attributes aside from the ones you create.

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.........>

Dynamically generate method from string?

I have a dict of different types for which I want to add a simple getter based on the name of the actual parameter.
For example, for three storage parameters, let's say:
self.storage = {'total':100,'used':88,'free':1}
I am looking now for a way (if possible?) to generate a function on the fly with some meta-programming magic.
Instead of
class spaceObj(object):
def getSize(what='total'):
return storage[what]
or hard coding
#property
def getSizeTotal():
return storage['total']
but
class spaceObj(object):
# manipulting the object's index and magic
#property
def getSize:
return ???
so that calling mySpaceObj.getSizeFree would be derived - with getSize only defined once in the object and related functions derived from it by manipulating the objects function list.
Is something like that possible?
While certainly possible to get an unknown attribute from a class as a property, this is not a pythonic approach (__getattr__ magic methods are rather rubyist)
class spaceObj(object):
storage = None
def __init__(self): # this is for testing only
self.storage = {'total':100,'used':88,'free':1}
def __getattr__(self, item):
if item[:7] == 'getSize': # check if an undefined attribute starts with this
return self.getSize(item[7:])
def getSize(self, what='total'):
return self.storage[what.lower()]
print (spaceObj().getSizeTotal) # 100
You can put the values into the object as properties:
class SpaceObj(object):
def __init__(self, **kwargs):
self.__dict__.update(kwargs)
storage = {'total':100,'used':88,'free':1}
o = SpaceObj(**storage)
print o.total
or
o = SpaceObj(total=100, used=88, free=1)
print o.total
or using __getattr__:
class SpaceObj(object):
def __init__(self, **kwargs):
self.storage = kwargs
def __getattr__(self,name):
return self.storage[name]
o = SpaceObj(total=100, used=88, free=1)
print o.total
The latter approach takes a bit more code but it's more safe; if you have a method foo and someone create the instance with SpaceObj(foo=1), then the method will be overwritten with the first approach.
>>> import new
>>> funcstr = "def wat(): print \"wat\";return;"
>>> funcbin = compile(funcstr,'','exec')
>>> ns = {}
>>> exec funcbin in ns
>>> watfunction = new.function(ns["wat"].func_code,globals(),"wat")
>>> globals()["wat"]=watfunction
>>> wat()
wat

Categories