Instantiate an object named in a variable - python

I have a library of component objects. I would like to include instantiations of a selection of those objects in another object. But I would like to provide that selection as a list so that each time I instantiate the container object with a list, it will be created with the specified sub objects in it.
Let's say my component library looks like this:
class ColorBlob(object):
...
def wipeItUp()
...
class RedBlob(ColorBlob):
...
def paintIt()
...
class YellowBlob(ColorBlob):
...
def paintIt()
...
class BlueBlob(ColorBlob):
...
def paintIt()
...
And my container object looks like this:
class Pallet(object):
def __init__(self, colorList):
for color in colorList:
#Ok, here is where I get lost if I know the color I can do this:
Pallet.BlueBlob = blobLib.BlueBlob()
#But I don't, so I am trying to do something like this:
blobSpecs = getattr(blobLib, color)
blobSpecs.Obj = blobSpecs().returnObj(self.page) # with "returnObj" defined in the library as some other method
setattr(self, Pallet.blobName, blobSpecs) #and I am completely lost.
But what I really want to do in my functional code is this:
workingPallet=Pallet(['RedBlob', 'BlueBlob'])
workingPallet.RedBlob.paintIt()
I know that I am lost when I try to instantiate the sub objects in the container. Can someone help me straighten out my "getattr" and "setattr" nonsense?

You were almost there, but it isn't your getattr or setattr that's the problem. You end up setting the class back on self, not the instance you created:
def __init__(self, colorList):
for color in colorList:
blobSpec = getattr(blobLib, color)
blob = blobSpec() # create an instance of the blob
blob.Obj = blob.returnObj(self.page)
setattr(self, color, blob)
It's the same thing as calling the class directly (BlueBlob()), but now through a variable.

Related

Creating a derived dict from a dict

I have a class derived from dict which looks like this
class Instance(dict):
def __init__(self, space, vector, value):
dict.__init__(self, space=space, vector=vector, value=value)
I will occasionally be creating dicts matching the 'schema' for this class, and I would like to just construct Instances out of those dicts for typing purposes. What is an elegant way to do this, without changing the existing constructor?
What I want to be able to do is something along the lines of
reconstructed = Instance(json.loads(data))
I don't really know about elegance, but I needed to do that and I just created a function on the class like so:
def as_simple_dict(self):
simple_dict = {}
simple_dict['id'] = str(self.id)
simple_dict['name'] = self.name
simple_dict['attribute'] = self.attribute
simple_dict['foo'] = self.foo
simple_dict['bar'] = self.bar
And call it whenever I need it.
Hope it helps.

Changing class attributes by reference

I'm relatively new to Python and have problems with immutable variables.
I'm trying to change the value of a class attribute (e.g. car.color). The difficulty is, that I can not use the namespace of car for doing this.
Up to now I did not find a satisvying answer to my questions. In the code below I tried to summarize the possible solutions (workarrounds) I found and their disadvantages:
class Car:
def __init__(self):
self.color = "green"
self.color_list = ["green"]
self.color_attrib = "green"
self.name = "VW Golf"
"""
and many more attributes...
"""
def makesetter(self, attribute):
def set_value(value):
attribute=value
return set_value
def set_color(self, value):
"in this function I directly have access to car.color and can change its value: "
self.color = value
def set_attrib(self, attribute_string, value):
setattr(self,attribute_string,value)
def change_attribute(attribute, value):
"In this function I can not access car.color directly"
attribute=value
def change_attribute_list(attribute, value):
"In this function I can not access car.color directly"
attribute[0] = value
if __name__ == "__main__":
car1 = Car()
change_attribute(car1.color, "red")
print(car1.color) # Color does not change because car1.color is immutable
g = car1.makesetter(car1.color)
g("red")
print(car1.color) # Color does not change because car1.color is immutable
change_attribute_list(car1.color_list, "red")
print(car1.color_list) # Color changes but seems like a workarround
# Disadvantage: in the namespace of car1, the user has to use a list to access a string value "car1.color_list[0]"
car1.set_color("red")
print(car1.color) # Color changes but seems like a workarround
# Disadvantage: Car needs a setter function for each attribute
car1.set_attrib("color_attrib","red")
print(car1.color_attrib) # Color changes but seems like a workarround
# Disadvantage: Attribute has to be passed as string & no auto completion while coding
Actually the function setattr() is internally exactly doing what I want. But it works with a string argument.
I tried to look into this function but it seems to be written in C++.
So do I have to use C++ to solve this problem without a workarround?
Or is there a Pythionic way of doing this?
The problem is you are trying to redefine the value of an instance from outside of the class. Since in __init__ you are defining your variables with self, they are only available for that instance. This is the point of a class - it's what makes them extensible and reusable.
Ideally, you would make a method within the class that would update those attributes, however, if you really need to update the class from an external function, you will have to define it as a class level variable. For instance:
class Car:
def __init__(self):
Car.color = "green"
can now be updated using:
def change_attribute(attribute, value):
"In this function I can not access car.color directly"
Car.color=value
outside of the class because you have not assigned it to one specific instance. Doing this presents a problem, however. Since we don't have a separate instance variable, if we try to re-instantiate the class, we are stuck with what was previously changed, i.e.
if name == "main":
car1 = Car()
car2 = Car()
change_attribute(car1.color, "red")
print(car1.color) # Prints red
print(car2.color) # Prints red
change_attribute(car2.color, "blue")
print(car1.color) # Prints blue
print(car2.color) # Prints blue
This is why classes themselves should be self contained and are meant to be immutable - the instance itself should be changed.

Create multiple classes or multiple objects in Python?

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

How to reference an existing class object with no defined variable?

I'm trying to use a function of a class object to create a new class object and running into problems. Here's the code I have so far:
class Room(object):
def __init__(self, name):
self.name = name
self.N = None
self.E = None
self.S = None
self.W = None
'''relevant code'''
def north(self,room):
self.N = Room(room)
self.N.S = self
def south(self,room):
self.S = Room(room)
self.S.N = self
So I want at least one of these print statements
room1 = Room('room1')
room1.north('room2')
print(room2.S)
print(Room(room2).S)
print(Room('room2').S)
to spit out 'room1', but the first two don't work because room2 as a variable is yet to be defined, and the last one doesn't work because it seems to be creating a new object instead of referencing the existing one, so it just prints the default 'None'.
Does there actually exist a way to reference an existing object with no variable set to it? Or is my only option to do something like this?
def north(self,room):
roomDict[room] = Room(room)
self.N = roomDict[room]
self.N.S = self
Edit: I realize I should probably be calling the new Room's south() function instead of directly changing the S variable, but that seems intuitively like it would cause a loop so I haven't touched it yet.
* Edited based on OP's clarification *
If you have a large number of objects you want to refer to without binding them to variables, dict is the way to go.
You can use #Berci's solution. But note that with that solution, if you already have a room named foo, you can't overwrite it by simply calling Room('foo') again -- doing that will just return the original foo room. To overwrite an existing room you must first do del Room.roomDict['foo'], and then call Room('foo'). This may be something you want, but maybe not.
The implementation below is less fanciful and doesn't require __new__ (in fact, Berci's solution doesn't need __new__ either and can be all done in __init__):
class Room:
registry = {}
def __init__(self, name):
self.registry[name] = self
# the rest of your __init__ code
If you want rooms to be non-overwritable, as they are in Berci's solution, just add two lines:
class Room:
registry = {}
def __init__(self, name):
if name in self.registry:
raise ValueError('room named "{}" already exists'.format(name))
self.registry[name] = self
It's not necessary to nest registry inside Room. You can make it an external dict if you want. The advantage of having the registry as a class attribute is that your Room object can access it as self.registry without knowing its global name. The (slight) disadvantage is that you need to type Room.registry or someroom.registry instead of just, say, registry, every time you access it.
Your dict solution can be brought to work. Use a class level roomDict and a new constructor not to create an already existing object referred by its name:
class Room(object):
roomDict = {}
def __new__(cls, name):
if name in cls.roomDict:
return cls.roomDict[name]
self = object.__new__(cls, name) # here the object is created
cls.roomDict[name] = self
return self
def __init__(self, name):
...
So that you can refer to room2 as Room('room2') afterwards.

Cannot find the list to remove an object

I have just started an exercise where I am supposed to complete a basic 'angrybirds' clone.
I am stuck at the point where I want to remove an object from a list. The list contains all of the obstacles used in-game (boxes).
So if I want to remove a box after it was hit I have to make a method to do that. This fails no matter how I do it.
class spel(object):
def __init__(self):
self.obstacles = [obstacle(50,pos=(200,90)),]
#defines all other stuff of the game
class obstacle(object):
def __init__(self,size,pos):
#defines how it looks like
def break(self):
#methode that defines what happens when the obstacles gets destroyed
spel.obstacles.remove(self)
The error I get is:
AttributeError: 'NoneType' object has no attribute 'obstacles'
After the last line.
Please excuse me for my noob-level, but the point is that I won't ever have to code again after this, so there is no need to explain everything.
You have defined 'spel' as a class, not an object. Thus, you have received an error because Python is trying to find a member 'obstacles' of the spel class, which doesn't exist before the __init__ method of individual spel objects is run.
To associate an object of the spel class with each individual obstacle you create, you could try giving objects of the obstacle class a data member that refers to their associated spel object. The data member can be instantiated in the obstacle class' __init__ function. Like this:
class obstacle(object):
def __init__(self, spel, size, pos):
self.spel = spel
#etc
def break(self):
self.spel.obstacles.remove(self)
Hope that helps.
You haven't instantiated the spel class.
If you want to use a class like that, you have to intantiate( create an instance of) it.
Outside of a class like so:
app = spel() # app is an arbitrary name, could be anything
then you would call it's method like this:
app.obstacles.remove(self)
Or in you're case, from within another class:
self.spel = spel()
self.spel.obstacles.remove(self)
I propose the following:
class spel(object):
obstacles = []
def __init__(self,size,pos):
spel.obstacles.append(obstacle(size,pos))
#defines all other stuff of the game
class obstacle(object):
def __init__(self,size,pos):
self.size = size
self.pos = pos
def brak(self):
#methode that defines what happens when the obstacles gets destroyed
spel.obstacles.remove(self)
from pprint import pprint
a = spel(50,(200,90))
pprint( spel.obstacles)
print
b = spel(5,(10,20))
pprint( spel.obstacles )
print
c = spel(3,None)
pprint( spel.obstacles )
print
spel.obstacles[0].brak()
pprint( spel.obstacles )
return
[<__main__.obstacle object at 0x011E0A30>]
[<__main__.obstacle object at 0x011E0A30>,
<__main__.obstacle object at 0x011E0B30>]
[<__main__.obstacle object at 0x011E0A30>,
<__main__.obstacle object at 0x011E0B30>,
<__main__.obstacle object at 0x011E0AF0>]
[<__main__.obstacle object at 0x011E0B30>,
<__main__.obstacle object at 0x011E0AF0>]

Categories