I have a project with a class "ListObject" containing a list and a string attribute "_quality" explaining the list content. The string attribute is calculated through a private method of the class called "_update_quality(self)". The list is populated with instances of an object "Element": it has an int attribute "_value" that can be changed through the public method "add(self, value)" that takes an int value and adds it to _value.
ListObject has a public method "change_item(self, index, value)" that let the user add an int value to the Element instance at the given index.
You can read and run the code below to see how it works.
class Element:
def __init__(self):
self._value = 0
def add(self, value):
self._value += value
def __str__(self):
return str(self._value)
class ListObject:
def __init__(self):
self._list = list()
self._quality = str()
self._list.append(Element())
self._list.append(Element())
self._list.append(Element())
self._update_quality()
def __getitem__(self, item):
return self._list[item]
def _update_quality(self):
self._quality = str()
for item in self._list:
self._quality += str(item)
def change_item(self, index, value):
self._list[index].add(value)
self._update_quality()
#property
def quality(self):
return self._quality
o = ListObject()
print(o.quality)
o.change_item(0, 5)
print(o.quality)
What I'd like to implement is a getitem method inside ListObject that works like "change_item". The method should automatically call "_update_quality". This should be the code:
o = ListObject()
print(o.quality)
o[0].add(5)
print(o.quality)
and it should generate the same output as the previous one.
Does anyone knows how I can achieve this?
Related
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]
...
In Python, is there are method of using a reference to an object as a key to a dictionary?
Lets say I have a class as follows:
class Example:
def __init__(self, id):
self.id = id
def __hash__(self):
return hash(self.id)
def __eq__(self, other):
if self.id == other.id:
return True
return False
Then, if I create an instance of my class:
object = Example("My object")
Is there any way in Python to use the object object as a key to a dictionary, WITHOUT defining the __hash__ and __eq__ methods?
As it stands now, if I do:
instances = {object: 1}
the later decide that I want to change the id of object
object.id = "Not my object"
I want the dictionary to "know" I changed the id, which it will not with this implementation, because the key is not a reference to the object itself. Is there any way of getting around this in Python?
Thank you!
Why not just add a method to change the keyname to a dict?
For example:
class Example(dict):
def change(self, name, new_name):
self[new_name] = self.pop(name)
return self
x = Example({'hi':'Hello'})
print(x)
x.change('hi','hello')
print(x)
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)
I have the following class:
class temp_con():
def __init__(self):
self.t = 0
#property
def t(self):
return self.t
#t.setter
def t(self,value):
self.t = value
I need to use it to compare against a number following this logic:
if num <= temp_con.t - 2:
#dothing
However i get the error:
Type error: unsupported operand type for -: 'property' and 'int'<
I have tried int(temp_con.t) and getattr(temp_con, t) but those did not work.
How can I utilize the property as an int?
You need to use separate names for the property and the attribute it wraps. A good convention is to use the property name prefixed with a _ as the attribute name.
class TempCon:
def __init__(self):
self._t = 0
#property
def t(self):
return self._t
#t.setter
def t(self, value):
self._t = value
Then you can access the property on an instance of the class.
temp_con = TempCon()
print(temp_con.t)
temp_con.t = 5
print(temp_con.t)
You're accessing t on CLASS, not on an OBJECT of CLASS.
Try:
q = temp_con()
if num <= q.t - 2:
pass
In you code temp_con.t returns property object, which wraps getter (and setter) you've defined in your class code, but it doesnt execute it.
UPDATE: (memo: read twice)
There's also another problem with your code. First (well, it's second in code, but it will happen first) you define getter t, then later you OVERWRITE it with self.t = 0. As a result you'll get (as t) property accessible as a class member (which happens in your example) and value 0 as object's member.
You need an instance of the class in order to use the property and, as pointed out in other answers, you need to use a different name for your object variable. Try:
class temp_con():
def __init__(self):
self._t = 0
#property
def t(self):
return self._t
#t.setter
def t(self,value):
self._t = value
my_temp_con = temp_con()
if num <= my_temp_con.t - 2:
pass
Thus, to access the value of the property and not the property function, you have to access it through my_temp_con.t.
Assume I were given the following class:
class foo(object):
def __init__(self, int):
self.count = int
def increment(self, int):
return foo(int + 1)
def decrement(self, int):
return foo(int - 1)
My goal is to chain together function calls to reach the result I want without having to assign each object to a variable. For instance, I know I can do this:
obj = foo(0)
obj = obj.increment(obj.count)
obj = obj.decrement(obj.count)
obj = obj.increment(obj.count)
obj = obj.decrement(obj.count)
print obj.count
0
but I would like to be able to do this:
finalcount = obj(0).increment(?.count).decrement(?.count)
but I don't know if there is something that I can put in place of ? to refer to the object who's method is being called since that object hasn't been assigned a name.
Your object doesn't really contribute anything in your current code. Note how the methods in your class don't refer to any object state — they never use self. Better would be to omit the int parameter entirely, and use self.count instead:
class foo(object):
def __init__(self, int):
self.count = int
def increment(self):
return foo(self.count + 1)
def decrement(self):
return foo(self.count - 1)
Then, what you wanted to write becomes this:
finalcount = foo(0).increment().decrement()
But, to answer your original question, there is no way to refer to the "current" object in the chain of calls. If you want to refer to an intermediate object, you must assign it to a variable.