How to get class instance using assigned "id" variable? - python

Is it possible to get the wooden_sword object using the id variable in the Item class?
class Item:
__ids = count(0)
def __init__(self):
self.id = next(self.__ids)
class Weapon(Item):
def __init__(self, **kwargs):
super().__init__(**kwargs)
wooden_sword = Weapon()

Have the __init__ of Item store to a shared (class attribute) WeakValueDictionary and you can do lookup that way from an alternate constructor (classmethod):
import weakref
class Item:
id_to_item = weakref.WeakValueDictionary()
__ids = count(0)
def __init__(self):
self.id = next(self.__ids)
self.id_to_item[self.id] = self
#classmethod
def from_id(cls, id):
return cls.id_to_item[id]
Item.from_id can raise an exception (probably KeyError like a normal dict; test it) if the object corresponding to that id has been garbage collected; using a plain dict would avoid that issue, though it risks memory "leaks" (not a real leak; the object is available, but might never be used again).

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: Storing class type on a class variable durig class initialization

I'm trying to initialize an objects field with a class that needs to know the type that is using it:
class Device(Model):
objects = AbstractManager(Device)
# the rest of the class here
This is how AbstractManager is defined:
class AbstractManager:
def __init__(self, cls: type):
self.cls = cls
def all(self):
result = []
for cls in self._get_subclasses():
result.extend(list(cls.objects.all()))
return result
def _get_subclasses(self):
return self.cls.__subclasses__()
So I can later call this and returns all() from all subclasses:
Device.objects.all()
The issue here is that I cannot use Device while initializing Device.objects, since Device is still not initialized.
As a work-around I'm initializing this outside of the class, but there's gotta be a better way:
class Device(Model):
objects = None
# the rest of the class here
Device.objects = AbstractManager(Device)
PD: I have a C#/C++ background, so maybe I'm thinking too much about this in a static-typing mindset, can't tell
You don't need to add any additional logic for this. Django allows you to access model class from manager using self.model attribute:
def _get_subclasses(self):
return self.model.__subclasses__()
You do not have to do that. Django will automatically call the contribute_to_class method, where it will pass the model, and for a manager, it will be stored in self.model. You can thus simply implement this as:
from django.db.models.manager import ManagerDescriptor
class AbstractManager(models.Manager):
def all(self):
result = []
for cls in self._get_subclasses():
result.extend(list(cls.objects.all()))
return result
def contribute_to_class(self, model, name):
self.name = self.name or name
self.model = model
setattr(model, name, AbstractManagerDescriptor(self))
model._meta.add_manager(self)
def _get_subclasses(self):
return self.model.__subclasses__()
class AbstractManagerDescriptor(ManagerDescriptor):
def __get__(self, instance, cls=None):
if instance is not None:
raise AttributeError("Manager isn't accessible via %s instances" % cls.__name__)
if cls._meta.swapped:
raise AttributeError(
"Manager isn't available; '%s.%s' has been swapped for '%s'" % (
cls._meta.app_label,
cls._meta.object_name,
cls._meta.swapped,
)
)
return cls._meta.managers_map[self.manager.name]
and add the manager as:
class Device(models.Model):
objects = AbstractManager()
That being said, I'm not sure that this is a good idea for two reasons:
you are returning a list, and normally .all() returns a QuerySet, you thus here "destroy" the laziness of the queryset, which can result in expensive querying; and
if one would use Device.objects.filter() for example, it would simply circumvent.
You might want to subclass the queryset, and then aim to implement that differently.

Inheritance in Python, init method overrriding

I'm trying to understand inheritance in Python. I have 4 different kind of logs that I want to process: cpu, ram, net and disk usage
I decided to implement this with classes, as they're formally the same except for the log file reading and the data type for the data. I have a the following code (log object is a logging object instance of a custom logging class)
class LogFile():
def __init__(self,log_file):
self._log_file=log_file
self.validate_log()
def validate_log(self):
try:
with open(self._log_file) as dummy_log_file:
pass
except IOError as e:
log.log_error(str(e[0])+' '+e[1]+' for log file '+self._log_file)
class Data(LogFile):
def __init__(self,log_file):
LogFile.__init__(self, log_file)
self._data=''
def get_data(self):
return self._data
def set_data(self,data):
self._data=data
def validate_data(self):
if self._data == '':
log.log_debug("Empty data list")
class DataCPU(Data):
def read_log(self):
self.validate_log()
reading and writing to LIST stuff
return LIST
class DataRAM(Data):
def read_log(self):
self.validate_log()
reading and writing to LIST stuff
return LIST
class DataNET(Data):
Now I want my DataNET class to be a object of the Data Class with some more attributes, in particular a dictionary for every one of the interfaces. How can I override the __init__() method to be the same as the Data.__init__() but adding self.dict={} without copying the Data builder? This is, without explicitly specifing the DataNet objects do have a ._data attribute, but inherited from Data.
Just call the Data.__init__() method from DataNET.__init__(), then set self._data = {}:
class DataNET(Data):
def __init__(self, logfile):
Data.__init__(self, logfile)
self._data = {}
Now whatever Data.__init__() does to self happens first, leaving your DataNET initializer to add new attributes or override attributes set by the parent initializer.
In Python 3 classes are already new-style, but if this is Python 2, I'd add object as a base class to LogFile() to make it new-style too:
class LogFile(object):
after which you can use super() to automatically look up the parent __init__ method to call; this has the advantage that in a more complex cooperative inheritance scheme the right methods are invoked in the right order:
class Data(LogFile):
def __init__(self,log_file):
super(Data, self).__init__(log_file)
self._data = ''
class DataNET(Data):
def __init__(self, logfile):
super(DataNET, self).__init__(logfile)
self._data = {}
super() provides you with bound methods, so you don't need to pass in self as an argument to __init__ in that case. In Python 3, you can omit the arguments to super() altogether:
class Data(LogFile):
def __init__(self,log_file):
super().__init__(log_file)
self._data = ''
class DataNET(Data):
def __init__(self, logfile):
super().__init__(logfile)
self._data = {}
Use new style classes (inherit from object) - change definition of LogFile to:
class LogFile(object):
and init method of Data to:
def __init__(self, log_file):
super(Data, self).__init__(log_file)
self._data = ''
Then you can define DataNET as:
class DataNET(Data):
def __init__(self, log_file):
super(DataNET, self).__init__(log_file)
self.dict = {}

Dividing Python properties to categories

I have a class which contains a large amount of properties. More specifically, the class represents my router.
I want to divide its properties to categories, meaning that in order to retrieve the LAN ip and the WAN ip I would not have to type:
router.wan_ip
router.lan_ip
But instead type:
router.wan.ip
router.lan.ip
The properties are dynamic and retrieved when calling their functions. My current implementation:
class Category(object):
def __init__(self, parent):
self._parent = parent
class Lan(Category):
#property
def ip(self):
self._parent._get_property("lanip")
class Wan(Category):
#property
def ip(self):
self._parent._get_property("wanip")
class Router(object):
def __init__(self, ):
self.lan = Lan(self)
self.wan = Wan(self)
def _get_property(self, property_name):
# Some code here
But I wounder if there is a better way
You are probably overthinking this structure, but if you want objects...
Wan and Lan should not exist. You can pass a prefix for the Category class so it will search for ...ip. The _get_property method could be replaced by a dictionary. If you need to calculate the values on the fly, you may create a dict with functions to be called.
class Category(object):
def __init__(self, parent, prefix):
self.parent = parent
self.prefix = prefix
#property
def ip(self):
self.parent.data[self.prefix + "ip"]
class Router(object):
def __init__(self, data):
self.lan = Category(self, 'lan')
self.wan = Category(self, 'wan')
self.data = data
By the way, this program doesn't need to have any class at all. Seems like you just need dictionaries. You can create a function to build these dictionaries if needed
router = {'wan': {'ip': '1.2.3.4'}, 'lan': {'ip': '2.3.4.5'}}
You can use __getattr__ that will allow you to access properties that don't really exist by that name, by examining the name of the property, and mapping it to an actual property.
I think you could use descriptors [1] for an unique Category class.
[1] http://docs.python.org/reference/datamodel.html#descriptors

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