How to dynamically compose and access class attributes in Python? [duplicate] - python

This question already has answers here:
How to access (get or set) object attribute given string corresponding to name of that attribute
(3 answers)
Closed 3 years ago.
I have a Python class that have attributes named: date1, date2, date3, etc.
During runtime, I have a variable i, which is an integer.
What I want to do is to access the appropriate date attribute in run time based on the value of i.
For example,
if i == 1, I want to access myobject.date1
if i == 2, I want to access myobject.date2
And I want to do something similar for class instead of attribute.
For example, I have a bunch of classes: MyClass1, MyClass2, MyClass3, etc. And I have a variable k.
if k == 1, I want to instantiate a new instance of MyClass1
if k == 2, I want to instantiate a new instance of MyClass2
How can i do that?
EDIT
I'm hoping to avoid using a giant if-then-else statement to select the appropriate attribute/class.
Is there a way in Python to compose the class name on the fly using the value of a variable?

You can use getattr() to access a property when you don't know its name until runtime:
obj = myobject()
i = 7
date7 = getattr(obj, 'date%d' % i) # same as obj.date7
If you keep your numbered classes in a module called foo, you can use getattr() again to access them by number.
foo.py:
class Class1: pass
class Class2: pass
[ etc ]
bar.py:
import foo
i = 3
someClass = getattr(foo, "Class%d" % i) # Same as someClass = foo.Class3
obj = someClass() # someClass is a pointer to foo.Class3
# short version:
obj = getattr(foo, "Class%d" % i)()
Having said all that, you really should avoid this sort of thing because you will never be able to find out where these numbered properties and classes are being used except by reading through your entire codebase. You are better off putting everything in a dictionary.

For the first case, you should be able to do:
getattr(myobject, 'date%s' % i)
For the second case, you can do:
myobject = locals()['MyClass%s' % k]()
However, the fact that you need to do this in the first place can be a sign that you're approaching the problem in a very non-Pythonic way.

OK, well... It seems like this needs a bit of work. Firstly, for your date* things, they should be perhaps stored as a dict of attributes. eg, myobj.dates[1], so on.
For the classes, it sounds like you want polymorphism. All of your MyClass* classes should have a common ancestor. The ancestor's __new__ method should figure out which of its children to instantiate.
One way for the parent to know what to make is to keep a dict of the children. There are ways that the parent class doesn't need to enumerate its children by searching for all of its subclasses but it's a bit more complex to implement. See here for more info on how you might take that approach. Read the comments especially, they expand on it.
class Parent(object):
_children = {
1: MyClass1,
2: MyClass2,
}
def __new__(k):
return object.__new__(Parent._children[k])
class MyClass1(Parent):
def __init__(self):
self.foo = 1
class MyClass2(Parent):
def __init__(self):
self.foo = 2
bar = Parent(1)
print bar.foo # 1
baz = Parent(2)
print bar.foo # 2
Thirdly, you really should rethink your variable naming. Don't use numbers to enumerate your variables, instead give them meaningful names. i and k are bad to use as they are by convention reserved for loop indexes.
A sample of your existing code would be very helpful in improving it.

to get a list of all the attributes, try:
dir(<class instance>)

I agree with Daenyth, but if you're feeling sassy you can use the dict method that comes with all classes:
>>> class nullclass(object):
def nullmethod():
pass
>>> nullclass.__dict__.keys()
['__dict__', '__module__', '__weakref__', 'nullmethod', '__doc__']
>>> nullclass.__dict__["nullmethod"]
<function nullmethod at 0x013366A8>

Related

Python class is defined, everything is defined but I can't figure out a print method to print it all out

It's set up in a class function with variables like this.
Season_1 = AHS('yada', 'yada', 'yada')
Season_2 = AHS('yada', 'yada', 'yada')
Etc... Through 9 seasons.
What I can't figure out is how to set up a print method to print all of them out instead of
Print(season_1.yada)
Print(season_2.yada)
What can I do to make it simpler?
enter link description here here is a link for better context. I'm a python and stack noob sorry for that :/
You need to define a function that takes a list of seasons:
def print_yada(seasons):
for s in seasons:
print(s.yada)
print_yada([season_1, season_2])
As mentioned in the comments, start with a list rather than a bunch of numbered variable names.
seasons = [
AHS('yada', 'yada', 'yada'),
AHS('yada', 'yada', 'yada'),
AHS('yada', 'yada', 'yada')
]
print_yada(seasons)
There won't be a single AHS method that can do this, because you aren't dealing with a single instance of AHS, but rather a collection of them.
I would suggest putting your seasons in a list, instead of individual variables
seasons = [AHS('foo'), AHS('bar'), ...]
Then you can loop through them
for season in seasons:
print(season.yada)
As others have said, you probably require an actual list, persumably within your class, if it provides any functionality on the members above what built-in Python collections already do. If not, you don't need a class.
If, on the other hand, "yada", "yada", "yada" are actually fixed members that define what your class is, and you are basically looking for a way to print an object of such a class, you can define the __str__ member method of your class to create a readable output format for your objects, persumably referring to their members.
__str__() should return a string that is used when an object of your class is converted to one, such as when printing.
It has no parameters aside from the mandatory instance parameter.
E.g.:
class MyClass:
def __init__(self, a, b):
self.a, self.b = (a, b)
def __str__(self):
return f"My members are: {self.a}, {self.b}"
print(MyClass(3, "abc"))
My members are: 3, abc

Variable supporting class like attribute assignments

I was wondering if there was some kind of python variable (which isn't a custom made class) which would support the following code :
a = some_creation_procedure
a.variable_a = 1
a.variable_b = 2
a.variable_c = 3
print ("{}, {}, {}".format(a.variable_a, a.variable_b, a.variable_c))
output -
[1, 2, 3]
I could probably create a custom class and support this with "get_attribute" function, but I was wondering if there was a built-in support for this.
Motivation:
I want to debug a certain function within a class (Which requires a lot of operations / variables for initliazing), so I want to create a sub-class instance which has variables corresponding to that specific function (and send it as self for that specific function).
for example :
class some_class():
def __init__(var1, var2, var3, var4 , ....):
do_a()
do_b()
and so on...
def minimal_func(self):
print (self.var1)
my_variable.var1 = "a"
some_class.minimal_func(my_variable)
The simplest way is a class without anything inside it:
class Namespace: pass
a = Namespace()
a.variable_a = 1
a.variable_b = 2
a.variable_c = 3
print ("{}, {}, {}".format(a.variable_a, a.variable_b, a.variable_c))
prints:
1, 2, 3
as #James said in the comments:
Instances of class objects can have attributes assigned to them on the fly. There are ways to restrict attribute assignment as well, but by default, you can just assign anything
As #Aran-Frey pointed out, you can also use the types.SimpleNamespace class instead of of the above empty class:
import types
a = types.SimpleNamespace()
This also allows you to add attributes in the constructor:
import types
a = types.SimpleNamespace(
variable_a=1,
variable_b=2,
variable_c=3)
Also it has a nice __repr__ function:
print(a)
prints:
namespace(variable_a=1, variable_b=2, variable_c=3)

How can I create n numbers of instances for a class?

I am trying to write a code whereby I can set a variable, say n, to create n numbers of instances for that particular class. The instances have to be named 'Node_1', 'Node_2'...'Node_n'. I've tried to do this in several ways using the for loop, however I always get the error: 'Can't assign to operator.'
My latest effort is as follows:
class C():
pass
for count in range(1,3):
"node"+str(count)=locals()["C"]()
print(node)
I understand that the "node" + str(count) is not possible, but I don't see how I can solve this issue.
Any help on the matter will be greatly appreciated.
You could do what you're trying to do, but it's a really bad idea. You should either use a list or a dict; since you seem to want the names to be nodeX, and starting from 1, you should use a dict.
nodes = {'node{}'.format(x): C() for x in range(1, 3)}
Depending on what you're doing, you could also use a defaultdict.
from collections import defaultdict
nodes = defaultdict(C)
print(nodes['node1'])
nodes['node2'].method()
print(nodes['anything-can-go-here'])
Once you're doing that though, there's no need for the 'node' prefix.
The best pattern for creating several similar objects is a list comprehension:
class C():
pass
nodes = [C() for i in range(3)]
This leaves you with three objects of class C, stored in a list called nodes. Access each object in the normal way, with indexing (e.g. nodes[0]).
You're trying to assign a value to a string. You can write Node_1 = C(), but "Node_1" = C() is meaningless, as "Node_1" is a string literal, not an identifier.
It's a little sketchy, but you can use the locals() dictionary to access the identifiers by name:
for count in range(1, 3):
locals()["node" + str(count)] = C()
...and, having done that, you can then use node1 and node2 as if they were defined explicitly in your code.
Typically, however, it's preferable to not access your locals this way, rather you should probably be using a separate dictionary of your own creation that stands on its own and contains the values there:
nodes = {}
for count in range(1, 3):
nodes[count] = C()
... and the values can then be accessed like so: nodes[1], nodes[2], etc.
What I like to do, to keep a registry of all the instances of a class:
class C(object):
instances = {}
def __new__(cls, *args, **kwargs):
instance = super(C, cls).__new__(cls, *args, **kwargs)
instance_name = 'node_{}'.format(len(cls.instances))
cls.instances[instance_name] = instance
return instance
if __name__ == '__main__':
for _ in range(3):
C()
print C.instances
OrderedDict([('node_0', <main.C object at 0x10c3fe8d0>), ('node_1', <main.C object at 0x10c4cb610>), ('node_2', <main.C object at 0x10c4e04d0>)])

Need help on creating object with class

I need help on creating an object (a sequence of numbers) in respect to some parameters of a class. Lets say I typed in to the Python IDLE shell:
SuperLotto = make_lottery_set_type('SuperLotto', 6, (1,50))
#means user can create a 'SuperLotto' with 6 numbers in range of 1 to 50
It would make 'SuperLotto' as a new class instance of a class called 'LotteryGameType'.
This is using the code so far:
class LotterySetError(Exception):
pass
def make_lottery_set_type(name:str, size:int, minmax:tuple):
if minmax[0] > minmax[1]:
raise LotterySetError('Illegal range for tuple')
else:
name = LotteryGameType(name, size, minmax[0], minmax[1])
return name
class LotteryGameType:
def __init__(self, name, set_size, min_set_number, max_set_number):
self.name = name
self.set_size = set_size
self.min_set_number = min_set_number
self.max_set_number = max_set_number
I want to be able to create a sequence of numbers and storing it for later use so I can use it with things like overload operators (e.g. eq and ne).
I want to be able to type into the Python IDLE shell:
SuperLotto([3, 4, 19, 23, 46, 27])
This would create an object under the parameters of SuperLotto, if not under parameters of 'SuperLotto' (say more than 6 numbers), it would raise an error. Any approach would be fine. Does anyone have any ideas on how to approach this?
It sounds like what you want is for make_lottery_set_type to return a new class, presumably one that's a subclass of LotteryGameType, rather than returning an instance of that type.
This is actually pretty easy to do in Python. Class definitions are just normal code, that you can run anywhere, even in the middle of a function. And they have access to the local environment while they're running. And classes themselves are "first-class values", meaning you can pass them around and return them from functions. So:
def make_lottery_set_type(name:str, size:int, minmax:tuple):
if minmax[0] > minmax[1]:
raise LotterySetError('Illegal range for tuple')
else:
class NewLotteryGameType(LotteryGameType):
def __init__(self, numbers):
super().__init__(name, size, minmax[0], minmax[1])
self.numbers = numbers
return NewLotteryGameType
If you want to add other methods, that's the same as adding methods to any other class. For example:
def make_lottery_set_type(name:str, size:int, minmax:tuple):
if minmax[0] > minmax[1]:
raise LotterySetError('Illegal range for tuple')
else:
class NewLotteryGameType(LotteryGameType):
def __init__(self, numbers):
super().__init__(name, size, minmax[0], minmax[1])
self.numbers = numbers
def __eq__(self, rhs):
return set(self.numbers) == set(rhs.numbers)
return NewLotteryGameType
So:
>>> SuperLotto = make_lottery_set_type('SuperLotto', 6, (1,50))
>>> super1 = SuperLotto([1,2,3,4,5,6])
>>> super2 = SuperLotto([6,5,4,3,2,1])
>>> super3 = SuperLotto([7,8,9,10,11,12])
>>> super1 == super2
True
>>> super1 == super3
False
(Obviously you can define __eq__ however you want, if set-equality isn't the right rule for your use.)
If you try to inspect the values you're generating, they don't look quite as pretty as you might like. For example, you'd probably rather see SuperLotto rather than NewLotteryGameType in places like this:
>>> super1
<__main__.NewLotteryGameType at 0x10259e490>
>>> SuperLotto.__name__
'NewLotteryGameType'
For that, just add NewLotteryGameType.__name__ = name. You might also want to copy over the docstring from the parent class, or various other things.
More generally, look at functools.update_wrapper (which is designed for wrapping up functions, not classes, but many of the details are the same) for inspiration, and the inspect module docs from your Python version for all of the attributes that classes can have.
In a comment, you ask:
The only problem is that I want NewLotteryGameType to inherit the parameters such as name, set_size, min_set_number, max_set_number from LotteryGameType. So lets say I wanted to type in NewLotteryGameType.set_size in to the Python Shell. I want it to return back to me 6.
That's contradictory. If you want to inherit the instance attributes of LotteryGameType… well, you already do. For example:
>>> super1.set_size
6
If you want them to be accessible off the class, then they can't be instance attributes, they have to be class attributes. And just changing set_size to a class attribute of LotteryGameType and inheriting it won't work, because the whole point of a class attribute is that the same value shared by all instances of the class or any of its subclasses, and the subclasses all need different values.
But you could do something like this:
class LotteryGameType:
def __init__(self, min_set_number, max_set_number):
self.min_set_number = min_set_number
self.max_set_number = max_set_number
def make_lottery_set_type(lottery_name:str, size:int, minmax:tuple):
if minmax[0] > minmax[1]:
raise LotterySetError('Illegal range for tuple')
else:
class NewLotteryGameType(LotteryGameType):
name = lottery_name
set_size = size
def __init__(self, numbers):
super().__init__(minmax[0], minmax[1])
self.numbers = numbers
def __eq__(self, rhs):
return set(self.numbers) == set(rhs.numbers)
return NewLotteryGameType
(Notice that I had to rename the first make_ parameter to lottery_name so it was different from the class attribute name, because of the way scopes work.) Now, name and set_size are not instance attributes, nor are they class attributes of LotteryGameType—but they're class attributes of each NewLotteryGameType. So:
>>> SuperLotto = make_lottery_set_type('SuperLotto', 6, (1,50))
>>> SuperDuperLotto = make_lottery_set_type('SuperDuperLotto', 8, (1,100))
>>> SuperLotto.set_size
6
>>> SuperDuperLotto.set_size
8
What if you create instances of those types? Well, Python looks for attributes in the instance, then in the most-derived class, and then the base classes. So as long as you don't create instance attributes with the same name (notice that I removed the extra params, and the code that set instance attributes, from the LotteryGameType.__init__ method), it does just what you'd want:
>>> super1 = SuperLotto([1,2,3,4,5,6])
>>> super1.set_size
6
>>> duper1 = SuperDuperLotto([1,2,3,4,5,6,7,8])
>>> duper1.set_size
8
Of course this means that LotteryGameType is no longer a usable type on its own; only its subclasses are usable. But that's probably what you wanted anyway, right? You could even consider making it explicitly an abstract base class to make sure nobody accidentally tries to use a direct LotteryGameType instance.
If you're feeling brave, you might want to read up on metaclasses and see how you could adapt this whole design into use a LotteryGameMetaclass, so each new class is an instance of that metaclass instead of a subclass of the (abstract) base class. The source for the new enum module in 3.4, or the near-equivalent external flufl.enum package, might make good sample code. Then you can play with both and see how similar and how different they are.

Python simple naked objects

What's the easiest way to create a naked object that I can assign attributes to?
The specific use case is: I'm doing various operations on a Django object instance, but sometimes the instance is None (there is on instance). In this case I'd like to create the simplest possible fake object such that I can assign values to its attributes (eg. myobject.foo = 'bar').
Basically I'm looking for the Python equivalent of this piece of Javascript:
myobject = {}
myobject.foo = 'bar'
I know I can use a mock object/library for this, but I'm hoping for a very simple solution (as simple as the Javascript above). Is there a way to create a naked object instance? Something like:
myobject = object()
myobject.foo = 'bar'
You need to create a simple class first:
class Foo(object):
pass
myobject = Foo()
myobject.foo = 'bar'
You can make it a one-liner like this:
myobject = type("Foo", (object,), {})()
myobject.foo = 'bar'
The call to type functions identically to the previous class statement.
If you want to be really minimal...
myobject = type("", (), {})()
The key is that the built-in types (such as list and object) don't support user-defined attributes, so you need to create a type using either a class statement or a call to the 3-parameter version of type.
If you're using Python >= 3.3 you could always use SimpleNamespace; which is included in the Python types module.
SimpleNamespace is great because you also get a repr and equivalency testing for free; both of which might come in handy even for a minimalist object.
Translating the JavaScript in the OP’s question would look like:
from types import SimpleNamespace
myobject = SimpleNamespace() # myobject = {}
myobject.foo = 'bar'
You can also use keyword arguments when instantiating SimpleNamespace. These arguments will become attributes on the instantiated SimpleNamespace:
p = SimpleNamespace(name='gary')
p.age = 32
p # => namespace(age=32, name='gary')
So a quick and easy way to turn a dictionary into a SimpleNamespace object —provided the dictionary keys are proper identifiers— is as simple as:
d = {
'name': 'gary',
'age': 33 # had a birthday.
}
p = SimpleNamespace(**d)
Python >= 3.7 has dataclasses which are basically “mutable named tuples”. This could be something you may want to use if you have a lot of data objects.
Use the Bunch module:
sudo pip install bunch
A bunch is a dictionary that allows to access its content via the dict.key syntax.
And then like that:
from bunch import Bunch
b = Bunch()
b.foo = "Bar"
b["foo2"] = "Bar2"
print b
>> Bunch(foo='Bar', foo2='Bar2')
b["foo"] = "Baz"
print b
>> Bunch(foo='Baz', foo2='Bar2')
I'm coming here very late, but I'm surprised nobody has mentioned namedtuples, which accomplish this kind of thing:
Foo = namedtuple('Foo', ['x'])
f = Foo(x='myattribute')
f.x
For Python 3,
class Obj: pass
o = Obj()
o.name = 'gary'
o.age = 32
o
# <__main__.Obj at 0x17235ca65c0>
o.__dict__
# {'name': 'gary', 'age': 32}
class NakedObject(object):
pass
myobject = NakedObject()
myobject.foo = 'bar'
Functions can have attributes in Python 3. Compared to a naked class, you can save one whole line of code.
naked = lambda: None
naked.foo = 'bar'
You would need to subclass object first like this...
class Myobject(object):
pass
myobject1 = Myobject()
myobject1.foo = 'bar'
Perhaps you are looking for something like this:
myobject={}
myobject['foo']='bar'
then it can be called like:
print myobject['foo']
or you could use a class object for this:
class holder(object):
pass
then you can use something like this:
hold=holder()
hold.myobject='bar'
print hold.myobject
You should probably just use a dict, as per #PsychicOak's answer.
However, if you really want an object you can manipulate, try:
class FooClass(object): pass
You can then assign attributes on FooClass itself, or on instances, as you wish.
I usually prefer to create a null object for my class:
class User(Model):
username = CharField()
password = CharField()
NONE_USER = User(username='', password='')
Then I use it where I would use your naked object.
In some cases extending a dict can help you
like:
class SpecificModelData(dict):
pass
...
class Payload(dict):
... enter code here
why a dict? it works nicely together with serializers.
Why new class? - it gives you a name and a new type

Categories