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
Related
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).
class A:
def __init__(self, *args, **kwargs):
for item in ["itemA", "itemB"]:
setattr(self, item, property(lambda : self.__get_method(item)))
def __get_method(self, item):
# do some stuff and return result
# this is pretty complex method which requires db lookups etc.
return result
I am trying to come up with above example to create class properties during init. Items list will get bigger in the future and don't want to add #property every time new entry is added.
However can't get the result from property but object location.
a = A()
a.itemA # returns <property at 0x113a41590>
Initially it was like this and realized this could be better.
class A:
#property
def itemA(self):
return self.__get_method("itemA")
#property
def itemX(self):
...
# and so on
How could I add new properties just by adding new entries to the items list and the class itself will create accessor for it?
Additional to #juanpa.arrivillaga comment.
You also can implement __getattr__ method
For example:
class A:
def __getattr__(self, name):
#make everybody happy
How do you access an instance in an object and pass it to another 'main' object? I'm working with a parser for a file that parses different tags, INDI(individual), BIRT(event), FAMS(spouse), FAMC(children)
Basically there are three classes: Person, Event, Family
class Person():
def __init__(self, ref):
self._id = ref
self._birth : None
def addBirth(self, event):
self._birth: event
class Event():
def __init__(self, ref):
self._id = ref
self._event = None
def addEvent(self, event):
self._event = event
#**event = ['12 Jul 1997', 'Seattle, WA'] (this is generated from a function outside a class)
I want to transfer self._event from the Event class into addBirth method to add it into my person class. I have little knowledge on how classes and class inhertiances work. Please help!
If I understand your question, you want to pass an (for example) Event object to an instance of Person?
Honestly, I don't understand the intent of your code, but you probably just need to pass self from one class instance to the other class instance.
self references the current instance.
class Person:
def __init__(self):
self._events = []
def add_event(self, event)
self._events.append(event)
class Event:
def add_to_person(self, person):
person.add_event(self)
The most proper way to handle situations like this is to use getter and setter methods; data encapsulation is important in OO programming. I don't always see this done in Python where I think it should, as compared to other languages. It simply means to add methods to your classes who sole purpose are to return args to a caller, or modify args from a caller. For example
Say you have class A and B, and class B (caller) wants to use a variable x from class A. Then class A should provide a getter interface to handle such situations. Setting you work the same:
class class_A():
def __init__(self, init_args):
x = 0
def someMethod():
doStuff()
def getX():
return x
def setX(val):
x = val
class class_B():
def init(self):
init_args = stuff
A = class_A(init_args)
x = class_A.getX()
def someOtherMethod():
doStuff()
So if class B wanted the x property of an instance object A of class class_A, B just needs to call the getter method.
As far as passing instances of objects themselves, say if you wanted A to pass an already-created instance object of itself to a method in class B, then indeed, you simply would pass self.
I have the following problem and I need advice on how to solve it the best technically in Python. As I am new to programming I would like to have some advice.
So I will have the following object and they should store something. Here is an example:
object 1: cash dividends (they will have the following properties)
exdate (will store a list of dates)
recorddate (will store a list of dates)
paydate (will store a list of dates)
ISIN (will store a list of text)
object 2: stocksplits (they will have the following prpoerties)
stockplitratio (will be some ration)
exdate(list of dates)
...
I have tried to solve it like this:
class cashDividends(object):
def __init__(self, _gross,_net,_ISIN, _paydate, _exdate, _recorddate, _frequency, _type, _announceddate, _currency):
self.gross = _gross
self.net = _net
self.ISIN = _ISIN
self.paydate = _paydate
self.exdate = _exdate
self.recorddate = _recorddate
self.frequency = _frequency
self.type = _type
self.announceddate = _announceddate
self.currency = _currency
So if I have this I would have to create another class named stockplits and then define an __init__ function again.
However is there a way where I can have one class like "Corporate Actions" and then have stock splits and cashdividends in there ?
Sure you can! In python you can pass classes to other classes.
Here a simple example:
class A():
def __init__(self):
self.x = 0
class B():
def __init__(self):
self.x = 1
class Container():
def __init__(self, objects):
self.x = [obj.x for obj in objects]
a = A()
b = B()
c = Container([a,b])
c.x
[0,1]
If I understood correctly what you want is an object that has other objects from a class you created as property?
class CorporateActions(object):
def __init__(self, aCashDividend, aStockSplit):
self.cashDividend = aCashDividend
self.stockSplit = aStockSplit
myCashDividends = CashDividends(...) #corresponding parameters here
myStockSplit = StockSplit(...)
myCorporateActions = CorporateActions(myCashDividends, myStockSplit)
Strictly speaking this answer isn't an answer for the final question. However, it is a way to make your life slightly easier.
Consider creating a sort-of template class (I'm using this term loosely; there's no such thing in Python) that does the __init__ work for you. Like this:
class KwargAttrs():
def __init__(self, **kwargs):
for k,v in kwargs.items():
setattr(self, k, v)
def _update(self, **kwargs):
args_dict = {k:(kwargs[k] if k in kwargs else self.__dict__[k]) for k in self.__dict__}
self.__dict__.update(args_dict)
This class uses every supplied keyword argument as an object attribute. Use it this way:
class CashDividends(KwargAttrs):
def __init__(self, gross, net, ISIN, paydate, exdate, recorddate, frequency, type, announceddate, currency):
# save the namespace before it gets polluted
super().__init__(**locals())
# work that might pollute local namespace goes here
# OPTIONAL: update the argument values in case they were modified:
super()._update(**locals())
Using a method like this, you don't have to go through the argument list and assign every single object attribute; it happens automatically.
We bookend everything you need to accomplish in the __init__ method with method calls to the parent-class via super(). We do this because locals() returns a dict every variable in the function's current namespace, so you need to 1.) capture that namespace before any other work pollutes it and 2.) update the namespace in case any work changes the argument values.
The call to update is optional, but the values of the supplied arguments will not be updated if something is done to them after the call to super().__init__() (that is, unless you change the values using setattr(self, 'argname, value)`, which is not a bad idea).
You can continue using this class like so:
class StockSplits(KwargAttrs):
def __init__(self, stocksplitratio, gross, net, ISIN, paydate, exdate, recorddate, frequency, type, announceddate, currency):
super().__init__(**locals())
As mentioned in the other answers you can create a container for our other classes, but you can even do that using this same template class:
class CorporateActions(KwargAttrs):
def __init__(self, stock_splits , cash_dividends):
super().__init__(**locals())
ca = CorporateActions(stock_splits = StockSplits(<arguments>), cash_dividends = CashDividends(<arguments>) )
I'm building a simulator, which will model various types of entities. So I've got a base class, ModelObject, and will use subclasses for all the different entities. Each entity will have a set of properties that I want to keep track of, so I've also got a class called RecordedDetail, that keeps tracks of changes (basically builds a list of (time_step, value) pairs) and each ModelObject has a dict to store these in. So I've got, effectively,
class ModelObject(object):
def __init__(self):
self.details = {}
self.time_step = 0
def get_detail(self, d_name):
""" get the current value of the specified RecordedDetail"""
return self.details[d_name].current_value()
def set_detail(self, d_name, value):
""" set the current value of the specified RecordedDetail"""
self.details[d_name].set_value(value, self.time_step)
class Widget(ModelObject):
def __init__(self):
super().__init__(self)
self.details["level"] = RecordedDetail()
self.details["angle"] = RecordedDetail()
#property
def level(self):
return self.get_detail("level")
#level.setter
def level(self, value):
self.set_detail("level", value)
#property
def angle(self):
return self.get_detail("angle")
#angle.setter
def angle(self):
self.set_detail("angle", value)
This gets terribly repetitious, and I can't help thinking there must be a way of automating it using a descriptor, but I can't work out how. I end up with
class RecordedProperty(object):
def __init__(self, p_name):
self.p_name = p_name
def __get__(self, instance, owner):
if instance is None:
return self
return instance.get_detail(self.p_name)
def __set__(self, instance, value):
instance.set_detail(self.p_name, value)
class Widget(ModelObject):
level = RecordedProperty("level")
angle = RecordedProperty("angle")
def __init__(self):
super().__init__(self)
self.details["level"] = RecordedDetail()
self.details["angle"] = RecordedDetail()
which is a bit of an improvement, but still a lot of typing.
So, a few questions.
Can I just add the descriptor stuff (__get__, __set__ etc) into the RecordedDetail class? Would there be any advantage to doing that?
Is there any way of typing the new property name (such as "level") fewer than three times, in two different places?
or
Am I barking up the wrong tree entirely?
The last bit of code is on the right track. You can make the process less nasty by using a metaclass to create a named RecordedProperty and a matching RecordedDetail for every item in a list. Here's a simple example:
class WidgetMeta(type):
def __new__(cls, name, parents, kwargs):
'''
Automate the creation of the class
'''
for item in kwargs['_ATTRIBS']:
kwargs[item] = RecordedProperty(item)
return super(WidgetMeta, cls).__new__(cls, name, parents, kwargs)
class Widget(ModelObject):
_ATTRIBS = ['level', 'angle']
__metaclass__ = WidgetMeta
def __init__(self, *args, **kwargs):
super().__init__(self)
self.Details = {}
for detail in self._ATTRIBS:
self.Details[detail] = RecordedDetail()
Subclasses would then just need to have different data in _ATTRIBS.
As an alternative (I think it's more complex) you could use the metaclass to customize the init in the same way you customize the new, creating the RecordedDetails out of the _ATTRIBS list.
A third option would be to create a RecordedDetail in every instance on first access. That would work fine as long as you don't have code that expects a RecordedDetail for every property even if the RecordedDetail has not been touched.
Caveat I'm not super familiar with Python3; I've used the above pattern often in 2.7x